@mnemom/mnemom 0.17.0 → 0.17.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.
- package/dist/commands/agent-doctor.js +3 -0
- package/dist/commands/agent-invite.d.ts +1 -1
- package/dist/commands/agent-invite.js +4 -1
- package/dist/commands/agent-setup.d.ts +2 -0
- package/dist/commands/agent-setup.js +2 -2
- package/dist/commands/agent.js +41 -22
- package/dist/lib/agent.d.ts +33 -0
- package/dist/lib/agent.js +43 -0
- package/dist/lib/api.d.ts +7 -0
- package/package.json +1 -1
|
@@ -164,6 +164,9 @@ export async function probeCodeAccess() {
|
|
|
164
164
|
return {
|
|
165
165
|
mark: "ok",
|
|
166
166
|
text: `Mnemom Agent access: enabled for ${orgLabel} (${sourceLabel(ent.source)})`,
|
|
167
|
+
hint: ent.remote_control
|
|
168
|
+
? undefined
|
|
169
|
+
: "Remote Control (steer from claude.ai / the Claude app) is an internal feature — your sessions run terminal-only",
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
172
|
catch (err) {
|
|
@@ -41,7 +41,7 @@ export declare function isCodeNotEnabled(err: unknown): err is MnemomApiError;
|
|
|
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
|
*/
|
|
44
|
-
export declare function assertCodeEnabled(orgId: string, orgLabel: string): Promise<
|
|
44
|
+
export declare function assertCodeEnabled(orgId: string, orgLabel: string): Promise<CodeEntitlement | null>;
|
|
45
45
|
/** Ensures a Mnemom session exists (injected so tests never touch the real login flow). */
|
|
46
46
|
export type EnsureSession = (opts: {
|
|
47
47
|
interactive?: boolean;
|
|
@@ -85,7 +85,7 @@ function printNotEnabled(orgLabel, hint) {
|
|
|
85
85
|
*/
|
|
86
86
|
export async function assertCodeEnabled(orgId, orgLabel) {
|
|
87
87
|
try {
|
|
88
|
-
await getCodeEntitlement(orgId);
|
|
88
|
+
return await getCodeEntitlement(orgId);
|
|
89
89
|
}
|
|
90
90
|
catch (err) {
|
|
91
91
|
if (isCodeNotEnabled(err)) {
|
|
@@ -93,6 +93,9 @@ export async function assertCodeEnabled(orgId, orgLabel) {
|
|
|
93
93
|
throw new CodeNotEnabledError(orgLabel);
|
|
94
94
|
}
|
|
95
95
|
say(fmt.dim(` (Could not verify Mnemom Agent access — ${err instanceof Error ? err.message : String(err)}. Continuing.)`));
|
|
96
|
+
// Inconclusive (network / 5xx / membership 403) → fail open on access, but
|
|
97
|
+
// the null entitlement fails CLOSED on Remote Control at the call site.
|
|
98
|
+
return null;
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
async function defaultEnsureSession(opts) {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* birthThroughGateway (wrap.ts), deriveHashProof (agents.ts), claimAgent +
|
|
24
24
|
* putAlignmentCard/putProtectionCard (lib/api.ts) — rather than reinventing it.
|
|
25
25
|
*/
|
|
26
|
+
import { type CodeEntitlement } from "../lib/api.js";
|
|
26
27
|
/**
|
|
27
28
|
* The default governed-agent slug for the current customer: `agent-<handle>`,
|
|
28
29
|
* where <handle> is the local-part of their mnemom login email (sanitised), or
|
|
@@ -112,4 +113,5 @@ export declare function ensureSessionAndBalance(opts: {
|
|
|
112
113
|
}): Promise<{
|
|
113
114
|
orgId: string;
|
|
114
115
|
orgLabel: string;
|
|
116
|
+
entitlement: CodeEntitlement | null;
|
|
115
117
|
}>;
|
|
@@ -379,9 +379,9 @@ export async function ensureSessionAndBalance(opts) {
|
|
|
379
379
|
const orgLabel = active ? `${active.name} (${active.slug})` : "your personal org";
|
|
380
380
|
// Invited-cohort gate FIRST: an org that is not enabled for Mnemom Agent gets
|
|
381
381
|
// the invite guidance, not a top-up message (the µ check would be moot).
|
|
382
|
-
await assertCodeEnabled(orgId, orgLabel);
|
|
382
|
+
const entitlement = await assertCodeEnabled(orgId, orgLabel);
|
|
383
383
|
await assertMuBalance(orgId, orgLabel);
|
|
384
|
-
return { orgId, orgLabel };
|
|
384
|
+
return { orgId, orgLabel, entitlement };
|
|
385
385
|
}
|
|
386
386
|
// ── The default coding posture (cards observe-only; goal contracts nudge) ────
|
|
387
387
|
function buildCodeAlignmentCard(agentId) {
|
package/dist/commands/agent.js
CHANGED
|
@@ -38,7 +38,7 @@ import { fmt } from "../lib/format.js";
|
|
|
38
38
|
import { askInput, askSelect, askYesNo, isInteractive } from "../lib/prompt.js";
|
|
39
39
|
import { goalAlignmentLine, resolveSessionSetup } from "./agent-session.js";
|
|
40
40
|
import { defaultAgentSlug, ensureGovernedAgent, ensureSessionAndBalance, isQuietPreambleExit, resolveDefaultSlug, } from "./agent-setup.js";
|
|
41
|
-
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, isProdGateway, launchPreferenceExplicit, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, resolveLaunchShape, sanitizeAgentName, } from "../lib/agent.js";
|
|
41
|
+
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, isProdGateway, launchPreferenceExplicit, remoteControlAllowed, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, resolveLaunchShape, resolveRemoteControlShape, sanitizeAgentName, } from "../lib/agent.js";
|
|
42
42
|
/** The CLI's own provider-key store (zero 1Password): ~/.mnemom/launcher.json, 0600. */
|
|
43
43
|
const CODE_STORE = join(MNEMOM_DIR, "launcher.json");
|
|
44
44
|
const say = (line = "") => console.error(line);
|
|
@@ -106,33 +106,26 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
106
106
|
catch (err) {
|
|
107
107
|
cliError = err instanceof Error ? err : new Error(String(err));
|
|
108
108
|
}
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (rc.ok) {
|
|
119
|
-
shape = "remote-control";
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
say(`mnemom agent: launching terminal-only (Remote Control unavailable: ${rc.missing.join("; ")}).`);
|
|
123
|
-
say("mnemom agent: once those are in place, `mnemom agent <scenario> --remote-control` (or `config set launch remote-control`) gives you a session steerable from claude.ai and the Claude app too.");
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
if (cli)
|
|
127
|
-
applyAutoShape(cli);
|
|
109
|
+
// NB: the launch SHAPE (terminal vs Remote Control) is resolved in two steps.
|
|
110
|
+
// Remote Control is an INTERNAL Mnemom capability, so the auto-upgrade and any
|
|
111
|
+
// explicit `--remote-control`/`--server` are gated on the org's entitlement —
|
|
112
|
+
// which we only know AFTER sign-in. The entitlement-aware decision
|
|
113
|
+
// (`resolveRemoteControlShape`) happens below, right after
|
|
114
|
+
// `ensureSessionAndBalance`. --dry-run prints the plan BEFORE sign-in, so it
|
|
115
|
+
// can't know the entitlement; it previews the shape the MACHINE is capable of
|
|
116
|
+
// (as the pre-gate behaviour did), then notes that the real launch applies the
|
|
117
|
+
// entitlement gate. `shape` itself stays the requested (ungated) shape.
|
|
128
118
|
if (options.dryRun) {
|
|
129
119
|
const planCli = cli ?? {
|
|
130
120
|
bin: "(not found — pass --cli <path> or `mnemom agent config set cli <path>`)",
|
|
131
121
|
name: basename((options.cli || env.MNEMOM_AGENT_CLI || "claude").trim()),
|
|
132
122
|
};
|
|
123
|
+
const previewShape = !shapeExplicit && shape === "terminal" && cli && checkRcCapability(cli).ok
|
|
124
|
+
? "remote-control"
|
|
125
|
+
: shape;
|
|
133
126
|
printPlan({
|
|
134
127
|
scenario,
|
|
135
|
-
shape,
|
|
128
|
+
shape: previewShape,
|
|
136
129
|
agent,
|
|
137
130
|
conversationId,
|
|
138
131
|
door,
|
|
@@ -142,6 +135,9 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
142
135
|
yolo,
|
|
143
136
|
cli: planCli,
|
|
144
137
|
});
|
|
138
|
+
if (previewShape === "remote-control") {
|
|
139
|
+
say("mnemom agent: the plan shows Remote Control because this machine can run it; the real launch uses it only if your account is entitled (internal Mnemom accounts), else terminal-only.");
|
|
140
|
+
}
|
|
145
141
|
return;
|
|
146
142
|
}
|
|
147
143
|
// Real launch: make sure we actually have a CLI to run. If we couldn't find it,
|
|
@@ -149,7 +145,6 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
149
145
|
// non-interactive session still gets the clear, actionable error.
|
|
150
146
|
if (!cli) {
|
|
151
147
|
cli = await promptForCliPath(options.cli, env, cliError);
|
|
152
|
-
applyAutoShape(cli);
|
|
153
148
|
}
|
|
154
149
|
// ── resolve the Anthropic key (zero 1Password; never logged) ──
|
|
155
150
|
const anthropicKey = await resolveAnthropicKey(env, {
|
|
@@ -171,6 +166,30 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
171
166
|
process.exit(1); // guidance already printed
|
|
172
167
|
throw err;
|
|
173
168
|
}
|
|
169
|
+
// ── Remote-Control entitlement gate ─────────────────────────────────────────
|
|
170
|
+
// Remote Control (a session steerable from claude.ai / the Claude app) is
|
|
171
|
+
// internal-only. Now that we know the org's entitlement, resolve the final
|
|
172
|
+
// shape: auto-upgrade only for an allowed account on a capable machine, and
|
|
173
|
+
// degrade an explicit RC/server request from a non-allowed account to
|
|
174
|
+
// terminal-only with a notice. FAIL-CLOSED — a null entitlement (gate failed
|
|
175
|
+
// open) or an api without the field yields no Remote Control.
|
|
176
|
+
const rcAllowed = remoteControlAllowed(account.entitlement);
|
|
177
|
+
const rc = checkRcCapability(cli);
|
|
178
|
+
// Preserve the "how to turn Remote Control on" guidance for an allowed account
|
|
179
|
+
// whose machine can't do it yet and who didn't pin a shape (the old
|
|
180
|
+
// applyAutoShape behaviour). Non-allowed accounts never see it — RC isn't
|
|
181
|
+
// theirs — resolveRemoteControlShape gives them the internal-only notice.
|
|
182
|
+
if (rcAllowed && !shapeExplicit && shape === "terminal" && !rc.ok) {
|
|
183
|
+
say(`mnemom agent: launching terminal-only (Remote Control unavailable: ${rc.missing.join("; ")}).`);
|
|
184
|
+
say("mnemom agent: once those are in place, `mnemom agent <scenario> --remote-control` (or `config set launch remote-control`) gives you a session steerable from claude.ai and the Claude app too.");
|
|
185
|
+
}
|
|
186
|
+
shape = resolveRemoteControlShape({
|
|
187
|
+
shape,
|
|
188
|
+
shapeExplicit,
|
|
189
|
+
rcAllowed,
|
|
190
|
+
rcCapable: rc.ok,
|
|
191
|
+
say,
|
|
192
|
+
});
|
|
174
193
|
// Now that a session exists, a defaulted slug uses the real login handle from
|
|
175
194
|
// /v1/auth/me (the stored token has no email, so the pre-sign-in value was the
|
|
176
195
|
// "agent-user" fallback). Only when the caller didn't name an agent.
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -156,6 +156,39 @@ export declare function launchPreferenceExplicit(opts: {
|
|
|
156
156
|
remoteControl?: boolean;
|
|
157
157
|
server?: boolean;
|
|
158
158
|
}, env?: NodeJS.ProcessEnv): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Is this account allowed to use Remote Control (a session steerable from
|
|
161
|
+
* claude.ai and the Claude app)? Remote Control is an INTERNAL Mnemom capability
|
|
162
|
+
* — only the internal cohort (@mnemom.ai) and platform staff get it; invited
|
|
163
|
+
* customers run terminal-only. The server decides and returns `remote_control`
|
|
164
|
+
* on `GET /v1/code/entitlement`; this reads that flag, FAIL-CLOSED: a `null`
|
|
165
|
+
* entitlement (the gate failed open / no session) or an absent field (an older
|
|
166
|
+
* api that predates this flag) both mean "not allowed".
|
|
167
|
+
*/
|
|
168
|
+
export declare function remoteControlAllowed(entitlement: {
|
|
169
|
+
remote_control?: boolean;
|
|
170
|
+
} | null | undefined): boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Final launch shape after applying the Remote-Control entitlement. Pure: the
|
|
173
|
+
* machine-capability probe is passed in as `rcCapable`, and narration goes
|
|
174
|
+
* through the injected `say`.
|
|
175
|
+
*
|
|
176
|
+
* - Not allowed + a requested RC/server shape (explicit flag, env, or
|
|
177
|
+
* config) → degrade to `terminal` with a one-line notice. The CLI can't
|
|
178
|
+
* block a binary the user owns, but it won't route them through Remote
|
|
179
|
+
* Control by default.
|
|
180
|
+
* - Not allowed + terminal → terminal (nothing to say).
|
|
181
|
+
* - Allowed + terminal + not pinned → AUTO-upgrade to `remote-control` when
|
|
182
|
+
* the machine can do it (the zero-friction default), else terminal.
|
|
183
|
+
* - Allowed + an explicit RC/server shape → kept as-is.
|
|
184
|
+
*/
|
|
185
|
+
export declare function resolveRemoteControlShape(args: {
|
|
186
|
+
shape: LaunchShape;
|
|
187
|
+
shapeExplicit: boolean;
|
|
188
|
+
rcAllowed: boolean;
|
|
189
|
+
rcCapable: boolean;
|
|
190
|
+
say?: (line?: string) => void;
|
|
191
|
+
}): LaunchShape;
|
|
159
192
|
/**
|
|
160
193
|
* Build the `ANTHROPIC_CUSTOM_HEADERS` value for the terminal-only launch: the
|
|
161
194
|
* real Anthropic key as `x-api-key`, plus the governed-identity headers and the
|
package/dist/lib/agent.js
CHANGED
|
@@ -291,6 +291,49 @@ export function launchPreferenceExplicit(opts, env = process.env) {
|
|
|
291
291
|
return true;
|
|
292
292
|
return (env.MNEMOM_AGENT_REMOTE_CONTROL ?? "").trim() !== "";
|
|
293
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Is this account allowed to use Remote Control (a session steerable from
|
|
296
|
+
* claude.ai and the Claude app)? Remote Control is an INTERNAL Mnemom capability
|
|
297
|
+
* — only the internal cohort (@mnemom.ai) and platform staff get it; invited
|
|
298
|
+
* customers run terminal-only. The server decides and returns `remote_control`
|
|
299
|
+
* on `GET /v1/code/entitlement`; this reads that flag, FAIL-CLOSED: a `null`
|
|
300
|
+
* entitlement (the gate failed open / no session) or an absent field (an older
|
|
301
|
+
* api that predates this flag) both mean "not allowed".
|
|
302
|
+
*/
|
|
303
|
+
export function remoteControlAllowed(entitlement) {
|
|
304
|
+
return entitlement?.remote_control === true;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Final launch shape after applying the Remote-Control entitlement. Pure: the
|
|
308
|
+
* machine-capability probe is passed in as `rcCapable`, and narration goes
|
|
309
|
+
* through the injected `say`.
|
|
310
|
+
*
|
|
311
|
+
* - Not allowed + a requested RC/server shape (explicit flag, env, or
|
|
312
|
+
* config) → degrade to `terminal` with a one-line notice. The CLI can't
|
|
313
|
+
* block a binary the user owns, but it won't route them through Remote
|
|
314
|
+
* Control by default.
|
|
315
|
+
* - Not allowed + terminal → terminal (nothing to say).
|
|
316
|
+
* - Allowed + terminal + not pinned → AUTO-upgrade to `remote-control` when
|
|
317
|
+
* the machine can do it (the zero-friction default), else terminal.
|
|
318
|
+
* - Allowed + an explicit RC/server shape → kept as-is.
|
|
319
|
+
*/
|
|
320
|
+
export function resolveRemoteControlShape(args) {
|
|
321
|
+
const emit = args.say ?? (() => { });
|
|
322
|
+
if (!args.rcAllowed) {
|
|
323
|
+
if (args.shape !== "terminal") {
|
|
324
|
+
emit("mnemom agent: Remote Control is an internal Mnemom feature — launching terminal-only.");
|
|
325
|
+
}
|
|
326
|
+
return "terminal";
|
|
327
|
+
}
|
|
328
|
+
// Allowed. Only auto-upgrade a non-pinned terminal shape, and only when the
|
|
329
|
+
// machine can do it. When it can't, stay terminal silently — the caller owns
|
|
330
|
+
// the detailed "why unavailable / how to enable" narration (it has the
|
|
331
|
+
// RcCapability with the specific missing pieces).
|
|
332
|
+
if (args.shape === "terminal" && !args.shapeExplicit) {
|
|
333
|
+
return args.rcCapable ? "remote-control" : "terminal";
|
|
334
|
+
}
|
|
335
|
+
return args.shape;
|
|
336
|
+
}
|
|
294
337
|
/**
|
|
295
338
|
* Build the `ANTHROPIC_CUSTOM_HEADERS` value for the terminal-only launch: the
|
|
296
339
|
* real Anthropic key as `x-api-key`, plus the governed-identity headers and the
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -1209,6 +1209,13 @@ export interface CodeEntitlement {
|
|
|
1209
1209
|
org_id: string;
|
|
1210
1210
|
enabled: true;
|
|
1211
1211
|
source: "internal_cohort" | "staff" | "invitation";
|
|
1212
|
+
/**
|
|
1213
|
+
* May this account use Remote Control (a session steerable from claude.ai and
|
|
1214
|
+
* the Claude app)? Internal cohort + staff only; invited customers run
|
|
1215
|
+
* terminal-only. OPTIONAL for fail-closed back-compat: an older api that
|
|
1216
|
+
* predates the flag omits it, and absent is treated as `false`.
|
|
1217
|
+
*/
|
|
1218
|
+
remote_control?: boolean;
|
|
1212
1219
|
}
|
|
1213
1220
|
/** The machine-readable code on the gate's 403 when the org is not enabled. */
|
|
1214
1221
|
export declare const CODE_NOT_ENABLED = "code_not_enabled";
|