@mnemom/mnemom 0.17.11-next.0 → 0.17.11-next.1

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.
@@ -9,11 +9,14 @@
9
9
  * Checks: settings file, coding-agent CLI, Anthropic key source, gateway health
10
10
  * (200 + the x-mnemom-verdict header that proves the governance pipeline is
11
11
  * live), Remote-Control capability (node + openssl + the --remote-control flag),
12
- * claude.ai login (informational — Remote Control needs it), and CLI auto-update status.
12
+ * claude.ai login (informational — Remote Control needs it), the agent's governance
13
+ * modes (off by default for new agents), and CLI auto-update status.
13
14
  */
14
15
  type Mark = "ok" | "warn" | "miss" | "info";
15
16
  export interface DoctorOptions {
16
17
  cli?: string;
18
+ /** The agent slug a launch would use (--agent / launcher.toml); default agent-<handle>. */
19
+ agent?: string;
17
20
  }
18
21
  /** Run the preflight and print the checklist. Sets process.exitCode on failure. */
19
22
  export declare function agentDoctorCommand(options?: DoctorOptions): Promise<void>;
@@ -29,4 +32,17 @@ export interface CodeAccessProbe {
29
32
  * anything inconclusive → info, never a MISS (the launch preamble fails open too).
30
33
  */
31
34
  export declare function probeCodeAccess(): Promise<CodeAccessProbe>;
35
+ export interface GovernanceProbe {
36
+ mark: Mark;
37
+ text: string;
38
+ hint?: string;
39
+ }
40
+ /**
41
+ * The governance line: the composed (every layer, strictest wins) integrity and
42
+ * Safe House modes of the agent a launch would use. Signed out, no such agent,
43
+ * or an unreadable card → info with the off-by-default note, never a MISS
44
+ * (governance never blocks a launch). On → info naming the per-turn cost; the
45
+ * agent's own card says off but a stricter layer keeps it on → warn.
46
+ */
47
+ export declare function probeGovernance(agent: string | undefined): Promise<GovernanceProbe>;
32
48
  export {};
@@ -9,7 +9,8 @@
9
9
  * Checks: settings file, coding-agent CLI, Anthropic key source, gateway health
10
10
  * (200 + the x-mnemom-verdict header that proves the governance pipeline is
11
11
  * live), Remote-Control capability (node + openssl + the --remote-control flag),
12
- * claude.ai login (informational — Remote Control needs it), and CLI auto-update status.
12
+ * claude.ai login (informational — Remote Control needs it), the agent's governance
13
+ * modes (off by default for new agents), and CLI auto-update status.
13
14
  */
14
15
  import { spawnSync } from "node:child_process";
15
16
  import { existsSync } from "node:fs";
@@ -19,11 +20,12 @@ import { resolveAgentAuth } from "../lib/subscription-login.js";
19
20
  import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost, singleCellError, } from "../lib/agent.js";
20
21
  import { getApiUrl, getGatewayUrl } from "../lib/config.js";
21
22
  import { LAUNCHER_CONFIG_PATH } from "../lib/launcher-config.js";
22
- import { getCodeEntitlement, MnemomApiError } from "../lib/api.js";
23
+ import { getAgentByName, getCardSources, getCodeEntitlement, MnemomApiError } from "../lib/api.js";
23
24
  import { isLoggedIn } from "../lib/auth.js";
24
25
  import { getActiveOrg } from "../lib/cli-config.js";
25
26
  import { isCodeNotEnabled, sourceLabel } from "./agent-invite.js";
26
27
  import { probeUpdateStatus } from "../lib/update/doctor.js";
28
+ import { describeModes, GOVERNANCE_OPT_IN_HINT, resolveDefaultSlug } from "./agent-setup.js";
27
29
  const MARK = { ok: "[ ok ]", warn: "[warn]", miss: "[MISS]", info: "[info]" };
28
30
  const out = (line = "") => console.log(line);
29
31
  /** Run the preflight and print the checklist. Sets process.exitCode on failure. */
@@ -115,6 +117,9 @@ export async function agentDoctorCommand(options = {}) {
115
117
  // ── Mnemom Agent access (invited cohort gate) ──
116
118
  const gate = await probeCodeAccess();
117
119
  add(gate.mark, gate.text, gate.hint);
120
+ // ── governance (off by default for new agents) ──
121
+ const gov = await probeGovernance(options.agent);
122
+ add(gov.mark, gov.text, gov.hint);
118
123
  // ── CLI auto-update ──
119
124
  const upd = await probeUpdateStatus();
120
125
  add(upd.mark, upd.text, upd.hint);
@@ -217,3 +222,74 @@ export async function probeCodeAccess() {
217
222
  return { mark: "info", text: `Mnemom Agent access: could not determine (${detail})` };
218
223
  }
219
224
  }
225
+ const OPT_IN_HINT = GOVERNANCE_OPT_IN_HINT.trim();
226
+ /**
227
+ * The governance line: the composed (every layer, strictest wins) integrity and
228
+ * Safe House modes of the agent a launch would use. Signed out, no such agent,
229
+ * or an unreadable card → info with the off-by-default note, never a MISS
230
+ * (governance never blocks a launch). On → info naming the per-turn cost; the
231
+ * agent's own card says off but a stricter layer keeps it on → warn.
232
+ */
233
+ export async function probeGovernance(agent) {
234
+ const offByDefault = {
235
+ mark: "info",
236
+ text: "governance: off by default for new agents (AIP integrity analysis, Safe House screening)",
237
+ hint: `${OPT_IN_HINT} Agents set up before this default keep their cards.`,
238
+ };
239
+ if (!(await isLoggedIn()))
240
+ return offByDefault;
241
+ try {
242
+ const slug = agent?.trim() || (await resolveDefaultSlug());
243
+ const found = await getAgentByName(slug);
244
+ if (!found)
245
+ return offByDefault;
246
+ const [align, prot] = await Promise.all([
247
+ getCardSources("alignment", found.id),
248
+ getCardSources("protection", found.id),
249
+ ]);
250
+ const read = (s, layer, field) => {
251
+ const v = s?.[layer]?.card_json?.[field];
252
+ return typeof v === "string" && v ? v : undefined;
253
+ };
254
+ // A composed card without the field (or none composed yet) is unknown, not
255
+ // off: the data plane falls back to its own default (observe for integrity),
256
+ // so the doctor never claims governance is off when it can't see it.
257
+ const composed = {
258
+ integrity: read(align, "composed", "integrity_mode") ?? "unknown",
259
+ protection: read(prot, "composed", "mode") ?? "unknown",
260
+ };
261
+ const own = {
262
+ integrity: read(align, "agent", "integrity_mode"),
263
+ protection: read(prot, "agent", "mode"),
264
+ };
265
+ const text = `governance: ${slug} — ${describeModes(composed)}`;
266
+ const stricter = (mine, eff) => mine === "off" && eff !== "off" && eff !== "unknown";
267
+ const overridden = stricter(own.integrity, composed.integrity) || stricter(own.protection, composed.protection);
268
+ if (overridden) {
269
+ return {
270
+ mark: "warn",
271
+ text,
272
+ hint: "the agent's own card says off, but a stricter org, team or platform policy keeps it on — it still runs and is billed",
273
+ };
274
+ }
275
+ if (composed.integrity === "off" && composed.protection === "off") {
276
+ return { mark: "ok", text, hint: OPT_IN_HINT };
277
+ }
278
+ if (composed.integrity === "unknown" || composed.protection === "unknown") {
279
+ return {
280
+ mark: "info",
281
+ text,
282
+ hint: "no composed card yet for at least one feature — it reads once the agent's cards compose (re-run `mnemom agent doctor` in a minute)",
283
+ };
284
+ }
285
+ return {
286
+ mark: "info",
287
+ text,
288
+ hint: "each feature that is on is a model call per turn, billed at cost on top of the agent fee — `mnemom agent setup --no-governance` turns it off",
289
+ };
290
+ }
291
+ catch (err) {
292
+ const detail = err instanceof Error ? err.message : String(err);
293
+ return { ...offByDefault, text: `governance: could not read the agent's cards (${detail})` };
294
+ }
295
+ }
@@ -16,6 +16,9 @@
16
16
  * screen_surfaces declared, ready for when it is turned on.
17
17
  * - goal-alignment: nudge — sealed per-conversation via the goal contract
18
18
  * (--goal …), graded and re-anchored by the gateway.
19
+ * `--governance [integrity,safe-house]` opts a setup in (observe-only, billed at
20
+ * cost on top of the agent fee); a refresh of an already-claimed agent keeps the
21
+ * modes its own cards carry unless a --governance / --no-governance flag says otherwise.
19
22
  *
20
23
  * A launch never runs on an unclaimed agent (operator 2026-10-03): an unclaimed agent lands in
21
24
  * the sandbox org and bills nobody. ensureClaimedServedAgent asks the gateway which agent it
@@ -26,6 +29,43 @@
26
29
  * reinventing it.
27
30
  */
28
31
  import { type CodeEntitlement } from "../lib/api.js";
32
+ /** A card's governance mode as the CLI writes it: off, or observe-only. */
33
+ export type GovernanceMode = "off" | "observe";
34
+ /**
35
+ * The governance features a setup turns on. Each is a model call per turn that
36
+ * the customer pays for at cost, so both are off unless asked for.
37
+ */
38
+ export interface GovernanceOptIn {
39
+ /** AIP integrity analysis (alignment card integrity_mode). */
40
+ integrity: boolean;
41
+ /** Safe House screening (protection card mode). */
42
+ safeHouse: boolean;
43
+ }
44
+ /** The feature names `--governance <list>` accepts (plus `all` / `none`). */
45
+ export declare const GOVERNANCE_FEATURES: readonly ["integrity", "safe-house"];
46
+ /**
47
+ * Parse the `--governance [features]` / `--no-governance` flag. undefined = not
48
+ * given (new agents get none; a refresh keeps the agent's current modes),
49
+ * true = every feature, false = none, a string = a comma-separated list of
50
+ * `integrity`, `safe-house`, `all` or `none`. Throws on an unknown name.
51
+ */
52
+ export declare function parseGovernanceFlag(value: boolean | string | undefined): GovernanceOptIn | undefined;
53
+ /**
54
+ * The modes a setup writes into the two cards: off/observe from the flag, or
55
+ * whatever an existing agent's own cards already carry (kept as-is on a refresh).
56
+ */
57
+ export interface CardModes {
58
+ /** autonomy_mode — verification + policy. No model call, so not billed; kept as-is on a refresh. */
59
+ autonomy: string;
60
+ integrity: string;
61
+ protection: string;
62
+ }
63
+ /** One phrase for the posture line: "integrity off, Safe House off". */
64
+ export declare function describeModes(m: Pick<CardModes, "integrity" | "protection">): string;
65
+ /** The modes for a NEW agent: off unless --governance asks for a feature. */
66
+ export declare function newAgentModes(governance?: GovernanceOptIn): CardModes;
67
+ /** Printed when a setup ran without --governance: how to opt in, and what it costs. */
68
+ export declare const GOVERNANCE_OPT_IN_HINT: string;
29
69
  /**
30
70
  * The default governed-agent slug for the current customer: `agent-<handle>`,
31
71
  * where <handle> is the local-part of their mnemom login email (sanitised), or
@@ -73,6 +113,12 @@ export interface ClaimedAgentOptions {
73
113
  orgLabel: string;
74
114
  /** true = --setup (re-ask the gateway, refresh the cards); false = --no-setup (no card writes). */
75
115
  setup?: boolean;
116
+ /**
117
+ * `--governance` / `--no-governance`: the modes the cards get. undefined = a new agent's are
118
+ * off, and a refresh keeps the agent's current modes. Given on a claimed agent, it also
119
+ * refreshes the cards (unless --no-setup).
120
+ */
121
+ governance?: GovernanceOptIn;
76
122
  /** Where the served-agent cache lives (defaults to ~/.mnemom). */
77
123
  cacheDir?: string;
78
124
  }
@@ -124,9 +170,10 @@ export declare function ensureClaimedServedAgent(opts: ClaimedAgentOptions): Pro
124
170
  * (`details.required_scope`). Never the token, never the body.
125
171
  */
126
172
  export declare function describeApiError(err: unknown): string;
127
- /** `mnemom agent setup [--agent <slug>]` — the explicit provisioning entry point. */
173
+ /** `mnemom agent setup [--agent <slug>] [--governance …]` — the explicit provisioning entry point. */
128
174
  export declare function agentSetupCommand(opts?: {
129
175
  agent?: string;
176
+ governance?: GovernanceOptIn;
130
177
  }): Promise<void>;
131
178
  /** Thrown when the agent's org has no spendable MU balance. The user-facing
132
179
  * top-up guidance is already printed when this is thrown — callers exit quietly. */
@@ -187,3 +234,5 @@ export declare function resolvePreflightedAccount(): Promise<{
187
234
  orgLabel: string;
188
235
  entitlement: CodeEntitlement | null;
189
236
  }>;
237
+ export declare function buildCodeAlignmentCard(agentId: string, modes?: Pick<CardModes, "autonomy" | "integrity">): Record<string, unknown>;
238
+ export declare function buildCodeProtectionCard(agentId: string, mode?: string): Record<string, unknown>;
@@ -16,6 +16,9 @@
16
16
  * screen_surfaces declared, ready for when it is turned on.
17
17
  * - goal-alignment: nudge — sealed per-conversation via the goal contract
18
18
  * (--goal …), graded and re-anchored by the gateway.
19
+ * `--governance [integrity,safe-house]` opts a setup in (observe-only, billed at
20
+ * cost on top of the agent fee); a refresh of an already-claimed agent keeps the
21
+ * modes its own cards carry unless a --governance / --no-governance flag says otherwise.
19
22
  *
20
23
  * A launch never runs on an unclaimed agent (operator 2026-10-03): an unclaimed agent lands in
21
24
  * the sandbox org and bills nobody. ensureClaimedServedAgent asks the gateway which agent it
@@ -25,7 +28,7 @@
25
28
  * (agents.ts), claimAgent + putAlignmentCard/putProtectionCard (lib/api.ts) — rather than
26
29
  * reinventing it.
27
30
  */
28
- import { claimAgent, getAgent, getAgentByName, getMe, getMuBalance, getMyPersonalOrg, MnemomApiError, mintProtectionCapability, putAlignmentCard, putProtectionCard, } from "../lib/api.js";
31
+ import { claimAgent, getAgent, getAgentByName, getCardSources, getMe, getMuBalance, getMyPersonalOrg, MnemomApiError, mintProtectionCapability, putAlignmentCard, putProtectionCard, } from "../lib/api.js";
29
32
  import { getAuthInfo, loginWithBrowser, loginWithDeviceFlow, resolveAuth } from "../lib/auth.js";
30
33
  import { getActiveOrg } from "../lib/cli-config.js";
31
34
  import { getApiUrl, getGatewayUrl, getWebsiteUrl, MNEMOM_DIR } from "../lib/config.js";
@@ -40,6 +43,102 @@ import { birthThroughGateway } from "./wrap.js";
40
43
  import { resolveAnthropicKey } from "./agent.js";
41
44
  import { assertCodeEnabled, CodeNotEnabledError } from "./agent-invite.js";
42
45
  const say = (line = "") => console.error(line);
46
+ /** The feature names `--governance <list>` accepts (plus `all` / `none`). */
47
+ export const GOVERNANCE_FEATURES = ["integrity", "safe-house"];
48
+ /**
49
+ * Parse the `--governance [features]` / `--no-governance` flag. undefined = not
50
+ * given (new agents get none; a refresh keeps the agent's current modes),
51
+ * true = every feature, false = none, a string = a comma-separated list of
52
+ * `integrity`, `safe-house`, `all` or `none`. Throws on an unknown name.
53
+ */
54
+ export function parseGovernanceFlag(value) {
55
+ if (value === undefined)
56
+ return undefined;
57
+ if (typeof value === "boolean")
58
+ return { integrity: value, safeHouse: value };
59
+ const choice = { integrity: false, safeHouse: false };
60
+ const names = value
61
+ .split(",")
62
+ .map((s) => s.trim().toLowerCase())
63
+ .filter(Boolean);
64
+ if (names.length === 0)
65
+ return { integrity: true, safeHouse: true };
66
+ for (const name of names) {
67
+ if (name === "all") {
68
+ choice.integrity = true;
69
+ choice.safeHouse = true;
70
+ }
71
+ else if (name === "none") {
72
+ // contributes nothing; `none` alone means both off
73
+ }
74
+ else if (name === "integrity") {
75
+ choice.integrity = true;
76
+ }
77
+ else if (name === "safe-house") {
78
+ choice.safeHouse = true;
79
+ }
80
+ else {
81
+ throw new Error(`--governance: unknown feature '${name}' — use ${GOVERNANCE_FEATURES.join(", ")}, all or none (comma-separated).`);
82
+ }
83
+ }
84
+ return choice;
85
+ }
86
+ const modeOf = (on) => (on ? "observe" : "off");
87
+ /** One phrase for the posture line: "integrity off, Safe House off". */
88
+ export function describeModes(m) {
89
+ return `integrity ${m.integrity}, Safe House ${m.protection}`;
90
+ }
91
+ /** The mode string a raw card carries in `field`, or undefined when absent/not a string. */
92
+ function modeField(card, field) {
93
+ const v = card?.[field];
94
+ return typeof v === "string" && v ? v : undefined;
95
+ }
96
+ /**
97
+ * An existing agent's own (agent-layer) card, to keep its modes on a refresh.
98
+ * null = it has none. THROWS when the read fails: the caller then leaves the
99
+ * cards alone rather than guess a mode in either direction.
100
+ */
101
+ async function existingAgentCard(kind, agentId) {
102
+ const sources = await getCardSources(kind, agentId);
103
+ return sources?.agent?.card_json ?? null;
104
+ }
105
+ /** The modes for a NEW agent: off unless --governance asks for a feature. */
106
+ export function newAgentModes(governance) {
107
+ const integrity = modeOf(governance?.integrity ?? false);
108
+ return { autonomy: integrity, integrity, protection: modeOf(governance?.safeHouse ?? false) };
109
+ }
110
+ /**
111
+ * The modes for refreshing an EXISTING agent: an explicit --governance /
112
+ * --no-governance wins; else it keeps the modes its own cards carry. No agent-layer
113
+ * card → off (it never had one; new setups are off). A card with no mode field →
114
+ * observe (the prior CLI always wrote observe). A failed read → null: skip the refresh.
115
+ */
116
+ async function existingAgentModes(agentId, governance) {
117
+ if (governance)
118
+ return newAgentModes(governance);
119
+ let align;
120
+ let prot;
121
+ try {
122
+ [align, prot] = await Promise.all([
123
+ existingAgentCard("alignment", agentId),
124
+ existingAgentCard("protection", agentId),
125
+ ]);
126
+ }
127
+ catch (err) {
128
+ say(fmt.warn(`mnemom agent: couldn't read ${agentId}'s current cards (${describeApiError(err)}) — leaving them unchanged.`));
129
+ say(fmt.dim(" Re-run `mnemom agent setup`, or pass --governance / --no-governance to set them."));
130
+ return null;
131
+ }
132
+ const integrity = align ? (modeField(align, "integrity_mode") ?? "observe") : "off";
133
+ return {
134
+ autonomy: align ? (modeField(align, "autonomy_mode") ?? "observe") : "off",
135
+ integrity,
136
+ protection: prot ? (modeField(prot, "mode") ?? "observe") : "off",
137
+ };
138
+ }
139
+ /** Printed when a setup ran without --governance: how to opt in, and what it costs. */
140
+ export const GOVERNANCE_OPT_IN_HINT = " Governance (AIP integrity analysis, Safe House screening) is off by default. Turn it on with " +
141
+ "`mnemom agent setup --governance` (or --governance integrity / safe-house); it is billed at cost on top of the agent fee.";
43
142
  /**
44
143
  * The default governed-agent slug for the current customer: `agent-<handle>`,
45
144
  * where <handle> is the local-part of their mnemom login email (sanitised), or
@@ -193,11 +292,18 @@ async function confirmClaimed(opts, anchorKey, fp) {
193
292
  const slug = opts.slug;
194
293
  const agentId = await resolveServedAgent(opts, fp);
195
294
  if (await isClaimed(slug, agentId)) {
196
- if (opts.setup === true) {
295
+ if (opts.setup === true || (opts.governance && opts.setup !== false)) {
197
296
  // An explicit `mnemom agent setup` / --setup repairs or completes the cards (e.g. a
198
297
  // protection card that failed on a prior run before its one-time grant was minted).
199
298
  say(fmt.dim(`mnemom agent: '${slug}' is ${agentId}, already claimed — refreshing its cards.`));
200
- await publishDefaultCards(agentId);
299
+ const modes = await existingAgentModes(agentId, opts.governance);
300
+ if (modes) {
301
+ if (!opts.governance) {
302
+ say(fmt.dim(` Keeping its current governance (${describeModes(modes)}). New agents default to off; ` +
303
+ "`--governance` turns it on, `--no-governance` turns it off."));
304
+ }
305
+ await publishDefaultCards(agentId, modes);
306
+ }
201
307
  persistAgentSlug(slug);
202
308
  }
203
309
  else {
@@ -229,10 +335,14 @@ async function confirmClaimed(opts, anchorKey, fp) {
229
335
  }
230
336
  say(fmt.success(`mnemom agent: claimed '${slug}' (${agentId}) in ${opts.orgLabel}.`));
231
337
  await noteSameNamedAgent(slug, agentId);
232
- // Publish the default (off) cards. A card write that can't complete does NOT undo the claim —
233
- // the agent is claimed and governed on whatever landed; we warn how to finish it.
234
- if (opts.setup !== false)
235
- await publishDefaultCards(agentId);
338
+ // Publish the default cards (off unless --governance). A card write that can't complete does
339
+ // NOT undo the claim — the agent is claimed and governed on whatever landed; we warn how to
340
+ // finish it.
341
+ if (opts.setup !== false) {
342
+ await publishDefaultCards(agentId, newAgentModes(opts.governance));
343
+ if (!opts.governance)
344
+ say(fmt.dim(GOVERNANCE_OPT_IN_HINT));
345
+ }
236
346
  persistAgentSlug(slug);
237
347
  return { slug, agentId, claimedNow: true };
238
348
  }
@@ -354,9 +464,9 @@ async function tryPublishCard(name, write) {
354
464
  }
355
465
  return { name, ok: false, error: "unknown error" };
356
466
  }
357
- /** Publish both default (off) cards; never throws — warns on a card that can't be written. */
358
- async function publishDefaultCards(agentId) {
359
- const align = await tryPublishCard("alignment", () => putAlignmentCard(agentId, JSON.stringify(buildCodeAlignmentCard(agentId)), "application/json"));
467
+ /** Publish both default cards at `modes`; never throws — warns on a card that can't be written. */
468
+ async function publishDefaultCards(agentId, modes) {
469
+ const align = await tryPublishCard("alignment", () => putAlignmentCard(agentId, JSON.stringify(buildCodeAlignmentCard(agentId, modes)), "application/json"));
360
470
  // Protection is the "locked door": it needs a one-time cards:write:protection
361
471
  // grant that the claim does NOT include (alignment rides a standing cap). Mint
362
472
  // it with our human login JWT — the human running `mnemom agent` (which claims
@@ -375,13 +485,13 @@ async function publishDefaultCards(agentId) {
375
485
  say(fmt.dim(" That agent is owned by an org other than your active one — `mnemom org use <org>` to switch, or set up under a different --agent."));
376
486
  }
377
487
  }
378
- const prot = await tryPublishCard("protection", () => putProtectionCard(agentId, JSON.stringify(buildCodeProtectionCard(agentId)), "application/json"));
488
+ const prot = await tryPublishCard("protection", () => putProtectionCard(agentId, JSON.stringify(buildCodeProtectionCard(agentId, modes.protection)), "application/json"));
379
489
  if (align.ok && prot.ok) {
380
- say(fmt.success("mnemom agent: default posture published (alignment + protection off until you turn them on; goal contracts nudge)."));
490
+ say(fmt.success(`mnemom agent: cards published (${describeModes(modes)}; goal contracts nudge).`));
381
491
  return;
382
492
  }
383
493
  if (align.ok)
384
- say(fmt.success("mnemom agent: alignment card published (off)."));
494
+ say(fmt.success(`mnemom agent: alignment card published (integrity ${modes.integrity}).`));
385
495
  for (const c of [align, prot]) {
386
496
  if (c.ok)
387
497
  continue;
@@ -392,7 +502,7 @@ async function publishDefaultCards(agentId) {
392
502
  say(fmt.dim(` The agent is claimed and running; finish it later with \`${publishCmd}\`.`));
393
503
  }
394
504
  }
395
- /** `mnemom agent setup [--agent <slug>]` — the explicit provisioning entry point. */
505
+ /** `mnemom agent setup [--agent <slug>] [--governance …]` — the explicit provisioning entry point. */
396
506
  export async function agentSetupCommand(opts = {}) {
397
507
  const gatewayHost = resolveGatewayHost(process.env);
398
508
  const cellError = singleCellError({
@@ -427,6 +537,7 @@ export async function agentSetupCommand(opts = {}) {
427
537
  orgId: account.orgId,
428
538
  orgLabel: account.orgLabel,
429
539
  setup: true,
540
+ governance: opts.governance,
430
541
  });
431
542
  }
432
543
  function persistAgentSlug(slug) {
@@ -603,12 +714,15 @@ export async function resolvePreflightedAccount() {
603
714
  return { orgId: active?.org_id ?? entitlement?.org_id ?? "", orgLabel, entitlement };
604
715
  }
605
716
  // ── The default coding posture (cards off; goal contracts nudge) ─────────────
606
- function buildCodeAlignmentCard(agentId) {
717
+ export function buildCodeAlignmentCard(agentId, modes = { autonomy: "off", integrity: "off" }) {
607
718
  return {
608
719
  card_version: "unified/2026-04-26",
609
720
  agent_id: agentId,
610
- autonomy_mode: "off",
611
- integrity_mode: "off",
721
+ // Integrity off ⇒ the gateway skips the AIP checkpoint and the observer skips
722
+ // its trace analysis: no model call, no charge. A new agent's autonomy
723
+ // (verification + policy) follows it; a refresh keeps the agent's own.
724
+ autonomy_mode: modes.autonomy,
725
+ integrity_mode: modes.integrity,
612
726
  principal: { type: "agent", identifier: agentId, relationship: "delegated_authority" },
613
727
  values: {
614
728
  declared: ["transparency", "safety", "honesty", "user_control", "harm_prevention"],
@@ -641,11 +755,11 @@ function buildCodeAlignmentCard(agentId) {
641
755
  audit: { retention_days: 30, queryable: false, trace_format: "otel" },
642
756
  };
643
757
  }
644
- function buildCodeProtectionCard(agentId) {
758
+ export function buildCodeProtectionCard(agentId, mode = "off") {
645
759
  return {
646
760
  card_version: "protection/2026-04-26",
647
761
  agent_id: agentId,
648
- mode: "off",
762
+ mode,
649
763
  thresholds: { warn: 0.6, quarantine: 0.8, block: 0.95 },
650
764
  screen_surfaces: {
651
765
  incoming: true,
@@ -29,6 +29,7 @@ import { type MnemomKeyDeps } from "../lib/subscription-login.js";
29
29
  import { type SessionRecord } from "../lib/agent-ledger.js";
30
30
  import { type ExperimentalId } from "../lib/experimental.js";
31
31
  import { type ConsoleKeyDeps } from "../lib/console-key.js";
32
+ import { type GovernanceOptIn } from "./agent-setup.js";
32
33
  import { type ContextProfile, type LaunchShape, type SealedContractEnvelope } from "../lib/agent.js";
33
34
  export interface AgentOptions {
34
35
  goal?: string;
@@ -102,6 +103,11 @@ export interface AgentOptions {
102
103
  * default cards, false (`--no-setup`) skips the card writes.
103
104
  */
104
105
  setup?: boolean;
106
+ /**
107
+ * `--governance` / `--no-governance`: the governance modes a provisioning (or
108
+ * `--setup` refresh) writes. undefined = off for a new agent, kept on a refresh.
109
+ */
110
+ governance?: GovernanceOptIn;
105
111
  /**
106
112
  * Where to source the Anthropic key (from ~/.mnemom/launcher.toml `key_source`).
107
113
  * Restricts the default env → store → prompt search: "env" uses env only,
@@ -402,6 +402,7 @@ export async function agentCommand(scenarioArg, options = {}) {
402
402
  orgId: account.orgId,
403
403
  orgLabel: account.orgLabel,
404
404
  setup: options.setup,
405
+ governance: options.governance,
405
406
  });
406
407
  // ── conversation id ── an implicit relaunch continues its earlier conversation (IG5 #1, D1)
407
408
  // so the goal the gateway inferred there is still the one it steers by. Resolved here, after
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import { agentCommand } from "./commands/agent.js";
26
26
  import { agentConfigCommand } from "./commands/agent-config.js";
27
27
  import { agentSlackCommand } from "./commands/agent-slack.js";
28
28
  import { agentDoctorCommand } from "./commands/agent-doctor.js";
29
- import { agentSetupCommand } from "./commands/agent-setup.js";
29
+ import { agentSetupCommand, parseGovernanceFlag } from "./commands/agent-setup.js";
30
30
  import { agentInviteCommand, runAgentInvocation } from "./commands/agent-invite.js";
31
31
  import { agentFirstRunCommand, offerFirstRunOnLaunch } from "./commands/agent-first-run.js";
32
32
  import { applyConfigToEnv, launcherEntries, loadLauncherConfig, withoutDisabledExperimental, } from "./lib/launcher-config.js";
@@ -170,6 +170,8 @@ const agentCmd = program
170
170
  .option("--invite <token>", "Redeem a Mnemom Agent invitation (pinv_…): unlocks Mnemom Agent for you + credits your starter µ; launches too when a session name is given")
171
171
  .option("--setup", "Re-check the served agent and refresh its default cards before launch")
172
172
  .option("--no-setup", "Skip the default-card writes (the agent is still confirmed and claimed)")
173
+ .option("--governance [features]", "Turn governance ON for the agent's cards: AIP integrity analysis + Safe House screening, observe-only (default: off for new agents). Pick features with a comma list: integrity, safe-house. Billed at cost on top of the agent fee")
174
+ .option("--no-governance", "Turn governance OFF for the agent's cards (a card refresh otherwise keeps an existing agent's modes)")
173
175
  .option("--no-yolo", "Do NOT pass --dangerously-skip-permissions (yolo is on by default)")
174
176
  .option("--no-context-hint", "Do NOT seed Claude Code's context-hint feature flag before launch")
175
177
  .addOption(new Option("--slack", "EXPERIMENTAL (`mnemom experimental enable slack`): attach the Slack channel — messages addressed to this session arrive in it, and it can reply (see `mnemom agent slack`)").hideHelp(hiddenUnless("slack")))
@@ -299,13 +301,15 @@ const agentCmd = program
299
301
  return;
300
302
  }
301
303
  if (scenario === "doctor") {
302
- await agentDoctorCommand({ cli: opts.cli });
304
+ await agentDoctorCommand({ cli: opts.cli, agent: program.opts().agent ?? cfg.agent });
303
305
  return;
304
306
  }
307
+ // Validated up front so a typo fails before any sign-in or provisioning.
308
+ const governance = parseGovernanceFlag(opts.governance);
305
309
  // Sub-verb: `mnemom agent setup` provisions/claims the governed agent and
306
310
  // never launches. The agent slug follows the same precedence as a launch.
307
311
  if (scenario === "setup") {
308
- await agentSetupCommand({ agent: program.opts().agent ?? cfg.agent });
312
+ await agentSetupCommand({ agent: program.opts().agent ?? cfg.agent, governance });
309
313
  return;
310
314
  }
311
315
  const numToStr = (n) => n === undefined ? undefined : String(n);
@@ -367,6 +371,7 @@ const agentCmd = program
367
371
  : false,
368
372
  experimental,
369
373
  setup: opts.setup,
374
+ governance,
370
375
  keySource: cfg.keySource,
371
376
  passthrough,
372
377
  dryRun: opts.dryRun,
package/dist/lib/api.d.ts CHANGED
@@ -630,6 +630,27 @@ export declare const PROTECTION_CARD_MAX_BYTES: number;
630
630
  export declare function putProtectionCard(agentId: string, body: string, contentType?: "text/yaml" | "application/json", opts?: {
631
631
  idempotencyKey?: string;
632
632
  }): Promise<PublishedCardResponse>;
633
+ /**
634
+ * The per-layer view of an agent's card (ADR-053 `?include=sources`): the raw
635
+ * card the agent layer itself carries, next to the composed card every layer
636
+ * produced. We type the two rows we read; the platform/org/teams rows ride along.
637
+ */
638
+ export interface CardSources {
639
+ agent: {
640
+ card_json: Record<string, unknown> | null;
641
+ available: boolean;
642
+ };
643
+ composed: {
644
+ card_json: Record<string, unknown> | null;
645
+ available: boolean;
646
+ };
647
+ [key: string]: unknown;
648
+ }
649
+ /**
650
+ * Fetch an agent's alignment or protection card sources envelope. Returns null
651
+ * when the agent has no canonical card yet (404).
652
+ */
653
+ export declare function getCardSources(kind: "alignment" | "protection", agentId: string): Promise<CardSources | null>;
633
654
  export interface CardPreviewResult {
634
655
  /** true ⇔ the server accepted+composed the card (HTTP 200). */
635
656
  valid: boolean;
package/dist/lib/api.js CHANGED
@@ -841,6 +841,22 @@ export async function putProtectionCard(agentId, body, contentType = "text/yaml"
841
841
  }
842
842
  return response.json();
843
843
  }
844
+ /**
845
+ * Fetch an agent's alignment or protection card sources envelope. Returns null
846
+ * when the agent has no canonical card yet (404).
847
+ */
848
+ export async function getCardSources(kind, agentId) {
849
+ const url = validateUrl(`${API_BASE}/v1/${kind}/agent/${encodeURIComponent(agentId)}?include=sources`);
850
+ const response = await mnemomFetch(url, {
851
+ headers: { Accept: "application/json", ...(await authHeaders()) },
852
+ });
853
+ if (!response.ok) {
854
+ if (response.status === 404)
855
+ return null;
856
+ throw await readApiError(response, `Failed to fetch ${kind} card sources`);
857
+ }
858
+ return response.json();
859
+ }
844
860
  /**
845
861
  * Dry-run validate + compose a card body against the server's authoritative
846
862
  * pipeline. Returns a structured result — a 422/400 validation failure is an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/mnemom",
3
- "version": "0.17.11-next.0",
3
+ "version": "0.17.11-next.1",
4
4
  "description": "Transparent AI agent tracing",
5
5
  "type": "module",
6
6
  "bin": {