@mnemom/mnemom 0.16.1 → 0.17.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/agents.d.ts +14 -0
- package/dist/commands/agents.js +100 -2
- package/dist/commands/card.d.ts +43 -0
- package/dist/commands/card.js +153 -102
- package/dist/commands/code-config.d.ts +17 -0
- package/dist/commands/code-config.js +147 -0
- package/dist/commands/code-doctor.d.ts +18 -0
- package/dist/commands/code-doctor.js +138 -0
- package/dist/commands/code-setup.d.ts +97 -0
- package/dist/commands/code-setup.js +330 -0
- package/dist/commands/code.d.ts +133 -0
- package/dist/commands/code.js +661 -0
- package/dist/commands/logs.js +11 -1
- package/dist/commands/onboard.d.ts +59 -0
- package/dist/commands/onboard.js +395 -0
- package/dist/commands/org.d.ts +13 -0
- package/dist/commands/org.js +63 -2
- package/dist/commands/protection.d.ts +10 -0
- package/dist/commands/protection.js +109 -0
- package/dist/commands/status.js +5 -0
- package/dist/commands/try-me.js +16 -1
- package/dist/commands/usage.d.ts +35 -0
- package/dist/commands/usage.js +265 -0
- package/dist/commands/wrap.d.ts +28 -0
- package/dist/commands/wrap.js +331 -0
- package/dist/index.js +315 -7
- package/dist/lib/agent-config.d.ts +27 -0
- package/dist/lib/agent-config.js +86 -0
- package/dist/lib/api.d.ts +139 -1
- package/dist/lib/api.js +132 -183
- package/dist/lib/cli-config.d.ts +33 -0
- package/dist/lib/cli-config.js +70 -0
- package/dist/lib/code-config.d.ts +78 -0
- package/dist/lib/code-config.js +281 -0
- package/dist/lib/code.d.ts +154 -0
- package/dist/lib/code.js +252 -0
- package/dist/lib/config.d.ts +12 -0
- package/dist/lib/config.js +55 -3
- package/dist/lib/keyed-identity.d.ts +35 -0
- package/dist/lib/keyed-identity.js +363 -0
- package/dist/lib/protection-drift.d.ts +117 -0
- package/dist/lib/protection-drift.js +180 -0
- package/dist/lib/skills.js +25 -12
- package/dist/lib/version-gate.d.ts +37 -0
- package/dist/lib/version-gate.js +84 -0
- package/dist/rc-proxy.mjs +341 -0
- package/package.json +9 -7
package/dist/lib/code.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/code.ts — the pure, IO-free core of `mnemom code`, the CLI's first-class
|
|
3
|
+
* governed-coding-agent launcher.
|
|
4
|
+
*
|
|
5
|
+
* `mnemom code` launches your coding-agent CLI (Claude Code in v1) with its
|
|
6
|
+
* dev-time model traffic routed through the Mnemom gateway for observability and
|
|
7
|
+
* governance, under a governed agent identity and (optionally) a governed
|
|
8
|
+
* per-conversation CONTRACT (goal / requirements / path allow-forbid lists +
|
|
9
|
+
* guardrail ceilings) that the gateway SEALS on the first turn of a conversation.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is deterministic and side-effect-free so it can be unit-tested
|
|
12
|
+
* without spawning a process or touching the network: the contract builder + its
|
|
13
|
+
* canonical seal (byte-identical to the gateway's `parseContractHeader` +
|
|
14
|
+
* `canonicalHash`), the guardrail validators, agent-name sanitisation, and the
|
|
15
|
+
* gateway-door resolution. The command shell (commands/code.ts) does the IO —
|
|
16
|
+
* key resolution, env assembly, and spawning the coding-agent CLI.
|
|
17
|
+
*
|
|
18
|
+
* The us-2/PROD gateway is the DEFAULT door (https://gateway.mnemom.ai). An
|
|
19
|
+
* explicit MNEMOM_CODE_GATEWAY still wins (so any other cell is reachable when
|
|
20
|
+
* NAMED), but prod is never selected silently — see `resolveGatewayHost`.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
23
|
+
/** The us-2/prod gateway host. The default door — never any other cell silently. */
|
|
24
|
+
export const PROD_GATEWAY_HOST = "https://gateway.mnemom.ai";
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the gateway HOST (scheme + host, no path). An explicit, non-empty
|
|
27
|
+
* `MNEMOM_CODE_GATEWAY` wins verbatim; otherwise the us-2/prod host. The default
|
|
28
|
+
* is PROD — an unset var resolves to prod, and any other cell is reached only by
|
|
29
|
+
* NAMING it. Trailing slash trimmed so door concatenation is clean.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveGatewayHost(env = process.env) {
|
|
32
|
+
const raw = (env.MNEMOM_CODE_GATEWAY ?? "").trim();
|
|
33
|
+
const host = raw.length > 0 ? raw : PROD_GATEWAY_HOST;
|
|
34
|
+
return host.replace(/\/+$/, "");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The Anthropic door the CLI points Claude Code at:
|
|
38
|
+
* `${MNEMOM_CODE_GATEWAY}/anthropic`, or the full-door override
|
|
39
|
+
* `MNEMOM_CODE_ANTHROPIC_GATEWAY` when set. v1 is Anthropic-only (no router door
|
|
40
|
+
* / Mnemom key), so the /anthropic door carries the whole contract: x-api-key +
|
|
41
|
+
* x-mnemom-{agent,conversation-id,contract}.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveAnthropicDoor(env = process.env) {
|
|
44
|
+
const override = (env.MNEMOM_CODE_ANTHROPIC_GATEWAY ?? "").trim();
|
|
45
|
+
if (override.length > 0)
|
|
46
|
+
return override.replace(/\/+$/, "");
|
|
47
|
+
return `${resolveGatewayHost(env)}/anthropic`;
|
|
48
|
+
}
|
|
49
|
+
/** True when the resolved gateway host is the us-2/prod cell (used for the launch banner). */
|
|
50
|
+
export function isProdGateway(env = process.env) {
|
|
51
|
+
return resolveGatewayHost(env) === PROD_GATEWAY_HOST;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Sanitise a header value into a safe wire token: lowercase, alphanumerics and
|
|
55
|
+
* single hyphens only, no leading/trailing hyphen. Returns "" when nothing
|
|
56
|
+
* survives (the caller rejects that).
|
|
57
|
+
*/
|
|
58
|
+
export function sanitizeAgentName(raw) {
|
|
59
|
+
return raw
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
62
|
+
.replace(/^-+/, "")
|
|
63
|
+
.replace(/-+$/, "");
|
|
64
|
+
}
|
|
65
|
+
/** A single-line, header-safe conversation id: honour a caller value, else a uuid. */
|
|
66
|
+
export function resolveConversationId(raw) {
|
|
67
|
+
const trimmed = (raw ?? "").replace(/[\r\n]+/g, "").trim();
|
|
68
|
+
if (trimmed.length > 0)
|
|
69
|
+
return trimmed;
|
|
70
|
+
return randomUUID().toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
/** A strictly-positive integer (rejects 0, negatives, decimals, NaN, non-numeric strings). */
|
|
73
|
+
export function isPositiveInt(value) {
|
|
74
|
+
const n = typeof value === "number" ? value : Number(String(value).trim());
|
|
75
|
+
return Number.isInteger(n) && n > 0 && /^\d+$/.test(String(value).trim());
|
|
76
|
+
}
|
|
77
|
+
/** A strictly-positive finite number (rejects 0, negatives, NaN, Infinity, non-numeric). */
|
|
78
|
+
export function isPositiveNumber(value) {
|
|
79
|
+
const s = String(value).trim();
|
|
80
|
+
if (!/^([0-9]+(\.[0-9]*)?|\.[0-9]+)$/.test(s))
|
|
81
|
+
return false;
|
|
82
|
+
const n = Number(s);
|
|
83
|
+
return Number.isFinite(n) && n > 0;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Canonical JSON — object keys sorted recursively, arrays in order, no
|
|
87
|
+
* whitespace, `undefined` keys dropped. Byte-identical to the gateway's
|
|
88
|
+
* `canonicalJson` (gateway/src/goal-contract.ts), so the base64 we send and the
|
|
89
|
+
* sha we print match what the gateway seals.
|
|
90
|
+
*/
|
|
91
|
+
export function canonicalJson(value) {
|
|
92
|
+
if (value === null || typeof value !== "object")
|
|
93
|
+
return JSON.stringify(value);
|
|
94
|
+
if (Array.isArray(value))
|
|
95
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
96
|
+
const obj = value;
|
|
97
|
+
const keys = Object.keys(obj)
|
|
98
|
+
.filter((k) => obj[k] !== undefined)
|
|
99
|
+
.sort();
|
|
100
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
|
|
101
|
+
}
|
|
102
|
+
/** sha256 hex (first 16 chars) of a string — the operator-eyeball contract hash. */
|
|
103
|
+
export function sha256Hex16(text) {
|
|
104
|
+
return createHash("sha256").update(text, "utf8").digest("hex").slice(0, 16);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Was ANY contract/guardrail flag supplied? A contract is built only then; if so
|
|
108
|
+
* `--goal` becomes required (see `assembleContract`). Purely presence-based.
|
|
109
|
+
*/
|
|
110
|
+
export function contractFlagsPresent(flags) {
|
|
111
|
+
return (hasText(flags.goal) ||
|
|
112
|
+
(flags.requirement?.length ?? 0) > 0 ||
|
|
113
|
+
(flags.allow?.length ?? 0) > 0 ||
|
|
114
|
+
(flags.forbid?.length ?? 0) > 0 ||
|
|
115
|
+
hasText(flags.goalId) ||
|
|
116
|
+
flags.maxTurns !== undefined ||
|
|
117
|
+
flags.budget !== undefined ||
|
|
118
|
+
flags.stall !== undefined);
|
|
119
|
+
}
|
|
120
|
+
function hasText(v) {
|
|
121
|
+
return typeof v === "string" && v.trim().length > 0;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Build + seal the contract from the raw flags, or return null when no contract
|
|
125
|
+
* flag was given. Throws (with a human message) on the two invalid states the
|
|
126
|
+
* gateway would otherwise silently swallow:
|
|
127
|
+
* - a contract flag present without `--goal` (the statement is required);
|
|
128
|
+
* - a guardrail ceiling that is not strictly positive (`parseContractHeader`
|
|
129
|
+
* drops those, so a "guarded" launch would actually be unguarded).
|
|
130
|
+
*
|
|
131
|
+
* The canonical form OMITS empty arrays and an empty goal_id, so the sha printed
|
|
132
|
+
* here matches whatever the gateway seals for the same flags.
|
|
133
|
+
*/
|
|
134
|
+
export function assembleContract(flags) {
|
|
135
|
+
if (!contractFlagsPresent(flags))
|
|
136
|
+
return null;
|
|
137
|
+
const statement = (flags.goal ?? "").trim();
|
|
138
|
+
if (!statement) {
|
|
139
|
+
throw new Error("--goal is required when any contract flag " +
|
|
140
|
+
"(--requirement/--allow/--forbid/--goal-id/--budget/--max-turns/--stall) is given.");
|
|
141
|
+
}
|
|
142
|
+
const contract = { statement };
|
|
143
|
+
const requirements = cleanList(flags.requirement);
|
|
144
|
+
if (requirements.length)
|
|
145
|
+
contract.requirements = requirements;
|
|
146
|
+
const allowed = cleanList(flags.allow);
|
|
147
|
+
if (allowed.length)
|
|
148
|
+
contract.allowed_paths = allowed;
|
|
149
|
+
const forbidden = cleanList(flags.forbid);
|
|
150
|
+
if (forbidden.length)
|
|
151
|
+
contract.forbidden_paths = forbidden;
|
|
152
|
+
const goalId = (flags.goalId ?? "").trim();
|
|
153
|
+
if (goalId)
|
|
154
|
+
contract.goal_id = goalId;
|
|
155
|
+
if (flags.maxTurns !== undefined) {
|
|
156
|
+
if (!isPositiveInt(flags.maxTurns)) {
|
|
157
|
+
throw new Error(`--max-turns must be a positive integer (got '${flags.maxTurns}').`);
|
|
158
|
+
}
|
|
159
|
+
contract.max_turns = Number(flags.maxTurns);
|
|
160
|
+
}
|
|
161
|
+
if (flags.budget !== undefined) {
|
|
162
|
+
if (!isPositiveNumber(flags.budget)) {
|
|
163
|
+
throw new Error(`--budget must be a positive USD amount (got '${flags.budget}').`);
|
|
164
|
+
}
|
|
165
|
+
contract.budget_usd = Number(flags.budget);
|
|
166
|
+
}
|
|
167
|
+
if (flags.stall !== undefined) {
|
|
168
|
+
if (!isPositiveInt(flags.stall)) {
|
|
169
|
+
throw new Error(`--stall must be a positive integer (got '${flags.stall}').`);
|
|
170
|
+
}
|
|
171
|
+
contract.stall_turns = Number(flags.stall);
|
|
172
|
+
}
|
|
173
|
+
const canonical = canonicalJson(contract);
|
|
174
|
+
return {
|
|
175
|
+
contract,
|
|
176
|
+
canonical,
|
|
177
|
+
base64: Buffer.from(canonical, "utf8").toString("base64"),
|
|
178
|
+
sha: sha256Hex16(canonical),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Trim, drop empties — matches the gateway's `stringArray` filtering. */
|
|
182
|
+
function cleanList(values) {
|
|
183
|
+
return (values ?? []).map((v) => v.trim()).filter((v) => v.length > 0);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolve the launch shape from the flags and the `MNEMOM_CODE_REMOTE_CONTROL`
|
|
187
|
+
* env var (the flags win). Default is `terminal` — a plain governed launch in
|
|
188
|
+
* THIS terminal. `--remote-control` → `remote-control` (interactive RC in this
|
|
189
|
+
* terminal); `--server` (or `MNEMOM_CODE_REMOTE_CONTROL=server`) → `server` (the
|
|
190
|
+
* headless `claude remote-control` dispatcher). Throws on an invalid env value.
|
|
191
|
+
*/
|
|
192
|
+
export function resolveLaunchShape(opts, env = process.env) {
|
|
193
|
+
let rc = false;
|
|
194
|
+
let server = false;
|
|
195
|
+
const envRc = (env.MNEMOM_CODE_REMOTE_CONTROL ?? "").trim();
|
|
196
|
+
switch (envRc) {
|
|
197
|
+
case "":
|
|
198
|
+
case "0":
|
|
199
|
+
break;
|
|
200
|
+
case "1":
|
|
201
|
+
case "terminal":
|
|
202
|
+
rc = true;
|
|
203
|
+
break;
|
|
204
|
+
case "server":
|
|
205
|
+
rc = true;
|
|
206
|
+
server = true;
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
throw new Error(`MNEMOM_CODE_REMOTE_CONTROL must be 0, 1, terminal or server (got '${envRc}').`);
|
|
210
|
+
}
|
|
211
|
+
if (opts.remoteControl)
|
|
212
|
+
rc = true;
|
|
213
|
+
if (opts.server) {
|
|
214
|
+
rc = true;
|
|
215
|
+
server = true;
|
|
216
|
+
}
|
|
217
|
+
if (server)
|
|
218
|
+
return "server";
|
|
219
|
+
if (rc)
|
|
220
|
+
return "remote-control";
|
|
221
|
+
return "terminal";
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Did anyone EXPLICITLY choose a launch shape — a `--remote-control`/`--server`
|
|
225
|
+
* flag, or a non-empty `MNEMOM_CODE_REMOTE_CONTROL` (which config.launch also
|
|
226
|
+
* sets)? When false, the command is free to AUTO-select the best shape: prefer
|
|
227
|
+
* Remote-Control-in-terminal when the machine can do it, else terminal-only. An
|
|
228
|
+
* explicit `terminal` choice (config `launch = "terminal"` → env "0") is
|
|
229
|
+
* explicit and must be respected — never auto-upgraded.
|
|
230
|
+
*/
|
|
231
|
+
export function launchPreferenceExplicit(opts, env = process.env) {
|
|
232
|
+
if (opts.remoteControl || opts.server)
|
|
233
|
+
return true;
|
|
234
|
+
return (env.MNEMOM_CODE_REMOTE_CONTROL ?? "").trim() !== "";
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Build the `ANTHROPIC_CUSTOM_HEADERS` value for the terminal-only launch: the
|
|
238
|
+
* real Anthropic key as `x-api-key`, plus the governed-identity headers and the
|
|
239
|
+
* sealed contract when present. Newline-separated, exactly the wire shape Claude
|
|
240
|
+
* Code sends to the /anthropic door. NB: the returned string contains the key —
|
|
241
|
+
* callers must place it only in the child's env, never in a log line.
|
|
242
|
+
*/
|
|
243
|
+
export function buildCustomHeaders(opts) {
|
|
244
|
+
const lines = [
|
|
245
|
+
`x-api-key: ${opts.anthropicKey}`,
|
|
246
|
+
`x-mnemom-agent: ${opts.agent}`,
|
|
247
|
+
`x-mnemom-conversation-id: ${opts.conversationId}`,
|
|
248
|
+
];
|
|
249
|
+
if (opts.contractB64)
|
|
250
|
+
lines.push(`x-mnemom-contract: ${opts.contractB64}`);
|
|
251
|
+
return lines.join("\n");
|
|
252
|
+
}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -13,6 +13,18 @@ export type Environment = "production" | "staging" | "local";
|
|
|
13
13
|
* Defaults to production.
|
|
14
14
|
*/
|
|
15
15
|
export declare function getEnvironment(): Environment;
|
|
16
|
+
export type CellRing = "us-1";
|
|
17
|
+
/** Explicitly select a ring for this process (clears on undefined). Wins over MNEMOM_RING. */
|
|
18
|
+
export declare function setRing(ring: CellRing | undefined): void;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the active ring, if any: an explicit `setRing()` call wins over
|
|
21
|
+
* the `MNEMOM_RING` env var. Returns undefined for an unset or unrecognized
|
|
22
|
+
* value — an unknown ring name is deliberately NOT resolved to production;
|
|
23
|
+
* callers fall through to the existing Environment-based URLs instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getRing(): CellRing | undefined;
|
|
26
|
+
/** Override the active API base for this process (clears on undefined/empty). */
|
|
27
|
+
export declare function setApiUrlOverride(url: string | undefined): void;
|
|
16
28
|
export declare function getApiUrl(): string;
|
|
17
29
|
export declare function getGatewayUrl(): string;
|
|
18
30
|
export declare function getWebsiteUrl(): string;
|
package/dist/lib/config.js
CHANGED
|
@@ -34,12 +34,64 @@ export function getEnvironment() {
|
|
|
34
34
|
return env;
|
|
35
35
|
return "production";
|
|
36
36
|
}
|
|
37
|
+
const RING_URLS = {
|
|
38
|
+
"us-1": {
|
|
39
|
+
api: "https://api-us1.mnemom.ai",
|
|
40
|
+
gateway: "https://gateway-us1.mnemom.ai",
|
|
41
|
+
website: "https://preview.mnemom.ai",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function isCellRing(value) {
|
|
45
|
+
return Object.prototype.hasOwnProperty.call(RING_URLS, value);
|
|
46
|
+
}
|
|
47
|
+
let ringOverride;
|
|
48
|
+
/** Explicitly select a ring for this process (clears on undefined). Wins over MNEMOM_RING. */
|
|
49
|
+
export function setRing(ring) {
|
|
50
|
+
ringOverride = ring;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the active ring, if any: an explicit `setRing()` call wins over
|
|
54
|
+
* the `MNEMOM_RING` env var. Returns undefined for an unset or unrecognized
|
|
55
|
+
* value — an unknown ring name is deliberately NOT resolved to production;
|
|
56
|
+
* callers fall through to the existing Environment-based URLs instead.
|
|
57
|
+
*/
|
|
58
|
+
export function getRing() {
|
|
59
|
+
if (ringOverride)
|
|
60
|
+
return ringOverride;
|
|
61
|
+
const env = process.env.MNEMOM_RING?.trim();
|
|
62
|
+
return env && isCellRing(env) ? env : undefined;
|
|
63
|
+
}
|
|
64
|
+
// Process-level base overrides. MNEMOM_ENV only selects prod/staging/local;
|
|
65
|
+
// to target an arbitrary ring (preview/us-1, test) a command can set an
|
|
66
|
+
// explicit override (e.g. `try-me --api <ring>`) or the operator can export
|
|
67
|
+
// MNEMOM_API_URL / MNEMOM_GATEWAY_URL / MNEMOM_WEBSITE_URL. Crucially these
|
|
68
|
+
// flow through EVERY surface — including OAuth discovery + the one-click claim
|
|
69
|
+
// login — so a non-prod ring's sign-in no longer falls back to the prod AS.
|
|
70
|
+
//
|
|
71
|
+
// Precedence (highest wins): apiUrlOverride > raw MNEMOM_*_URL env >
|
|
72
|
+
// ring (setRing()/MNEMOM_RING) > Environment table. A ring only fills the
|
|
73
|
+
// gap the raw overrides leave — it never has to be perfectly bypass-able,
|
|
74
|
+
// because the raw overrides above it in the chain always can.
|
|
75
|
+
let apiUrlOverride;
|
|
76
|
+
/** Override the active API base for this process (clears on undefined/empty). */
|
|
77
|
+
export function setApiUrlOverride(url) {
|
|
78
|
+
const trimmed = url?.trim().replace(/\/+$/, "");
|
|
79
|
+
apiUrlOverride = trimmed || undefined;
|
|
80
|
+
}
|
|
81
|
+
function envBase(name) {
|
|
82
|
+
const v = process.env[name]?.trim();
|
|
83
|
+
return v ? v.replace(/\/+$/, "") : undefined;
|
|
84
|
+
}
|
|
85
|
+
function ringUrls() {
|
|
86
|
+
const ring = getRing();
|
|
87
|
+
return ring ? RING_URLS[ring] : undefined;
|
|
88
|
+
}
|
|
37
89
|
export function getApiUrl() {
|
|
38
|
-
return API_URLS[getEnvironment()];
|
|
90
|
+
return (apiUrlOverride ?? envBase("MNEMOM_API_URL") ?? ringUrls()?.api ?? API_URLS[getEnvironment()]);
|
|
39
91
|
}
|
|
40
92
|
export function getGatewayUrl() {
|
|
41
|
-
return GATEWAY_URLS[getEnvironment()];
|
|
93
|
+
return envBase("MNEMOM_GATEWAY_URL") ?? ringUrls()?.gateway ?? GATEWAY_URLS[getEnvironment()];
|
|
42
94
|
}
|
|
43
95
|
export function getWebsiteUrl() {
|
|
44
|
-
return WEBSITE_URLS[getEnvironment()];
|
|
96
|
+
return envBase("MNEMOM_WEBSITE_URL") ?? ringUrls()?.website ?? WEBSITE_URLS[getEnvironment()];
|
|
45
97
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ValidationCheck } from "../commands/protection.js";
|
|
2
|
+
export declare const KEYED_MODES: readonly ["observe", "nudge", "enforce"];
|
|
3
|
+
export type KeyedMode = (typeof KEYED_MODES)[number];
|
|
4
|
+
export declare const EVAL_BATTERY_MODEL: "claude-opus-5";
|
|
5
|
+
export interface KeyedModeEntry {
|
|
6
|
+
label?: unknown;
|
|
7
|
+
mode?: unknown;
|
|
8
|
+
model?: unknown;
|
|
9
|
+
agent_id?: unknown;
|
|
10
|
+
agent_hash?: unknown;
|
|
11
|
+
secret_ref?: unknown;
|
|
12
|
+
snapshot?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface KeyedDirectLane {
|
|
15
|
+
label?: unknown;
|
|
16
|
+
mode?: unknown;
|
|
17
|
+
path?: unknown;
|
|
18
|
+
secret_ref?: unknown;
|
|
19
|
+
model?: unknown;
|
|
20
|
+
snapshot?: unknown;
|
|
21
|
+
agent_id?: unknown;
|
|
22
|
+
agent_hash?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validate the keyed-mode manifest against the three parsed snapshots.
|
|
26
|
+
*
|
|
27
|
+
* @param manifest Parsed `cards/keyed-modes.manifest.yaml` (`{ entries: [...] }`).
|
|
28
|
+
* @param snapshots Map of snapshot filename → parsed protection card, keyed by
|
|
29
|
+
* the `snapshot:` value each manifest entry declares.
|
|
30
|
+
* @returns Flat list of `{ name, passed, message }` checks (mirrors
|
|
31
|
+
* ValidationCheck) so callers can render/aggregate. A returned list
|
|
32
|
+
* with every `passed === true` means the record is internally
|
|
33
|
+
* consistent; any `passed === false` is a failing invariant.
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateKeyedModeManifest(manifest: Record<string, unknown>, snapshots: Record<string, Record<string, unknown>>): ValidationCheck[];
|