@mnemom/mnemom 0.17.7-next.0 → 0.17.7-next.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/README.md +23 -11
- package/dist/commands/agent.d.ts +7 -6
- package/dist/commands/agent.js +48 -12
- package/dist/index.js +1 -1
- package/dist/lib/agent-ledger.d.ts +22 -3
- package/dist/lib/agent-ledger.js +23 -0
- package/dist/lib/agent.d.ts +35 -24
- package/dist/lib/agent.js +38 -24
- package/dist/lib/launcher-config.d.ts +5 -4
- package/dist/lib/launcher-config.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,20 +87,27 @@ or `launch` in `launcher.toml`) also skips the Remote Control capability probe
|
|
|
87
87
|
#### Context profile: how the gateway folds a session's context
|
|
88
88
|
|
|
89
89
|
`--context-profile <off|balanced|aggressive|adaptive>` picks how the Mnemom gateway folds a
|
|
90
|
-
session's context.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
session's context. **Unset is the default:** the launch sends no fold setting at all and the
|
|
91
|
+
gateway cell's own default decides (the launch note reads `context: cell default`). A profile
|
|
92
|
+
you choose is sent in full, so it means the same thing on every cell whatever that cell's
|
|
93
|
+
default is. Each profile serves a different set of use cases:
|
|
94
|
+
|
|
95
|
+
- **`balanced`** keeps more of the earlier work verbatim and folds later — for long sessions
|
|
96
|
+
that revisit earlier files and decisions. It sends `CONTEXT_RUNTIME_SIZE_POLICY=off`, so the
|
|
97
|
+
cell's own fold budgets apply at every size, even on a cell whose default is `adaptive`.
|
|
94
98
|
- **`aggressive`** folds earlier and keeps a tighter working set — for cost-sensitive
|
|
95
|
-
sessions and short, focused tasks.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
sessions and short, focused tasks. It sends its three budget keys plus
|
|
100
|
+
`CONTEXT_RUNTIME_SIZE_POLICY=off`.
|
|
101
|
+
- **`adaptive`** lets the gateway size the fold to the conversation's original context: no
|
|
102
|
+
fold below 200k tokens (folding a small conversation costs more than it saves), the
|
|
103
|
+
`aggressive` budgets from 200k to 1M, and `balanced` above 1M. It sends
|
|
99
104
|
`CONTEXT_RUNTIME_SIZE_POLICY=banded` in the per-session config; the band thresholds are the
|
|
100
105
|
gateway's defaults.
|
|
101
106
|
- **`off`** leaves compaction to the coding agent.
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
Choose it for one launch, as a standing default, or in the environment (flag > env >
|
|
109
|
+
`launcher.toml`). Only `mnemom agent config set` (or editing `launcher.toml`) saves a default;
|
|
110
|
+
a flag applies to that launch alone and is never written anywhere later launches read:
|
|
104
111
|
|
|
105
112
|
```bash
|
|
106
113
|
mnemom agent my-task --context-profile aggressive # this launch only
|
|
@@ -109,11 +116,16 @@ export MNEMOM_AGENT_CONTEXT_PROFILE=aggressive # for every launch in this s
|
|
|
109
116
|
mnemom agent my-task --context-profile adaptive # size-banded folding, this launch only
|
|
110
117
|
```
|
|
111
118
|
|
|
119
|
+
A launch that resumes a Claude Code session (`-- --resume <id>`, or the implicit-goal relaunch)
|
|
120
|
+
keeps the profile that session ran under, ahead of the standing default; a `--context-profile`
|
|
121
|
+
flag on the resuming launch still wins.
|
|
122
|
+
|
|
112
123
|
`--no-context` (or `context = false` in `launcher.toml`) is the same as `off`. Pass one or the
|
|
113
124
|
other: `--no-context` together with `--context-profile balanced`, `aggressive` or `adaptive` is refused. A
|
|
114
125
|
`--context-profile` flag overrides a standing `context = false` in `launcher.toml`. `--dry-run`
|
|
115
|
-
prints the resolved profile and, when the launch sends the
|
|
116
|
-
decoded key/value list; `mnemom agent show` and the session
|
|
126
|
+
prints the resolved profile (`cell default` when none is chosen) and, when the launch sends the
|
|
127
|
+
per-session config header, the decoded key/value list; `mnemom agent show` and the session
|
|
128
|
+
ledger record a chosen profile.
|
|
117
129
|
|
|
118
130
|
#### Context handoff (experimental, off by default)
|
|
119
131
|
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -66,8 +66,9 @@ export interface AgentOptions {
|
|
|
66
66
|
context?: boolean;
|
|
67
67
|
/**
|
|
68
68
|
* How the gateway folds this session's context (`--context-profile`): `off`, `balanced`,
|
|
69
|
-
* `aggressive` or `adaptive`. Undefined ⇒
|
|
70
|
-
*
|
|
69
|
+
* `aggressive` or `adaptive`. Undefined ⇒ the resumed session's own profile, else
|
|
70
|
+
* MNEMOM_AGENT_CONTEXT_PROFILE (the `context_profile` setting), else none — the cell default,
|
|
71
|
+
* nothing sent. `--no-context` is `off`; combined with balanced|aggressive|adaptive the launch refuses.
|
|
71
72
|
*/
|
|
72
73
|
contextProfile?: ContextProfile;
|
|
73
74
|
/**
|
|
@@ -195,8 +196,8 @@ export declare function recordSessionLaunch(ctx: {
|
|
|
195
196
|
goalMode: string | null;
|
|
196
197
|
/** The Claude Code session id (pickCcSessionId); null/absent when Claude Code picks it. */
|
|
197
198
|
ccSessionId?: string | null;
|
|
198
|
-
/** The resolved context profile this launch runs under. */
|
|
199
|
-
contextProfile: ContextProfile;
|
|
199
|
+
/** The resolved context profile this launch runs under; `null` = none chosen (cell default). */
|
|
200
|
+
contextProfile: ContextProfile | null;
|
|
200
201
|
/** The resolved compaction target this launch runs under (`resolveCompactAt`). */
|
|
201
202
|
compactAt: number | null;
|
|
202
203
|
}): Promise<void>;
|
|
@@ -374,8 +375,8 @@ export declare function printPlan(ctx: {
|
|
|
374
375
|
goalMode: string | null;
|
|
375
376
|
yolo: boolean;
|
|
376
377
|
cli: ResolvedCli;
|
|
377
|
-
/** The resolved context profile (`resolveContextProfile`). */
|
|
378
|
-
contextProfile: ContextProfile;
|
|
378
|
+
/** The resolved context profile (`resolveContextProfile`); `null` = none chosen (cell default). */
|
|
379
|
+
contextProfile: ContextProfile | null;
|
|
379
380
|
/** The resolved compaction target (`resolveCompactAt`); `null` = no target (default). */
|
|
380
381
|
compactAt: number | null;
|
|
381
382
|
/** The decoded `x-mnemom-session-config` overlay (`buildSessionConfig`); empty ⇒ no header. */
|
package/dist/commands/agent.js
CHANGED
|
@@ -35,7 +35,7 @@ import { statuslineLaunch } from "../lib/statusline.js";
|
|
|
35
35
|
import { getApiUrl, getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
|
|
36
36
|
import { apiAcceptsMnemomKey, createOrgApiKey, getAgentByName, listOwnApiKeysForCheck, revokeApiKey, } from "../lib/api.js";
|
|
37
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, toLedgerContract, } from "../lib/agent-ledger.js";
|
|
38
|
+
import { LEDGER_FILE, appendSessionRecord, findResumeCandidate, readSessionRecords, resolveRetentionDays, resumedContextProfile, toLedgerContract, } from "../lib/agent-ledger.js";
|
|
39
39
|
import { getActiveOrg, 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";
|
|
@@ -54,13 +54,19 @@ 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
|
|
58
|
-
*
|
|
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.
|
|
59
59
|
*/
|
|
60
60
|
function sayContextProfile(profile) {
|
|
61
|
-
if (profile ===
|
|
61
|
+
if (profile === null) {
|
|
62
|
+
say("mnemom agent: context: cell default");
|
|
63
|
+
}
|
|
64
|
+
else if (profile === "off") {
|
|
62
65
|
say("mnemom agent: context folding OFF for this session (--no-context or context = false).");
|
|
63
66
|
}
|
|
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
|
+
}
|
|
64
70
|
else if (profile === "aggressive") {
|
|
65
71
|
say("mnemom agent: context profile aggressive — the gateway folds earlier and keeps a tighter working set.");
|
|
66
72
|
}
|
|
@@ -174,11 +180,12 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
174
180
|
const cellNote = isProdGateway(env) ? "us-2/prod" : `custom (${gatewayHost})`;
|
|
175
181
|
const yolo = options.yolo !== false; // default ON
|
|
176
182
|
const contextHint = options.contextHint !== false; // default ON
|
|
177
|
-
// Context profile:
|
|
178
|
-
// MNEMOM_AGENT_CONTEXT_PROFILE (the `context_profile` setting)
|
|
179
|
-
// (or `context = false` in launcher.toml → MNEMOM_AGENT_CONTEXT) is `off`.
|
|
180
|
-
//
|
|
181
|
-
|
|
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({
|
|
182
189
|
contextProfile: options.contextProfile,
|
|
183
190
|
context: options.context,
|
|
184
191
|
env,
|
|
@@ -209,7 +216,7 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
209
216
|
compactionIntercept,
|
|
210
217
|
compactAt,
|
|
211
218
|
});
|
|
212
|
-
|
|
219
|
+
let sessionConfigB64 = buildSessionConfigB64({
|
|
213
220
|
profile: contextProfile,
|
|
214
221
|
contextHandoff,
|
|
215
222
|
compactionIntercept,
|
|
@@ -431,6 +438,33 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
431
438
|
passthrough: [...(options.passthrough ?? []), "--resume", continuation.resumeCcSessionId],
|
|
432
439
|
};
|
|
433
440
|
}
|
|
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
|
+
}
|
|
434
468
|
// ── local session ledger (~/.mnemom/sessions.jsonl) ── one line per real
|
|
435
469
|
// launch so `mnemom agent sessions` / `show` can find this conversation later.
|
|
436
470
|
// Written after provisioning so a freshly claimed agent's id resolves. Holds
|
|
@@ -703,7 +737,9 @@ export async function recordSessionLaunch(ctx) {
|
|
|
703
737
|
cli: ctx.cli.name,
|
|
704
738
|
contract: toLedgerContract(ctx.envelope?.contract ?? null),
|
|
705
739
|
goal_mode: goalAlignmentMode(ctx.envelope, ctx.goalMode),
|
|
706
|
-
|
|
740
|
+
...(ctx.contextProfile !== null
|
|
741
|
+
? { context_profile: ctx.contextProfile, context_profile_v: 2 }
|
|
742
|
+
: {}),
|
|
707
743
|
compact_at: ctx.compactAt,
|
|
708
744
|
cc_session_id: ctx.ccSessionId ?? null,
|
|
709
745
|
...(ctx.options.experimental?.length ? { experimental: [...ctx.options.experimental] } : {}),
|
|
@@ -1618,7 +1654,7 @@ export function printPlan(ctx) {
|
|
|
1618
1654
|
L.push(` agent: ${ctx.agent}`);
|
|
1619
1655
|
L.push(` conversation: ${ctx.conversationId}`);
|
|
1620
1656
|
L.push(` yolo: ${ctx.yolo ? "on (--dangerously-skip-permissions)" : "off"}`);
|
|
1621
|
-
L.push(` context: ${ctx.contextProfile}`);
|
|
1657
|
+
L.push(` context: ${ctx.contextProfile ?? "cell default"}`);
|
|
1622
1658
|
L.push(` compaction: ${describeCompactAt(ctx.compactAt)}`);
|
|
1623
1659
|
const overlay = Object.entries(ctx.sessionConfig);
|
|
1624
1660
|
if (overlay.length > 0) {
|
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.
|
|
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) => {
|
|
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
|
|
17
|
+
import { type ContextProfile, type LaunchShape, type 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,10 +77,17 @@ 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
|
|
81
|
-
* before profiles existed
|
|
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`).
|
|
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;
|
|
84
91
|
/**
|
|
85
92
|
* The resolved gateway compaction target (`--compact-at` / `compact_at` /
|
|
86
93
|
* `MNEMOM_COMPACT_AT`): `null` means no target (Claude Code's own 1M window; the default,
|
|
@@ -168,3 +175,15 @@ export declare function findResumeCandidate(records: readonly SessionRecord[], w
|
|
|
168
175
|
gatewayHost: string;
|
|
169
176
|
resumeId?: string | null;
|
|
170
177
|
}): 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;
|
package/dist/lib/agent-ledger.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
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";
|
|
19
20
|
/** The ledger's file name under MNEMOM_DIR. */
|
|
20
21
|
export const LEDGER_FILE = "sessions.jsonl";
|
|
21
22
|
/** Days a launch stays in the ledger when `sessions.retention_days` is unset. */
|
|
@@ -197,3 +198,25 @@ export function findResumeCandidate(records, want) {
|
|
|
197
198
|
}
|
|
198
199
|
return latest ? { record: latest, source: "session_name", byCcSession: false } : null;
|
|
199
200
|
}
|
|
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
|
+
}
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -294,22 +294,26 @@ export declare const CONTEXT_TAIL_BUDGET_KEY = "CONTEXT_RUNTIME_TAIL_BUDGET_TOKE
|
|
|
294
294
|
export declare const CONTEXT_READ_CACHE_KEY = "CONTEXT_RUNTIME_READ_CACHE_TOKENS";
|
|
295
295
|
export declare const CONTEXT_HEAD_RECLAIM_KEY = "CONTEXT_RUNTIME_HEAD_BREAKPOINT_RECLAIM";
|
|
296
296
|
/**
|
|
297
|
-
* The gateway key the
|
|
298
|
-
*
|
|
299
|
-
*
|
|
297
|
+
* The gateway key that turns the size-banded fold policy on or off (allow-listed per session).
|
|
298
|
+
* Only the literal `banded` enables it; any other value — the profiles send `off` — disables it,
|
|
299
|
+
* and a session's value replaces the cell's own. The band thresholds and budgets stay at the
|
|
300
|
+
* gateway's own defaults.
|
|
300
301
|
*/
|
|
301
302
|
export declare const CONTEXT_SIZE_POLICY_KEY = "CONTEXT_RUNTIME_SIZE_POLICY";
|
|
302
303
|
/**
|
|
303
304
|
* The named presets for how the gateway folds a session's context (`--context-profile`,
|
|
304
|
-
* launcher.toml `context_profile`, `MNEMOM_AGENT_CONTEXT_PROFILE`)
|
|
305
|
+
* launcher.toml `context_profile`, `MNEMOM_AGENT_CONTEXT_PROFILE`). Choosing NONE sends no fold
|
|
306
|
+
* key at all and the cell's own default decides (see `resolveContextProfile`). A chosen profile
|
|
307
|
+
* is complete on its own, so a change to a cell's default can never change what it means:
|
|
305
308
|
* - `off` → `CONTEXT_RUNTIME_ENABLED: "false"` — compaction is left to the coding agent
|
|
306
309
|
* (the same bytes `--no-context` has always sent).
|
|
307
|
-
* - `balanced` →
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
+
* - `balanced` → `CONTEXT_RUNTIME_SIZE_POLICY: "off"` — the cell's own fold budgets, never
|
|
311
|
+
* the size bands, even on a cell whose default is adaptive.
|
|
312
|
+
* - `aggressive` → a smaller tail budget + read cache and head-breakpoint reclaim on, size
|
|
313
|
+
* policy off: folds earlier and keeps a tighter working set at every size.
|
|
310
314
|
* - `adaptive` → `CONTEXT_RUNTIME_SIZE_POLICY: "banded"` — the gateway picks the fold by the
|
|
311
315
|
* conversation's original size: no fold below 200k tokens, the aggressive
|
|
312
|
-
* budgets from 200k to 1M, balanced above.
|
|
316
|
+
* budgets from 200k to 1M, balanced above.
|
|
313
317
|
*/
|
|
314
318
|
export declare const CONTEXT_PROFILES: readonly ["off", "balanced", "aggressive", "adaptive"];
|
|
315
319
|
export type ContextProfile = (typeof CONTEXT_PROFILES)[number];
|
|
@@ -556,36 +560,42 @@ export declare function resolveContextFold(opts: {
|
|
|
556
560
|
*/
|
|
557
561
|
export declare function parseContextProfile(raw: string, source: string): ContextProfile;
|
|
558
562
|
/**
|
|
559
|
-
* Which context profile this launch runs under
|
|
563
|
+
* Which context profile this launch runs under, or `null` when none was chosen anywhere — then
|
|
564
|
+
* no fold key is sent and the cell's own default decides. Precedence, highest wins:
|
|
560
565
|
* 1. `--no-context` — always `off`. Combined with an explicit `--context-profile
|
|
561
566
|
* balanced|aggressive|adaptive` it is a CONFLICT and the launch refuses (never a silent override);
|
|
562
567
|
* `--context-profile off` agrees with it.
|
|
563
568
|
* 2. an explicit `--context-profile <profile>` flag (`--context` alongside it is fine — the
|
|
564
569
|
* profile wins);
|
|
565
|
-
* 3. `
|
|
570
|
+
* 3. `resumed`: the profile the session this launch resumes ran under (from the session
|
|
571
|
+
* ledger), so a resume keeps that session's own choice over the saved default;
|
|
572
|
+
* 4. `MNEMOM_AGENT_CONTEXT_PROFILE` (which `applyConfigToEnv` sets from the `context_profile`
|
|
566
573
|
* config key) — an invalid value is an error, never a silent default;
|
|
567
|
-
*
|
|
574
|
+
* 5. the folding boolean when NO profile is set anywhere: `context = false` in launcher.toml
|
|
568
575
|
* (→ `MNEMOM_AGENT_CONTEXT`, see `resolveContextFold`) means `off`;
|
|
569
|
-
*
|
|
576
|
+
* 6. `null` — unset, the cell default.
|
|
570
577
|
*/
|
|
571
578
|
export declare function resolveContextProfile(opts: {
|
|
572
579
|
contextProfile?: ContextProfile;
|
|
573
580
|
context?: boolean;
|
|
581
|
+
resumed?: ContextProfile | null;
|
|
574
582
|
env?: NodeJS.ProcessEnv;
|
|
575
|
-
}): ContextProfile;
|
|
583
|
+
}): ContextProfile | null;
|
|
576
584
|
/**
|
|
577
585
|
* The decoded per-session env overlay for this launch — the `config` object of the
|
|
578
586
|
* `x-mnemom-session-config` payload, in wire order: the profile's keys first
|
|
579
|
-
* (`CONTEXT_RUNTIME_ENABLED` for `off`; the three fold-tuning keys for
|
|
580
|
-
* `
|
|
587
|
+
* (`CONTEXT_RUNTIME_ENABLED` for `off`; the three fold-tuning keys + size policy off for
|
|
588
|
+
* `aggressive`; size policy off for `balanced`; size policy banded for `adaptive`; nothing when
|
|
589
|
+
* no profile was chosen), then `CONTEXT_HANDOFF_ENABLED: "true"` when context handoff is ON, then
|
|
581
590
|
* `GATEWAY_USAGE_REAL_SIZE: "on"` + `GATEWAY_COMPACTION_INTERCEPT: "on"` when EITHER the
|
|
582
591
|
* `compaction_intercept` setting is ON OR `--compact-at` arms a target below 1M (the target
|
|
583
592
|
* cannot be honored without the intercept), then `GATEWAY_COMPACTION_UPLOAD_TARGET_TOKENS: "<N>"`
|
|
584
|
-
* when a target is set. Empty
|
|
585
|
-
* header is sent at all. Exported so `--dry-run` can list exactly what the header carries.
|
|
593
|
+
* when a target is set. Empty with no profile chosen and everything off — the common case, where
|
|
594
|
+
* no header is sent at all. Exported so `--dry-run` can list exactly what the header carries.
|
|
586
595
|
*/
|
|
587
596
|
export declare function buildSessionConfig(opts: {
|
|
588
|
-
|
|
597
|
+
/** `resolveContextProfile` output: `null` ⇒ no profile chosen, no fold key sent. */
|
|
598
|
+
profile: ContextProfile | null;
|
|
589
599
|
contextHandoff: boolean;
|
|
590
600
|
compactionIntercept?: boolean;
|
|
591
601
|
/** `resolveCompactAt` output: `null` (or absent) ⇒ no target, send nothing new. */
|
|
@@ -597,7 +607,7 @@ export declare function buildSessionConfig(opts: {
|
|
|
597
607
|
* gateway's allow-list never sees them as env keys; it ignores unknown envelope fields.
|
|
598
608
|
*/
|
|
599
609
|
export declare function buildSessionConfigPayload(opts: {
|
|
600
|
-
profile: ContextProfile;
|
|
610
|
+
profile: ContextProfile | null;
|
|
601
611
|
contextHandoff: boolean;
|
|
602
612
|
compactionIntercept?: boolean;
|
|
603
613
|
compactAt?: number | null;
|
|
@@ -609,11 +619,12 @@ export declare function buildSessionConfigPayload(opts: {
|
|
|
609
619
|
} | undefined;
|
|
610
620
|
/**
|
|
611
621
|
* The `x-mnemom-session-config` value for this launch, or `undefined` when there is
|
|
612
|
-
* nothing to override (the
|
|
613
|
-
* byte-identical to a launch before profiles existed). Independent overlays:
|
|
622
|
+
* nothing to override (the cell's own defaults stand — no profile chosen with everything else
|
|
623
|
+
* off, byte-identical to a launch before profiles existed). Independent overlays:
|
|
614
624
|
* - the context profile (`buildSessionConfig`): `off` → `CONTEXT_RUNTIME_ENABLED: "false"`,
|
|
615
|
-
* `aggressive` → the three fold-tuning keys
|
|
616
|
-
* "
|
|
625
|
+
* `aggressive` → the three fold-tuning keys + `CONTEXT_RUNTIME_SIZE_POLICY: "off"`,
|
|
626
|
+
* `balanced` → `CONTEXT_RUNTIME_SIZE_POLICY: "off"`, `adaptive` →
|
|
627
|
+
* `CONTEXT_RUNTIME_SIZE_POLICY: "banded"`, none chosen → nothing;
|
|
617
628
|
* - `compactionIntercept: true` → `GATEWAY_USAGE_REAL_SIZE` + `GATEWAY_COMPACTION_INTERCEPT`
|
|
618
629
|
* `"on"` (the `compaction_intercept` setting);
|
|
619
630
|
* - `compactAt: <N>` (`--compact-at`, below 1M) → the SAME two intercept keys plus
|
|
@@ -626,7 +637,7 @@ export declare function buildSessionConfigPayload(opts: {
|
|
|
626
637
|
* base64url so it survives the gateway's inbound strip.
|
|
627
638
|
*/
|
|
628
639
|
export declare function buildSessionConfigB64(opts: {
|
|
629
|
-
profile: ContextProfile;
|
|
640
|
+
profile: ContextProfile | null;
|
|
630
641
|
contextHandoff: boolean;
|
|
631
642
|
compactionIntercept?: boolean;
|
|
632
643
|
compactAt?: number | null;
|
package/dist/lib/agent.js
CHANGED
|
@@ -452,32 +452,37 @@ export const CONTEXT_TAIL_BUDGET_KEY = "CONTEXT_RUNTIME_TAIL_BUDGET_TOKENS";
|
|
|
452
452
|
export const CONTEXT_READ_CACHE_KEY = "CONTEXT_RUNTIME_READ_CACHE_TOKENS";
|
|
453
453
|
export const CONTEXT_HEAD_RECLAIM_KEY = "CONTEXT_RUNTIME_HEAD_BREAKPOINT_RECLAIM";
|
|
454
454
|
/**
|
|
455
|
-
* The gateway key the
|
|
456
|
-
*
|
|
457
|
-
*
|
|
455
|
+
* The gateway key that turns the size-banded fold policy on or off (allow-listed per session).
|
|
456
|
+
* Only the literal `banded` enables it; any other value — the profiles send `off` — disables it,
|
|
457
|
+
* and a session's value replaces the cell's own. The band thresholds and budgets stay at the
|
|
458
|
+
* gateway's own defaults.
|
|
458
459
|
*/
|
|
459
460
|
export const CONTEXT_SIZE_POLICY_KEY = "CONTEXT_RUNTIME_SIZE_POLICY";
|
|
460
461
|
/**
|
|
461
462
|
* The named presets for how the gateway folds a session's context (`--context-profile`,
|
|
462
|
-
* launcher.toml `context_profile`, `MNEMOM_AGENT_CONTEXT_PROFILE`)
|
|
463
|
+
* launcher.toml `context_profile`, `MNEMOM_AGENT_CONTEXT_PROFILE`). Choosing NONE sends no fold
|
|
464
|
+
* key at all and the cell's own default decides (see `resolveContextProfile`). A chosen profile
|
|
465
|
+
* is complete on its own, so a change to a cell's default can never change what it means:
|
|
463
466
|
* - `off` → `CONTEXT_RUNTIME_ENABLED: "false"` — compaction is left to the coding agent
|
|
464
467
|
* (the same bytes `--no-context` has always sent).
|
|
465
|
-
* - `balanced` →
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
+
* - `balanced` → `CONTEXT_RUNTIME_SIZE_POLICY: "off"` — the cell's own fold budgets, never
|
|
469
|
+
* the size bands, even on a cell whose default is adaptive.
|
|
470
|
+
* - `aggressive` → a smaller tail budget + read cache and head-breakpoint reclaim on, size
|
|
471
|
+
* policy off: folds earlier and keeps a tighter working set at every size.
|
|
468
472
|
* - `adaptive` → `CONTEXT_RUNTIME_SIZE_POLICY: "banded"` — the gateway picks the fold by the
|
|
469
473
|
* conversation's original size: no fold below 200k tokens, the aggressive
|
|
470
|
-
* budgets from 200k to 1M, balanced above.
|
|
474
|
+
* budgets from 200k to 1M, balanced above.
|
|
471
475
|
*/
|
|
472
476
|
export const CONTEXT_PROFILES = ["off", "balanced", "aggressive", "adaptive"];
|
|
473
477
|
/** The per-session env overlay each profile expands to (key order is the wire order). */
|
|
474
478
|
const CONTEXT_PROFILE_CONFIG = {
|
|
475
479
|
off: [[CONTEXT_RUNTIME_KEY, "false"]],
|
|
476
|
-
balanced: [],
|
|
480
|
+
balanced: [[CONTEXT_SIZE_POLICY_KEY, "off"]],
|
|
477
481
|
aggressive: [
|
|
478
482
|
[CONTEXT_TAIL_BUDGET_KEY, "60000"],
|
|
479
483
|
[CONTEXT_READ_CACHE_KEY, "8000"],
|
|
480
484
|
[CONTEXT_HEAD_RECLAIM_KEY, "true"],
|
|
485
|
+
[CONTEXT_SIZE_POLICY_KEY, "off"],
|
|
481
486
|
],
|
|
482
487
|
adaptive: [[CONTEXT_SIZE_POLICY_KEY, "banded"]],
|
|
483
488
|
};
|
|
@@ -912,17 +917,20 @@ export function parseContextProfile(raw, source) {
|
|
|
912
917
|
throw new Error(`${source} must be one of ${CONTEXT_PROFILES.join(", ")} (got '${raw}').`);
|
|
913
918
|
}
|
|
914
919
|
/**
|
|
915
|
-
* Which context profile this launch runs under
|
|
920
|
+
* Which context profile this launch runs under, or `null` when none was chosen anywhere — then
|
|
921
|
+
* no fold key is sent and the cell's own default decides. Precedence, highest wins:
|
|
916
922
|
* 1. `--no-context` — always `off`. Combined with an explicit `--context-profile
|
|
917
923
|
* balanced|aggressive|adaptive` it is a CONFLICT and the launch refuses (never a silent override);
|
|
918
924
|
* `--context-profile off` agrees with it.
|
|
919
925
|
* 2. an explicit `--context-profile <profile>` flag (`--context` alongside it is fine — the
|
|
920
926
|
* profile wins);
|
|
921
|
-
* 3. `
|
|
927
|
+
* 3. `resumed`: the profile the session this launch resumes ran under (from the session
|
|
928
|
+
* ledger), so a resume keeps that session's own choice over the saved default;
|
|
929
|
+
* 4. `MNEMOM_AGENT_CONTEXT_PROFILE` (which `applyConfigToEnv` sets from the `context_profile`
|
|
922
930
|
* config key) — an invalid value is an error, never a silent default;
|
|
923
|
-
*
|
|
931
|
+
* 5. the folding boolean when NO profile is set anywhere: `context = false` in launcher.toml
|
|
924
932
|
* (→ `MNEMOM_AGENT_CONTEXT`, see `resolveContextFold`) means `off`;
|
|
925
|
-
*
|
|
933
|
+
* 6. `null` — unset, the cell default.
|
|
926
934
|
*/
|
|
927
935
|
export function resolveContextProfile(opts) {
|
|
928
936
|
if (opts.context === false) {
|
|
@@ -933,28 +941,33 @@ export function resolveContextProfile(opts) {
|
|
|
933
941
|
}
|
|
934
942
|
if (opts.contextProfile !== undefined)
|
|
935
943
|
return opts.contextProfile;
|
|
944
|
+
if (opts.resumed)
|
|
945
|
+
return opts.resumed;
|
|
936
946
|
const raw = (opts.env?.MNEMOM_AGENT_CONTEXT_PROFILE ?? "").trim();
|
|
937
947
|
if (raw !== "")
|
|
938
948
|
return parseContextProfile(raw, "MNEMOM_AGENT_CONTEXT_PROFILE");
|
|
939
949
|
if (!resolveContextFold({ context: opts.context, env: opts.env }))
|
|
940
950
|
return "off";
|
|
941
|
-
return
|
|
951
|
+
return null;
|
|
942
952
|
}
|
|
943
953
|
/**
|
|
944
954
|
* The decoded per-session env overlay for this launch — the `config` object of the
|
|
945
955
|
* `x-mnemom-session-config` payload, in wire order: the profile's keys first
|
|
946
|
-
* (`CONTEXT_RUNTIME_ENABLED` for `off`; the three fold-tuning keys for
|
|
947
|
-
* `
|
|
956
|
+
* (`CONTEXT_RUNTIME_ENABLED` for `off`; the three fold-tuning keys + size policy off for
|
|
957
|
+
* `aggressive`; size policy off for `balanced`; size policy banded for `adaptive`; nothing when
|
|
958
|
+
* no profile was chosen), then `CONTEXT_HANDOFF_ENABLED: "true"` when context handoff is ON, then
|
|
948
959
|
* `GATEWAY_USAGE_REAL_SIZE: "on"` + `GATEWAY_COMPACTION_INTERCEPT: "on"` when EITHER the
|
|
949
960
|
* `compaction_intercept` setting is ON OR `--compact-at` arms a target below 1M (the target
|
|
950
961
|
* cannot be honored without the intercept), then `GATEWAY_COMPACTION_UPLOAD_TARGET_TOKENS: "<N>"`
|
|
951
|
-
* when a target is set. Empty
|
|
952
|
-
* header is sent at all. Exported so `--dry-run` can list exactly what the header carries.
|
|
962
|
+
* when a target is set. Empty with no profile chosen and everything off — the common case, where
|
|
963
|
+
* no header is sent at all. Exported so `--dry-run` can list exactly what the header carries.
|
|
953
964
|
*/
|
|
954
965
|
export function buildSessionConfig(opts) {
|
|
955
966
|
const config = {};
|
|
956
|
-
|
|
957
|
-
|
|
967
|
+
if (opts.profile !== null) {
|
|
968
|
+
for (const [k, v] of CONTEXT_PROFILE_CONFIG[opts.profile])
|
|
969
|
+
config[k] = v;
|
|
970
|
+
}
|
|
958
971
|
if (opts.contextHandoff)
|
|
959
972
|
config[CONTEXT_HANDOFF_KEY] = "true";
|
|
960
973
|
const hasCompactionTarget = opts.compactAt !== undefined && opts.compactAt !== null;
|
|
@@ -986,11 +999,12 @@ export function buildSessionConfigPayload(opts) {
|
|
|
986
999
|
}
|
|
987
1000
|
/**
|
|
988
1001
|
* The `x-mnemom-session-config` value for this launch, or `undefined` when there is
|
|
989
|
-
* nothing to override (the
|
|
990
|
-
* byte-identical to a launch before profiles existed). Independent overlays:
|
|
1002
|
+
* nothing to override (the cell's own defaults stand — no profile chosen with everything else
|
|
1003
|
+
* off, byte-identical to a launch before profiles existed). Independent overlays:
|
|
991
1004
|
* - the context profile (`buildSessionConfig`): `off` → `CONTEXT_RUNTIME_ENABLED: "false"`,
|
|
992
|
-
* `aggressive` → the three fold-tuning keys
|
|
993
|
-
* "
|
|
1005
|
+
* `aggressive` → the three fold-tuning keys + `CONTEXT_RUNTIME_SIZE_POLICY: "off"`,
|
|
1006
|
+
* `balanced` → `CONTEXT_RUNTIME_SIZE_POLICY: "off"`, `adaptive` →
|
|
1007
|
+
* `CONTEXT_RUNTIME_SIZE_POLICY: "banded"`, none chosen → nothing;
|
|
994
1008
|
* - `compactionIntercept: true` → `GATEWAY_USAGE_REAL_SIZE` + `GATEWAY_COMPACTION_INTERCEPT`
|
|
995
1009
|
* `"on"` (the `compaction_intercept` setting);
|
|
996
1010
|
* - `compactAt: <N>` (`--compact-at`, below 1M) → the SAME two intercept keys plus
|
|
@@ -56,10 +56,11 @@ export interface LauncherConfig {
|
|
|
56
56
|
*/
|
|
57
57
|
compactionIntercept?: boolean;
|
|
58
58
|
/**
|
|
59
|
-
* How the gateway folds each session's context (
|
|
60
|
-
* compaction to the coding agent, `balanced` keeps more
|
|
61
|
-
* folds earlier with a tighter working set, `adaptive`
|
|
62
|
-
* (none below 200k tokens, aggressive up to 1M, balanced
|
|
59
|
+
* How the gateway folds each session's context (unset: nothing is sent, the gateway cell's
|
|
60
|
+
* own default decides): `off` leaves compaction to the coding agent, `balanced` keeps more
|
|
61
|
+
* earlier work verbatim, `aggressive` folds earlier with a tighter working set, `adaptive`
|
|
62
|
+
* sizes the fold to the conversation (none below 200k tokens, aggressive up to 1M, balanced
|
|
63
|
+
* above). `--context-profile` overrides it for one launch without saving anything.
|
|
63
64
|
*/
|
|
64
65
|
contextProfile?: ContextProfile;
|
|
65
66
|
/**
|
|
@@ -215,7 +215,7 @@ export const CONFIG_KEYS = [
|
|
|
215
215
|
},
|
|
216
216
|
{
|
|
217
217
|
key: "context_profile",
|
|
218
|
-
describe: `How the gateway folds each session's context (${CONTEXT_PROFILES.join("|")};
|
|
218
|
+
describe: `How the gateway folds each session's context (${CONTEXT_PROFILES.join("|")}; unset = the gateway cell's default)`,
|
|
219
219
|
get: (c) => c.contextProfile,
|
|
220
220
|
set: (c, v) => {
|
|
221
221
|
c.contextProfile = parseContextProfile(v, "context_profile");
|