@mnemom/mnemom 0.17.31 → 0.17.32-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/dist/base-url-proxy.mjs +30 -0
- package/dist/commands/agent-fuel.d.ts +75 -0
- package/dist/commands/agent-fuel.js +298 -0
- package/dist/commands/agent-implicit-notice.d.ts +31 -0
- package/dist/commands/agent-implicit-notice.js +73 -0
- package/dist/commands/agent-session.d.ts +2 -1
- package/dist/commands/agent-session.js +2 -1
- package/dist/commands/agent.d.ts +52 -1
- package/dist/commands/agent.js +111 -26
- package/dist/commands/status.js +1 -1
- package/dist/commands/update.js +3 -1
- package/dist/commands/validate.d.ts +1 -1
- package/dist/commands/validate.js +1 -1
- package/dist/index.js +51 -12
- package/dist/lib/agent.d.ts +32 -19
- package/dist/lib/agent.js +34 -32
- package/dist/lib/api.d.ts +53 -0
- package/dist/lib/api.js +31 -10
- package/dist/lib/canonical-json.d.ts +12 -0
- package/dist/lib/canonical-json.js +22 -0
- package/dist/lib/conduct-receipt.d.ts +81 -0
- package/dist/lib/conduct-receipt.js +177 -0
- package/dist/lib/contract-hash.d.ts +61 -0
- package/dist/lib/contract-hash.js +126 -0
- package/dist/lib/experimental.d.ts +10 -1
- package/dist/lib/experimental.js +35 -1
- package/dist/lib/oauth.d.ts +2 -1
- package/dist/lib/oauth.js +2 -1
- package/dist/lib/statusline-bin.d.ts +2 -2
- package/dist/lib/statusline-bin.js +4 -7
- package/dist/lib/statusline-feed.d.ts +33 -0
- package/dist/lib/statusline-feed.js +68 -0
- package/dist/lib/statusline.d.ts +39 -88
- package/dist/lib/statusline.js +58 -250
- package/dist/lib/update/decide.d.ts +22 -3
- package/dist/lib/update/decide.js +35 -12
- package/dist/lib/update/doctor.js +5 -1
- package/dist/lib/update/run.d.ts +2 -0
- package/dist/lib/update/run.js +22 -8
- package/package.json +1 -1
package/dist/commands/agent.js
CHANGED
|
@@ -31,7 +31,9 @@ import { randomUUID } from "node:crypto";
|
|
|
31
31
|
import { tmpdir, homedir, hostname } from "node:os";
|
|
32
32
|
import { basename, join, dirname } from "node:path";
|
|
33
33
|
import { fileURLToPath } from "node:url";
|
|
34
|
-
import {
|
|
34
|
+
import { FEED_ENV, statuslineLaunch } from "../lib/statusline.js";
|
|
35
|
+
import { startFeed } from "../lib/statusline-feed.js";
|
|
36
|
+
import { isPrerelease } from "../lib/update/semver.js";
|
|
35
37
|
import { ALLOW_AUTOCOMPACT_OFF_ENV, dropAutocompactOffEnv, hasOwnSettingsFlag, mergedSettingsArgs, planAutocompactGuard, } from "../lib/autocompact-guard.js";
|
|
36
38
|
import { getApiUrl, getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
|
|
37
39
|
import { createApiKey } from "../lib/api.js";
|
|
@@ -41,6 +43,7 @@ import { isLoggedIn } from "../lib/auth.js";
|
|
|
41
43
|
import { loadCliConfig } from "../lib/cli-config.js";
|
|
42
44
|
import { configKeySpec, loadLauncherConfig, writeLauncherConfig } from "../lib/launcher-config.js";
|
|
43
45
|
import { experimentalNotice } from "../lib/experimental.js";
|
|
46
|
+
import { conductReceiptFor } from "../lib/conduct-receipt.js";
|
|
44
47
|
import { fmt } from "../lib/format.js";
|
|
45
48
|
import { askInput, askSelect, askYesNo, isInteractive } from "../lib/prompt.js";
|
|
46
49
|
import { authConfigured, chooseAuth, detectClaudeLogin, readClaudeLoginState, } from "../lib/login-detect.js";
|
|
@@ -169,7 +172,8 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
169
172
|
liftPassthroughName(options.passthrough ?? []);
|
|
170
173
|
// ── session name + goal ── (a bare `mnemom agent` in a terminal asks for the name;
|
|
171
174
|
// goal alignment is resolved upstream: --goal seals a contract, implicit alignment
|
|
172
|
-
// is on by default
|
|
175
|
+
// is on by default while the `implicit` experimental feature is on, and on a `next`
|
|
176
|
+
// build for every launch but `-p` (T16).
|
|
173
177
|
const session = await resolveSessionSetup({
|
|
174
178
|
scenario: scenarioArg,
|
|
175
179
|
goal: options.goal,
|
|
@@ -729,6 +733,10 @@ export async function agentCommand(scenarioArg, options = {}) {
|
|
|
729
733
|
}),
|
|
730
734
|
knownCcSessionId: cc.id ?? continuedCcSessionId,
|
|
731
735
|
plugin,
|
|
736
|
+
receipt: experimental.includes("record")
|
|
737
|
+
? { orgId: account.orgId, agentId: servedAgentId }
|
|
738
|
+
: undefined,
|
|
739
|
+
orgId: account.orgId,
|
|
732
740
|
});
|
|
733
741
|
return; // launchTerminal exits the process
|
|
734
742
|
}
|
|
@@ -1237,6 +1245,7 @@ function launchTerminal(ctx) {
|
|
|
1237
1245
|
// A subscription launch (no Anthropic key) sets NO auth token: Claude Code then sends its
|
|
1238
1246
|
// own claude.ai login to the gateway (lib/subscription-login.ts).
|
|
1239
1247
|
let childEnv;
|
|
1248
|
+
let feed = null;
|
|
1240
1249
|
if (ctx.anthropicKey) {
|
|
1241
1250
|
childEnv = { ...process.env };
|
|
1242
1251
|
delete childEnv.ANTHROPIC_API_KEY;
|
|
@@ -1262,10 +1271,8 @@ function launchTerminal(ctx) {
|
|
|
1262
1271
|
applyGatewayHintHeaders(childEnv);
|
|
1263
1272
|
if (ctx.options.slack)
|
|
1264
1273
|
args.push(...slackChannelArgs(sessionName));
|
|
1265
|
-
// The status-bar
|
|
1266
|
-
//
|
|
1267
|
-
// decision alone: a value inherited from an outer `mnemom agent` session never rides along.
|
|
1268
|
-
Reflect.deleteProperty(childEnv, MEASURED_ENV);
|
|
1274
|
+
// The status-bar segment (lib/statusline.ts). Claude Code only; the user's own
|
|
1275
|
+
// statusLine keeps running in front of it.
|
|
1269
1276
|
if (ctx.cli.name === "claude") {
|
|
1270
1277
|
const passthrough = ctx.options.passthrough ?? [];
|
|
1271
1278
|
const sl = statuslineLaunch({
|
|
@@ -1274,10 +1281,17 @@ function launchTerminal(ctx) {
|
|
|
1274
1281
|
cwd: process.cwd(),
|
|
1275
1282
|
node: process.execPath,
|
|
1276
1283
|
bin: join(dirname(fileURLToPath(import.meta.url)), "..", "lib", "statusline-bin.js"),
|
|
1277
|
-
measured: ctx.usageSavings,
|
|
1278
1284
|
});
|
|
1279
1285
|
if (sl)
|
|
1280
1286
|
Object.assign(childEnv, sl.env);
|
|
1287
|
+
feed = prepareStatuslineFeed({
|
|
1288
|
+
statusline: sl !== null,
|
|
1289
|
+
orgId: ctx.orgId,
|
|
1290
|
+
version: CLI_VERSION,
|
|
1291
|
+
goalMode: Boolean(ctx.goalMode),
|
|
1292
|
+
env: process.env,
|
|
1293
|
+
childEnv,
|
|
1294
|
+
});
|
|
1281
1295
|
// Autocompact guard (lib/autocompact-guard.ts): one --settings carries both layers.
|
|
1282
1296
|
const guard = applyAutocompactGuard(childEnv, ctx.options, !hasOwnSettingsFlag(passthrough));
|
|
1283
1297
|
args.push(...mergedSettingsArgs(guard, sl?.settings ?? null));
|
|
@@ -1287,6 +1301,7 @@ function launchTerminal(ctx) {
|
|
|
1287
1301
|
childEnv.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
|
|
1288
1302
|
childEnv.CLAUDE_CODE_GB_DISK_CACHE_WHEN_TELEMETRY_OFF = "1";
|
|
1289
1303
|
}
|
|
1304
|
+
let recordStatePath;
|
|
1290
1305
|
const run = () => {
|
|
1291
1306
|
const child = spawn(ctx.cli.bin, args, { stdio: "inherit", env: childEnv });
|
|
1292
1307
|
child.on("error", (err) => {
|
|
@@ -1296,33 +1311,84 @@ function launchTerminal(ctx) {
|
|
|
1296
1311
|
child.on("exit", (code, signal) => {
|
|
1297
1312
|
if (signal)
|
|
1298
1313
|
process.kill(process.pid, signal);
|
|
1299
|
-
else
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1314
|
+
else
|
|
1315
|
+
void (async () => {
|
|
1316
|
+
// The conduct receipt never blocks exit (a hard deadline inside) and never changes
|
|
1317
|
+
// the exit code.
|
|
1318
|
+
if (ctx.receipt)
|
|
1319
|
+
await sayConductReceipt({ ...ctx.receipt, statePath: recordStatePath });
|
|
1320
|
+
// Claude Code's own last line is "Resume this session with: claude --resume <id>",
|
|
1321
|
+
// which reopens it OUTSIDE Mnemom (ungoverned, unbilled). Ours goes after it, last.
|
|
1322
|
+
if (code === 0 && ctx.resumeHint && wantsResumeHint(ctx.cli, args)) {
|
|
1323
|
+
const id = ctx.knownCcSessionId ?? newestCcSessionIn(process.cwd(), process.env);
|
|
1324
|
+
say(ctx.resumeHint(id));
|
|
1325
|
+
}
|
|
1326
|
+
process.exit(code ?? 0);
|
|
1327
|
+
})();
|
|
1308
1328
|
});
|
|
1309
1329
|
};
|
|
1310
1330
|
// Implicit goal mode: put the local base-URL proxy in front of the door so each main request
|
|
1311
1331
|
// carries x-mnemom-turn (where each recent user turn came from, read from Claude Code's own
|
|
1312
1332
|
// transcript — base-url-proxy.mjs / turn-origin.mjs). The prompt id it joins on comes from the
|
|
1313
|
-
// gateway hint headers set above.
|
|
1314
|
-
|
|
1333
|
+
// gateway hint headers set above. Experimental `record` runs it too (turn header off unless
|
|
1334
|
+
// implicit), to capture the lineage key the conduct receipt reads. Fail-open: no proxy → the
|
|
1335
|
+
// door directly, as before, and a receipt that links instead of giving figures.
|
|
1336
|
+
const turnHeader = Boolean(ctx.goalMode) && turnProxyWanted(process.env);
|
|
1337
|
+
if (!turnHeader && !ctx.receipt) {
|
|
1315
1338
|
run();
|
|
1316
1339
|
return;
|
|
1317
1340
|
}
|
|
1318
|
-
startBaseUrlProxy(ctx.door, (baseUrl) => {
|
|
1319
|
-
if (baseUrl)
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
exemptLoopbackFromProxy(childEnv);
|
|
1323
|
-
}
|
|
1341
|
+
startBaseUrlProxy(ctx.door, (baseUrl, statePath) => {
|
|
1342
|
+
if (baseUrl)
|
|
1343
|
+
recordStatePath = statePath;
|
|
1344
|
+
onBaseUrlProxyReady(baseUrl, statePath, childEnv, feed && ctx.orgId ? { ...feed, orgId: ctx.orgId } : null);
|
|
1324
1345
|
run();
|
|
1325
|
-
});
|
|
1346
|
+
}, { turnHeader, recordState: wantsRecordState(Boolean(ctx.receipt), feed !== null) });
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Whether the proxy captures the gateway's record state: the conduct receipt reads it at exit,
|
|
1350
|
+
* and the fed status line reads its lineage key while the session runs.
|
|
1351
|
+
*/
|
|
1352
|
+
export function wantsRecordState(receipt, fed) {
|
|
1353
|
+
return receipt || fed;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Point Claude Code at a ready proxy and, on a fed launch, start the status-line feed on the
|
|
1357
|
+
* proxy's record state (no state file, no feed: the line stays "mnemom").
|
|
1358
|
+
*/
|
|
1359
|
+
export function onBaseUrlProxyReady(baseUrl, recordStateFile, childEnv, feed, start = startFeed) {
|
|
1360
|
+
if (!baseUrl)
|
|
1361
|
+
return;
|
|
1362
|
+
childEnv.ANTHROPIC_BASE_URL = baseUrl;
|
|
1363
|
+
// An inherited HTTPS_PROXY must not carry the loopback hop.
|
|
1364
|
+
exemptLoopbackFromProxy(childEnv);
|
|
1365
|
+
if (feed && recordStateFile)
|
|
1366
|
+
start({ ...feed, recordStateFile });
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* The status-line feed file for a fed launch (and FEED_ENV set on the child), or null: the
|
|
1370
|
+
* segment off, no billing org, or {@link fedStatuslineWanted} false.
|
|
1371
|
+
*/
|
|
1372
|
+
export function prepareStatuslineFeed(o) {
|
|
1373
|
+
if (!o.statusline || !o.orgId || !fedStatuslineWanted(o.version, o.goalMode, o.env))
|
|
1374
|
+
return null;
|
|
1375
|
+
const feed = (o.files ?? fedStatuslineFiles)();
|
|
1376
|
+
o.childEnv[FEED_ENV] = feed.feedFile;
|
|
1377
|
+
return feed;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Whether this launch feeds the status line measured model cost and μ (W1.4): prerelease
|
|
1381
|
+
* builds only (stable stays "mnemom", decision 14), and only when the turn proxy runs, since
|
|
1382
|
+
* the proxy is what learns the gateway's lineage key for the session.
|
|
1383
|
+
*/
|
|
1384
|
+
export function fedStatuslineWanted(version, goalMode, env) {
|
|
1385
|
+
return isPrerelease(version) && goalMode && turnProxyWanted(env);
|
|
1386
|
+
}
|
|
1387
|
+
/** A private per-session dir holding the feed (launcher → bin). */
|
|
1388
|
+
function fedStatuslineFiles() {
|
|
1389
|
+
const dir = mkdtempSync(join(tmpdir(), "mnemom-statusline."));
|
|
1390
|
+
process.on("exit", () => rmSync(dir, { recursive: true, force: true }));
|
|
1391
|
+
return { feedFile: join(dir, "feed.json") };
|
|
1326
1392
|
}
|
|
1327
1393
|
/**
|
|
1328
1394
|
* Add 127.0.0.1 and localhost to BOTH NO_PROXY and no_proxy (proxy clients disagree on which one
|
|
@@ -1334,6 +1400,20 @@ export function exemptLoopbackFromProxy(env) {
|
|
|
1334
1400
|
env.NO_PROXY = [upper, "127.0.0.1,localhost"].filter(Boolean).join(",");
|
|
1335
1401
|
env.no_proxy = [lower, "127.0.0.1,localhost"].filter(Boolean).join(",");
|
|
1336
1402
|
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Print the conduct receipt (experimental `record`) — the exit call site here, and the one a
|
|
1405
|
+
* durable park calls. Never throws: a failure is one warning line, and the caller's exit code
|
|
1406
|
+
* is untouched.
|
|
1407
|
+
*/
|
|
1408
|
+
export async function sayConductReceipt(launch, trigger = "exit", receipt = conductReceiptFor) {
|
|
1409
|
+
try {
|
|
1410
|
+
for (const line of await receipt(launch, trigger))
|
|
1411
|
+
say(line);
|
|
1412
|
+
}
|
|
1413
|
+
catch (err) {
|
|
1414
|
+
say(fmt.warn(`mnemom agent: no conduct receipt (${err instanceof Error ? err.message : String(err)})`));
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1337
1417
|
/** Whether a terminal implicit launch runs the turn-origin proxy (MNEMOM_AGENT_TURN_HEADER=0 opts out). */
|
|
1338
1418
|
export function turnProxyWanted(env) {
|
|
1339
1419
|
const raw = (env.MNEMOM_AGENT_TURN_HEADER ?? "").trim().toLowerCase();
|
|
@@ -1350,6 +1430,9 @@ export function startBaseUrlProxy(door, done, opts = {}) {
|
|
|
1350
1430
|
const script = opts.script ?? join(dirname(fileURLToPath(import.meta.url)), "..", "base-url-proxy.mjs");
|
|
1351
1431
|
// The log carries method / path / status / latency / turn note only (never a header value or body).
|
|
1352
1432
|
const logDir = mkdtempSync(join(tmpdir(), "mnemom-turn-proxy."));
|
|
1433
|
+
// The receipt's record state (lineage key, record URL: non-secret) lives beside the log and is
|
|
1434
|
+
// removed with it on exit, after the receipt has read it.
|
|
1435
|
+
const recordStatePath = opts.recordState ? join(logDir, "record.json") : undefined;
|
|
1353
1436
|
const proxy = spawn(opts.node ?? process.execPath, [script], {
|
|
1354
1437
|
env: {
|
|
1355
1438
|
...process.env,
|
|
@@ -1357,6 +1440,8 @@ export function startBaseUrlProxy(door, done, opts = {}) {
|
|
|
1357
1440
|
MNEMOM_AGENT_BU_CWD: process.cwd(),
|
|
1358
1441
|
MNEMOM_AGENT_BU_LOG: process.env.MNEMOM_AGENT_TURN_PROXY_LOG || join(logDir, "proxy.log"),
|
|
1359
1442
|
MNEMOM_AGENT_BU_PARENT_PID: String(process.pid),
|
|
1443
|
+
...(opts.turnHeader === false ? { MNEMOM_AGENT_TURN_HEADER: "0" } : {}),
|
|
1444
|
+
...(recordStatePath ? { MNEMOM_AGENT_BU_RECORD_STATE: recordStatePath } : {}),
|
|
1360
1445
|
},
|
|
1361
1446
|
stdio: ["ignore", "pipe", "ignore"],
|
|
1362
1447
|
});
|
|
@@ -1375,7 +1460,7 @@ export function startBaseUrlProxy(door, done, opts = {}) {
|
|
|
1375
1460
|
if (!url) {
|
|
1376
1461
|
say(fmt.warn(`mnemom agent: turn-origin proxy unavailable (${why}) — launching straight to the gateway; the gateway reads turn origins from the wire only.`));
|
|
1377
1462
|
}
|
|
1378
|
-
done(url);
|
|
1463
|
+
done(url, url ? recordStatePath : undefined);
|
|
1379
1464
|
};
|
|
1380
1465
|
const timeoutMs = opts.readyTimeoutMs ?? 5000;
|
|
1381
1466
|
const timer = setTimeout(() => finish(null, `no READY within ${timeoutMs}ms`), timeoutMs);
|
package/dist/commands/status.js
CHANGED
|
@@ -138,7 +138,7 @@ async function checkApiConnectivity(agentId) {
|
|
|
138
138
|
catch (error) {
|
|
139
139
|
// 404 = agent not registered yet. Branch on effectiveStatus (enforce hook may
|
|
140
140
|
// rewrite an undocumented 404 → synthetic 500 + spec_deviation.original_status;
|
|
141
|
-
// effectiveStatus === status when documented). getAgent →
|
|
141
|
+
// effectiveStatus === status when documented). getAgent → readApiError → MnemomApiError.
|
|
142
142
|
if (error instanceof MnemomApiError && error.effectiveStatus === 404) {
|
|
143
143
|
return {
|
|
144
144
|
name: "API",
|
package/dist/commands/update.js
CHANGED
|
@@ -43,7 +43,9 @@ export function formatUpdateResult(result, current) {
|
|
|
43
43
|
lines.push(` Try manually: ${manual(action.version)}`);
|
|
44
44
|
}
|
|
45
45
|
else if (action.kind === "notify") {
|
|
46
|
-
lines.push(
|
|
46
|
+
lines.push(result.movedBack
|
|
47
|
+
? `mnemom ${current} was withdrawn; ${result.channel} is back on ${action.version}.`
|
|
48
|
+
: `mnemom ${action.version} is available on ${result.channel} (you have ${current}).`);
|
|
47
49
|
lines.push(` Run: ${canSelfUpdate(result.method ?? "unknown") ? "mnemom update" : manual(action.version)}`);
|
|
48
50
|
}
|
|
49
51
|
else {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `mnemom validate ...` commands — Safe House Hardening T1-4 (CI gate
|
|
3
|
-
* integration). Surface for engineers to check whether
|
|
3
|
+
* integration). Surface for engineers to check whether the test cell is safe
|
|
4
4
|
* to promote to production based on the most recent harness run state.
|
|
5
5
|
*
|
|
6
6
|
* mnemom validate safe-house [--against=test] [--max-age=24h]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `mnemom validate ...` commands — Safe House Hardening T1-4 (CI gate
|
|
3
|
-
* integration). Surface for engineers to check whether
|
|
3
|
+
* integration). Surface for engineers to check whether the test cell is safe
|
|
4
4
|
* to promote to production based on the most recent harness run state.
|
|
5
5
|
*
|
|
6
6
|
* mnemom validate safe-house [--against=test] [--max-age=24h]
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { agentsListCommand, agentsClaimCommand, agentsMoveCommand } from "./comm
|
|
|
16
16
|
import { agentExportCommand, liveExportDeps } from "./commands/agent-export.js";
|
|
17
17
|
import { agentSessionsCommand, agentShowCommand, liveSessionDeps, } from "./commands/agent-sessions.js";
|
|
18
18
|
import { agentGoalPinCommand, agentGoalResetCommand, agentGoalShowCommand, liveGoalDeps, } from "./commands/agent-goal.js";
|
|
19
|
+
import { agentFuelCommand, liveFuelDeps } from "./commands/agent-fuel.js";
|
|
19
20
|
import { orgListCommand, orgShowCommand, orgUseCommand } from "./commands/org.js";
|
|
20
21
|
import { teamListCommand, teamShowCommand, teamTemplateCommand, teamPreviewComposeCommand, teamAdminGrantCommand, teamAdminRevokeCommand, teamAdminListCommand, teamCoverageCommand, } from "./commands/team.js";
|
|
21
22
|
import { advisoriesListCommand, advisoriesShowCommand } from "./commands/advisories.js";
|
|
@@ -33,11 +34,13 @@ import { agentDoctorCommand } from "./commands/agent-doctor.js";
|
|
|
33
34
|
import { agentSetupCommand, parseGovernanceFlag } from "./commands/agent-setup.js";
|
|
34
35
|
import { agentInviteCommand, runAgentInvocation } from "./commands/agent-invite.js";
|
|
35
36
|
import { agentFirstRunCommand, offerFirstRunOnLaunch } from "./commands/agent-first-run.js";
|
|
37
|
+
import { showImplicitDefaultNoticeOnce } from "./commands/agent-implicit-notice.js";
|
|
38
|
+
import { isPrerelease } from "./lib/update/semver.js";
|
|
36
39
|
import { applyConfigToEnv, launcherEntries, loadLauncherConfig, withoutDisabledExperimental, } from "./lib/launcher-config.js";
|
|
37
40
|
import { loadExperimentalState } from "./lib/cli-config.js";
|
|
38
|
-
import { assertEnabled, EXPERIMENTAL_CONTRACT, gateCheck, ignoredSettingLines, reportCliError, } from "./lib/experimental.js";
|
|
41
|
+
import { assertEnabled, availableFeatures, EXPERIMENTAL_CONTRACT, gateCheck, ignoredSettingLines, reportCliError, } from "./lib/experimental.js";
|
|
39
42
|
import { experimentalDisableCommand, experimentalEnableCommand, experimentalListCommand, runExperimentalMigration, } from "./commands/experimental.js";
|
|
40
|
-
import { parseCompactAt, parseContextProfile,
|
|
43
|
+
import { parseCompactAt, parseContextProfile, passthroughIsPrint, resolveImplicitSource, selectLaunchAgent, splitSessionArg, } from "./lib/agent.js";
|
|
41
44
|
import { wrapCommand } from "./commands/wrap.js";
|
|
42
45
|
import { onboardCommand } from "./commands/onboard.js";
|
|
43
46
|
import { skillsListCommand, skillsDescribeCommand } from "./commands/skills.js";
|
|
@@ -114,11 +117,14 @@ program
|
|
|
114
117
|
// ── experimental features (lib/experimental.ts) ── While a feature is off its flags and
|
|
115
118
|
// commands are hidden from --help (decided once, at load) and using one fails with a
|
|
116
119
|
// pointer to `mnemom experimental enable <id>` (decided per invocation, in the action).
|
|
117
|
-
|
|
120
|
+
// A prerelease (`next`) build also makes PRERELEASE_AVAILABLE usable (door one's T16).
|
|
121
|
+
const PRERELEASE_BUILD = isPrerelease(CLI_VERSION);
|
|
122
|
+
const loadAvailableFeatures = () => availableFeatures(loadExperimentalState().enabled, PRERELEASE_BUILD);
|
|
123
|
+
const experimentalAtLoad = loadAvailableFeatures();
|
|
118
124
|
const hiddenUnless = (id) => !experimentalAtLoad.includes(id);
|
|
119
125
|
/** preAction guard for a gated command: print the guidance and exit 2 while `id` is off. */
|
|
120
126
|
function requireExperimental(what, id) {
|
|
121
|
-
const message = gateCheck(what, id,
|
|
127
|
+
const message = gateCheck(what, id, loadAvailableFeatures());
|
|
122
128
|
if (message) {
|
|
123
129
|
console.error(message);
|
|
124
130
|
process.exit(2);
|
|
@@ -161,7 +167,7 @@ const agentCmd = program
|
|
|
161
167
|
"platform cards (strictest wins). See `mnemom protection` for that mode.")
|
|
162
168
|
.option("--lead-directed", "Seal the standard lead-directed requirement: own-branch push and draft/ready PR creation, " +
|
|
163
169
|
"from a git worktree, on the lead's direction — not merging. Requires --goal; idempotent.")
|
|
164
|
-
.addOption(new Option("--implicit", "EXPERIMENTAL (`mnemom experimental enable implicit`): implicit goal alignment — the gateway infers the goal from your messages and revises it as you steer (the default while the feature is on
|
|
170
|
+
.addOption(new Option("--implicit", "EXPERIMENTAL (`mnemom experimental enable implicit`): implicit goal alignment — the gateway infers the goal from your messages and revises it as you steer (the default while the feature is on, and on a `next` build for every launch but `-p`; not combinable with --goal/--requirement/--allow/--forbid/--goal-id/--mode/--lead-directed; the guardrail ceilings still apply)").hideHelp(hiddenUnless("implicit")))
|
|
165
171
|
.option("--no-implicit", "Turn implicit goal alignment OFF for this launch (overrides launcher.toml goal_mode = implicit)")
|
|
166
172
|
.option("--conversation-id <id>", "x-mnemom-conversation-id (default: auto-generated)")
|
|
167
173
|
.option("--cli <name|path>", "Coding-agent CLI to launch: a name or full path (default: claude)")
|
|
@@ -259,6 +265,8 @@ const agentCmd = program
|
|
|
259
265
|
}
|
|
260
266
|
async function launchAgent(scenario) {
|
|
261
267
|
const experimental = loadExperimentalState().enabled;
|
|
268
|
+
// What a flag, key or command may use: the enabled features, plus implicit on `next`.
|
|
269
|
+
const available = availableFeatures(experimental, PRERELEASE_BUILD);
|
|
262
270
|
// The one place the flag's raw string becomes `number | null` (see the argParser's
|
|
263
271
|
// comment for why commander can't carry `null` through parseArg directly). Already
|
|
264
272
|
// validated by the argParser, so this never throws.
|
|
@@ -266,7 +274,7 @@ const agentCmd = program
|
|
|
266
274
|
// Sub-verb: `mnemom agent config …` manages persistent settings and never
|
|
267
275
|
// launches. (A scenario literally named "config" is reserved for this.)
|
|
268
276
|
if (scenario === "config") {
|
|
269
|
-
await agentConfigCommand(passthrough, { enabled:
|
|
277
|
+
await agentConfigCommand(passthrough, { enabled: available });
|
|
270
278
|
return;
|
|
271
279
|
}
|
|
272
280
|
// Sub-verb: `mnemom agent slack …` posts/receives Slack messages as this
|
|
@@ -286,7 +294,7 @@ const agentCmd = program
|
|
|
286
294
|
used: opts.contextHandoff === true,
|
|
287
295
|
},
|
|
288
296
|
{ what: "--implicit", id: "implicit", used: opts.implicit === true },
|
|
289
|
-
],
|
|
297
|
+
], available);
|
|
290
298
|
if (opts.gateway)
|
|
291
299
|
process.env.MNEMOM_AGENT_GATEWAY = opts.gateway;
|
|
292
300
|
// Persistent settings (~/.mnemom/launcher.toml) sit UNDER env and flags:
|
|
@@ -299,11 +307,11 @@ const agentCmd = program
|
|
|
299
307
|
for (const line of ignoredSettingLines({
|
|
300
308
|
entries: launcherEntries(rawCfg),
|
|
301
309
|
env: process.env,
|
|
302
|
-
enabled:
|
|
310
|
+
enabled: available,
|
|
303
311
|
})) {
|
|
304
312
|
console.error(line);
|
|
305
313
|
}
|
|
306
|
-
const cfg = withoutDisabledExperimental(rawCfg,
|
|
314
|
+
const cfg = withoutDisabledExperimental(rawCfg, available);
|
|
307
315
|
applyConfigToEnv(cfg, process.env);
|
|
308
316
|
// Sub-verb: `mnemom agent doctor` runs a read-only preflight and never
|
|
309
317
|
// launches. Placed after config load so it reports the effective settings.
|
|
@@ -334,14 +342,22 @@ const agentCmd = program
|
|
|
334
342
|
const numToStr = (n) => n === undefined ? undefined : String(n);
|
|
335
343
|
// Implicit mode: --implicit / --no-implicit win; a --goal statement seals
|
|
336
344
|
// (the standing config default never overrides it); else launcher.toml
|
|
337
|
-
// `goal_mode`; else implicit while the `implicit` feature is on,
|
|
338
|
-
//
|
|
339
|
-
|
|
345
|
+
// `goal_mode`; else implicit while the `implicit` feature is on; else, on a
|
|
346
|
+
// `next` build, implicit unless the launch is headless `-p` (T16); else off.
|
|
347
|
+
// Pure + unit-tested in lib/agent.ts.
|
|
348
|
+
const goalMode = resolveImplicitSource({
|
|
340
349
|
goal: opts.goal,
|
|
341
350
|
implicit: opts.implicit,
|
|
342
351
|
configGoalMode: cfg.goalMode,
|
|
343
352
|
experimentalOn: experimental.includes("implicit"),
|
|
353
|
+
prerelease: PRERELEASE_BUILD,
|
|
354
|
+
print: passthroughIsPrint(passthrough),
|
|
344
355
|
});
|
|
356
|
+
const implicit = goalMode.implicit;
|
|
357
|
+
// The T16 default tells the user once, the first time it applies.
|
|
358
|
+
if (implicit && goalMode.source === "prerelease") {
|
|
359
|
+
showImplicitDefaultNoticeOnce({ dryRun: opts.dryRun });
|
|
360
|
+
}
|
|
345
361
|
// Config guardrails apply only when goal alignment is actually intended (a
|
|
346
362
|
// --goal is present, or implicit mode — where they ride on the mode header),
|
|
347
363
|
// so a saved ceiling never forces an unrequested contract (which would
|
|
@@ -935,6 +951,29 @@ agentCmd
|
|
|
935
951
|
process.exit(1);
|
|
936
952
|
}
|
|
937
953
|
});
|
|
954
|
+
// `mnemom agent fuel <file>` — the fuel step (Doors W3.2): one confirmation seals a batch of job
|
|
955
|
+
// contracts through the batch-seal endpoint and commits them to the repo.
|
|
956
|
+
agentCmd
|
|
957
|
+
.command("fuel <file>", { hidden: hiddenUnless("fuel") })
|
|
958
|
+
.description("EXPERIMENTAL (`mnemom experimental enable fuel`): seal a batch of job contracts with one confirmation. " +
|
|
959
|
+
'`file` is a JSON array of contracts (or `{ "contracts": [...] }`; `-` reads stdin); each is sealed, ' +
|
|
960
|
+
"then written to .mnemom/contracts/<sha256>.json and committed")
|
|
961
|
+
.option("--repo <owner/name>", "The repo the jobs run against (default: the GitHub origin remote)")
|
|
962
|
+
.option("--org <org_id>", "The org to seal under (default: your only organization)")
|
|
963
|
+
.option("--yes", "Seal without the confirmation prompt")
|
|
964
|
+
.option("--no-commit", "Seal only; do not write or commit the contract files")
|
|
965
|
+
.option("--json", "Output the batch-seal response, the files and the commit as JSON")
|
|
966
|
+
.option("--key <idempotency-key>", "Replay a batch already sealed with this key (printed when committing its files failed)")
|
|
967
|
+
.hook("preAction", () => requireExperimental("`mnemom agent fuel`", "fuel"))
|
|
968
|
+
.action(async (file, opts) => {
|
|
969
|
+
try {
|
|
970
|
+
await agentFuelCommand(file, opts, liveFuelDeps);
|
|
971
|
+
}
|
|
972
|
+
catch (error) {
|
|
973
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
974
|
+
process.exitCode = 1;
|
|
975
|
+
}
|
|
976
|
+
});
|
|
938
977
|
// `mnemom agent goal show|pin|reset` — the implicit goal the gateway inferred for a session
|
|
939
978
|
// (implicit-goal CLI). The handlers set process.exitCode themselves (1 / 2); the catch is for
|
|
940
979
|
// the unexpected.
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -359,13 +359,7 @@ export declare function resolveConversationId(raw: string | undefined): string;
|
|
|
359
359
|
export declare function isPositiveInt(value: unknown): boolean;
|
|
360
360
|
/** A strictly-positive finite number (rejects 0, negatives, NaN, Infinity, non-numeric). */
|
|
361
361
|
export declare function isPositiveNumber(value: unknown): boolean;
|
|
362
|
-
|
|
363
|
-
* Canonical JSON — object keys sorted recursively, arrays in order, no
|
|
364
|
-
* whitespace, `undefined` keys dropped. Byte-identical to the gateway's
|
|
365
|
-
* `canonicalJson` (gateway/src/goal-contract.ts), so the base64 we send and the
|
|
366
|
-
* sha we print match what the gateway seals.
|
|
367
|
-
*/
|
|
368
|
-
export declare function canonicalJson(value: unknown): string;
|
|
362
|
+
export { canonicalJson } from "./canonical-json.js";
|
|
369
363
|
/** sha256 hex (first 16 chars) of a string — the operator-eyeball contract hash. */
|
|
370
364
|
export declare function sha256Hex16(text: string): string;
|
|
371
365
|
/**
|
|
@@ -447,7 +441,8 @@ export declare const COMPACTION_INTERCEPT_KEY = "GATEWAY_COMPACTION_INTERCEPT";
|
|
|
447
441
|
/**
|
|
448
442
|
* The gateway env key that has it measure each request's saving for a single session and put
|
|
449
443
|
* it in the client-facing usage (`mnemom_savings` in the read `usage.iterations` entry), where
|
|
450
|
-
*
|
|
444
|
+
* it lands in Claude Code's transcript. The status bar never shows it (decision 14: never
|
|
445
|
+
* "saved"). Default OFF on the gateway; sent `"on"`
|
|
451
446
|
* only while the `measured-savings` experimental feature is on and the launched CLI is Claude
|
|
452
447
|
* Code (`resolveUsageSavings`). Independent of folding, so every profile sends it — `off` too,
|
|
453
448
|
* where the measured saving is honestly $0.00.
|
|
@@ -834,9 +829,9 @@ export declare function resolveCompactionIntercept(opts: {
|
|
|
834
829
|
env?: NodeJS.ProcessEnv;
|
|
835
830
|
}): boolean;
|
|
836
831
|
/**
|
|
837
|
-
* Does the gateway measure this launch's saving (`GATEWAY_USAGE_SAVINGS` in the header
|
|
838
|
-
*
|
|
839
|
-
*
|
|
832
|
+
* Does the gateway measure this launch's saving (`GATEWAY_USAGE_SAVINGS` in the header)? Only
|
|
833
|
+
* with the `measured-savings` experimental feature on, and only for Claude Code, whose
|
|
834
|
+
* transcript keeps the measurement. No flag or
|
|
840
835
|
* launcher.toml key: `mnemom experimental enable measured-savings` is the switch.
|
|
841
836
|
*/
|
|
842
837
|
export declare function resolveUsageSavings(opts: {
|
|
@@ -921,29 +916,47 @@ export declare function buildHandoffMcpConfigJson(p: HandoffMcpParams): string;
|
|
|
921
916
|
* per-session statement, so it is always a per-launch `--goal`.
|
|
922
917
|
*/
|
|
923
918
|
export type GoalModeDefault = "implicit" | "off";
|
|
919
|
+
/**
|
|
920
|
+
* Where a launch's goal mode came from (`resolveImplicitSource`). `prerelease` is door
|
|
921
|
+
* one's T16 default (decisions 12 and 22): implicit by default on a `-next.N` build with
|
|
922
|
+
* nothing configured. It is the only source that shows the one-time notice.
|
|
923
|
+
*/
|
|
924
|
+
export type ImplicitSource = "flag" | "goal" | "config" | "experimental" | "prerelease" | "default";
|
|
925
|
+
/** Does a Claude Code passthrough run headless (`-p` / `--print`)? */
|
|
926
|
+
export declare function passthroughIsPrint(passthrough: readonly string[]): boolean;
|
|
924
927
|
/**
|
|
925
928
|
* Is this launch implicit-mode? Precedence, highest wins:
|
|
926
929
|
* 1. an explicit flag — `--implicit` (true) or `--no-implicit` (false);
|
|
927
930
|
* 2. a `--goal` statement — a sealed contract is a per-launch choice that the
|
|
928
|
-
* standing config default must never silently override
|
|
931
|
+
* standing config default must never silently override, so a launch never
|
|
932
|
+
* runs both modes;
|
|
929
933
|
* 3. the config default `goal_mode` — `"off"` turns alignment off, `"implicit"`
|
|
930
934
|
* keeps it implicit;
|
|
931
|
-
* 4. with the `implicit` experimental feature ON, IMPLICIT
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
+
* 4. with the `implicit` experimental feature ON, IMPLICIT (as before T16);
|
|
936
|
+
* 5. on a prerelease (`next`) build, IMPLICIT for an interactive or Remote Control
|
|
937
|
+
* launch and OFF for a headless `-p` one (OD-13: T16 never covers `-p`);
|
|
938
|
+
* 6. otherwise OFF — stable without the experimental feature behaves as today.
|
|
935
939
|
* Pure so every arm is unit-tested; the caller passes the resolved value on to
|
|
936
940
|
* `assembleContract` / `buildGoalModeHeader` (which still refuse `--implicit`
|
|
937
941
|
* combined with a contract flag — that conflict is EXPLICIT and is reported).
|
|
938
942
|
*/
|
|
939
|
-
export declare function
|
|
943
|
+
export declare function resolveImplicitSource(opts: {
|
|
940
944
|
goal?: string;
|
|
941
945
|
implicit?: boolean;
|
|
942
946
|
configGoalMode?: GoalModeDefault;
|
|
943
947
|
/**
|
|
944
948
|
* Is the `implicit` experimental feature on? Off ⇒ the default is OFF and
|
|
945
949
|
* `goal_mode = implicit` is ignored (`--implicit` is refused upstream, before
|
|
946
|
-
* this runs). Omitted ⇒ on.
|
|
950
|
+
* this runs), unless this is a prerelease build. Omitted ⇒ on.
|
|
947
951
|
*/
|
|
948
952
|
experimentalOn?: boolean;
|
|
949
|
-
|
|
953
|
+
/** Is this a prerelease (`-next.N`) CLI build? `isPrerelease(CLI_VERSION)`. Omitted ⇒ no. */
|
|
954
|
+
prerelease?: boolean;
|
|
955
|
+
/** Does the launch run Claude Code headless (`-p`)? `passthroughIsPrint`. */
|
|
956
|
+
print?: boolean;
|
|
957
|
+
}): {
|
|
958
|
+
implicit: boolean;
|
|
959
|
+
source: ImplicitSource;
|
|
960
|
+
};
|
|
961
|
+
/** `resolveImplicitSource(opts).implicit`. */
|
|
962
|
+
export declare function resolveImplicit(opts: Parameters<typeof resolveImplicitSource>[0]): boolean;
|
package/dist/lib/agent.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { createHash, randomUUID } from "node:crypto";
|
|
23
23
|
import { readFileSync } from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
+
import { canonicalJson } from "./canonical-json.js";
|
|
25
26
|
import { getGatewayUrl } from "./config.js";
|
|
26
27
|
import { normSession } from "./slack-route.js";
|
|
27
28
|
/** The us-2/prod gateway host. The default door — never any other cell silently. */
|
|
@@ -438,6 +439,7 @@ export const AGENT_SUB_VERBS = new Set([
|
|
|
438
439
|
"move",
|
|
439
440
|
"export",
|
|
440
441
|
"sessions",
|
|
442
|
+
"fuel",
|
|
441
443
|
"show",
|
|
442
444
|
"goal",
|
|
443
445
|
]);
|
|
@@ -480,23 +482,7 @@ export function isPositiveNumber(value) {
|
|
|
480
482
|
const n = Number(s);
|
|
481
483
|
return Number.isFinite(n) && n > 0;
|
|
482
484
|
}
|
|
483
|
-
|
|
484
|
-
* Canonical JSON — object keys sorted recursively, arrays in order, no
|
|
485
|
-
* whitespace, `undefined` keys dropped. Byte-identical to the gateway's
|
|
486
|
-
* `canonicalJson` (gateway/src/goal-contract.ts), so the base64 we send and the
|
|
487
|
-
* sha we print match what the gateway seals.
|
|
488
|
-
*/
|
|
489
|
-
export function canonicalJson(value) {
|
|
490
|
-
if (value === null || typeof value !== "object")
|
|
491
|
-
return JSON.stringify(value);
|
|
492
|
-
if (Array.isArray(value))
|
|
493
|
-
return `[${value.map(canonicalJson).join(",")}]`;
|
|
494
|
-
const obj = value;
|
|
495
|
-
const keys = Object.keys(obj)
|
|
496
|
-
.filter((k) => obj[k] !== undefined)
|
|
497
|
-
.sort();
|
|
498
|
-
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
|
|
499
|
-
}
|
|
485
|
+
export { canonicalJson } from "./canonical-json.js";
|
|
500
486
|
/** sha256 hex (first 16 chars) of a string — the operator-eyeball contract hash. */
|
|
501
487
|
export function sha256Hex16(text) {
|
|
502
488
|
return createHash("sha256").update(text, "utf8").digest("hex").slice(0, 16);
|
|
@@ -652,7 +638,8 @@ export const COMPACTION_INTERCEPT_KEY = "GATEWAY_COMPACTION_INTERCEPT";
|
|
|
652
638
|
/**
|
|
653
639
|
* The gateway env key that has it measure each request's saving for a single session and put
|
|
654
640
|
* it in the client-facing usage (`mnemom_savings` in the read `usage.iterations` entry), where
|
|
655
|
-
*
|
|
641
|
+
* it lands in Claude Code's transcript. The status bar never shows it (decision 14: never
|
|
642
|
+
* "saved"). Default OFF on the gateway; sent `"on"`
|
|
656
643
|
* only while the `measured-savings` experimental feature is on and the launched CLI is Claude
|
|
657
644
|
* Code (`resolveUsageSavings`). Independent of folding, so every profile sends it — `off` too,
|
|
658
645
|
* where the measured saving is honestly $0.00.
|
|
@@ -1283,9 +1270,9 @@ export function resolveCompactionIntercept(opts) {
|
|
|
1283
1270
|
return !(raw === "0" || raw === "false" || raw === "off" || raw === "no");
|
|
1284
1271
|
}
|
|
1285
1272
|
/**
|
|
1286
|
-
* Does the gateway measure this launch's saving (`GATEWAY_USAGE_SAVINGS` in the header
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1273
|
+
* Does the gateway measure this launch's saving (`GATEWAY_USAGE_SAVINGS` in the header)? Only
|
|
1274
|
+
* with the `measured-savings` experimental feature on, and only for Claude Code, whose
|
|
1275
|
+
* transcript keeps the measurement. No flag or
|
|
1289
1276
|
* launcher.toml key: `mnemom experimental enable measured-savings` is the switch.
|
|
1290
1277
|
*/
|
|
1291
1278
|
export function resolveUsageSavings(opts) {
|
|
@@ -1333,27 +1320,42 @@ export function buildHandoffMcpConfig(p) {
|
|
|
1333
1320
|
export function buildHandoffMcpConfigJson(p) {
|
|
1334
1321
|
return JSON.stringify(buildHandoffMcpConfig(p));
|
|
1335
1322
|
}
|
|
1323
|
+
/** Does a Claude Code passthrough run headless (`-p` / `--print`)? */
|
|
1324
|
+
export function passthroughIsPrint(passthrough) {
|
|
1325
|
+
return passthrough.some((a) => a === "-p" || a === "--print" || a.startsWith("--print="));
|
|
1326
|
+
}
|
|
1336
1327
|
/**
|
|
1337
1328
|
* Is this launch implicit-mode? Precedence, highest wins:
|
|
1338
1329
|
* 1. an explicit flag — `--implicit` (true) or `--no-implicit` (false);
|
|
1339
1330
|
* 2. a `--goal` statement — a sealed contract is a per-launch choice that the
|
|
1340
|
-
* standing config default must never silently override
|
|
1331
|
+
* standing config default must never silently override, so a launch never
|
|
1332
|
+
* runs both modes;
|
|
1341
1333
|
* 3. the config default `goal_mode` — `"off"` turns alignment off, `"implicit"`
|
|
1342
1334
|
* keeps it implicit;
|
|
1343
|
-
* 4. with the `implicit` experimental feature ON, IMPLICIT
|
|
1344
|
-
*
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1335
|
+
* 4. with the `implicit` experimental feature ON, IMPLICIT (as before T16);
|
|
1336
|
+
* 5. on a prerelease (`next`) build, IMPLICIT for an interactive or Remote Control
|
|
1337
|
+
* launch and OFF for a headless `-p` one (OD-13: T16 never covers `-p`);
|
|
1338
|
+
* 6. otherwise OFF — stable without the experimental feature behaves as today.
|
|
1347
1339
|
* Pure so every arm is unit-tested; the caller passes the resolved value on to
|
|
1348
1340
|
* `assembleContract` / `buildGoalModeHeader` (which still refuse `--implicit`
|
|
1349
1341
|
* combined with a contract flag — that conflict is EXPLICIT and is reported).
|
|
1350
1342
|
*/
|
|
1351
|
-
export function
|
|
1343
|
+
export function resolveImplicitSource(opts) {
|
|
1352
1344
|
if (opts.implicit !== undefined)
|
|
1353
|
-
return opts.implicit;
|
|
1345
|
+
return { implicit: opts.implicit, source: "flag" };
|
|
1354
1346
|
if (hasText(opts.goal))
|
|
1355
|
-
return false;
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1347
|
+
return { implicit: false, source: "goal" };
|
|
1348
|
+
const experimentalOn = opts.experimentalOn !== false;
|
|
1349
|
+
if (!experimentalOn && !opts.prerelease)
|
|
1350
|
+
return { implicit: false, source: "default" };
|
|
1351
|
+
if (opts.configGoalMode !== undefined) {
|
|
1352
|
+
return { implicit: opts.configGoalMode === "implicit", source: "config" };
|
|
1353
|
+
}
|
|
1354
|
+
if (experimentalOn)
|
|
1355
|
+
return { implicit: true, source: "experimental" };
|
|
1356
|
+
return { implicit: !opts.print, source: opts.print ? "default" : "prerelease" };
|
|
1357
|
+
}
|
|
1358
|
+
/** `resolveImplicitSource(opts).implicit`. */
|
|
1359
|
+
export function resolveImplicit(opts) {
|
|
1360
|
+
return resolveImplicitSource(opts).implicit;
|
|
1359
1361
|
}
|