@mnemom/mnemom 0.17.7-next.2 → 0.17.8-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.
- package/dist/commands/agent-doctor.d.ts +4 -21
- package/dist/commands/agent-doctor.js +5 -51
- package/dist/commands/agent-goal.d.ts +1 -1
- package/dist/commands/agent-goal.js +3 -6
- package/dist/commands/agent-setup.d.ts +5 -16
- package/dist/commands/agent-setup.js +12 -29
- package/dist/commands/agent.d.ts +3 -25
- package/dist/commands/agent.js +31 -83
- package/dist/commands/wrap.d.ts +0 -8
- package/dist/commands/wrap.js +3 -36
- package/dist/lib/agent.d.ts +3 -29
- package/dist/lib/agent.js +2 -20
- package/dist/lib/api.d.ts +2 -27
- package/dist/lib/api.js +2 -64
- package/dist/lib/subscription-login.d.ts +20 -152
- package/dist/lib/subscription-login.js +28 -215
- package/dist/mcp/handoff-server.mjs +13 -35
- package/dist/rc-proxy.mjs +3 -6
- package/package.json +1 -1
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
* `mnemom agent doctor` — a read-only preflight for `mnemom agent`. It checks the
|
|
3
3
|
* things a governed launch needs and prints a checklist with clear, actionable
|
|
4
4
|
* remediation, so a customer can see at a glance why a launch would fail before
|
|
5
|
-
* it does. Writes nothing and
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* it does. Writes nothing and reads no secret VALUE (only whether a key is
|
|
6
|
+
* resolvable). Exits non-zero when the machine is not launch-ready (no coding
|
|
7
|
+
* agent CLI, or no Anthropic key), so it can gate a script.
|
|
8
8
|
*
|
|
9
|
-
* Checks: settings file, coding-agent CLI, Anthropic key source,
|
|
10
|
-
* whether it is still active (every launch sends one; none yet is informational — the launch
|
|
11
|
-
* mints it; a saved key proven dead is a warning — the launch re-mints it), gateway health
|
|
9
|
+
* Checks: settings file, coding-agent CLI, Anthropic key source, gateway health
|
|
12
10
|
* (200 + the x-mnemom-verdict header that proves the governance pipeline is
|
|
13
11
|
* live), Remote-Control capability (node + openssl + the --remote-control flag),
|
|
14
12
|
* claude.ai login (informational — Remote Control needs it), and CLI auto-update status.
|
|
@@ -19,21 +17,6 @@ export interface DoctorOptions {
|
|
|
19
17
|
}
|
|
20
18
|
/** Run the preflight and print the checklist. Sets process.exitCode on failure. */
|
|
21
19
|
export declare function agentDoctorCommand(options?: DoctorOptions): Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* The Mnemom API key row: where the key a launch would send comes from, and whether it passes
|
|
24
|
-
* the launch's own one-time check (commands/agent.ts verifyLocalMnemomKey). A key counts as dead
|
|
25
|
-
* only when proven (lib/subscription-login.ts checkMnemomKey), so only a saved key this CLI
|
|
26
|
-
* minted can be:
|
|
27
|
-
* valid → ok
|
|
28
|
-
* saved key, invalid → warn (the next launch mints a new one and revokes the old)
|
|
29
|
-
* anything else → info (not verified: a launch sends it as is)
|
|
30
|
-
* none → info (the next launch mints one for the launch's org)
|
|
31
|
-
*/
|
|
32
|
-
export declare function mnemomKeyRow(): Promise<{
|
|
33
|
-
mark: Mark;
|
|
34
|
-
text: string;
|
|
35
|
-
hint?: string;
|
|
36
|
-
}>;
|
|
37
20
|
export interface CodeAccessProbe {
|
|
38
21
|
mark: Mark;
|
|
39
22
|
text: string;
|
|
@@ -2,20 +2,18 @@
|
|
|
2
2
|
* `mnemom agent doctor` — a read-only preflight for `mnemom agent`. It checks the
|
|
3
3
|
* things a governed launch needs and prints a checklist with clear, actionable
|
|
4
4
|
* remediation, so a customer can see at a glance why a launch would fail before
|
|
5
|
-
* it does. Writes nothing and
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* it does. Writes nothing and reads no secret VALUE (only whether a key is
|
|
6
|
+
* resolvable). Exits non-zero when the machine is not launch-ready (no coding
|
|
7
|
+
* agent CLI, or no Anthropic key), so it can gate a script.
|
|
8
8
|
*
|
|
9
|
-
* Checks: settings file, coding-agent CLI, Anthropic key source,
|
|
10
|
-
* whether it is still active (every launch sends one; none yet is informational — the launch
|
|
11
|
-
* mints it; a saved key proven dead is a warning — the launch re-mints it), gateway health
|
|
9
|
+
* Checks: settings file, coding-agent CLI, Anthropic key source, gateway health
|
|
12
10
|
* (200 + the x-mnemom-verdict header that proves the governance pipeline is
|
|
13
11
|
* live), Remote-Control capability (node + openssl + the --remote-control flag),
|
|
14
12
|
* claude.ai login (informational — Remote Control needs it), and CLI auto-update status.
|
|
15
13
|
*/
|
|
16
14
|
import { spawnSync } from "node:child_process";
|
|
17
15
|
import { existsSync } from "node:fs";
|
|
18
|
-
import { anthropicKeySource, checkRcCapability, defaultConsoleKeyDeps,
|
|
16
|
+
import { anthropicKeySource, checkRcCapability, defaultConsoleKeyDeps, resolveCliBin, } from "./agent.js";
|
|
19
17
|
import { readConsoleKey } from "../lib/console-key.js";
|
|
20
18
|
import { resolveAgentAuth } from "../lib/subscription-login.js";
|
|
21
19
|
import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost, singleCellError, } from "../lib/agent.js";
|
|
@@ -75,9 +73,6 @@ export async function agentDoctorCommand(options = {}) {
|
|
|
75
73
|
else
|
|
76
74
|
add("miss", "Anthropic key: none found", "export MNEMOM_AGENT_ANTHROPIC_KEY=sk-ant-… (or ANTHROPIC_API_KEY), or run `mnemom agent` once and accept the save prompt");
|
|
77
75
|
}
|
|
78
|
-
// ── Mnemom API key: every launch sends one, whatever the auth mode ──
|
|
79
|
-
const keyRow = await mnemomKeyRow();
|
|
80
|
-
add(keyRow.mark, keyRow.text, keyRow.hint);
|
|
81
76
|
// ── one cell: the launch door must be the CLI's own cell ──
|
|
82
77
|
const cellError = singleCellError({
|
|
83
78
|
door: resolveAnthropicDoor(env),
|
|
@@ -181,47 +176,6 @@ function claudeLoggedIn(bin) {
|
|
|
181
176
|
return null;
|
|
182
177
|
}
|
|
183
178
|
}
|
|
184
|
-
const MNEMOM_KEY_MINT_HINT = "minting needs a signed-in session (`mnemom login`) and an org role that may mint a gateway key (owner, admin or member); or ask an org owner/admin for an org API key with the `gateway` scope and set MNEMOM_AGENT_MNEMOM_KEY";
|
|
185
|
-
/**
|
|
186
|
-
* The Mnemom API key row: where the key a launch would send comes from, and whether it passes
|
|
187
|
-
* the launch's own one-time check (commands/agent.ts verifyLocalMnemomKey). A key counts as dead
|
|
188
|
-
* only when proven (lib/subscription-login.ts checkMnemomKey), so only a saved key this CLI
|
|
189
|
-
* minted can be:
|
|
190
|
-
* valid → ok
|
|
191
|
-
* saved key, invalid → warn (the next launch mints a new one and revokes the old)
|
|
192
|
-
* anything else → info (not verified: a launch sends it as is)
|
|
193
|
-
* none → info (the next launch mints one for the launch's org)
|
|
194
|
-
*/
|
|
195
|
-
export async function mnemomKeyRow() {
|
|
196
|
-
const local = localLaunchMnemomKey();
|
|
197
|
-
const org = getActiveOrg();
|
|
198
|
-
const orgLabel = org ? `org ${org.slug}` : "no active org";
|
|
199
|
-
if (!local) {
|
|
200
|
-
return {
|
|
201
|
-
mark: "info",
|
|
202
|
-
text: `Mnemom API key: none yet for ${orgLabel} — the next launch mints a gateway-only org key and saves it`,
|
|
203
|
-
hint: MNEMOM_KEY_MINT_HINT,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
const where = local.source === "env"
|
|
207
|
-
? "MNEMOM_AGENT_MNEMOM_KEY (environment)"
|
|
208
|
-
: `~/.mnemom/launcher.json (saved for ${orgLabel})`;
|
|
209
|
-
const verdict = await verifyLocalMnemomKey(local);
|
|
210
|
-
if (verdict === "valid")
|
|
211
|
-
return { mark: "ok", text: `Mnemom API key: ${where} — verified` };
|
|
212
|
-
if (verdict === "invalid" && local.source === "store") {
|
|
213
|
-
return {
|
|
214
|
-
mark: "warn",
|
|
215
|
-
text: `Mnemom API key: the key saved for ${orgLabel} is no longer active (revoked or rotated) — the next launch mints a new one`,
|
|
216
|
-
hint: MNEMOM_KEY_MINT_HINT,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
mark: "info",
|
|
221
|
-
text: `Mnemom API key: ${where} — not verified (API unreachable, not signed in, or a gateway-only key the API cannot look up); a launch sends it as is`,
|
|
222
|
-
hint: "sign in (`mnemom login`) so the doctor and each launch can check a saved key; the gateway refuses a dead key with a clear message",
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
179
|
/**
|
|
226
180
|
* The invited-cohort gate, as the doctor reports it. Signed out → info (we can't
|
|
227
181
|
* check without a session); enabled → ok with the reason; a determined
|
|
@@ -31,7 +31,7 @@ export interface GoalCommandDeps {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* The Anthropic key the launch would resolve (env, then ~/.mnemom/launcher.json), never prompted
|
|
34
|
-
* for
|
|
34
|
+
* for. Lazy import: commands/agent.ts is the launcher and carries a heavy import graph.
|
|
35
35
|
*/
|
|
36
36
|
export declare function resolveLaunchCreds(): Promise<GoalControlCreds>;
|
|
37
37
|
export declare const liveGoalDeps: GoalCommandDeps;
|
|
@@ -24,20 +24,17 @@ import { askYesNo, isInteractive } from "../lib/prompt.js";
|
|
|
24
24
|
import { localGoalFlag, resolveSessionRef } from "./agent-sessions.js";
|
|
25
25
|
/**
|
|
26
26
|
* The Anthropic key the launch would resolve (env, then ~/.mnemom/launcher.json), never prompted
|
|
27
|
-
* for
|
|
27
|
+
* for. Lazy import: commands/agent.ts is the launcher and carries a heavy import graph.
|
|
28
28
|
*/
|
|
29
29
|
export async function resolveLaunchCreds() {
|
|
30
|
-
const
|
|
31
|
-
let anthropicKey;
|
|
30
|
+
const { resolveAnthropicKey } = await import("./agent.js");
|
|
32
31
|
try {
|
|
33
|
-
anthropicKey
|
|
32
|
+
return { anthropicKey: await resolveAnthropicKey(process.env, { allowPrompt: false }) };
|
|
34
33
|
}
|
|
35
34
|
catch {
|
|
36
35
|
throw new Error("no Anthropic key in env (MNEMOM_AGENT_ANTHROPIC_KEY / ANTHROPIC_API_KEY) or ~/.mnemom/launcher.json — " +
|
|
37
36
|
"the goal commands authenticate with the same key the session launched with");
|
|
38
37
|
}
|
|
39
|
-
// The Mnemom key the launch sent too (env, then the store — never minted here).
|
|
40
|
-
return { anthropicKey, mnemomKey: agentMod.localLaunchMnemomKey()?.key ?? null };
|
|
41
38
|
}
|
|
42
39
|
export const liveGoalDeps = {
|
|
43
40
|
readRecords: () => readSessionRecords({ dir: MNEMOM_DIR }).reverse(),
|
|
@@ -68,15 +68,6 @@ export interface EnsureAgentOptions {
|
|
|
68
68
|
slug: string;
|
|
69
69
|
/** The customer's Anthropic key (for the birth call + hash-proof). Never logged. */
|
|
70
70
|
anthropicKey: string;
|
|
71
|
-
/**
|
|
72
|
-
* The launch's Mnemom API key, sent on the gateway probe/birth call (x-mnemom-api-key, with a
|
|
73
|
-
* no-op x-mnemom-session-config — see birthThroughGateway). That makes the call Mnemom agent
|
|
74
|
-
* traffic: the gateway verifies the key and births a new agent in the key's org — the active
|
|
75
|
-
* org, else the personal org, i.e. the launch org the key was minted for. The claim below
|
|
76
|
-
* records the claim in that same org, which the gateway's agent-traffic org check requires of
|
|
77
|
-
* every governed turn. Never logged.
|
|
78
|
-
*/
|
|
79
|
-
mnemomKey?: string;
|
|
80
71
|
/** undefined = auto, true = force (--setup), false handled by the caller (skip). */
|
|
81
72
|
setup?: boolean;
|
|
82
73
|
/** Gateway host the launch targets (birth goes to the same cell). */
|
|
@@ -166,13 +157,11 @@ export declare function ensureSessionAndBalance(opts: {
|
|
|
166
157
|
}>;
|
|
167
158
|
/**
|
|
168
159
|
* The account for a launch an orchestrator already preflighted
|
|
169
|
-
* (`MNEMOM_AGENT_PREFLIGHTED=1`, see launchPreflighted): no session prompt
|
|
170
|
-
* µ-balance read. The
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* org
|
|
174
|
-
* the access gate and decides Remote Control fail-closed, which a caller's
|
|
175
|
-
* preflight can't vouch for.
|
|
160
|
+
* (`MNEMOM_AGENT_PREFLIGHTED=1`, see launchPreflighted): no session prompt, no
|
|
161
|
+
* personal-org lookup, no µ-balance read. The entitlement is still fetched — it
|
|
162
|
+
* is the access gate and decides Remote Control fail-closed, which a caller's
|
|
163
|
+
* preflight can't vouch for. With no active org the api resolves the caller's
|
|
164
|
+
* primary org, and the ledger records the org id the entitlement names.
|
|
176
165
|
*/
|
|
177
166
|
export declare function resolvePreflightedAccount(): Promise<{
|
|
178
167
|
orgId: string;
|
|
@@ -34,8 +34,7 @@ import { configKeySpec, loadLauncherConfig, writeLauncherConfig } from "../lib/l
|
|
|
34
34
|
import { readServedAgent, servedFingerprint, writeServedAgent } from "../lib/served-agent-cache.js";
|
|
35
35
|
import { deriveHashProof } from "./agents.js";
|
|
36
36
|
import { birthThroughGateway } from "./wrap.js";
|
|
37
|
-
import {
|
|
38
|
-
import { resolveLaunchMnemomKey } from "../lib/subscription-login.js";
|
|
37
|
+
import { resolveAnthropicKey } from "./agent.js";
|
|
39
38
|
import { assertCodeEnabled, CodeNotEnabledError } from "./agent-invite.js";
|
|
40
39
|
const say = (line = "") => console.error(line);
|
|
41
40
|
/**
|
|
@@ -146,7 +145,6 @@ export async function ensureGovernedAgent(opts) {
|
|
|
146
145
|
cached ??
|
|
147
146
|
(await birthThroughGateway(gatewayUrl, "anthropic", slug, opts.anthropicKey, {
|
|
148
147
|
timeoutMs: opts.setup === true ? undefined : SERVED_PROBE_TIMEOUT_MS,
|
|
149
|
-
mnemomKey: opts.mnemomKey,
|
|
150
148
|
}));
|
|
151
149
|
}
|
|
152
150
|
catch (err) {
|
|
@@ -208,9 +206,7 @@ export async function ensureGovernedAgent(opts) {
|
|
|
208
206
|
}
|
|
209
207
|
// 5. Birth → claim → publish observe-only cards. (Key never logged.)
|
|
210
208
|
const gatewayUrl = opts.gatewayHost.replace(/\/+$/, "");
|
|
211
|
-
const agentId = await birthThroughGateway(gatewayUrl, "anthropic", slug, opts.anthropicKey
|
|
212
|
-
mnemomKey: opts.mnemomKey,
|
|
213
|
-
});
|
|
209
|
+
const agentId = await birthThroughGateway(gatewayUrl, "anthropic", slug, opts.anthropicKey);
|
|
214
210
|
const hashProof = deriveHashProof(opts.anthropicKey, slug);
|
|
215
211
|
await claimAgent(agentId, { hashProof, orgId: org?.org_id });
|
|
216
212
|
say(fmt.success(`mnemom agent: claimed '${slug}' (${agentId}) in ${orgLabel}.`));
|
|
@@ -326,9 +322,8 @@ export async function agentSetupCommand(opts = {}) {
|
|
|
326
322
|
if (cellError)
|
|
327
323
|
throw new Error(cellError.replace("mnemom agent:", "mnemom agent setup:"));
|
|
328
324
|
// Sign in (offer sign-up) and enforce the MU balance before any account write.
|
|
329
|
-
let launchOrgId;
|
|
330
325
|
try {
|
|
331
|
-
|
|
326
|
+
await ensureSessionAndBalance({});
|
|
332
327
|
}
|
|
333
328
|
catch (err) {
|
|
334
329
|
if (isQuietPreambleExit(err))
|
|
@@ -343,16 +338,7 @@ export async function agentSetupCommand(opts = {}) {
|
|
|
343
338
|
: await resolveDefaultSlug({ strict: true });
|
|
344
339
|
if (!slug)
|
|
345
340
|
throw new Error("mnemom agent setup: agent slug sanitised to empty — pass --agent <slug>.");
|
|
346
|
-
|
|
347
|
-
// is claimed into (see EnsureAgentOptions.mnemomKey).
|
|
348
|
-
const mnemomKey = await resolveLaunchMnemomKey(defaultMnemomKeyDeps(undefined, launchOrgId));
|
|
349
|
-
const result = await ensureGovernedAgent({
|
|
350
|
-
slug,
|
|
351
|
-
anthropicKey,
|
|
352
|
-
mnemomKey,
|
|
353
|
-
setup: true,
|
|
354
|
-
gatewayHost,
|
|
355
|
-
});
|
|
341
|
+
const result = await ensureGovernedAgent({ slug, anthropicKey, setup: true, gatewayHost });
|
|
356
342
|
if (!result.provisioned) {
|
|
357
343
|
say(fmt.dim(` '${slug}' is ready (already claimed) or setup was skipped.`));
|
|
358
344
|
}
|
|
@@ -522,20 +508,17 @@ export async function ensureSessionAndBalance(opts) {
|
|
|
522
508
|
}
|
|
523
509
|
/**
|
|
524
510
|
* The account for a launch an orchestrator already preflighted
|
|
525
|
-
* (`MNEMOM_AGENT_PREFLIGHTED=1`, see launchPreflighted): no session prompt
|
|
526
|
-
* µ-balance read. The
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
* org
|
|
530
|
-
* the access gate and decides Remote Control fail-closed, which a caller's
|
|
531
|
-
* preflight can't vouch for.
|
|
511
|
+
* (`MNEMOM_AGENT_PREFLIGHTED=1`, see launchPreflighted): no session prompt, no
|
|
512
|
+
* personal-org lookup, no µ-balance read. The entitlement is still fetched — it
|
|
513
|
+
* is the access gate and decides Remote Control fail-closed, which a caller's
|
|
514
|
+
* preflight can't vouch for. With no active org the api resolves the caller's
|
|
515
|
+
* primary org, and the ledger records the org id the entitlement names.
|
|
532
516
|
*/
|
|
533
517
|
export async function resolvePreflightedAccount() {
|
|
534
518
|
const active = getActiveOrg();
|
|
535
|
-
const
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
return { orgId, orgLabel, entitlement };
|
|
519
|
+
const orgLabel = active ? `${active.name} (${active.slug})` : "your primary org";
|
|
520
|
+
const entitlement = await assertCodeEnabled(active?.org_id ?? "", orgLabel);
|
|
521
|
+
return { orgId: active?.org_id ?? entitlement?.org_id ?? "", orgLabel, entitlement };
|
|
539
522
|
}
|
|
540
523
|
// ── The default coding posture (cards observe-only; goal contracts nudge) ────
|
|
541
524
|
function buildCodeAlignmentCard(agentId) {
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* line, or the proxy's stdin under RC). It is never logged, printed, or passed as
|
|
26
26
|
* an argv value.
|
|
27
27
|
*/
|
|
28
|
-
import { type
|
|
28
|
+
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";
|
|
@@ -201,30 +201,8 @@ export declare function recordSessionLaunch(ctx: {
|
|
|
201
201
|
/** The resolved compaction target this launch runs under (`resolveCompactAt`). */
|
|
202
202
|
compactAt: number | null;
|
|
203
203
|
}): Promise<void>;
|
|
204
|
-
/**
|
|
205
|
-
export declare
|
|
206
|
-
/**
|
|
207
|
-
* Mint the launch's key: an ORG-owned key for `orgId` (the launch's org) with scopes exactly
|
|
208
|
-
* ["gateway"], which any owner/admin/member may mint — so the gateway's agent-traffic org check
|
|
209
|
-
* (agent org = key org) passes for the agent the launcher claims into that org. Never a personal
|
|
210
|
-
* key: it has no org, and the gateway would refuse every governed turn with it. A refusal (403:
|
|
211
|
-
* an API that predates member org keys, or a viewer/auditor role) is thrown as is; the launch
|
|
212
|
-
* stops with guidance (lib/subscription-login.ts mintAndStore).
|
|
213
|
-
*/
|
|
214
|
-
export declare function mintLaunchMnemomKey(orgId: string): Promise<StoredMnemomKey>;
|
|
215
|
-
/**
|
|
216
|
-
* The real I/O behind every launch's Mnemom key (lib/subscription-login.ts). `launchOrgId` is
|
|
217
|
-
* the org the launch runs in (ensureSessionAndBalance's `orgId`: the active org, else the
|
|
218
|
-
* caller's personal org); omitted or empty, it is the active org.
|
|
219
|
-
*/
|
|
220
|
-
export declare function defaultMnemomKeyDeps(storePath?: string, launchOrgId?: string | null): MnemomKeyDeps;
|
|
221
|
-
/**
|
|
222
|
-
* The Mnemom key already on this machine (MNEMOM_AGENT_MNEMOM_KEY, then ~/.mnemom/launcher.json),
|
|
223
|
-
* never minted: `mnemom agent doctor` reports its source, the goal commands send it.
|
|
224
|
-
*/
|
|
225
|
-
export declare function localLaunchMnemomKey(storePath?: string): LocalMnemomKey | null;
|
|
226
|
-
/** The launch's one-time check of that key (`mnemom agent doctor` reports it). Never throws. */
|
|
227
|
-
export declare function verifyLocalMnemomKey(local: LocalMnemomKey): Promise<MnemomKeyCheck>;
|
|
204
|
+
/** The real I/O behind a subscription launch's Mnemom key (lib/subscription-login.ts). */
|
|
205
|
+
export declare function defaultMnemomKeyDeps(storePath?: string): MnemomKeyDeps;
|
|
228
206
|
/** The real machine behind lib/console-key.ts: the macOS keychain via `security`, no shell. */
|
|
229
207
|
export declare function defaultConsoleKeyDeps(env: NodeJS.ProcessEnv): ConsoleKeyDeps;
|
|
230
208
|
/**
|
package/dist/commands/agent.js
CHANGED
|
@@ -33,10 +33,10 @@ import { basename, join, dirname } from "node:path";
|
|
|
33
33
|
import { fileURLToPath } from "node:url";
|
|
34
34
|
import { statuslineLaunch } from "../lib/statusline.js";
|
|
35
35
|
import { getApiUrl, getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
36
|
+
import { createApiKey, getAgentByName } from "../lib/api.js";
|
|
37
|
+
import { MNEMOM_KEY_STORE_FIELD, resolveAgentAuth, resolveSubscriptionMnemomKey, subscriptionChildEnv, subscriptionShape, } from "../lib/subscription-login.js";
|
|
38
38
|
import { LEDGER_FILE, appendSessionRecord, findResumeCandidate, readSessionRecords, resolveRetentionDays, resumedContextProfile, toLedgerContract, } from "../lib/agent-ledger.js";
|
|
39
|
-
import {
|
|
39
|
+
import { loadCliConfig } from "../lib/cli-config.js";
|
|
40
40
|
import { configKeySpec, loadLauncherConfig, writeLauncherConfig } from "../lib/launcher-config.js";
|
|
41
41
|
import { experimentalNotice } from "../lib/experimental.js";
|
|
42
42
|
import { fmt } from "../lib/format.js";
|
|
@@ -45,8 +45,8 @@ import { authConfigured, chooseAuth, detectClaudeLogin, readClaudeLoginState, }
|
|
|
45
45
|
import { requireConsoleKey, readConsoleKey } from "../lib/console-key.js";
|
|
46
46
|
import { goalAlignmentLine, resolveSessionSetup } from "./agent-session.js";
|
|
47
47
|
import { AgentIdentityMismatchError, defaultAgentSlug, ensureGovernedAgent, ensureSessionAndBalance, isQuietPreambleExit, resolveDefaultSlug, resolvePreflightedAccount, } from "./agent-setup.js";
|
|
48
|
-
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, assertCompactAtCompatible, assertOffProfileCompatible, compactAtWindowWarning, describeCompactAt, launchWindow, resolveCompactAt, applyGatewayHintHeaders, resolveCompactionIntercept, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, HANDOFF_RC_KEY_ENV_NAME,
|
|
49
|
-
import { LAUNCHER_STORE_PATH, mergeStore
|
|
48
|
+
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, assertCompactAtCompatible, assertOffProfileCompatible, compactAtWindowWarning, describeCompactAt, launchWindow, resolveCompactAt, applyGatewayHintHeaders, resolveCompactionIntercept, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, HANDOFF_RC_KEY_ENV_NAME, isProdGateway, launchPreferenceExplicit, launchPreflighted, remoteControlAllowed, applyToolSearchDefault, DISABLE_1M_ENV, readCcSettings, resolveWindowModel, resolveAnthropicDoor, resolveLaunchModel, resolveConversationId, resolveGatewayHost, singleCellError, resolveLaunchShape, resolveRemoteControlShape, passthroughName, sanitizeAgentName, sessionDisplayName, SESSION_NAME_ENV, SESSION_CONFIG_HEADER, } from "../lib/agent.js";
|
|
49
|
+
import { LAUNCHER_STORE_PATH, mergeStore } from "../lib/launcher-store.js";
|
|
50
50
|
import { slackChannelArgs } from "../lib/slack-launch.js";
|
|
51
51
|
/** The CLI's own provider-key store (zero 1Password): ~/.mnemom/launcher.json, 0600. */
|
|
52
52
|
const CODE_STORE = LAUNCHER_STORE_PATH;
|
|
@@ -328,9 +328,8 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
328
328
|
// lives in (default: the user's personal org). No µ ⇒ blocked with a
|
|
329
329
|
// portal-style top-up message. Skipped only on --dry-run (handled above).
|
|
330
330
|
// MNEMOM_AGENT_PREFLIGHTED=1 (orchestrators such as the ADW fleet that already
|
|
331
|
-
// ran their own session + balance preflight) skips those round trips; the
|
|
332
|
-
//
|
|
333
|
-
// resolvePreflightedAccount.
|
|
331
|
+
// ran their own session + balance preflight) skips those round trips; the
|
|
332
|
+
// entitlement is still fetched — see resolvePreflightedAccount.
|
|
334
333
|
let account;
|
|
335
334
|
try {
|
|
336
335
|
account = launchPreflighted(env)
|
|
@@ -370,19 +369,14 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
370
369
|
rcCapable: rc.ok,
|
|
371
370
|
say,
|
|
372
371
|
});
|
|
373
|
-
// Subscription login: Remote Control only for RC-entitled (internal) accounts
|
|
372
|
+
// Subscription login: Remote Control only for RC-entitled (internal) accounts, and the governed identity anchors on a
|
|
373
|
+
// Mnemom API key (minted once and stored if there is none). Resolved after sign-in so
|
|
374
|
+
// minting can use the session.
|
|
375
|
+
let mnemomKey;
|
|
374
376
|
if (auth === "subscription") {
|
|
375
377
|
shape = subscriptionShape(shape, shapeExplicit, rcAllowed, say);
|
|
378
|
+
mnemomKey = await resolveSubscriptionMnemomKey(defaultMnemomKeyDeps());
|
|
376
379
|
}
|
|
377
|
-
// Every launch — api-key, console and subscription, every shape — sends a Mnemom API key
|
|
378
|
-
// (x-mnemom-api-key): the gateway refuses `mnemom agent` traffic without a verified one.
|
|
379
|
-
// Resolved after sign-in so minting and the one-time liveness check can use the session: the
|
|
380
|
-
// key stored for the active org, checked once (a revoked/rotated one is re-minted), else a
|
|
381
|
-
// fresh key minted for the launch's org (account.orgId: the active org, else the personal
|
|
382
|
-
// org — the org the agent is claimed into, which the gateway's org check requires of the key)
|
|
383
|
-
// and stored. No key ⇒ this throws and the launch does not start (a session the gateway
|
|
384
|
-
// would refuse is worse than an actionable error).
|
|
385
|
-
const mnemomKey = await resolveLaunchMnemomKey(defaultMnemomKeyDeps(CODE_STORE, account.orgId));
|
|
386
380
|
// Now that a session exists, a defaulted slug uses the real login handle from
|
|
387
381
|
// /v1/auth/me (the stored token has no email, so the pre-sign-in value was the
|
|
388
382
|
// "agent-user" fallback). Only when the caller didn't name an agent. Under an
|
|
@@ -413,7 +407,6 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
413
407
|
const ensured = await ensureGovernedAgent({
|
|
414
408
|
slug: agent,
|
|
415
409
|
anthropicKey,
|
|
416
|
-
mnemomKey,
|
|
417
410
|
setup: options.setup,
|
|
418
411
|
gatewayHost,
|
|
419
412
|
});
|
|
@@ -543,22 +536,15 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
543
536
|
// back out at request time. Remote Control (both shapes) strips every ANTHROPIC_* auth
|
|
544
537
|
// var from the child's env, so `spawnRcCli` below instead sets a dedicated, non-ANTHROPIC
|
|
545
538
|
// var (HANDOFF_RC_KEY_ENV_NAME) on that same child env — still nothing in this file.
|
|
546
|
-
|
|
547
|
-
// in ANTHROPIC_CUSTOM_HEADERS (terminal), or HANDOFF_RC_MNEMOM_KEY_ENV_NAME (RC).
|
|
548
|
-
const terminal = shape === "terminal";
|
|
549
|
-
const anthropicKeySource = terminal
|
|
539
|
+
const anthropicKeySource = shape === "terminal"
|
|
550
540
|
? { envName: "ANTHROPIC_CUSTOM_HEADERS" }
|
|
551
541
|
: { envName: HANDOFF_RC_KEY_ENV_NAME };
|
|
552
|
-
const mnemomKeySource = terminal
|
|
553
|
-
? { envName: "ANTHROPIC_CUSTOM_HEADERS" }
|
|
554
|
-
: { envName: HANDOFF_RC_MNEMOM_KEY_ENV_NAME };
|
|
555
542
|
handoffMcpConfigPath = writeHandoffMcpConfigFile(buildHandoffMcpConfigJson({
|
|
556
543
|
// The SAME origin the session's own model traffic goes to — not resolveGatewayHost,
|
|
557
544
|
// which would ignore a MNEMOM_AGENT_ANTHROPIC_GATEWAY override (a test-cell launch
|
|
558
545
|
// must never send its handoff calls to prod, or vice versa).
|
|
559
546
|
gatewayHost: resolveHandoffGatewayHost(door),
|
|
560
547
|
anthropicKeySource,
|
|
561
|
-
mnemomKeySource,
|
|
562
548
|
agent,
|
|
563
549
|
conversationId,
|
|
564
550
|
sessionConfigB64,
|
|
@@ -778,55 +764,25 @@ export async function recordSessionLaunch(ctx) {
|
|
|
778
764
|
// ───────────────────────────────────────────────────────────────────────────
|
|
779
765
|
// Credential resolution (zero 1Password)
|
|
780
766
|
// ───────────────────────────────────────────────────────────────────────────
|
|
781
|
-
/**
|
|
782
|
-
export
|
|
783
|
-
/**
|
|
784
|
-
* Mint the launch's key: an ORG-owned key for `orgId` (the launch's org) with scopes exactly
|
|
785
|
-
* ["gateway"], which any owner/admin/member may mint — so the gateway's agent-traffic org check
|
|
786
|
-
* (agent org = key org) passes for the agent the launcher claims into that org. Never a personal
|
|
787
|
-
* key: it has no org, and the gateway would refuse every governed turn with it. A refusal (403:
|
|
788
|
-
* an API that predates member org keys, or a viewer/auditor role) is thrown as is; the launch
|
|
789
|
-
* stops with guidance (lib/subscription-login.ts mintAndStore).
|
|
790
|
-
*/
|
|
791
|
-
export async function mintLaunchMnemomKey(orgId) {
|
|
792
|
-
const k = await createOrgApiKey(orgId, `mnemom-agent ${hostname()}`, ["gateway"]);
|
|
793
|
-
return { key: k.key, keyId: k.key_id };
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* The real I/O behind every launch's Mnemom key (lib/subscription-login.ts). `launchOrgId` is
|
|
797
|
-
* the org the launch runs in (ensureSessionAndBalance's `orgId`: the active org, else the
|
|
798
|
-
* caller's personal org); omitted or empty, it is the active org.
|
|
799
|
-
*/
|
|
800
|
-
export function defaultMnemomKeyDeps(storePath = CODE_STORE, launchOrgId) {
|
|
801
|
-
const activeOrgId = getActiveOrg()?.org_id || null;
|
|
767
|
+
/** The real I/O behind a subscription launch's Mnemom key (lib/subscription-login.ts). */
|
|
768
|
+
export function defaultMnemomKeyDeps(storePath = CODE_STORE) {
|
|
802
769
|
return {
|
|
803
770
|
env: process.env,
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
771
|
+
readStored: () => {
|
|
772
|
+
try {
|
|
773
|
+
const data = JSON.parse(readFileSync(storePath, "utf8"));
|
|
774
|
+
const v = data[MNEMOM_KEY_STORE_FIELD];
|
|
775
|
+
return typeof v === "string" ? stripCrlf(v) : undefined;
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
return undefined;
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
mint: async () => (await createApiKey(`mnemom-agent ${hostname()}`, ["gateway"])).key,
|
|
782
|
+
store: (key) => mergeStore({ [MNEMOM_KEY_STORE_FIELD]: key }, storePath),
|
|
816
783
|
say,
|
|
817
784
|
};
|
|
818
785
|
}
|
|
819
|
-
/**
|
|
820
|
-
* The Mnemom key already on this machine (MNEMOM_AGENT_MNEMOM_KEY, then ~/.mnemom/launcher.json),
|
|
821
|
-
* never minted: `mnemom agent doctor` reports its source, the goal commands send it.
|
|
822
|
-
*/
|
|
823
|
-
export function localLaunchMnemomKey(storePath = CODE_STORE) {
|
|
824
|
-
return readLocalMnemomKey(defaultMnemomKeyDeps(storePath));
|
|
825
|
-
}
|
|
826
|
-
/** The launch's one-time check of that key (`mnemom agent doctor` reports it). Never throws. */
|
|
827
|
-
export function verifyLocalMnemomKey(local) {
|
|
828
|
-
return defaultMnemomKeyDeps().check(local.key, local.keyId);
|
|
829
|
-
}
|
|
830
786
|
/** The real machine behind lib/console-key.ts: the macOS keychain via `security`, no shell. */
|
|
831
787
|
export function defaultConsoleKeyDeps(env) {
|
|
832
788
|
return {
|
|
@@ -1102,8 +1058,6 @@ function launchTerminal(ctx) {
|
|
|
1102
1058
|
if (ctx.anthropicKey) {
|
|
1103
1059
|
childEnv = { ...process.env };
|
|
1104
1060
|
delete childEnv.ANTHROPIC_API_KEY;
|
|
1105
|
-
// The Mnemom key already rides in the headers; the child has no other use for it.
|
|
1106
|
-
Reflect.deleteProperty(childEnv, MNEMOM_KEY_ENV);
|
|
1107
1061
|
childEnv.ANTHROPIC_BASE_URL = ctx.door;
|
|
1108
1062
|
childEnv.ANTHROPIC_AUTH_TOKEN = "mnemom-gateway";
|
|
1109
1063
|
childEnv.ANTHROPIC_CUSTOM_HEADERS = headers;
|
|
@@ -1288,10 +1242,9 @@ function launchRemoteControl(ctx) {
|
|
|
1288
1242
|
}
|
|
1289
1243
|
};
|
|
1290
1244
|
process.on("exit", onExit);
|
|
1291
|
-
// Secrets on stdin — three lines (anthropic, mnemom, openai).
|
|
1292
|
-
// key
|
|
1293
|
-
//
|
|
1294
|
-
// OAuth bearer is what pays). openai is always empty on this Anthropic-only door.
|
|
1245
|
+
// Secrets on stdin — three lines (anthropic, mnemom, openai). An api-key launch sends the
|
|
1246
|
+
// Anthropic key only; a subscription launch sends the Mnemom key only (the identity anchor —
|
|
1247
|
+
// the user's OAuth bearer is what pays). openai is always empty on this Anthropic-only door.
|
|
1295
1248
|
// A proxy that exits before reading them turns this write into an EPIPE; that is
|
|
1296
1249
|
// not a failure of its own — the exited-before-READY check below refuses the
|
|
1297
1250
|
// launch. Any other stdin error fails closed like a spawn error.
|
|
@@ -1302,7 +1255,7 @@ function launchRemoteControl(ctx) {
|
|
|
1302
1255
|
onExit();
|
|
1303
1256
|
process.exit(1);
|
|
1304
1257
|
});
|
|
1305
|
-
proxy.stdin?.end(`${ctx.anthropicKey ?? ""}\n${ctx.mnemomKey}\n\n`);
|
|
1258
|
+
proxy.stdin?.end(`${ctx.anthropicKey ?? ""}\n${ctx.anthropicKey ? "" : (ctx.mnemomKey ?? "")}\n\n`);
|
|
1306
1259
|
let stdoutBuf = "";
|
|
1307
1260
|
const start = Date.now();
|
|
1308
1261
|
const tryStart = () => {
|
|
@@ -1370,9 +1323,6 @@ function spawnRcCli(ctx) {
|
|
|
1370
1323
|
"no_proxy",
|
|
1371
1324
|
"HTTP_PROXY",
|
|
1372
1325
|
"http_proxy",
|
|
1373
|
-
// The Mnemom key rides to the gateway via the proxy (stdin) and, with handoff on, in
|
|
1374
|
-
// HANDOFF_RC_MNEMOM_KEY_ENV_NAME below; the CLI itself has no use for the launch's env var.
|
|
1375
|
-
MNEMOM_KEY_ENV,
|
|
1376
1326
|
]);
|
|
1377
1327
|
const childEnv = {};
|
|
1378
1328
|
for (const [k, v] of Object.entries(process.env)) {
|
|
@@ -1400,10 +1350,8 @@ function spawnRcCli(ctx) {
|
|
|
1400
1350
|
// ride through here ungated), so this rides through untouched. Only set when the tool is
|
|
1401
1351
|
// actually registered — never on a plain RC launch.
|
|
1402
1352
|
// (Never on a subscription launch — handoff is turned off there, so there is no key to set.)
|
|
1403
|
-
// The Mnemom key the gateway requires rides beside it in its own non-ANTHROPIC var.
|
|
1404
1353
|
if (ctx.handoffMcpConfigPath && ctx.anthropicKey) {
|
|
1405
1354
|
childEnv[HANDOFF_RC_KEY_ENV_NAME] = ctx.anthropicKey;
|
|
1406
|
-
childEnv[HANDOFF_RC_MNEMOM_KEY_ENV_NAME] = ctx.mnemomKey;
|
|
1407
1355
|
}
|
|
1408
1356
|
let args;
|
|
1409
1357
|
if (ctx.shape === "server") {
|
package/dist/commands/wrap.d.ts
CHANGED
|
@@ -26,13 +26,5 @@ export interface WrapOptions {
|
|
|
26
26
|
export declare function wrapCommand(options?: WrapOptions): Promise<void>;
|
|
27
27
|
export declare function birthThroughGateway(gatewayUrl: string, provider: Provider, agentName: string, providerKey: string, opts?: {
|
|
28
28
|
timeoutMs?: number;
|
|
29
|
-
/**
|
|
30
|
-
* The caller's Mnemom API key, sent as `x-mnemom-api-key` together with a no-op
|
|
31
|
-
* `x-mnemom-session-config` (NOOP_SESSION_CONFIG_B64). The session config makes the call
|
|
32
|
-
* Mnemom agent traffic, so the gateway verifies the key and births a NEW agent in the key's
|
|
33
|
-
* org; the caller's claim step then records the claim in that same org. `mnemom agent`
|
|
34
|
-
* always sends its launch key; `mnemom wrap` sends neither header (plain traffic, as before).
|
|
35
|
-
*/
|
|
36
|
-
mnemomKey?: string;
|
|
37
29
|
}): Promise<string>;
|
|
38
30
|
export {};
|
package/dist/commands/wrap.js
CHANGED
|
@@ -20,9 +20,7 @@ import { mergeAgentConfig } from "../lib/agent-config.js";
|
|
|
20
20
|
import { askSelect, askInput, isInteractive } from "../lib/prompt.js";
|
|
21
21
|
import { getGatewayUrl } from "../lib/config.js";
|
|
22
22
|
import { fmt } from "../lib/format.js";
|
|
23
|
-
import { NOOP_SESSION_CONFIG_B64, SESSION_CONFIG_HEADER } from "../lib/agent.js";
|
|
24
23
|
const AGENT_HEADER = "x-mnemom-agent";
|
|
25
|
-
const MNEMOM_KEY_HEADER = "x-mnemom-api-key";
|
|
26
24
|
const PROVIDERS = {
|
|
27
25
|
anthropic: {
|
|
28
26
|
label: "Anthropic",
|
|
@@ -210,10 +208,6 @@ export async function birthThroughGateway(gatewayUrl, provider, agentName, provi
|
|
|
210
208
|
[prov.keyHeader]: authValue,
|
|
211
209
|
[AGENT_HEADER]: agentName,
|
|
212
210
|
};
|
|
213
|
-
if (opts.mnemomKey) {
|
|
214
|
-
headers[MNEMOM_KEY_HEADER] = opts.mnemomKey;
|
|
215
|
-
headers[SESSION_CONFIG_HEADER] = NOOP_SESSION_CONFIG_B64;
|
|
216
|
-
}
|
|
217
211
|
if (provider === "anthropic") {
|
|
218
212
|
headers["anthropic-version"] = "2023-06-01";
|
|
219
213
|
}
|
|
@@ -241,24 +235,13 @@ export async function birthThroughGateway(gatewayUrl, provider, agentName, provi
|
|
|
241
235
|
messages: [{ role: "user", content: "hello" }],
|
|
242
236
|
});
|
|
243
237
|
}
|
|
244
|
-
const
|
|
238
|
+
const response = await fetch(url, {
|
|
245
239
|
method: "POST",
|
|
246
|
-
headers
|
|
240
|
+
headers,
|
|
247
241
|
body,
|
|
248
242
|
signal: opts.timeoutMs ? AbortSignal.timeout(opts.timeoutMs) : undefined,
|
|
249
243
|
});
|
|
250
|
-
|
|
251
|
-
let agentId = response.headers.get(AGENT_HEADER);
|
|
252
|
-
// An agent an earlier plain call already birthed sits unclaimed in the sandbox org, and the
|
|
253
|
-
// gateway refuses agent traffic for it with 402 `agent_unclaimed` (and no agent id) — yet that
|
|
254
|
-
// is exactly the agent `mnemom agent setup` must claim. Ask once more as plain traffic (no
|
|
255
|
-
// session config): that serves it exactly as before and returns the id for the claim step.
|
|
256
|
-
if (!agentId && SESSION_CONFIG_HEADER in headers && (await isAgentUnclaimedRefusal(response))) {
|
|
257
|
-
const plain = { ...headers };
|
|
258
|
-
Reflect.deleteProperty(plain, SESSION_CONFIG_HEADER);
|
|
259
|
-
response = await post(plain);
|
|
260
|
-
agentId = response.headers.get(AGENT_HEADER);
|
|
261
|
-
}
|
|
244
|
+
const agentId = response.headers.get(AGENT_HEADER);
|
|
262
245
|
if (!response.ok && !agentId) {
|
|
263
246
|
const text = await response.text().catch(() => "");
|
|
264
247
|
throw new MnemomApiError(response.status, `Gateway returned ${response.status}: ${text.slice(0, 300)}`);
|
|
@@ -268,22 +251,6 @@ export async function birthThroughGateway(gatewayUrl, provider, agentName, provi
|
|
|
268
251
|
}
|
|
269
252
|
return agentId;
|
|
270
253
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Is `response` the gateway's agent-traffic refusal for an agent still unclaimed in the sandbox
|
|
273
|
-
* org — 402 with `error.details.reason === "agent_unclaimed"`? Reads a clone, so the caller can
|
|
274
|
-
* still read the body. Anything unparseable is `false`.
|
|
275
|
-
*/
|
|
276
|
-
async function isAgentUnclaimedRefusal(response) {
|
|
277
|
-
if (response.status !== 402)
|
|
278
|
-
return false;
|
|
279
|
-
try {
|
|
280
|
-
const body = (await response.clone().json());
|
|
281
|
-
return body?.error?.details?.reason === "agent_unclaimed";
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
254
|
// ── Starter cards ─────────────────────────────────────────────────────────────
|
|
288
255
|
function buildStarterAlignmentCard(agentId) {
|
|
289
256
|
return {
|