@mnemom/mnemom 0.17.0-next.1 → 0.17.0-next.2
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.
- package/dist/commands/code-setup.d.ts +8 -4
- package/dist/commands/code-setup.js +16 -12
- package/dist/commands/code.d.ts +1 -1
- package/dist/commands/code.js +81 -11
- package/package.json +1 -1
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
* If a customer names no agent, `mnemom code` launches under a per-customer
|
|
6
6
|
* governed identity `code-<handle>`. This module makes that identity REAL in
|
|
7
7
|
* their Mnemom org: it births the agent through the gateway, claims it (so the
|
|
8
|
-
* customer owns it), and publishes a sane default coding posture
|
|
9
|
-
*
|
|
8
|
+
* customer owns it), and publishes a sane default coding posture. The CARDS are
|
|
9
|
+
* observe-only (visibility, no active intervention); the goal contract is what
|
|
10
|
+
* nudges (re-anchor on drift). Specifically:
|
|
11
|
+
* - alignment card: autonomy_mode + integrity_mode = observe,
|
|
10
12
|
* principal.relationship = delegated_authority, coding bounded/forbidden
|
|
11
13
|
* actions, escalate-on-irreversible;
|
|
12
|
-
* - protection card: mode =
|
|
13
|
-
* screen_surfaces on.
|
|
14
|
+
* - protection card: mode = observe, thresholds 0.60/0.80/0.95, all four
|
|
15
|
+
* screen_surfaces on (screened for observation).
|
|
16
|
+
* - goal-alignment: nudge — sealed per-conversation via the goal contract
|
|
17
|
+
* (--goal …), graded and re-anchored by the gateway.
|
|
14
18
|
*
|
|
15
19
|
* All account writes are gated: interactive runs show the plan and confirm;
|
|
16
20
|
* non-interactive runs provision only with an explicit `--setup`, and otherwise
|
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
* If a customer names no agent, `mnemom code` launches under a per-customer
|
|
6
6
|
* governed identity `code-<handle>`. This module makes that identity REAL in
|
|
7
7
|
* their Mnemom org: it births the agent through the gateway, claims it (so the
|
|
8
|
-
* customer owns it), and publishes a sane default coding posture
|
|
9
|
-
*
|
|
8
|
+
* customer owns it), and publishes a sane default coding posture. The CARDS are
|
|
9
|
+
* observe-only (visibility, no active intervention); the goal contract is what
|
|
10
|
+
* nudges (re-anchor on drift). Specifically:
|
|
11
|
+
* - alignment card: autonomy_mode + integrity_mode = observe,
|
|
10
12
|
* principal.relationship = delegated_authority, coding bounded/forbidden
|
|
11
13
|
* actions, escalate-on-irreversible;
|
|
12
|
-
* - protection card: mode =
|
|
13
|
-
* screen_surfaces on.
|
|
14
|
+
* - protection card: mode = observe, thresholds 0.60/0.80/0.95, all four
|
|
15
|
+
* screen_surfaces on (screened for observation).
|
|
16
|
+
* - goal-alignment: nudge — sealed per-conversation via the goal contract
|
|
17
|
+
* (--goal …), graded and re-anchored by the gateway.
|
|
14
18
|
*
|
|
15
19
|
* All account writes are gated: interactive runs show the plan and confirm;
|
|
16
20
|
* non-interactive runs provision only with an explicit `--setup`, and otherwise
|
|
@@ -83,21 +87,21 @@ export async function ensureGovernedAgent(opts) {
|
|
|
83
87
|
say(fmt.section("Set up a governed coding agent"));
|
|
84
88
|
say(fmt.label(" Agent:", slug));
|
|
85
89
|
say(fmt.label(" Org:", orgLabel));
|
|
86
|
-
say(fmt.label(" Posture:", "
|
|
90
|
+
say(fmt.label(" Posture:", "observe (alignment + protection observe-only, all surfaces screened); goal contracts nudge"));
|
|
87
91
|
say(fmt.dim(" Births the agent through the gateway, claims it to your org, and publishes its cards."));
|
|
88
92
|
if (!(await askYesNo("Provision and claim this agent now?", true))) {
|
|
89
93
|
say(fmt.dim(" Skipped — launching under the unclaimed identity. Run `mnemom code setup` anytime."));
|
|
90
94
|
return { slug, provisioned: false };
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
|
-
// 5. Birth → claim → publish
|
|
97
|
+
// 5. Birth → claim → publish observe-only cards. (Key never logged.)
|
|
94
98
|
const gatewayUrl = opts.gatewayHost.replace(/\/+$/, "");
|
|
95
99
|
const agentId = await birthThroughGateway(gatewayUrl, "anthropic", slug, opts.anthropicKey);
|
|
96
100
|
const hashProof = deriveHashProof(opts.anthropicKey, slug);
|
|
97
101
|
await claimAgent(agentId, { hashProof, orgId: org?.org_id });
|
|
98
102
|
await putAlignmentCard(agentId, JSON.stringify(buildCodeAlignmentCard(agentId)), "application/json");
|
|
99
103
|
await putProtectionCard(agentId, JSON.stringify(buildCodeProtectionCard(agentId)), "application/json");
|
|
100
|
-
say(fmt.success(`mnemom code: provisioned + claimed '${slug}' (${agentId}) in ${orgLabel} —
|
|
104
|
+
say(fmt.success(`mnemom code: provisioned + claimed '${slug}' (${agentId}) in ${orgLabel} — observe posture published (goal contracts nudge).`));
|
|
101
105
|
// 6. Remember the slug so future launches skip straight to usable.
|
|
102
106
|
persistAgentSlug(slug);
|
|
103
107
|
return { slug, provisioned: true };
|
|
@@ -125,7 +129,7 @@ export async function codeSetupCommand(opts = {}) {
|
|
|
125
129
|
}
|
|
126
130
|
function warnUnclaimed(slug) {
|
|
127
131
|
say(fmt.warn(`mnemom code: agent '${slug}' is not set up in your org — launching with an UNCLAIMED identity (governance is advisory/fail-open).`));
|
|
128
|
-
say(fmt.dim(" Provision it (creates + claims the agent, publishes
|
|
132
|
+
say(fmt.dim(" Provision it (creates + claims the agent, publishes observe cards): mnemom code setup"));
|
|
129
133
|
}
|
|
130
134
|
function persistAgentSlug(slug) {
|
|
131
135
|
try {
|
|
@@ -273,13 +277,13 @@ export async function ensureSessionAndBalance(opts) {
|
|
|
273
277
|
await assertMuBalance(orgId, orgLabel);
|
|
274
278
|
return { orgId, orgLabel };
|
|
275
279
|
}
|
|
276
|
-
// ── The default coding posture (nudge
|
|
280
|
+
// ── The default coding posture (cards observe-only; goal contracts nudge) ────
|
|
277
281
|
function buildCodeAlignmentCard(agentId) {
|
|
278
282
|
return {
|
|
279
283
|
card_version: "unified/2026-04-26",
|
|
280
284
|
agent_id: agentId,
|
|
281
|
-
autonomy_mode: "
|
|
282
|
-
integrity_mode: "
|
|
285
|
+
autonomy_mode: "observe",
|
|
286
|
+
integrity_mode: "observe",
|
|
283
287
|
principal: { type: "agent", identifier: agentId, relationship: "delegated_authority" },
|
|
284
288
|
values: {
|
|
285
289
|
declared: ["transparency", "safety", "honesty", "user_control", "harm_prevention"],
|
|
@@ -316,7 +320,7 @@ function buildCodeProtectionCard(agentId) {
|
|
|
316
320
|
return {
|
|
317
321
|
card_version: "protection/2026-04-26",
|
|
318
322
|
agent_id: agentId,
|
|
319
|
-
mode: "
|
|
323
|
+
mode: "observe",
|
|
320
324
|
thresholds: { warn: 0.6, quarantine: 0.8, block: 0.95 },
|
|
321
325
|
screen_surfaces: {
|
|
322
326
|
incoming: true,
|
package/dist/commands/code.d.ts
CHANGED
|
@@ -100,7 +100,7 @@ export interface ResolvedCli {
|
|
|
100
100
|
* a non-claude executable is launched through it with a warning.
|
|
101
101
|
*/
|
|
102
102
|
export declare function resolveCliBin(cliOpt: string | undefined, env: NodeJS.ProcessEnv): ResolvedCli;
|
|
103
|
-
export declare function
|
|
103
|
+
export declare function promptForCliPath(cliOpt: string | undefined, env: NodeJS.ProcessEnv, origErr: Error | undefined): Promise<ResolvedCli>;
|
|
104
104
|
/**
|
|
105
105
|
* Does the resolved coding-agent CLI advertise the `--remote-control` startup
|
|
106
106
|
* flag (Claude Code >= 2.1.269)? Determined by scanning its `--help`. Best-effort:
|
package/dist/commands/code.js
CHANGED
|
@@ -31,6 +31,7 @@ import { tmpdir, homedir } from "node:os";
|
|
|
31
31
|
import { basename, join, dirname } from "node:path";
|
|
32
32
|
import { fileURLToPath } from "node:url";
|
|
33
33
|
import { MNEMOM_DIR } from "../lib/config.js";
|
|
34
|
+
import { configKeySpec, loadCodeConfig, writeCodeConfig } from "../lib/code-config.js";
|
|
34
35
|
import { fmt } from "../lib/format.js";
|
|
35
36
|
import { askInput, askYesNo, isInteractive } from "../lib/prompt.js";
|
|
36
37
|
import { defaultAgentSlug, ensureGovernedAgent, ensureSessionAndBalance, MuInsufficientError, } from "./code-setup.js";
|
|
@@ -72,14 +73,25 @@ export async function codeCommand(scenario, options = {}) {
|
|
|
72
73
|
const cellNote = isProdGateway(env) ? "us-2/prod" : `custom (${gatewayHost})`;
|
|
73
74
|
const yolo = options.yolo !== false; // default ON
|
|
74
75
|
const contextHint = options.contextHint !== false; // default ON
|
|
75
|
-
// ── which CLI executable (name or full path) ──
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
// ── which CLI executable (name or full path) ── resolve GRACEFULLY: if it's not
|
|
77
|
+
// found we don't crash with a technical error — we ask for the path at launch.
|
|
78
|
+
let cli;
|
|
79
|
+
let cliError;
|
|
80
|
+
try {
|
|
81
|
+
cli = resolveCliBin(options.cli, env);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
cliError = err instanceof Error ? err : new Error(String(err));
|
|
85
|
+
}
|
|
86
|
+
// Default launch shape: prefer Remote-Control-in-terminal (the good default —
|
|
87
|
+
// one session steerable from this terminal AND claude.ai/the app) when the
|
|
88
|
+
// machine can do it; otherwise terminal-only with a clear notice. An explicit
|
|
89
|
+
// choice (flag / env / config launch=) is always respected. Needs a resolved
|
|
90
|
+
// CLI to test RC capability, so it's applied once the CLI is known.
|
|
91
|
+
const applyAutoShape = (resolved) => {
|
|
92
|
+
if (shapeExplicit || shape !== "terminal")
|
|
93
|
+
return;
|
|
94
|
+
const rc = checkRcCapability(resolved);
|
|
83
95
|
if (rc.ok) {
|
|
84
96
|
shape = "remote-control";
|
|
85
97
|
}
|
|
@@ -87,11 +99,24 @@ export async function codeCommand(scenario, options = {}) {
|
|
|
87
99
|
say(`mnemom code: launching terminal-only (Remote Control unavailable: ${rc.missing.join("; ")}).`);
|
|
88
100
|
say("mnemom code: once those are in place, `mnemom code <scenario> --remote-control` (or `config set launch remote-control`) gives you a session steerable from claude.ai and the Claude app too.");
|
|
89
101
|
}
|
|
90
|
-
}
|
|
102
|
+
};
|
|
103
|
+
if (cli)
|
|
104
|
+
applyAutoShape(cli);
|
|
91
105
|
if (options.dryRun) {
|
|
92
|
-
|
|
106
|
+
const planCli = cli ?? {
|
|
107
|
+
bin: "(not found — pass --cli <path> or `mnemom code config set cli <path>`)",
|
|
108
|
+
name: basename((options.cli || env.MNEMOM_CODE_CLI || "claude").trim()),
|
|
109
|
+
};
|
|
110
|
+
printPlan({ scenario, shape, agent, conversationId, door, cellNote, envelope, yolo, cli: planCli });
|
|
93
111
|
return;
|
|
94
112
|
}
|
|
113
|
+
// Real launch: make sure we actually have a CLI to run. If we couldn't find it,
|
|
114
|
+
// ask for the path (a good experience beats a hard technical error). A
|
|
115
|
+
// non-interactive session still gets the clear, actionable error.
|
|
116
|
+
if (!cli) {
|
|
117
|
+
cli = await promptForCliPath(options.cli, env, cliError);
|
|
118
|
+
applyAutoShape(cli);
|
|
119
|
+
}
|
|
95
120
|
// ── resolve the Anthropic key (zero 1Password; never logged) ──
|
|
96
121
|
const anthropicKey = await resolveAnthropicKey(env, {
|
|
97
122
|
allowPrompt: true,
|
|
@@ -295,7 +320,52 @@ export function resolveCliBin(cliOpt, env) {
|
|
|
295
320
|
(name === "claude" ? " and in the fnm/nvm/asdf node dirs" : "") +
|
|
296
321
|
".\n Pass --cli <name|/full/path> or set MNEMOM_CODE_CLI.");
|
|
297
322
|
}
|
|
298
|
-
export
|
|
323
|
+
export /**
|
|
324
|
+
* Recover when the coding-agent CLI can't be found — a good experience beats a
|
|
325
|
+
* hard technical error. In an interactive session, tell the customer we couldn't
|
|
326
|
+
* find it and let them paste the full path (offering to save it as their default
|
|
327
|
+
* so they never retype it). A non-interactive session re-throws the clear,
|
|
328
|
+
* actionable error rather than hanging on a prompt.
|
|
329
|
+
*/ async function promptForCliPath(cliOpt, env, origErr) {
|
|
330
|
+
const requested = (cliOpt || env.MNEMOM_CODE_CLI || "claude").trim();
|
|
331
|
+
if (!isInteractive()) {
|
|
332
|
+
throw origErr ?? new Error(`mnemom code: coding-agent CLI '${requested}' not found.`);
|
|
333
|
+
}
|
|
334
|
+
say(`mnemom code: couldn't find the '${requested}' CLI on your PATH.`);
|
|
335
|
+
if (requested === "claude") {
|
|
336
|
+
say(" If Claude Code is installed, paste its full path (find it with: which claude).");
|
|
337
|
+
say(" Not installed yet? Get it at https://claude.com/claude-code, then re-run.");
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
say(" Paste its full path, or install it and re-run.");
|
|
341
|
+
}
|
|
342
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
343
|
+
const entered = (await askInput("Path to your coding-agent CLI (Enter to cancel):")).trim();
|
|
344
|
+
if (!entered) {
|
|
345
|
+
throw new Error("mnemom code: no coding-agent CLI available.\n" +
|
|
346
|
+
" Install Claude Code (https://claude.com/claude-code), or pass --cli <path> " +
|
|
347
|
+
"(or save it: mnemom code config set cli <path>).");
|
|
348
|
+
}
|
|
349
|
+
if (!existsSync(entered)) {
|
|
350
|
+
say(` '${entered}' doesn't exist — try again.`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (await askYesNo("Save this as your default CLI so you don't retype it?", true)) {
|
|
354
|
+
try {
|
|
355
|
+
const cfg = loadCodeConfig();
|
|
356
|
+
configKeySpec("cli").set(cfg, entered);
|
|
357
|
+
writeCodeConfig(cfg);
|
|
358
|
+
say(" Saved — future launches will use it.");
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// best effort — the launch proceeds with the entered path either way
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return { bin: entered, name: basename(entered) };
|
|
365
|
+
}
|
|
366
|
+
throw new Error("mnemom code: couldn't resolve a coding-agent CLI path — pass --cli <path>.");
|
|
367
|
+
}
|
|
368
|
+
function commandExists(cmd) {
|
|
299
369
|
const res = spawnSync("sh", ["-c", `command -v "$1" >/dev/null 2>&1`, "sh", cmd], {
|
|
300
370
|
stdio: "ignore",
|
|
301
371
|
});
|