@mnemom/mnemom 0.17.2-next.0 → 0.17.2-next.2
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-sessions.d.ts +15 -4
- package/dist/commands/agent-sessions.js +37 -7
- package/dist/commands/agent.d.ts +7 -0
- package/dist/commands/agent.js +18 -1
- package/dist/index.js +3 -0
- package/dist/lib/agent-ledger.d.ts +11 -0
- package/dist/lib/agent.d.ts +28 -0
- package/dist/lib/agent.js +37 -0
- package/dist/lib/launcher-config.d.ts +3 -1
- package/dist/lib/launcher-config.js +25 -0
- package/dist/rc-proxy.mjs +3 -0
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* local sha is never printed as "the sealed hash" and no mismatch is flagged —
|
|
14
14
|
* only the server's hash is shown, labelled "sealed hash".
|
|
15
15
|
*/
|
|
16
|
-
import { type SessionRecord } from "../lib/agent-ledger.js";
|
|
16
|
+
import { type GoalMode, type SessionRecord } from "../lib/agent-ledger.js";
|
|
17
17
|
import { type AgentSessionResponse } from "../lib/api.js";
|
|
18
18
|
export interface SessionCommandDeps {
|
|
19
19
|
/** The ledger, newest first. */
|
|
@@ -47,8 +47,19 @@ export interface SessionRefAmbiguous {
|
|
|
47
47
|
* - nothing matches (or the ledger is empty) → null.
|
|
48
48
|
*/
|
|
49
49
|
export declare function resolveSessionRef(records: SessionRecord[], ref?: string): SessionRefMatch | SessionRefAmbiguous | null;
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Goal-alignment mode as the ledger recorded it. Prefers the explicit
|
|
52
|
+
* `goal_mode` (which alone distinguishes `implicit` from `off`); records
|
|
53
|
+
* written before that field existed fall back to `contract ? "on" : "off"`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function localGoalFlag(record: SessionRecord): GoalMode;
|
|
56
|
+
/**
|
|
57
|
+
* Goal-alignment mode for the `show` view: the live gateway state wins when it
|
|
58
|
+
* is present (a sealed contract ⇒ `on`), otherwise the locally recorded mode.
|
|
59
|
+
* A gateway with no sealed contract can still be an `implicit` session, so the
|
|
60
|
+
* local `goal_mode` fills that in.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveGoalMode(local: SessionRecord, remote: AgentSessionResponse | null): GoalMode;
|
|
52
63
|
/**
|
|
53
64
|
* Render the ledger as a table, newest first — the newest
|
|
54
65
|
* `DEFAULT_SESSIONS_LIMIT` rows unless `all`, with a footer naming the rest.
|
|
@@ -69,7 +80,7 @@ export interface ShowJson {
|
|
|
69
80
|
session: SessionRecord & {
|
|
70
81
|
source: "local";
|
|
71
82
|
};
|
|
72
|
-
goal_alignment:
|
|
83
|
+
goal_alignment: GoalMode;
|
|
73
84
|
live: {
|
|
74
85
|
source: "gateway";
|
|
75
86
|
available: true;
|
|
@@ -55,15 +55,46 @@ export function resolveSessionRef(records, ref) {
|
|
|
55
55
|
return null;
|
|
56
56
|
}
|
|
57
57
|
// ─── sessions table ───────────────────────────────────────────────────────────
|
|
58
|
-
const COL = { session: 26, started: 16, agent: 22, goal:
|
|
58
|
+
const COL = { session: 26, started: 16, agent: 22, goal: 10, conv: 8 };
|
|
59
59
|
function cell(text, width) {
|
|
60
60
|
const max = width - 2;
|
|
61
61
|
const shown = text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
62
62
|
return shown.padEnd(width);
|
|
63
63
|
}
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Goal-alignment mode as the ledger recorded it. Prefers the explicit
|
|
66
|
+
* `goal_mode` (which alone distinguishes `implicit` from `off`); records
|
|
67
|
+
* written before that field existed fall back to `contract ? "on" : "off"`.
|
|
68
|
+
*/
|
|
65
69
|
export function localGoalFlag(record) {
|
|
66
|
-
return record.contract ? "on" : "off";
|
|
70
|
+
return record.goal_mode ?? (record.contract ? "on" : "off");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Goal-alignment mode for the `show` view: the live gateway state wins when it
|
|
74
|
+
* is present (a sealed contract ⇒ `on`), otherwise the locally recorded mode.
|
|
75
|
+
* A gateway with no sealed contract can still be an `implicit` session, so the
|
|
76
|
+
* local `goal_mode` fills that in.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveGoalMode(local, remote) {
|
|
79
|
+
if (remote) {
|
|
80
|
+
// Live truth wins: a sealed contract ⇒ on. When the gateway has NO sealed
|
|
81
|
+
// contract, the session is either implicit or off — and only the local
|
|
82
|
+
// `goal_mode` distinguishes those. A stale local "on" (a contract that has
|
|
83
|
+
// since cleared/expired/been revoked on the gateway) must NOT resurrect an
|
|
84
|
+
// "on" the live state contradicts, so only "implicit" is recovered here.
|
|
85
|
+
if (remote.sealed !== null)
|
|
86
|
+
return "on";
|
|
87
|
+
return local.goal_mode === "implicit" ? "implicit" : "off";
|
|
88
|
+
}
|
|
89
|
+
return localGoalFlag(local);
|
|
90
|
+
}
|
|
91
|
+
/** The badge for a goal mode: on → green, implicit → cyan, off → yellow. */
|
|
92
|
+
function goalBadge(mode) {
|
|
93
|
+
if (mode === "on")
|
|
94
|
+
return fmt.badge("ON", "green");
|
|
95
|
+
if (mode === "implicit")
|
|
96
|
+
return fmt.badge("IMPLICIT", "cyan");
|
|
97
|
+
return fmt.badge("OFF", "yellow");
|
|
67
98
|
}
|
|
68
99
|
/**
|
|
69
100
|
* Render the ledger as a table, newest first — the newest
|
|
@@ -123,8 +154,8 @@ export function buildShowView(local, remote, reason) {
|
|
|
123
154
|
launchBits.push(`effort ${local.effort}`);
|
|
124
155
|
L.push(fmt.label("launch: ", launchBits.join(" · ")));
|
|
125
156
|
L.push(fmt.label("conversation: ", local.conversation_id));
|
|
126
|
-
const
|
|
127
|
-
L.push(fmt.label("goal alignment:",
|
|
157
|
+
const goalMode = resolveGoalMode(local, remote);
|
|
158
|
+
L.push(fmt.label("goal alignment:", goalBadge(goalMode)));
|
|
128
159
|
if (local.contract) {
|
|
129
160
|
L.push(fmt.label("goal (at launch):", local.contract.statement));
|
|
130
161
|
}
|
|
@@ -176,10 +207,9 @@ export function buildShowView(local, remote, reason) {
|
|
|
176
207
|
return L;
|
|
177
208
|
}
|
|
178
209
|
export function buildShowJson(local, remote, reason) {
|
|
179
|
-
const goalOn = remote ? remote.sealed !== null : local.contract !== null;
|
|
180
210
|
return {
|
|
181
211
|
session: { ...local, source: "local" },
|
|
182
|
-
goal_alignment:
|
|
212
|
+
goal_alignment: resolveGoalMode(local, remote),
|
|
183
213
|
live: remote
|
|
184
214
|
? {
|
|
185
215
|
source: "gateway",
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -52,6 +52,12 @@ export interface AgentOptions {
|
|
|
52
52
|
trimMcp?: boolean;
|
|
53
53
|
/** yolo default: commander `--no-yolo` sets this false. */
|
|
54
54
|
yolo?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Context folding: undefined = default ON, false = `--no-context`, true =
|
|
57
|
+
* `--context`. When OFF the launch sends `x-mnemom-session-config` telling the
|
|
58
|
+
* gateway to skip folding for this session only.
|
|
59
|
+
*/
|
|
60
|
+
context?: boolean;
|
|
55
61
|
/** context-hint seed default: commander `--no-context-hint` sets this false. */
|
|
56
62
|
contextHint?: boolean;
|
|
57
63
|
/**
|
|
@@ -91,6 +97,7 @@ export declare function recordSessionLaunch(ctx: {
|
|
|
91
97
|
options: AgentOptions;
|
|
92
98
|
cli: ResolvedCli;
|
|
93
99
|
envelope: SealedContractEnvelope | null;
|
|
100
|
+
goalMode: string | null;
|
|
94
101
|
}): Promise<void>;
|
|
95
102
|
/**
|
|
96
103
|
* Resolve the Anthropic key with NO 1Password dependency (built for external
|
package/dist/commands/agent.js
CHANGED
|
@@ -38,7 +38,7 @@ import { fmt } from "../lib/format.js";
|
|
|
38
38
|
import { askInput, askSelect, askYesNo, isInteractive } from "../lib/prompt.js";
|
|
39
39
|
import { goalAlignmentLine, resolveSessionSetup } from "./agent-session.js";
|
|
40
40
|
import { defaultAgentSlug, ensureGovernedAgent, ensureSessionAndBalance, isQuietPreambleExit, resolveDefaultSlug, } from "./agent-setup.js";
|
|
41
|
-
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, isProdGateway, launchPreferenceExplicit, remoteControlAllowed, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, resolveLaunchShape, resolveRemoteControlShape, sanitizeAgentName, } from "../lib/agent.js";
|
|
41
|
+
import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildSessionConfigB64, resolveContextFold, isProdGateway, launchPreferenceExplicit, remoteControlAllowed, resolveAnthropicDoor, resolveConversationId, resolveGatewayHost, resolveLaunchShape, resolveRemoteControlShape, sanitizeAgentName, } from "../lib/agent.js";
|
|
42
42
|
/** The CLI's own provider-key store (zero 1Password): ~/.mnemom/launcher.json, 0600. */
|
|
43
43
|
const CODE_STORE = join(MNEMOM_DIR, "launcher.json");
|
|
44
44
|
const say = (line = "") => console.error(line);
|
|
@@ -96,6 +96,11 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
96
96
|
const cellNote = isProdGateway(env) ? "us-2/prod" : `custom (${gatewayHost})`;
|
|
97
97
|
const yolo = options.yolo !== false; // default ON
|
|
98
98
|
const contextHint = options.contextHint !== false; // default ON
|
|
99
|
+
// Context folding is ON by default; --no-context (or `context = false` in
|
|
100
|
+
// launcher.toml → MNEMOM_AGENT_CONTEXT) turns it off for this session via the
|
|
101
|
+
// per-session config header. `undefined` sessionConfigB64 ⇒ nothing sent.
|
|
102
|
+
const contextFold = resolveContextFold({ context: options.context, env });
|
|
103
|
+
const sessionConfigB64 = buildSessionConfigB64(contextFold);
|
|
99
104
|
// ── which CLI executable (name or full path) ── resolve GRACEFULLY: if it's not
|
|
100
105
|
// found we don't crash with a technical error — we ask for the path at launch.
|
|
101
106
|
let cli;
|
|
@@ -138,6 +143,9 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
138
143
|
if (previewShape === "remote-control") {
|
|
139
144
|
say("mnemom agent: the plan shows Remote Control because this machine can run it; the real launch uses it only if your account is entitled (internal Mnemom accounts), else terminal-only.");
|
|
140
145
|
}
|
|
146
|
+
if (!contextFold) {
|
|
147
|
+
say("mnemom agent: context folding OFF for this session (--no-context or context = false).");
|
|
148
|
+
}
|
|
141
149
|
return;
|
|
142
150
|
}
|
|
143
151
|
// Real launch: make sure we actually have a CLI to run. If we couldn't find it,
|
|
@@ -231,9 +239,13 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
231
239
|
options,
|
|
232
240
|
cli,
|
|
233
241
|
envelope,
|
|
242
|
+
goalMode,
|
|
234
243
|
});
|
|
235
244
|
logLaunch({ shape, door, agent, conversationId, cellNote, envelope, goalMode, cli });
|
|
236
245
|
say(goalAlignmentLine(goalAlignmentMode(envelope, goalMode)));
|
|
246
|
+
if (!contextFold) {
|
|
247
|
+
say("mnemom agent: context folding OFF for this session (--no-context or context = false).");
|
|
248
|
+
}
|
|
237
249
|
if (shape === "terminal") {
|
|
238
250
|
launchTerminal({
|
|
239
251
|
cli,
|
|
@@ -243,6 +255,7 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
243
255
|
conversationId,
|
|
244
256
|
envelope,
|
|
245
257
|
goalMode,
|
|
258
|
+
sessionConfigB64,
|
|
246
259
|
options,
|
|
247
260
|
yolo,
|
|
248
261
|
contextHint,
|
|
@@ -259,6 +272,7 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
259
272
|
scenario,
|
|
260
273
|
envelope,
|
|
261
274
|
goalMode,
|
|
275
|
+
sessionConfigB64,
|
|
262
276
|
options,
|
|
263
277
|
yolo,
|
|
264
278
|
});
|
|
@@ -292,6 +306,7 @@ export async function recordSessionLaunch(ctx) {
|
|
|
292
306
|
effort: ctx.options.effort?.trim() || null,
|
|
293
307
|
cli: ctx.cli.name,
|
|
294
308
|
contract: toLedgerContract(ctx.envelope?.contract ?? null),
|
|
309
|
+
goal_mode: goalAlignmentMode(ctx.envelope, ctx.goalMode),
|
|
295
310
|
}, { dir: MNEMOM_DIR, now: () => new Date(), pid: process.pid });
|
|
296
311
|
if (!result.ok) {
|
|
297
312
|
say(fmt.warn(`mnemom agent: could not record this session in ~/.mnemom/${LEDGER_FILE} (${result.error}) — launching anyway.`));
|
|
@@ -542,6 +557,7 @@ function launchTerminal(ctx) {
|
|
|
542
557
|
conversationId: ctx.conversationId,
|
|
543
558
|
contractB64: ctx.envelope?.base64,
|
|
544
559
|
goalMode: ctx.goalMode ?? undefined,
|
|
560
|
+
sessionConfigB64: ctx.sessionConfigB64,
|
|
545
561
|
});
|
|
546
562
|
const args = buildCliArgs(ctx.options, { yolo: ctx.yolo, allowCliFlags: true });
|
|
547
563
|
// Claude Code logged in via claude.ai (OAuth) PREEMPTS ANTHROPIC_API_KEY, so
|
|
@@ -590,6 +606,7 @@ function launchRemoteControl(ctx) {
|
|
|
590
606
|
MNEMOM_AGENT_RC_CONVERSATION_ID: ctx.conversationId,
|
|
591
607
|
MNEMOM_AGENT_RC_CONTRACT_B64: ctx.envelope?.base64 ?? "",
|
|
592
608
|
MNEMOM_AGENT_RC_GOAL_MODE: ctx.goalMode ?? "",
|
|
609
|
+
MNEMOM_AGENT_RC_SESSION_CONFIG: ctx.sessionConfigB64 ?? "",
|
|
593
610
|
MNEMOM_AGENT_RC_PROXY_LOG: proxyLog,
|
|
594
611
|
MNEMOM_AGENT_RC_PARENT_PID: String(process.pid),
|
|
595
612
|
},
|
package/dist/index.js
CHANGED
|
@@ -134,6 +134,8 @@ const agentCmd = program
|
|
|
134
134
|
.option("--no-setup", "Skip governed-agent provisioning entirely for this launch")
|
|
135
135
|
.option("--no-yolo", "Do NOT pass --dangerously-skip-permissions (yolo is on by default)")
|
|
136
136
|
.option("--no-context-hint", "Do NOT seed Claude Code's context-hint feature flag before launch")
|
|
137
|
+
.option("--context", "Force context folding ON for this launch even when launcher.toml (context = false) or MNEMOM_AGENT_CONTEXT turns it off")
|
|
138
|
+
.option("--no-context", "Disable context folding for this launch (folding is on by default; tells the gateway to skip it for this session)")
|
|
137
139
|
.option("--dry-run", "Print the resolved launch plan and exit — no key read, no spawn")
|
|
138
140
|
.action(async (scenario, passthrough, opts) => {
|
|
139
141
|
try {
|
|
@@ -215,6 +217,7 @@ const agentCmd = program
|
|
|
215
217
|
trimMcp: opts.trimMcp,
|
|
216
218
|
yolo: opts.yolo,
|
|
217
219
|
contextHint: opts.contextHint,
|
|
220
|
+
context: opts.context,
|
|
218
221
|
setup: opts.setup,
|
|
219
222
|
keySource: cfg.keySource,
|
|
220
223
|
passthrough,
|
|
@@ -46,7 +46,18 @@ export interface SessionRecord {
|
|
|
46
46
|
/** The coding-agent CLI's basename, e.g. "claude". */
|
|
47
47
|
cli: string;
|
|
48
48
|
contract: SessionRecordContract | null;
|
|
49
|
+
/**
|
|
50
|
+
* The goal-alignment mode the launch ran under: `"on"` (a sealed contract),
|
|
51
|
+
* `"implicit"` (`--implicit` — the gateway infers the goal, no sealed
|
|
52
|
+
* contract), or `"off"`. Optional: records written before this field existed
|
|
53
|
+
* omit it, and readers fall back to `contract ? "on" : "off"` (an old
|
|
54
|
+
* `--implicit` launch is indistinguishable from `off` — its mode was never
|
|
55
|
+
* captured).
|
|
56
|
+
*/
|
|
57
|
+
goal_mode?: GoalMode;
|
|
49
58
|
}
|
|
59
|
+
/** Goal-alignment mode as recorded at launch. */
|
|
60
|
+
export type GoalMode = "off" | "on" | "implicit";
|
|
50
61
|
/** What the call site supplies — the ledger stamps `v` and `started_at`. */
|
|
51
62
|
export type SessionRecordInput = Omit<SessionRecord, "v" | "started_at">;
|
|
52
63
|
/** The injected environment of the ledger. */
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -126,6 +126,15 @@ export interface SealedContractEnvelope {
|
|
|
126
126
|
export declare function assembleContract(flags: ContractFlags): SealedContractEnvelope | null;
|
|
127
127
|
/** The request header that opts a conversation into implicit goal mode. */
|
|
128
128
|
export declare const GOAL_MODE_HEADER = "x-mnemom-goal-mode";
|
|
129
|
+
/**
|
|
130
|
+
* Per-session env overlay the gateway honours (`session-config.ts`
|
|
131
|
+
* SESSION_CONFIG_HEADER). base64url(JSON) of `{ v, config: { KEY: "value" } }`,
|
|
132
|
+
* keys drawn from the gateway's allow-list. Today the CLI uses it for exactly
|
|
133
|
+
* one thing: turning context folding off for a single launch.
|
|
134
|
+
*/
|
|
135
|
+
export declare const SESSION_CONFIG_HEADER = "x-mnemom-session-config";
|
|
136
|
+
/** The gateway env key that gates context folding (allow-listed for per-session overlay). */
|
|
137
|
+
export declare const CONTEXT_RUNTIME_KEY = "CONTEXT_RUNTIME_ENABLED";
|
|
129
138
|
/**
|
|
130
139
|
* The `x-mnemom-goal-mode` header value for `--implicit`, or null when the flag
|
|
131
140
|
* is absent: `implicit[; max_turns=N][; budget_usd=X][; stall_turns=N]` — the
|
|
@@ -204,7 +213,26 @@ export declare function buildCustomHeaders(opts: {
|
|
|
204
213
|
contractB64?: string;
|
|
205
214
|
/** `buildGoalModeHeader` output for an `--implicit` launch. */
|
|
206
215
|
goalMode?: string;
|
|
216
|
+
/** `buildSessionConfigB64` output — the per-session env overlay (e.g. fold off). */
|
|
217
|
+
sessionConfigB64?: string;
|
|
207
218
|
}): string;
|
|
219
|
+
/**
|
|
220
|
+
* Whether context folding is ON for this launch. Folding is a gateway feature
|
|
221
|
+
* that summarises older turns to fit the window; it is ON by default. Precedence:
|
|
222
|
+
* an explicit `--context`/`--no-context` flag wins, else `MNEMOM_AGENT_CONTEXT`
|
|
223
|
+
* (which `applyConfigToEnv` sets from the `context` config key), else ON.
|
|
224
|
+
* Recognised OFF values for the env var: `0`, `false`, `off`, `no` (case-insensitive).
|
|
225
|
+
*/
|
|
226
|
+
export declare function resolveContextFold(opts: {
|
|
227
|
+
context?: boolean;
|
|
228
|
+
env?: NodeJS.ProcessEnv;
|
|
229
|
+
}): boolean;
|
|
230
|
+
/**
|
|
231
|
+
* The `x-mnemom-session-config` value that turns context folding OFF, or
|
|
232
|
+
* `undefined` when folding is ON (the default — nothing is sent, the gateway's
|
|
233
|
+
* own default stands). base64url so it survives the gateway's inbound strip.
|
|
234
|
+
*/
|
|
235
|
+
export declare function buildSessionConfigB64(fold: boolean): string | undefined;
|
|
208
236
|
/**
|
|
209
237
|
* The persistable goal-alignment default (`launcher.toml` `goal_mode`). Only the
|
|
210
238
|
* two statement-free modes can be a standing default: a SEALED contract needs a
|
package/dist/lib/agent.js
CHANGED
|
@@ -186,6 +186,15 @@ export function assembleContract(flags) {
|
|
|
186
186
|
}
|
|
187
187
|
/** The request header that opts a conversation into implicit goal mode. */
|
|
188
188
|
export const GOAL_MODE_HEADER = "x-mnemom-goal-mode";
|
|
189
|
+
/**
|
|
190
|
+
* Per-session env overlay the gateway honours (`session-config.ts`
|
|
191
|
+
* SESSION_CONFIG_HEADER). base64url(JSON) of `{ v, config: { KEY: "value" } }`,
|
|
192
|
+
* keys drawn from the gateway's allow-list. Today the CLI uses it for exactly
|
|
193
|
+
* one thing: turning context folding off for a single launch.
|
|
194
|
+
*/
|
|
195
|
+
export const SESSION_CONFIG_HEADER = "x-mnemom-session-config";
|
|
196
|
+
/** The gateway env key that gates context folding (allow-listed for per-session overlay). */
|
|
197
|
+
export const CONTEXT_RUNTIME_KEY = "CONTEXT_RUNTIME_ENABLED";
|
|
189
198
|
const SEALED_ONLY_FLAGS = "--goal/--requirement/--allow/--forbid/--goal-id";
|
|
190
199
|
/**
|
|
191
200
|
* `--implicit` and a sealed contract are mutually exclusive: on the gateway a
|
|
@@ -352,8 +361,36 @@ export function buildCustomHeaders(opts) {
|
|
|
352
361
|
lines.push(`x-mnemom-contract: ${opts.contractB64}`);
|
|
353
362
|
if (opts.goalMode)
|
|
354
363
|
lines.push(`${GOAL_MODE_HEADER}: ${opts.goalMode}`);
|
|
364
|
+
if (opts.sessionConfigB64)
|
|
365
|
+
lines.push(`${SESSION_CONFIG_HEADER}: ${opts.sessionConfigB64}`);
|
|
355
366
|
return lines.join("\n");
|
|
356
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Whether context folding is ON for this launch. Folding is a gateway feature
|
|
370
|
+
* that summarises older turns to fit the window; it is ON by default. Precedence:
|
|
371
|
+
* an explicit `--context`/`--no-context` flag wins, else `MNEMOM_AGENT_CONTEXT`
|
|
372
|
+
* (which `applyConfigToEnv` sets from the `context` config key), else ON.
|
|
373
|
+
* Recognised OFF values for the env var: `0`, `false`, `off`, `no` (case-insensitive).
|
|
374
|
+
*/
|
|
375
|
+
export function resolveContextFold(opts) {
|
|
376
|
+
if (opts.context !== undefined)
|
|
377
|
+
return opts.context;
|
|
378
|
+
const raw = (opts.env?.MNEMOM_AGENT_CONTEXT ?? "").trim().toLowerCase();
|
|
379
|
+
if (raw === "")
|
|
380
|
+
return true;
|
|
381
|
+
return !(raw === "0" || raw === "false" || raw === "off" || raw === "no");
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* The `x-mnemom-session-config` value that turns context folding OFF, or
|
|
385
|
+
* `undefined` when folding is ON (the default — nothing is sent, the gateway's
|
|
386
|
+
* own default stands). base64url so it survives the gateway's inbound strip.
|
|
387
|
+
*/
|
|
388
|
+
export function buildSessionConfigB64(fold) {
|
|
389
|
+
if (fold)
|
|
390
|
+
return undefined;
|
|
391
|
+
const payload = JSON.stringify({ v: 1, config: { [CONTEXT_RUNTIME_KEY]: "false" } });
|
|
392
|
+
return Buffer.from(payload, "utf8").toString("base64url");
|
|
393
|
+
}
|
|
357
394
|
/**
|
|
358
395
|
* Is this launch implicit-mode? Precedence, highest wins:
|
|
359
396
|
* 1. an explicit flag — `--implicit` (true) or `--no-implicit` (false);
|
|
@@ -31,6 +31,8 @@ export interface LauncherConfig {
|
|
|
31
31
|
keySource?: KeySource;
|
|
32
32
|
/** Standing goal-alignment default when neither --goal nor --implicit is given. */
|
|
33
33
|
goalMode?: GoalModeDefault;
|
|
34
|
+
/** Context folding default (on when unset; `false` disables it for every launch). */
|
|
35
|
+
context?: boolean;
|
|
34
36
|
guardrails?: {
|
|
35
37
|
maxTurns?: number;
|
|
36
38
|
budgetUsd?: number;
|
|
@@ -45,7 +47,7 @@ export interface LauncherConfig {
|
|
|
45
47
|
export interface ConfigKeySpec {
|
|
46
48
|
key: string;
|
|
47
49
|
describe: string;
|
|
48
|
-
get(cfg: LauncherConfig): string | number | undefined;
|
|
50
|
+
get(cfg: LauncherConfig): string | number | boolean | undefined;
|
|
49
51
|
set(cfg: LauncherConfig, raw: string): void;
|
|
50
52
|
}
|
|
51
53
|
/** The full set of settable keys — drives get/set/unset/list and validation. */
|
|
@@ -38,6 +38,16 @@ function coerceEffort(raw) {
|
|
|
38
38
|
return v;
|
|
39
39
|
throw new Error(`effort must be one of ${EFFORTS.join(", ")} (got '${raw}').`);
|
|
40
40
|
}
|
|
41
|
+
const TRUE_WORDS = ["true", "1", "on", "yes"];
|
|
42
|
+
const FALSE_WORDS = ["false", "0", "off", "no"];
|
|
43
|
+
function coerceBool(name, raw) {
|
|
44
|
+
const s = raw.trim().toLowerCase();
|
|
45
|
+
if (TRUE_WORDS.includes(s))
|
|
46
|
+
return true;
|
|
47
|
+
if (FALSE_WORDS.includes(s))
|
|
48
|
+
return false;
|
|
49
|
+
throw new Error(`${name} must be a boolean (${[...TRUE_WORDS, ...FALSE_WORDS].join("|")}); got '${raw}'.`);
|
|
50
|
+
}
|
|
41
51
|
function coerceKeySource(raw) {
|
|
42
52
|
const v = raw.trim();
|
|
43
53
|
if (KEY_SOURCES.includes(v))
|
|
@@ -127,6 +137,14 @@ export const CONFIG_KEYS = [
|
|
|
127
137
|
c.goalMode = coerceGoalMode(v);
|
|
128
138
|
},
|
|
129
139
|
},
|
|
140
|
+
{
|
|
141
|
+
key: "context",
|
|
142
|
+
describe: "Context folding (on by default; false disables it for every launch)",
|
|
143
|
+
get: (c) => c.context,
|
|
144
|
+
set: (c, v) => {
|
|
145
|
+
c.context = coerceBool("context", v);
|
|
146
|
+
},
|
|
147
|
+
},
|
|
130
148
|
{
|
|
131
149
|
key: "guardrails.max_turns",
|
|
132
150
|
describe: "Contract guardrail: turn ceiling (positive integer)",
|
|
@@ -193,6 +211,8 @@ export function parseLauncherConfig(text) {
|
|
|
193
211
|
cfg.keySource = coerceKeySource(str(raw.key_source) ?? "");
|
|
194
212
|
if (raw.goal_mode !== undefined)
|
|
195
213
|
cfg.goalMode = coerceGoalMode(str(raw.goal_mode) ?? "");
|
|
214
|
+
if (raw.context !== undefined)
|
|
215
|
+
cfg.context = coerceBool("context", str(raw.context) ?? "");
|
|
196
216
|
const g = raw.guardrails;
|
|
197
217
|
if (g !== undefined) {
|
|
198
218
|
if (typeof g !== "object" || g === null || Array.isArray(g)) {
|
|
@@ -251,6 +271,8 @@ export function serializeLauncherConfig(cfg) {
|
|
|
251
271
|
doc.key_source = cfg.keySource;
|
|
252
272
|
if (cfg.goalMode !== undefined)
|
|
253
273
|
doc.goal_mode = cfg.goalMode;
|
|
274
|
+
if (cfg.context !== undefined)
|
|
275
|
+
doc.context = cfg.context;
|
|
254
276
|
const g = cfg.guardrails;
|
|
255
277
|
if (g && (g.maxTurns !== undefined || g.budgetUsd !== undefined || g.stallTurns !== undefined)) {
|
|
256
278
|
const gr = {};
|
|
@@ -297,4 +319,7 @@ export function applyConfigToEnv(cfg, env = process.env) {
|
|
|
297
319
|
env.MNEMOM_AGENT_REMOTE_CONTROL =
|
|
298
320
|
cfg.launch === "server" ? "server" : cfg.launch === "remote-control" ? "1" : "0";
|
|
299
321
|
}
|
|
322
|
+
if (cfg.context !== undefined && !(env.MNEMOM_AGENT_CONTEXT ?? "").trim()) {
|
|
323
|
+
env.MNEMOM_AGENT_CONTEXT = cfg.context ? "1" : "0";
|
|
324
|
+
}
|
|
300
325
|
}
|
package/dist/rc-proxy.mjs
CHANGED
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
// MNEMOM_AGENT_RC_CONTRACT_B64 base64 contract for x-mnemom-contract (optional)
|
|
65
65
|
// MNEMOM_AGENT_RC_GOAL_MODE x-mnemom-goal-mode value (optional; `--implicit` — no contract,
|
|
66
66
|
// the gateway infers the goal from the user's messages)
|
|
67
|
+
// MNEMOM_AGENT_RC_SESSION_CONFIG x-mnemom-session-config value (optional; per-session env overlay, e.g. context folding off)
|
|
67
68
|
// MNEMOM_AGENT_RC_PROVIDER x-mnemom-provider directive JSON (optional; router door)
|
|
68
69
|
// MNEMOM_AGENT_RC_THREAD_MODE `reject` (default: answer thread-bearing requests locally,
|
|
69
70
|
// see above) | `strip` (legacy: delete `thread`, forward)
|
|
@@ -101,6 +102,7 @@ const CONV =
|
|
|
101
102
|
env.MNEMOM_AGENT_RC_CONVERSATION_ID || die("MNEMOM_AGENT_RC_CONVERSATION_ID is required");
|
|
102
103
|
const CONTRACT_B64 = env.MNEMOM_AGENT_RC_CONTRACT_B64 || "";
|
|
103
104
|
const GOAL_MODE = env.MNEMOM_AGENT_RC_GOAL_MODE || "";
|
|
105
|
+
const SESSION_CONFIG = env.MNEMOM_AGENT_RC_SESSION_CONFIG || "";
|
|
104
106
|
const PROVIDER = env.MNEMOM_AGENT_RC_PROVIDER || "";
|
|
105
107
|
const THREAD_MODE = env.MNEMOM_AGENT_RC_THREAD_MODE || "reject";
|
|
106
108
|
if (THREAD_MODE !== "reject" && THREAD_MODE !== "strip")
|
|
@@ -351,6 +353,7 @@ function handle(req, res, viaPlain) {
|
|
|
351
353
|
if (secrets.openai) headers["x-mnemom-egress-key"] = secrets.openai;
|
|
352
354
|
if (CONTRACT_B64) headers["x-mnemom-contract"] = CONTRACT_B64;
|
|
353
355
|
if (GOAL_MODE) headers["x-mnemom-goal-mode"] = GOAL_MODE;
|
|
356
|
+
if (SESSION_CONFIG) headers["x-mnemom-session-config"] = SESSION_CONFIG;
|
|
354
357
|
if (headers["anthropic-beta"]) {
|
|
355
358
|
// drop the OAuth-only beta tokens
|
|
356
359
|
const b = String(headers["anthropic-beta"])
|