@mnemom/mnemom 0.17.3-next.6 → 0.17.3
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 +10 -1
- package/dist/commands/agent-sessions.d.ts +5 -0
- package/dist/commands/agent-sessions.js +7 -1
- package/dist/commands/agent-setup.js +9 -2
- package/dist/commands/agent.js +5 -2
- package/dist/index.js +3 -3
- package/dist/lib/agent.d.ts +21 -5
- package/dist/lib/agent.js +42 -5
- package/dist/lib/launcher-config.js +1 -1
- package/dist/lib/slack-router.js +3 -1
- package/package.json +1 -1
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
import { spawnSync } from "node:child_process";
|
|
15
15
|
import { existsSync } from "node:fs";
|
|
16
16
|
import { anthropicKeySource, checkRcCapability, resolveCliBin } from "./agent.js";
|
|
17
|
-
import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost } from "../lib/agent.js";
|
|
17
|
+
import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost, singleCellError, } from "../lib/agent.js";
|
|
18
|
+
import { getApiUrl, getGatewayUrl } from "../lib/config.js";
|
|
18
19
|
import { LAUNCHER_CONFIG_PATH } from "../lib/launcher-config.js";
|
|
19
20
|
import { getCodeEntitlement, MnemomApiError } from "../lib/api.js";
|
|
20
21
|
import { isLoggedIn } from "../lib/auth.js";
|
|
@@ -52,6 +53,14 @@ export async function agentDoctorCommand(options = {}) {
|
|
|
52
53
|
add("ok", "Anthropic key: found in ~/.mnemom/launcher.json");
|
|
53
54
|
else
|
|
54
55
|
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");
|
|
56
|
+
// ── one cell: the launch door must be the CLI's own cell ──
|
|
57
|
+
const cellError = singleCellError({
|
|
58
|
+
door: resolveAnthropicDoor(env),
|
|
59
|
+
apiUrl: getApiUrl(),
|
|
60
|
+
cellGateway: getGatewayUrl(),
|
|
61
|
+
});
|
|
62
|
+
if (cellError)
|
|
63
|
+
add("miss", "cell: mixed — a launch would be refused", cellError);
|
|
55
64
|
// ── gateway health ──
|
|
56
65
|
const host = resolveGatewayHost(env);
|
|
57
66
|
const cell = isProdGateway(env) ? "us-2/prod" : "custom";
|
|
@@ -26,6 +26,11 @@ export interface SessionCommandDeps {
|
|
|
26
26
|
* one `live goal` line an implicit session gets. Optional: absent ⇒ no line.
|
|
27
27
|
*/
|
|
28
28
|
fetchGoal?: (record: SessionRecord) => Promise<GoalShowResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* The gateway of the cell this CLI reads live state from. A session that ran on another
|
|
31
|
+
* cell is not looked up there (it would always read as "no sealed contract"). Absent ⇒ no check.
|
|
32
|
+
*/
|
|
33
|
+
cellGateway?: () => string;
|
|
29
34
|
out: (line: string) => void;
|
|
30
35
|
err: (line: string) => void;
|
|
31
36
|
}
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* only the server's hash is shown, labelled "sealed hash".
|
|
15
15
|
*/
|
|
16
16
|
import { readSessionRecords } from "../lib/agent-ledger.js";
|
|
17
|
-
import { MNEMOM_DIR } from "../lib/config.js";
|
|
17
|
+
import { getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
|
|
18
|
+
import { sameOrigin } from "../lib/agent.js";
|
|
18
19
|
import { getAgentSession, MnemomApiError } from "../lib/api.js";
|
|
19
20
|
import { fmt, formatRelativeAge } from "../lib/format.js";
|
|
20
21
|
import { goalControlFetch } from "../lib/goal-api.js";
|
|
@@ -31,6 +32,7 @@ export const liveSessionDeps = {
|
|
|
31
32
|
const creds = await resolveLaunchCreds();
|
|
32
33
|
return goalControlFetch(record, "show", { thread: "main" }, { fetch, creds });
|
|
33
34
|
},
|
|
35
|
+
cellGateway: () => getGatewayUrl(),
|
|
34
36
|
out: (line) => console.log(line),
|
|
35
37
|
err: (line) => console.error(line),
|
|
36
38
|
};
|
|
@@ -318,9 +320,13 @@ export async function agentShowCommand(ref, opts, deps) {
|
|
|
318
320
|
const { record, others } = resolved;
|
|
319
321
|
let remote = null;
|
|
320
322
|
let reason;
|
|
323
|
+
const cell = deps.cellGateway?.();
|
|
321
324
|
if (record.agent_id === null) {
|
|
322
325
|
reason = "agent id was not resolved at launch";
|
|
323
326
|
}
|
|
327
|
+
else if (cell !== undefined && !sameOrigin(record.gateway_host, cell)) {
|
|
328
|
+
reason = `this session ran on ${record.gateway_host}, not this CLI's cell (${cell}) — point the CLI at that cell to read it`;
|
|
329
|
+
}
|
|
324
330
|
else {
|
|
325
331
|
try {
|
|
326
332
|
remote = await deps.fetchSession(record.conversation_id, record.agent_id, record.org_id);
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
import { claimAgent, getAgentByName, getMe, getMuBalance, getMyPersonalOrg, MnemomApiError, mintProtectionCapability, putAlignmentCard, putProtectionCard, } from "../lib/api.js";
|
|
27
27
|
import { getAuthInfo, isLoggedIn, loginWithBrowser, loginWithDeviceFlow, resolveAuth, } from "../lib/auth.js";
|
|
28
28
|
import { getActiveOrg } from "../lib/cli-config.js";
|
|
29
|
-
import { getWebsiteUrl } from "../lib/config.js";
|
|
29
|
+
import { getApiUrl, getGatewayUrl, getWebsiteUrl } from "../lib/config.js";
|
|
30
30
|
import { fmt } from "../lib/format.js";
|
|
31
31
|
import { askYesNo, isInteractive } from "../lib/prompt.js";
|
|
32
|
-
import { resolveGatewayHost, sanitizeAgentName } from "../lib/agent.js";
|
|
32
|
+
import { resolveAnthropicDoor, resolveGatewayHost, sanitizeAgentName, singleCellError, } from "../lib/agent.js";
|
|
33
33
|
import { configKeySpec, loadLauncherConfig, writeLauncherConfig } from "../lib/launcher-config.js";
|
|
34
34
|
import { deriveHashProof } from "./agents.js";
|
|
35
35
|
import { birthThroughGateway } from "./wrap.js";
|
|
@@ -273,6 +273,13 @@ async function publishDefaultCards(agentId) {
|
|
|
273
273
|
/** `mnemom agent setup [--agent <slug>]` — the explicit provisioning entry point. */
|
|
274
274
|
export async function agentSetupCommand(opts = {}) {
|
|
275
275
|
const gatewayHost = resolveGatewayHost(process.env);
|
|
276
|
+
const cellError = singleCellError({
|
|
277
|
+
door: resolveAnthropicDoor(process.env),
|
|
278
|
+
apiUrl: getApiUrl(),
|
|
279
|
+
cellGateway: getGatewayUrl(),
|
|
280
|
+
});
|
|
281
|
+
if (cellError)
|
|
282
|
+
throw new Error(cellError.replace("mnemom agent:", "mnemom agent setup:"));
|
|
276
283
|
// Sign in (offer sign-up) and enforce the MU balance before any account write.
|
|
277
284
|
try {
|
|
278
285
|
await ensureSessionAndBalance({});
|
package/dist/commands/agent.js
CHANGED
|
@@ -31,7 +31,7 @@ import { randomUUID } from "node:crypto";
|
|
|
31
31
|
import { tmpdir, homedir } from "node:os";
|
|
32
32
|
import { basename, join, dirname } from "node:path";
|
|
33
33
|
import { fileURLToPath } from "node:url";
|
|
34
|
-
import { MNEMOM_DIR } from "../lib/config.js";
|
|
34
|
+
import { getApiUrl, getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
|
|
35
35
|
import { getAgentByName } from "../lib/api.js";
|
|
36
36
|
import { LEDGER_FILE, appendSessionRecord, resolveRetentionDays, toLedgerContract, } from "../lib/agent-ledger.js";
|
|
37
37
|
import { loadCliConfig } from "../lib/cli-config.js";
|
|
@@ -41,7 +41,7 @@ import { fmt } from "../lib/format.js";
|
|
|
41
41
|
import { askInput, askYesNo, isInteractive } from "../lib/prompt.js";
|
|
42
42
|
import { goalAlignmentLine, resolveSessionSetup } from "./agent-session.js";
|
|
43
43
|
import { AgentIdentityMismatchError, defaultAgentSlug, ensureGovernedAgent, ensureSessionAndBalance, isQuietPreambleExit, resolveDefaultSlug, resolvePreflightedAccount, } from "./agent-setup.js";
|
|
44
|
-
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, HANDOFF_RC_KEY_ENV_NAME, isProdGateway, launchPreferenceExplicit, launchPreflighted, remoteControlAllowed, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, resolveLaunchShape, resolveRemoteControlShape, passthroughName, sanitizeAgentName, sessionDisplayName, SESSION_NAME_ENV, SESSION_CONFIG_HEADER, } from "../lib/agent.js";
|
|
44
|
+
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, HANDOFF_RC_KEY_ENV_NAME, isProdGateway, launchPreferenceExplicit, launchPreflighted, remoteControlAllowed, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, singleCellError, resolveLaunchShape, resolveRemoteControlShape, passthroughName, sanitizeAgentName, sessionDisplayName, SESSION_NAME_ENV, SESSION_CONFIG_HEADER, } from "../lib/agent.js";
|
|
45
45
|
import { LAUNCHER_STORE_PATH, mergeStore } from "../lib/launcher-store.js";
|
|
46
46
|
import { slackChannelArgs } from "../lib/slack-launch.js";
|
|
47
47
|
/** The CLI's own provider-key store (zero 1Password): ~/.mnemom/launcher.json, 0600. */
|
|
@@ -109,6 +109,9 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
109
109
|
// ── door ──
|
|
110
110
|
const gatewayHost = resolveGatewayHost(env);
|
|
111
111
|
const door = resolveAnthropicDoor(env);
|
|
112
|
+
const cellError = singleCellError({ door, apiUrl: getApiUrl(), cellGateway: getGatewayUrl() });
|
|
113
|
+
if (cellError)
|
|
114
|
+
throw new Error(cellError);
|
|
112
115
|
const cellNote = isProdGateway(env) ? "us-2/prod" : `custom (${gatewayHost})`;
|
|
113
116
|
const yolo = options.yolo !== false; // default ON
|
|
114
117
|
const contextHint = options.contextHint !== false; // default ON
|
package/dist/index.js
CHANGED
|
@@ -100,8 +100,8 @@ program
|
|
|
100
100
|
// CONTRACT the gateway seals on the first turn. Three launch shapes — terminal
|
|
101
101
|
// (default), --remote-control (interactive RC in this terminal), --server
|
|
102
102
|
// (headless RC dispatcher) — with yolo on by default. `--cli` selects the
|
|
103
|
-
// executable (name or full path; default claude). The
|
|
104
|
-
//
|
|
103
|
+
// executable (name or full path; default claude). The door is the gateway of the
|
|
104
|
+
// cell the CLI uses (prod by default); a door on another cell is refused. The
|
|
105
105
|
// Anthropic key is resolved with zero 1Password dependency (env → ~/.mnemom store
|
|
106
106
|
// → interactive prompt) inside the launched process and never logged. See
|
|
107
107
|
// commands/code.ts.
|
|
@@ -139,7 +139,7 @@ const agentCmd = program
|
|
|
139
139
|
.option("--no-implicit", "Turn implicit goal alignment OFF for this launch (overrides launcher.toml goal_mode = implicit)")
|
|
140
140
|
.option("--conversation-id <id>", "x-mnemom-conversation-id (default: auto-generated)")
|
|
141
141
|
.option("--cli <name|path>", "Coding-agent CLI to launch: a name or full path (default: claude)")
|
|
142
|
-
.option("--gateway <url>", "Gateway host override (default:
|
|
142
|
+
.option("--gateway <url>", "Gateway host override — must be the same cell as the rest of the CLI (default: that cell's gateway; or MNEMOM_AGENT_GATEWAY)")
|
|
143
143
|
.option("--remote-control", "Interactive Remote Control session in THIS terminal (behind a local proxy)")
|
|
144
144
|
.option("--server", "Headless `claude remote-control` dispatcher (behind a local proxy)")
|
|
145
145
|
.option("--model <alias|id>", "Model to launch (CLI alias or concrete id)")
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -63,11 +63,27 @@ export interface ContractFlags {
|
|
|
63
63
|
export type LaunchShape = "terminal" | "remote-control" | "server";
|
|
64
64
|
/**
|
|
65
65
|
* Resolve the gateway HOST (scheme + host, no path). An explicit, non-empty
|
|
66
|
-
* `MNEMOM_AGENT_GATEWAY` wins verbatim; otherwise the
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
* `MNEMOM_AGENT_GATEWAY` wins verbatim; otherwise the gateway of the cell the rest
|
|
67
|
+
* of the CLI uses (`getGatewayUrl()`: prod unless MNEMOM_ENV / MNEMOM_RING /
|
|
68
|
+
* MNEMOM_GATEWAY_URL name another). An override on a different cell is refused at
|
|
69
|
+
* launch — see `singleCellError`. Trailing slash trimmed so door concatenation is clean.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveGatewayHost(env?: NodeJS.ProcessEnv, cellGateway?: string): string;
|
|
72
|
+
/** Same scheme+host+port. An unparseable URL never matches. */
|
|
73
|
+
export declare function sameOrigin(a: string, b: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* A launch must use ONE cell: the Anthropic `door` its traffic goes to must be the
|
|
76
|
+
* gateway of the cell the CLI signs in, resolves the agent and reads sessions on
|
|
77
|
+
* (`cellGateway`, paired with `apiUrl`). A mixed launch records one cell's agent
|
|
78
|
+
* and runs as another cell's, so `sessions`/`show` never find its goal state.
|
|
79
|
+
* Returns the refusal message, or null. A loopback door is a local dev gateway and
|
|
80
|
+
* is exempt.
|
|
81
|
+
*/
|
|
82
|
+
export declare function singleCellError(opts: {
|
|
83
|
+
door: string;
|
|
84
|
+
apiUrl: string;
|
|
85
|
+
cellGateway: string;
|
|
86
|
+
}): string | null;
|
|
71
87
|
/**
|
|
72
88
|
* The Anthropic door the CLI points Claude Code at:
|
|
73
89
|
* `${MNEMOM_AGENT_GATEWAY}/anthropic`, or the full-door override
|
package/dist/lib/agent.js
CHANGED
|
@@ -20,19 +20,56 @@
|
|
|
20
20
|
* NAMED), but prod is never selected silently — see `resolveGatewayHost`.
|
|
21
21
|
*/
|
|
22
22
|
import { createHash, randomUUID } from "node:crypto";
|
|
23
|
+
import { getGatewayUrl } from "./config.js";
|
|
23
24
|
/** The us-2/prod gateway host. The default door — never any other cell silently. */
|
|
24
25
|
export const PROD_GATEWAY_HOST = "https://gateway.mnemom.ai";
|
|
25
26
|
/**
|
|
26
27
|
* Resolve the gateway HOST (scheme + host, no path). An explicit, non-empty
|
|
27
|
-
* `MNEMOM_AGENT_GATEWAY` wins verbatim; otherwise the
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* `MNEMOM_AGENT_GATEWAY` wins verbatim; otherwise the gateway of the cell the rest
|
|
29
|
+
* of the CLI uses (`getGatewayUrl()`: prod unless MNEMOM_ENV / MNEMOM_RING /
|
|
30
|
+
* MNEMOM_GATEWAY_URL name another). An override on a different cell is refused at
|
|
31
|
+
* launch — see `singleCellError`. Trailing slash trimmed so door concatenation is clean.
|
|
30
32
|
*/
|
|
31
|
-
export function resolveGatewayHost(env = process.env) {
|
|
33
|
+
export function resolveGatewayHost(env = process.env, cellGateway = getGatewayUrl()) {
|
|
32
34
|
const raw = (env.MNEMOM_AGENT_GATEWAY ?? "").trim();
|
|
33
|
-
const host = raw.length > 0 ? raw :
|
|
35
|
+
const host = raw.length > 0 ? raw : cellGateway;
|
|
34
36
|
return host.replace(/\/+$/, "");
|
|
35
37
|
}
|
|
38
|
+
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
39
|
+
/** Same scheme+host+port. An unparseable URL never matches. */
|
|
40
|
+
export function sameOrigin(a, b) {
|
|
41
|
+
try {
|
|
42
|
+
return new URL(a).origin === new URL(b).origin;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A launch must use ONE cell: the Anthropic `door` its traffic goes to must be the
|
|
50
|
+
* gateway of the cell the CLI signs in, resolves the agent and reads sessions on
|
|
51
|
+
* (`cellGateway`, paired with `apiUrl`). A mixed launch records one cell's agent
|
|
52
|
+
* and runs as another cell's, so `sessions`/`show` never find its goal state.
|
|
53
|
+
* Returns the refusal message, or null. A loopback door is a local dev gateway and
|
|
54
|
+
* is exempt.
|
|
55
|
+
*/
|
|
56
|
+
export function singleCellError(opts) {
|
|
57
|
+
let doorUrl;
|
|
58
|
+
try {
|
|
59
|
+
doorUrl = new URL(opts.door);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return `mnemom agent: the gateway '${opts.door}' is not a valid URL.`;
|
|
63
|
+
}
|
|
64
|
+
if (LOOPBACK_HOSTS.has(doorUrl.hostname))
|
|
65
|
+
return null;
|
|
66
|
+
if (sameOrigin(opts.door, opts.cellGateway))
|
|
67
|
+
return null;
|
|
68
|
+
return (`mnemom agent: the gateway ${doorUrl.origin} is on a different cell from the one this CLI uses ` +
|
|
69
|
+
`(API ${opts.apiUrl}, gateway ${opts.cellGateway}). A launch must use one cell. Point the whole ` +
|
|
70
|
+
"CLI at that cell (MNEMOM_API_URL + MNEMOM_GATEWAY_URL, or MNEMOM_RING) and drop the gateway " +
|
|
71
|
+
"override (--gateway, MNEMOM_AGENT_GATEWAY, MNEMOM_AGENT_ANTHROPIC_GATEWAY, or `gateway` in launcher.toml).");
|
|
72
|
+
}
|
|
36
73
|
/**
|
|
37
74
|
* The Anthropic door the CLI points Claude Code at:
|
|
38
75
|
* `${MNEMOM_AGENT_GATEWAY}/anthropic`, or the full-door override
|
|
@@ -86,7 +86,7 @@ function ensureGuardrails(cfg) {
|
|
|
86
86
|
export const CONFIG_KEYS = [
|
|
87
87
|
{
|
|
88
88
|
key: "gateway",
|
|
89
|
-
describe: "Gateway host (default
|
|
89
|
+
describe: "Gateway host (default: the gateway of the cell the CLI uses — prod unless MNEMOM_ENV/MNEMOM_RING/MNEMOM_GATEWAY_URL say otherwise; a different cell is refused)",
|
|
90
90
|
get: (c) => c.gateway,
|
|
91
91
|
set: (c, v) => {
|
|
92
92
|
c.gateway = coerceNonEmpty("gateway", v);
|
package/dist/lib/slack-router.js
CHANGED
|
@@ -53,7 +53,9 @@ export function ensureRouter(argv = [process.execPath, process.argv[1] ?? "mnemo
|
|
|
53
53
|
}
|
|
54
54
|
/** Run the router until the process is killed. Reconnects on disconnect. */
|
|
55
55
|
export async function runRouter(opts) {
|
|
56
|
-
|
|
56
|
+
// Lines carry Slack-supplied values (channel, handles, disconnect reason);
|
|
57
|
+
// strip CR/LF so a crafted message cannot forge extra log lines.
|
|
58
|
+
const log = opts.log ?? ((l) => console.error(`[slack-router] ${l.replace(/\n|\r/g, "")}`));
|
|
57
59
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
58
60
|
const WS = globalThis.WebSocket;
|
|
59
61
|
if (!WS)
|