@mnemom/mnemom 0.17.7-next.1 → 0.17.7

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.
@@ -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 { apiAcceptsMnemomKey, createOrgApiKey, getAgentByName, listOwnApiKeysForCheck, revokeApiKey, } from "../lib/api.js";
37
- import { checkMnemomKey, MNEMOM_KEY_ENV, readLocalMnemomKey, readStoredMnemomKey, resolveAgentAuth, resolveLaunchMnemomKey, storePatchForMnemomKey, subscriptionChildEnv, subscriptionShape, } from "../lib/subscription-login.js";
38
- import { LEDGER_FILE, appendSessionRecord, findResumeCandidate, readSessionRecords, resolveRetentionDays, resumedContextProfile, toLedgerContract, } from "../lib/agent-ledger.js";
39
- import { getActiveOrg, loadCliConfig } from "../lib/cli-config.js";
36
+ import { createApiKey, getAgentByName } from "../lib/api.js";
37
+ import { MNEMOM_KEY_STORE_FIELD, resolveAgentAuth, resolveSubscriptionMnemomKey, subscriptionChildEnv, subscriptionShape, } from "../lib/subscription-login.js";
38
+ import { LEDGER_FILE, appendSessionRecord, findResumeCandidate, readSessionRecords, resolveRetentionDays, toLedgerContract, } from "../lib/agent-ledger.js";
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, compactAtWindowWarning, describeCompactAt, launchWindow, resolveCompactAt, applyGatewayHintHeaders, resolveCompactionIntercept, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, HANDOFF_RC_KEY_ENV_NAME, HANDOFF_RC_MNEMOM_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, readStore } from "../lib/launcher-store.js";
48
+ import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, assertCompactAtCompatible, 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;
@@ -54,19 +54,13 @@ const say = (line = "") => console.error(line);
54
54
  /** The one-line launch note when the compaction intercept is ON. */
55
55
  const COMPACTION_INTERCEPT_NOTE = "mnemom agent: compaction intercept ON (experimental) — requested from the gateway (GATEWAY_USAGE_REAL_SIZE, GATEWAY_COMPACTION_INTERCEPT); a gateway without the companion support drops both keys and the session runs as before.";
56
56
  /**
57
- * The one-line launch note for the context profile: `null` (none chosen — nothing is sent) names
58
- * the cell default. `off` keeps the wording `--no-context` has always printed.
57
+ * The one-line launch note for a non-default context profile (`balanced`, the default, is
58
+ * silent — nothing is sent). `off` keeps the wording `--no-context` has always printed.
59
59
  */
60
60
  function sayContextProfile(profile) {
61
- if (profile === null) {
62
- say("mnemom agent: context: cell default");
63
- }
64
- else if (profile === "off") {
61
+ if (profile === "off") {
65
62
  say("mnemom agent: context folding OFF for this session (--no-context or context = false).");
66
63
  }
67
- else if (profile === "balanced") {
68
- say("mnemom agent: context profile balanced — the gateway keeps more of the earlier work verbatim and folds later.");
69
- }
70
64
  else if (profile === "aggressive") {
71
65
  say("mnemom agent: context profile aggressive — the gateway folds earlier and keeps a tighter working set.");
72
66
  }
@@ -180,12 +174,11 @@ export async function agentCommand(scenarioArg, options = {}) {
180
174
  const cellNote = isProdGateway(env) ? "us-2/prod" : `custom (${gatewayHost})`;
181
175
  const yolo = options.yolo !== false; // default ON
182
176
  const contextHint = options.contextHint !== false; // default ON
183
- // Context profile: none (null — the cell's own default, no fold key sent) unless
184
- // --context-profile / MNEMOM_AGENT_CONTEXT_PROFILE (the `context_profile` setting) chooses
185
- // one; --no-context (or `context = false` in launcher.toml → MNEMOM_AGENT_CONTEXT) is `off`. A
186
- // chosen profile rides in the per-session config header; `undefined` sessionConfigB64 ⇒
187
- // nothing sent. A resume re-resolves it below, once the resumed session is known.
188
- let contextProfile = resolveContextProfile({
177
+ // Context profile: `balanced` (the gateway's own defaults) unless --context-profile /
178
+ // MNEMOM_AGENT_CONTEXT_PROFILE (the `context_profile` setting) says otherwise; --no-context
179
+ // (or `context = false` in launcher.toml → MNEMOM_AGENT_CONTEXT) is `off`. Anything but
180
+ // balanced rides in the per-session config header; `undefined` sessionConfigB64 ⇒ nothing sent.
181
+ const contextProfile = resolveContextProfile({
189
182
  contextProfile: options.contextProfile,
190
183
  context: options.context,
191
184
  env,
@@ -216,7 +209,7 @@ export async function agentCommand(scenarioArg, options = {}) {
216
209
  compactionIntercept,
217
210
  compactAt,
218
211
  });
219
- let sessionConfigB64 = buildSessionConfigB64({
212
+ const sessionConfigB64 = buildSessionConfigB64({
220
213
  profile: contextProfile,
221
214
  contextHandoff,
222
215
  compactionIntercept,
@@ -309,9 +302,8 @@ export async function agentCommand(scenarioArg, options = {}) {
309
302
  // lives in (default: the user's personal org). No µ ⇒ blocked with a
310
303
  // portal-style top-up message. Skipped only on --dry-run (handled above).
311
304
  // MNEMOM_AGENT_PREFLIGHTED=1 (orchestrators such as the ADW fleet that already
312
- // ran their own session + balance preflight) skips those round trips; the launch
313
- // org resolves the same way and its entitlement is still fetched — see
314
- // resolvePreflightedAccount.
305
+ // ran their own session + balance preflight) skips those round trips; the
306
+ // entitlement is still fetched — see resolvePreflightedAccount.
315
307
  let account;
316
308
  try {
317
309
  account = launchPreflighted(env)
@@ -351,19 +343,14 @@ export async function agentCommand(scenarioArg, options = {}) {
351
343
  rcCapable: rc.ok,
352
344
  say,
353
345
  });
354
- // Subscription login: Remote Control only for RC-entitled (internal) accounts.
346
+ // Subscription login: Remote Control only for RC-entitled (internal) accounts, and the governed identity anchors on a
347
+ // Mnemom API key (minted once and stored if there is none). Resolved after sign-in so
348
+ // minting can use the session.
349
+ let mnemomKey;
355
350
  if (auth === "subscription") {
356
351
  shape = subscriptionShape(shape, shapeExplicit, rcAllowed, say);
352
+ mnemomKey = await resolveSubscriptionMnemomKey(defaultMnemomKeyDeps());
357
353
  }
358
- // Every launch — api-key, console and subscription, every shape — sends a Mnemom API key
359
- // (x-mnemom-api-key): the gateway refuses `mnemom agent` traffic without a verified one.
360
- // Resolved after sign-in so minting and the one-time liveness check can use the session: the
361
- // key stored for the active org, checked once (a revoked/rotated one is re-minted), else a
362
- // fresh key minted for the launch's org (account.orgId: the active org, else the personal
363
- // org — the org the agent is claimed into, which the gateway's org check requires of the key)
364
- // and stored. No key ⇒ this throws and the launch does not start (a session the gateway
365
- // would refuse is worse than an actionable error).
366
- const mnemomKey = await resolveLaunchMnemomKey(defaultMnemomKeyDeps(CODE_STORE, account.orgId));
367
354
  // Now that a session exists, a defaulted slug uses the real login handle from
368
355
  // /v1/auth/me (the stored token has no email, so the pre-sign-in value was the
369
356
  // "agent-user" fallback). Only when the caller didn't name an agent. Under an
@@ -394,7 +381,6 @@ export async function agentCommand(scenarioArg, options = {}) {
394
381
  const ensured = await ensureGovernedAgent({
395
382
  slug: agent,
396
383
  anthropicKey,
397
- mnemomKey,
398
384
  setup: options.setup,
399
385
  gatewayHost,
400
386
  });
@@ -438,33 +424,6 @@ export async function agentCommand(scenarioArg, options = {}) {
438
424
  passthrough: [...(options.passthrough ?? []), "--resume", continuation.resumeCcSessionId],
439
425
  };
440
426
  }
441
- // A launch that resumes a Claude Code session (the continuation above, or the caller's own
442
- // `-- --resume <id>`) keeps the profile that session ran under, unless this launch chose one
443
- // with a flag. Read from the ledger only — nothing here writes a default anywhere.
444
- const resumeId = shape === "server" ? null : passthroughResumeId(options.passthrough ?? []);
445
- if (resumeId && options.contextProfile === undefined && options.context !== false) {
446
- let resumed;
447
- try {
448
- resumed = resumedContextProfile(readSessionRecords({ dir: MNEMOM_DIR }), {
449
- ccSessionId: resumeId,
450
- orgId: account.orgId,
451
- gatewayHost,
452
- });
453
- }
454
- catch {
455
- resumed = null; // best-effort: an unreadable ledger keeps the launch's own resolution
456
- }
457
- if (resumed !== null && resumed !== contextProfile) {
458
- contextProfile = resolveContextProfile({ context: options.context, resumed, env });
459
- sessionConfigB64 = buildSessionConfigB64({
460
- profile: contextProfile,
461
- contextHandoff,
462
- compactionIntercept,
463
- compactAt,
464
- experimental,
465
- });
466
- }
467
- }
468
427
  // ── local session ledger (~/.mnemom/sessions.jsonl) ── one line per real
469
428
  // launch so `mnemom agent sessions` / `show` can find this conversation later.
470
429
  // Written after provisioning so a freshly claimed agent's id resolves. Holds
@@ -521,22 +480,15 @@ export async function agentCommand(scenarioArg, options = {}) {
521
480
  // back out at request time. Remote Control (both shapes) strips every ANTHROPIC_* auth
522
481
  // var from the child's env, so `spawnRcCli` below instead sets a dedicated, non-ANTHROPIC
523
482
  // var (HANDOFF_RC_KEY_ENV_NAME) on that same child env — still nothing in this file.
524
- // The Mnemom key the gateway requires rides the same way: its `x-mnemom-api-key:` line
525
- // in ANTHROPIC_CUSTOM_HEADERS (terminal), or HANDOFF_RC_MNEMOM_KEY_ENV_NAME (RC).
526
- const terminal = shape === "terminal";
527
- const anthropicKeySource = terminal
483
+ const anthropicKeySource = shape === "terminal"
528
484
  ? { envName: "ANTHROPIC_CUSTOM_HEADERS" }
529
485
  : { envName: HANDOFF_RC_KEY_ENV_NAME };
530
- const mnemomKeySource = terminal
531
- ? { envName: "ANTHROPIC_CUSTOM_HEADERS" }
532
- : { envName: HANDOFF_RC_MNEMOM_KEY_ENV_NAME };
533
486
  handoffMcpConfigPath = writeHandoffMcpConfigFile(buildHandoffMcpConfigJson({
534
487
  // The SAME origin the session's own model traffic goes to — not resolveGatewayHost,
535
488
  // which would ignore a MNEMOM_AGENT_ANTHROPIC_GATEWAY override (a test-cell launch
536
489
  // must never send its handoff calls to prod, or vice versa).
537
490
  gatewayHost: resolveHandoffGatewayHost(door),
538
491
  anthropicKeySource,
539
- mnemomKeySource,
540
492
  agent,
541
493
  conversationId,
542
494
  sessionConfigB64,
@@ -737,9 +689,7 @@ export async function recordSessionLaunch(ctx) {
737
689
  cli: ctx.cli.name,
738
690
  contract: toLedgerContract(ctx.envelope?.contract ?? null),
739
691
  goal_mode: goalAlignmentMode(ctx.envelope, ctx.goalMode),
740
- ...(ctx.contextProfile !== null
741
- ? { context_profile: ctx.contextProfile, context_profile_v: 2 }
742
- : {}),
692
+ context_profile: ctx.contextProfile,
743
693
  compact_at: ctx.compactAt,
744
694
  cc_session_id: ctx.ccSessionId ?? null,
745
695
  ...(ctx.options.experimental?.length ? { experimental: [...ctx.options.experimental] } : {}),
@@ -756,55 +706,25 @@ export async function recordSessionLaunch(ctx) {
756
706
  // ───────────────────────────────────────────────────────────────────────────
757
707
  // Credential resolution (zero 1Password)
758
708
  // ───────────────────────────────────────────────────────────────────────────
759
- /** Per-call timeout of the launch's Mnemom-key check: a slow API must not hold up a launch. */
760
- export const MNEMOM_KEY_CHECK_TIMEOUT_MS = 2500;
761
- /**
762
- * Mint the launch's key: an ORG-owned key for `orgId` (the launch's org) with scopes exactly
763
- * ["gateway"], which any owner/admin/member may mint — so the gateway's agent-traffic org check
764
- * (agent org = key org) passes for the agent the launcher claims into that org. Never a personal
765
- * key: it has no org, and the gateway would refuse every governed turn with it. A refusal (403:
766
- * an API that predates member org keys, or a viewer/auditor role) is thrown as is; the launch
767
- * stops with guidance (lib/subscription-login.ts mintAndStore).
768
- */
769
- export async function mintLaunchMnemomKey(orgId) {
770
- const k = await createOrgApiKey(orgId, `mnemom-agent ${hostname()}`, ["gateway"]);
771
- return { key: k.key, keyId: k.key_id };
772
- }
773
- /**
774
- * The real I/O behind every launch's Mnemom key (lib/subscription-login.ts). `launchOrgId` is
775
- * the org the launch runs in (ensureSessionAndBalance's `orgId`: the active org, else the
776
- * caller's personal org); omitted or empty, it is the active org.
777
- */
778
- export function defaultMnemomKeyDeps(storePath = CODE_STORE, launchOrgId) {
779
- const activeOrgId = getActiveOrg()?.org_id || null;
709
+ /** The real I/O behind a subscription launch's Mnemom key (lib/subscription-login.ts). */
710
+ export function defaultMnemomKeyDeps(storePath = CODE_STORE) {
780
711
  return {
781
712
  env: process.env,
782
- activeOrgId,
783
- launchOrgId: launchOrgId || activeOrgId,
784
- readStored: (orgId) => readStoredMnemomKey(readStore(storePath), orgId),
785
- mint: mintLaunchMnemomKey,
786
- store: (orgId, key) => mergeStore(storePatchForMnemomKey(readStore(storePath), orgId, key), storePath),
787
- check: (key, keyId) => checkMnemomKey(key, keyId, {
788
- apiAccepts: (k) => apiAcceptsMnemomKey(k, MNEMOM_KEY_CHECK_TIMEOUT_MS),
789
- listOwnKeys: () => listOwnApiKeysForCheck(MNEMOM_KEY_CHECK_TIMEOUT_MS),
790
- }),
791
- // DELETE /v1/api-keys/:key_id revokes only keys the signed-in user created (org keys
792
- // included), so a replaced key minted by someone else is never touched.
793
- revoke: (keyId) => revokeApiKey(keyId, { timeoutMs: MNEMOM_KEY_CHECK_TIMEOUT_MS }),
713
+ readStored: () => {
714
+ try {
715
+ const data = JSON.parse(readFileSync(storePath, "utf8"));
716
+ const v = data[MNEMOM_KEY_STORE_FIELD];
717
+ return typeof v === "string" ? stripCrlf(v) : undefined;
718
+ }
719
+ catch {
720
+ return undefined;
721
+ }
722
+ },
723
+ mint: async () => (await createApiKey(`mnemom-agent ${hostname()}`, ["gateway"])).key,
724
+ store: (key) => mergeStore({ [MNEMOM_KEY_STORE_FIELD]: key }, storePath),
794
725
  say,
795
726
  };
796
727
  }
797
- /**
798
- * The Mnemom key already on this machine (MNEMOM_AGENT_MNEMOM_KEY, then ~/.mnemom/launcher.json),
799
- * never minted: `mnemom agent doctor` reports its source, the goal commands send it.
800
- */
801
- export function localLaunchMnemomKey(storePath = CODE_STORE) {
802
- return readLocalMnemomKey(defaultMnemomKeyDeps(storePath));
803
- }
804
- /** The launch's one-time check of that key (`mnemom agent doctor` reports it). Never throws. */
805
- export function verifyLocalMnemomKey(local) {
806
- return defaultMnemomKeyDeps().check(local.key, local.keyId);
807
- }
808
728
  /** The real machine behind lib/console-key.ts: the macOS keychain via `security`, no shell. */
809
729
  export function defaultConsoleKeyDeps(env) {
810
730
  return {
@@ -1080,8 +1000,6 @@ function launchTerminal(ctx) {
1080
1000
  if (ctx.anthropicKey) {
1081
1001
  childEnv = { ...process.env };
1082
1002
  delete childEnv.ANTHROPIC_API_KEY;
1083
- // The Mnemom key already rides in the headers; the child has no other use for it.
1084
- Reflect.deleteProperty(childEnv, MNEMOM_KEY_ENV);
1085
1003
  childEnv.ANTHROPIC_BASE_URL = ctx.door;
1086
1004
  childEnv.ANTHROPIC_AUTH_TOKEN = "mnemom-gateway";
1087
1005
  childEnv.ANTHROPIC_CUSTOM_HEADERS = headers;
@@ -1266,10 +1184,9 @@ function launchRemoteControl(ctx) {
1266
1184
  }
1267
1185
  };
1268
1186
  process.on("exit", onExit);
1269
- // Secrets on stdin — three lines (anthropic, mnemom, openai). Every launch sends the Mnemom
1270
- // key (the proxy adds it as x-mnemom-api-key on every gateway request); an api-key launch
1271
- // also sends the Anthropic key, a subscription launch leaves that line empty (the user's
1272
- // OAuth bearer is what pays). openai is always empty on this Anthropic-only door.
1187
+ // Secrets on stdin — three lines (anthropic, mnemom, openai). An api-key launch sends the
1188
+ // Anthropic key only; a subscription launch sends the Mnemom key only (the identity anchor —
1189
+ // the user's OAuth bearer is what pays). openai is always empty on this Anthropic-only door.
1273
1190
  // A proxy that exits before reading them turns this write into an EPIPE; that is
1274
1191
  // not a failure of its own — the exited-before-READY check below refuses the
1275
1192
  // launch. Any other stdin error fails closed like a spawn error.
@@ -1280,7 +1197,7 @@ function launchRemoteControl(ctx) {
1280
1197
  onExit();
1281
1198
  process.exit(1);
1282
1199
  });
1283
- proxy.stdin?.end(`${ctx.anthropicKey ?? ""}\n${ctx.mnemomKey}\n\n`);
1200
+ proxy.stdin?.end(`${ctx.anthropicKey ?? ""}\n${ctx.anthropicKey ? "" : (ctx.mnemomKey ?? "")}\n\n`);
1284
1201
  let stdoutBuf = "";
1285
1202
  const start = Date.now();
1286
1203
  const tryStart = () => {
@@ -1348,9 +1265,6 @@ function spawnRcCli(ctx) {
1348
1265
  "no_proxy",
1349
1266
  "HTTP_PROXY",
1350
1267
  "http_proxy",
1351
- // The Mnemom key rides to the gateway via the proxy (stdin) and, with handoff on, in
1352
- // HANDOFF_RC_MNEMOM_KEY_ENV_NAME below; the CLI itself has no use for the launch's env var.
1353
- MNEMOM_KEY_ENV,
1354
1268
  ]);
1355
1269
  const childEnv = {};
1356
1270
  for (const [k, v] of Object.entries(process.env)) {
@@ -1378,10 +1292,8 @@ function spawnRcCli(ctx) {
1378
1292
  // ride through here ungated), so this rides through untouched. Only set when the tool is
1379
1293
  // actually registered — never on a plain RC launch.
1380
1294
  // (Never on a subscription launch — handoff is turned off there, so there is no key to set.)
1381
- // The Mnemom key the gateway requires rides beside it in its own non-ANTHROPIC var.
1382
1295
  if (ctx.handoffMcpConfigPath && ctx.anthropicKey) {
1383
1296
  childEnv[HANDOFF_RC_KEY_ENV_NAME] = ctx.anthropicKey;
1384
- childEnv[HANDOFF_RC_MNEMOM_KEY_ENV_NAME] = ctx.mnemomKey;
1385
1297
  }
1386
1298
  let args;
1387
1299
  if (ctx.shape === "server") {
@@ -1654,7 +1566,7 @@ export function printPlan(ctx) {
1654
1566
  L.push(` agent: ${ctx.agent}`);
1655
1567
  L.push(` conversation: ${ctx.conversationId}`);
1656
1568
  L.push(` yolo: ${ctx.yolo ? "on (--dangerously-skip-permissions)" : "off"}`);
1657
- L.push(` context: ${ctx.contextProfile ?? "cell default"}`);
1569
+ L.push(` context: ${ctx.contextProfile}`);
1658
1570
  L.push(` compaction: ${describeCompactAt(ctx.compactAt)}`);
1659
1571
  const overlay = Object.entries(ctx.sessionConfig);
1660
1572
  if (overlay.length > 0) {
@@ -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 {};
@@ -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 post = (h) => fetch(url, {
238
+ const response = await fetch(url, {
245
239
  method: "POST",
246
- headers: h,
240
+ headers,
247
241
  body,
248
242
  signal: opts.timeoutMs ? AbortSignal.timeout(opts.timeoutMs) : undefined,
249
243
  });
250
- let response = await post(headers);
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 {
package/dist/index.js CHANGED
@@ -175,7 +175,7 @@ const agentCmd = program
175
175
  .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")))
176
176
  .option("--context", "Force context folding ON for this launch even when launcher.toml (context = false) or MNEMOM_AGENT_CONTEXT turns it off")
177
177
  .option("--no-context", "Disable context folding for this launch (folding is on by default; tells the gateway to skip it for this session)")
178
- .addOption(new Option("--context-profile <profile>", 'How the gateway folds this session\'s context. Unset (the default): nothing is sent and the gateway cell\'s own default decides. "balanced" keeps more of the earlier work verbatim and folds later — for long sessions that revisit earlier files and decisions. "aggressive" folds earlier and keeps a tighter working set — for cost-sensitive sessions and short, focused tasks. "adaptive" sizes the fold to the conversation: no fold below 200k tokens, aggressive from 200k to 1M, balanced above. "off" leaves compaction to the coding agent. Applies to this launch only (a resumed session keeps its own); to change the default for every launch: config key context_profile, env MNEMOM_AGENT_CONTEXT_PROFILE.').argParser((v) => {
178
+ .addOption(new Option("--context-profile <profile>", 'How the gateway folds this session\'s context. "balanced" (default) keeps more of the earlier work verbatim and folds later — for long sessions that revisit earlier files and decisions. "aggressive" folds earlier and keeps a tighter working set — for cost-sensitive sessions and short, focused tasks. "adaptive" sizes the fold to the conversation: no fold below 200k tokens, aggressive from 200k to 1M, balanced above. "off" leaves compaction to the coding agent. Also: config key context_profile, env MNEMOM_AGENT_CONTEXT_PROFILE.').argParser((v) => {
179
179
  // One validator for all three surfaces (flag, MNEMOM_AGENT_CONTEXT_PROFILE,
180
180
  // launcher.toml context_profile): trimmed, case-insensitive, same error wording.
181
181
  try {
@@ -14,7 +14,7 @@
14
14
  * `sessions.retention_days` (default 30) are dropped when the next one is
15
15
  * recorded. No count cap — a long retention keeps everything in the window.
16
16
  */
17
- import { type ContextProfile, type LaunchShape, type SealedContract } from "./agent.js";
17
+ import type { ContextProfile, LaunchShape, SealedContract } from "./agent.js";
18
18
  /** The ledger's file name under MNEMOM_DIR. */
19
19
  export declare const LEDGER_FILE = "sessions.jsonl";
20
20
  /** Days a launch stays in the ledger when `sessions.retention_days` is unset. */
@@ -77,17 +77,10 @@ export interface SessionRecord {
77
77
  cc_session_id?: string | null;
78
78
  /**
79
79
  * The context profile the launch ran under (`--context-profile` / `context_profile` /
80
- * `MNEMOM_AGENT_CONTEXT_PROFILE`, or the resumed session's own). Absent when none was chosen
81
- * (the cell default) and on records written before profiles existed (still `v: 1`).
80
+ * `MNEMOM_AGENT_CONTEXT_PROFILE`, or `balanced` by default). Optional: records written
81
+ * before profiles existed omit it (still `v: 1`).
82
82
  */
83
83
  context_profile?: ContextProfile;
84
- /**
85
- * `2` when `context_profile` was written with complete profiles (`balanced` sends size policy
86
- * off, `aggressive` adds it). Absent on older records, where `balanced` was also written for a
87
- * launch that chose nothing and sent no fold key — so a resume reads an unmarked `balanced` as
88
- * the cell default, which is what that session actually ran under.
89
- */
90
- context_profile_v?: 2;
91
84
  /**
92
85
  * The resolved gateway compaction target (`--compact-at` / `compact_at` /
93
86
  * `MNEMOM_COMPACT_AT`): `null` means no target (Claude Code's own 1M window; the default,
@@ -175,15 +168,3 @@ export declare function findResumeCandidate(records: readonly SessionRecord[], w
175
168
  gatewayHost: string;
176
169
  resumeId?: string | null;
177
170
  }): ResumeCandidate | null;
178
- /**
179
- * The context profile the Claude Code session `ccSessionId` ran under — the newest row with the
180
- * same org and gateway host that pinned that session — so a launch that resumes it
181
- * (`--resume <id>`) keeps that session's own profile. `null` when no row matches, the row chose
182
- * no profile (the cell default), it is an older row's `balanced` (see `context_profile_v`), or its
183
- * value is not a known profile (the file is hand-editable).
184
- */
185
- export declare function resumedContextProfile(records: readonly SessionRecord[], want: {
186
- ccSessionId: string | null;
187
- orgId: string;
188
- gatewayHost: string;
189
- }): ContextProfile | null;
@@ -16,7 +16,6 @@
16
16
  */
17
17
  import { appendFileSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
18
18
  import { join } from "node:path";
19
- import { CONTEXT_PROFILES, } from "./agent.js";
20
19
  /** The ledger's file name under MNEMOM_DIR. */
21
20
  export const LEDGER_FILE = "sessions.jsonl";
22
21
  /** Days a launch stays in the ledger when `sessions.retention_days` is unset. */
@@ -198,25 +197,3 @@ export function findResumeCandidate(records, want) {
198
197
  }
199
198
  return latest ? { record: latest, source: "session_name", byCcSession: false } : null;
200
199
  }
201
- /**
202
- * The context profile the Claude Code session `ccSessionId` ran under — the newest row with the
203
- * same org and gateway host that pinned that session — so a launch that resumes it
204
- * (`--resume <id>`) keeps that session's own profile. `null` when no row matches, the row chose
205
- * no profile (the cell default), it is an older row's `balanced` (see `context_profile_v`), or its
206
- * value is not a known profile (the file is hand-editable).
207
- */
208
- export function resumedContextProfile(records, want) {
209
- const id = want.ccSessionId?.trim();
210
- if (!id || !want.orgId)
211
- return null;
212
- for (let i = records.length - 1; i >= 0; i--) {
213
- const r = records[i];
214
- if (r.cc_session_id !== id || r.org_id !== want.orgId || r.gateway_host !== want.gatewayHost)
215
- continue;
216
- const p = r.context_profile;
217
- if (p === "balanced" && r.context_profile_v !== 2)
218
- return null; // an older row: sent nothing
219
- return p !== undefined && CONTEXT_PROFILES.includes(p) ? p : null;
220
- }
221
- return null;
222
- }