@mnemom/mnemom 0.17.34-next.2 → 0.17.34-next.4
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-run.d.ts +16 -6
- package/dist/commands/agent-run.js +52 -27
- package/dist/commands/agent-share-test-issuer.d.ts +67 -0
- package/dist/commands/agent-share-test-issuer.js +139 -0
- package/dist/commands/agent-share.d.ts +7 -1
- package/dist/commands/agent-share.js +14 -4
- package/dist/commands/agent.d.ts +7 -5
- package/dist/commands/agent.js +84 -45
- package/dist/commands/durable-lock.d.ts +8 -1
- package/dist/commands/durable-lock.js +9 -4
- package/dist/commands/scan.d.ts +12 -1
- package/dist/commands/scan.js +22 -3
- package/dist/commands/wrap.js +1 -1
- package/dist/durable/adopt-live.js +5 -8
- package/dist/durable/adopt.d.ts +8 -0
- package/dist/durable/adopt.js +27 -6
- package/dist/durable/cwd-rule.d.ts +9 -1
- package/dist/durable/cwd-rule.js +18 -0
- package/dist/durable/launch-spec.d.ts +2 -0
- package/dist/durable/launch-spec.js +1 -1
- package/dist/durable/test-mode.mjs +25 -1
- package/dist/host/client.d.ts +3 -1
- package/dist/host/daemon.d.ts +7 -3
- package/dist/host/daemon.js +13 -3
- package/dist/host/owner.d.ts +3 -0
- package/dist/host/owner.js +2 -0
- package/dist/host/run.d.ts +2 -3
- package/dist/host/run.js +5 -20
- package/dist/host/s1-names.d.ts +1 -0
- package/dist/host/s1-names.js +1 -0
- package/dist/host/verbs.d.ts +2 -0
- package/dist/host/verbs.js +11 -0
- package/dist/index.js +13 -9
- package/dist/lib/agent.d.ts +5 -3
- package/dist/lib/agent.js +6 -3
- package/dist/lib/claude-bin.d.ts +6 -0
- package/dist/lib/claude-bin.js +26 -0
- package/dist/lib/commit-report.d.ts +5 -4
- package/dist/lib/commit-report.js +6 -9
- package/dist/lib/config.d.ts +8 -1
- package/dist/lib/config.js +28 -0
- package/dist/lib/durable/launch.d.ts +28 -11
- package/dist/lib/durable/launch.js +33 -15
- package/dist/lib/durable/live.d.ts +15 -19
- package/dist/lib/durable/live.js +41 -43
- package/dist/lib/launcher-config.d.ts +6 -0
- package/dist/lib/launcher-config.js +11 -0
- package/dist/lib/runner/exec-append.d.ts +15 -0
- package/dist/lib/runner/exec-append.js +2 -0
- package/dist/lib/runner/executor-client.d.ts +2 -1
- package/dist/lib/runner/executor-state.d.ts +10 -0
- package/dist/lib/runner/executor-state.js +24 -0
- package/dist/lib/runner/github-base.d.ts +24 -0
- package/dist/lib/runner/github-base.js +27 -0
- package/dist/lib/runner/job.d.ts +11 -4
- package/dist/lib/runner/job.js +59 -16
- package/dist/lib/runner/oidc.d.ts +3 -2
- package/dist/lib/runner/oidc.js +7 -4
- package/dist/lib/runner/options.d.ts +6 -0
- package/dist/lib/runner/options.js +19 -1
- package/dist/lib/runner/outcome.js +2 -3
- package/dist/lib/runner/phases.d.ts +7 -4
- package/dist/lib/runner/phases.js +8 -4
- package/dist/lib/runner/proxy.d.ts +5 -1
- package/dist/lib/runner/proxy.js +7 -1
- package/dist/lib/scan/cost-per-pr.d.ts +93 -0
- package/dist/lib/scan/cost-per-pr.js +101 -0
- package/dist/lib/scan/record.d.ts +27 -1
- package/dist/lib/scan/record.js +114 -16
- package/dist/lib/scan/report-email.d.ts +25 -5
- package/dist/lib/scan/report-email.js +23 -2
- package/dist/lib/scan/scan.d.ts +3 -0
- package/dist/lib/scan/scan.js +13 -1
- package/dist/lib/session-key.d.ts +4 -3
- package/dist/vendor/durable-contract/index.js +207 -169
- package/dist/vendor/shared/durable-contract/doors.d.ts +26 -1
- package/dist/vendor/shared/durable-contract/doors.js +37 -1
- package/dist/vendor/shared/durable-contract/events.d.ts +1 -1
- package/dist/vendor/shared/durable-contract/events.js +13 -3
- package/dist/vendor/shared/durable-contract/freeze-record.d.ts +2 -2
- package/dist/vendor/shared/durable-contract/schema.d.ts +5 -0
- package/dist/vendor/shared/durable-contract/schema.js +6 -0
- package/dist/vendor/shared/durable-contract/states.d.ts +2 -1
- package/dist/vendor/shared/durable-contract/states.js +2 -1
- package/package.json +1 -1
- package/dist/lib/durable/first-turn.d.ts +0 -50
- package/dist/lib/durable/first-turn.js +0 -102
|
@@ -270,7 +270,7 @@ export async function compareWithCommitted(built, committed) {
|
|
|
270
270
|
export function launchSpecChangeFixLines(ctx) {
|
|
271
271
|
const render = {
|
|
272
272
|
launcherKey: ctx.launcherKey,
|
|
273
|
-
nameHeld: true,
|
|
273
|
+
nameHeld: ctx.nameHeld ?? true,
|
|
274
274
|
params: { name: ctx.name, flags: ctx.flags },
|
|
275
275
|
};
|
|
276
276
|
const lines = [renderFixLine("launch_spec_change", render)];
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
// is a loopback literal; an unset URL counts as non-loopback. Otherwise a seam env is ignored.
|
|
7
7
|
//
|
|
8
8
|
// The CLI seams (closed list): T1 DURABLE_BOOT_ID_FILE, T2 DURABLE_HOST_TEST_PAUSE_AT, T3 the
|
|
9
|
-
// credential proxy's http door on a loopback host
|
|
9
|
+
// credential proxy's http door on a loopback host, and `agent run`'s GitHub base, read from the
|
|
10
|
+
// api's own T4 variable DURABLE_TEST_GITHUB_API_BASE (Durable lead, Doors W-L13).
|
|
10
11
|
|
|
11
12
|
const TEST_MODE = "DURABLE_SESSIONS_TEST_MODE";
|
|
12
13
|
/** S1 GUARD_LISTS.cli, in order. */
|
|
@@ -40,6 +41,29 @@ export function bootIdFileOverride(env = process.env) {
|
|
|
40
41
|
return { file, ignored: false };
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* `agent run`'s GitHub base on a lane: the same fake GitHub the api uses (T4,
|
|
46
|
+
* DURABLE_TEST_GITHUB_API_BASE), serving git and REST from one origin. Honoured only under the seam
|
|
47
|
+
* guard and only for an http(s) loopback URL with no credentials, query or fragment; anything else
|
|
48
|
+
* is null, and `ignored` says a value was set but not used (the caller warns).
|
|
49
|
+
*/
|
|
50
|
+
export function testGithubBase(env = process.env) {
|
|
51
|
+
const raw = env.DURABLE_TEST_GITHUB_API_BASE;
|
|
52
|
+
if (typeof raw !== "string" || raw === "") return { base: null, ignored: false };
|
|
53
|
+
if (!testSeamsHonoured(env) || !isLoopbackUrl(raw)) return { base: null, ignored: true };
|
|
54
|
+
const u = new URL(raw);
|
|
55
|
+
if (
|
|
56
|
+
(u.protocol !== "http:" && u.protocol !== "https:") ||
|
|
57
|
+
u.username ||
|
|
58
|
+
u.password ||
|
|
59
|
+
u.search ||
|
|
60
|
+
u.hash
|
|
61
|
+
) {
|
|
62
|
+
return { base: null, ignored: true };
|
|
63
|
+
}
|
|
64
|
+
return { base: `${u.origin}${u.pathname.replace(/\/+$/, "")}`, ignored: false };
|
|
65
|
+
}
|
|
66
|
+
|
|
43
67
|
/** S1 T2_PAUSE_POINTS. */
|
|
44
68
|
export const PAUSE_POINTS = Object.freeze([
|
|
45
69
|
"after_claim_before_spawn",
|
package/dist/host/client.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type ClaimOutcome, type ClaimUnavailableCause } from "../vendor/shared/durable-contract/codes.js";
|
|
2
|
+
import { type ExecAppendBody } from "./s1-names.js";
|
|
3
|
+
export type { ExecAppendBody };
|
|
2
4
|
/** The S1 claim outcomes that grant the lease (CLAIM_OUTCOME_STATUS 200). */
|
|
3
5
|
export type ClaimSuccess = Extract<ClaimOutcome, "acquired" | "reclaimed" | "epoch_bump">;
|
|
4
6
|
/** The header that carries the executor key (ruling 33.28(1); S1 EXECUTOR_KEY_HEADER once frozen). */
|
|
@@ -64,7 +66,7 @@ export interface ExecutorApi {
|
|
|
64
66
|
}>;
|
|
65
67
|
attest(sessionId: string, nonce: string): Promise<HttpResult>;
|
|
66
68
|
claim(sessionId: string, body: ClaimBody): Promise<HttpResult>;
|
|
67
|
-
append(sessionId: string, body:
|
|
69
|
+
append(sessionId: string, body: ExecAppendBody): Promise<HttpResult>;
|
|
68
70
|
hello(sessionId: string, body: {
|
|
69
71
|
epoch: number;
|
|
70
72
|
nonce: string;
|
package/dist/host/daemon.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { ClaimUnavailableCause } from "../vendor/shared/durable-contract/codes.js";
|
|
1
|
+
import type { ClaimUnavailableCause, CwdCheckCause } from "../vendor/shared/durable-contract/codes.js";
|
|
2
2
|
import { type ExecutorApi, type SessionItem } from "./client.js";
|
|
3
3
|
import { type SeatPane } from "./tmux.js";
|
|
4
4
|
export declare const TICK_MS = 1000;
|
|
5
5
|
export declare const ADOPT_RETRY_MS = 2000;
|
|
6
|
-
export { SPAWNED_KIND } from "./s1-names.js";
|
|
7
|
-
export declare const spawnIdempotencyKey: (sessionId: string, epoch: number, spawnId: string) => string;
|
|
6
|
+
export { SPAWNED_KIND, spawnIdempotencyKey } from "./s1-names.js";
|
|
8
7
|
export type PausePoint = "after_claim_before_spawn" | "after_spawn_before_register" | "after_register_before_hello" | "after_first_batch";
|
|
9
8
|
/** One seat this daemon holds (spawned or adopted). */
|
|
10
9
|
export interface Seat {
|
|
@@ -71,6 +70,11 @@ export interface DaemonDeps {
|
|
|
71
70
|
}>;
|
|
72
71
|
/** Transcript guard: refresh for an item; returns the transcript path now on disk (or null). */
|
|
73
72
|
readonly guard: (item: SessionItem) => string | null;
|
|
73
|
+
/**
|
|
74
|
+
* The spawn-time cwd check (C14-R04, mnemom-test #1002) of the session's committed cwd: null
|
|
75
|
+
* when it is a work tree's top level, else the S1 CWD_CHECK_CAUSES value.
|
|
76
|
+
*/
|
|
77
|
+
readonly cwdCheck: (cwd: string | null) => CwdCheckCause | null;
|
|
74
78
|
/** Before --resume: present, restored from the guard, or lost. */
|
|
75
79
|
readonly ensureTranscript: (item: SessionItem) => {
|
|
76
80
|
kind: "present" | "restored";
|
package/dist/host/daemon.js
CHANGED
|
@@ -19,11 +19,10 @@ import { TERMINAL_STATES } from "../vendor/shared/durable-contract/states.js";
|
|
|
19
19
|
import { IDLE_LOG } from "./gate.js";
|
|
20
20
|
import { classifyClaim, errorCode, } from "./client.js";
|
|
21
21
|
import { seatTmuxSession } from "./tmux.js";
|
|
22
|
-
import { CODE, SEAT_UNKNOWN, SPAWNED_KIND } from "./s1-names.js";
|
|
22
|
+
import { CODE, SEAT_UNKNOWN, SPAWNED_KIND, spawnIdempotencyKey } from "./s1-names.js";
|
|
23
23
|
export const TICK_MS = 1000;
|
|
24
24
|
export const ADOPT_RETRY_MS = 2000;
|
|
25
|
-
export { SPAWNED_KIND } from "./s1-names.js";
|
|
26
|
-
export const spawnIdempotencyKey = (sessionId, epoch, spawnId) => `spawn:${sessionId}:${epoch}:${spawnId}`;
|
|
25
|
+
export { SPAWNED_KIND, spawnIdempotencyKey } from "./s1-names.js";
|
|
27
26
|
/** S1 claim backoff: 2 s doubling to 60 s, scaled by 1 + jitter in [0, CLAIM_BACKOFF_JITTER) (R31). */
|
|
28
27
|
export function backoffMs(count, random) {
|
|
29
28
|
const base = Math.min(CLAIM_BACKOFF_MAX_S, CLAIM_BACKOFF_BASE_S * 2 ** Math.max(0, count - 1));
|
|
@@ -417,6 +416,11 @@ export class HostDaemon {
|
|
|
417
416
|
const tokens = at.status === 200 && at.body && typeof at.body === "object"
|
|
418
417
|
? at.body.tokens
|
|
419
418
|
: undefined;
|
|
419
|
+
// mnemom-test #1002 (lead ruling 20:00Z): the cwd check runs right before the claim, with no
|
|
420
|
+
// other work in between, and a failure rides on the claim as cwd_failure. The API suspends the
|
|
421
|
+
// session attestation_failed {check: cwd, cause} (after its own checks 2 and 7, before 12) and
|
|
422
|
+
// acquires nothing, so the claim answers ineligible and nothing is spawned.
|
|
423
|
+
const cwdFailure = this.d.cwdCheck(item.cwd);
|
|
420
424
|
// mnemom-test #1001 (lead ruling, consistent with 33.28.5): a daemon with no local lease key
|
|
421
425
|
// for the session asks for one on its first claim. It never holds one here: a session it
|
|
422
426
|
// seats is not claimable, and the seat takes the key once. A bare claim and then a new_key
|
|
@@ -426,8 +430,14 @@ export class HostDaemon {
|
|
|
426
430
|
nonce: this.d.nonce,
|
|
427
431
|
...(tokens !== undefined ? { tokens } : {}),
|
|
428
432
|
new_key: true,
|
|
433
|
+
...(cwdFailure !== null ? { cwd_failure: cwdFailure } : {}),
|
|
429
434
|
});
|
|
430
435
|
const c = classifyClaim(r, now);
|
|
436
|
+
// An API that did not apply the reported failure still never gets a seat in a bad cwd.
|
|
437
|
+
if (cwdFailure !== null && c.kind === "ok") {
|
|
438
|
+
did.push(`cwd_failure_unheeded:${item.id}:${cwdFailure}`);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
431
441
|
if (c.kind === "ok" && c.claim.leaseKey === null) {
|
|
432
442
|
did.push(`claim_none:${item.id}:no_lease_key`);
|
|
433
443
|
return false;
|
package/dist/host/owner.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ export interface EnrollStart {
|
|
|
38
38
|
readonly expiresIn: number;
|
|
39
39
|
readonly interval: number;
|
|
40
40
|
readonly pollSecret: string;
|
|
41
|
+
/** The org the machine will join (mnemom-test #972); null when the api names none. */
|
|
42
|
+
readonly orgId: string | null;
|
|
43
|
+
readonly orgName: string | null;
|
|
41
44
|
}
|
|
42
45
|
export declare function parseEnrollStart(body: unknown): EnrollStart | null;
|
|
43
46
|
export type PollOutcome = {
|
package/dist/host/owner.js
CHANGED
|
@@ -97,6 +97,8 @@ export function parseEnrollStart(body) {
|
|
|
97
97
|
expiresIn: typeof o.expires_in === "number" ? o.expires_in : 900,
|
|
98
98
|
interval: typeof o.interval === "number" && o.interval > 0 ? o.interval : 2,
|
|
99
99
|
pollSecret: secret,
|
|
100
|
+
orgId: typeof o.org_id === "string" && o.org_id ? o.org_id : null,
|
|
101
|
+
orgName: typeof o.org_name === "string" && o.org_name ? o.org_name : null,
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
104
|
export function classifyPoll(r) {
|
package/dist/host/run.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type HostPaths, type HostInstance } from "./instance.js";
|
|
2
2
|
import { type LoadedState } from "./state.js";
|
|
3
3
|
import { type AutoContinueDeps } from "./auto-continue.js";
|
|
4
|
+
import { resolveClaudeBin } from "../lib/claude-bin.js";
|
|
4
5
|
export interface RunIo {
|
|
5
6
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
7
|
readonly home: string;
|
|
@@ -8,8 +9,7 @@ export interface RunIo {
|
|
|
8
9
|
}
|
|
9
10
|
/** A tag that names this HOME on a shared tmux server (not secret: a hash of the path). */
|
|
10
11
|
export declare const homeTag: (home: string) => string;
|
|
11
|
-
|
|
12
|
-
export declare function resolveClaudeBin(env: Readonly<Record<string, string | undefined>>): string | null;
|
|
12
|
+
export { resolveClaudeBin };
|
|
13
13
|
/** The argv that runs this CLI again: node + the real entry script. */
|
|
14
14
|
export declare function cliExec(): string[];
|
|
15
15
|
type Startup = {
|
|
@@ -63,4 +63,3 @@ export declare function writeRuntimeOrLog(write: () => void, log: (line: string)
|
|
|
63
63
|
* exit, since a normal exit would unlink that daemon's socket.
|
|
64
64
|
*/
|
|
65
65
|
export declare function exitIfSocketLeft(released: "closed" | "left", exit: (code: number) => void): void;
|
|
66
|
-
export {};
|
package/dist/host/run.js
CHANGED
|
@@ -26,28 +26,11 @@ import { writeRuntime } from "./runtime.js";
|
|
|
26
26
|
import { autoContinue, interruptedTurnId, turnContinuedJournal, } from "./auto-continue.js";
|
|
27
27
|
import { hostTestPauseAt } from "../durable/test-mode.mjs";
|
|
28
28
|
import { resolvePluginDir } from "../lib/claude-plugin.js";
|
|
29
|
+
import { liveCwdDeps, spawnCwdCause } from "../durable/cwd-rule.js";
|
|
30
|
+
import { resolveClaudeBin } from "../lib/claude-bin.js";
|
|
29
31
|
/** A tag that names this HOME on a shared tmux server (not secret: a hash of the path). */
|
|
30
32
|
export const homeTag = (home) => createHash("sha256").update(path.resolve(home)).digest("hex").slice(0, 16);
|
|
31
|
-
|
|
32
|
-
export function resolveClaudeBin(env) {
|
|
33
|
-
const v = (env.MNEMOM_AGENT_CLI ?? "").trim();
|
|
34
|
-
if (v.startsWith("/"))
|
|
35
|
-
return v;
|
|
36
|
-
const name = v || "claude";
|
|
37
|
-
for (const dir of (env.PATH ?? "").split(":")) {
|
|
38
|
-
if (!dir)
|
|
39
|
-
continue;
|
|
40
|
-
const p = path.join(dir, name);
|
|
41
|
-
try {
|
|
42
|
-
fs.accessSync(p, fs.constants.X_OK);
|
|
43
|
-
return p;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
/* next */
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
33
|
+
export { resolveClaudeBin };
|
|
51
34
|
/** The argv that runs this CLI again: node + the real entry script. */
|
|
52
35
|
export function cliExec() {
|
|
53
36
|
const entry = process.argv[1] ? fs.realpathSync(process.argv[1]) : "";
|
|
@@ -133,6 +116,7 @@ export async function hostRun(io) {
|
|
|
133
116
|
const procs = defaultProcDeps();
|
|
134
117
|
const tag = homeTag(io.home);
|
|
135
118
|
const projects = ccProjectsDir(env, io.home);
|
|
119
|
+
const cwdDeps = liveCwdDeps(env);
|
|
136
120
|
const sockDir = socketDir(paths.runDir, io.home);
|
|
137
121
|
ensureSocketDir(sockDir);
|
|
138
122
|
const control = controlSocketPath(sockDir);
|
|
@@ -207,6 +191,7 @@ export async function hostRun(io) {
|
|
|
207
191
|
guardMode = g.mode;
|
|
208
192
|
return orig;
|
|
209
193
|
},
|
|
194
|
+
cwdCheck: (cwd) => spawnCwdCause(cwd, cwdDeps),
|
|
210
195
|
ensureTranscript: (item) => ensureTranscript(io.home, item.id, projects, item.cwd, item.cc_session_id ?? "", lastKnown.get(item.id) ?? null),
|
|
211
196
|
removeGuard: (id) => removeGuard(io.home, id),
|
|
212
197
|
guardedIds: () => guardedSessionIds(io.home),
|
package/dist/host/s1-names.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const CODE: {
|
|
|
15
15
|
export declare const SPAWNED_KIND: "executor.spawned";
|
|
16
16
|
/** The executor.turn_continued kind: S1 EXECUTOR_WRITABLE_KINDS[2] (lead ruling 2026-10-28). */
|
|
17
17
|
export declare const TURN_CONTINUED_KIND: "executor.turn_continued";
|
|
18
|
+
export { spawnIdempotencyKey, type ExecAppendBody } from "../lib/runner/exec-append.js";
|
|
18
19
|
/**
|
|
19
20
|
* The control socket's answer when no registered seat has the relayed spawn id. It is local to the
|
|
20
21
|
* daemon and its seat proxy, never a wire code, so it is not a durable_* code (S1 owns those).
|
package/dist/host/s1-names.js
CHANGED
|
@@ -14,6 +14,7 @@ export const CODE = Object.freeze(Object.fromEntries(Object.keys(DURABLE_CODES).
|
|
|
14
14
|
export const SPAWNED_KIND = EXECUTOR_WRITABLE_KINDS[0];
|
|
15
15
|
/** The executor.turn_continued kind: S1 EXECUTOR_WRITABLE_KINDS[2] (lead ruling 2026-10-28). */
|
|
16
16
|
export const TURN_CONTINUED_KIND = EXECUTOR_WRITABLE_KINDS[2];
|
|
17
|
+
export { spawnIdempotencyKey } from "../lib/runner/exec-append.js";
|
|
17
18
|
/**
|
|
18
19
|
* The control socket's answer when no registered seat has the relayed spawn id. It is local to the
|
|
19
20
|
* daemon and its seat proxy, never a wire code, so it is not a durable_* code (S1 owns those).
|
package/dist/host/verbs.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export type Liveness = {
|
|
|
38
38
|
export declare function executorLiveness(io: VerbIo, executorId: string): Promise<Liveness>;
|
|
39
39
|
/** The S1 risk line (BACKBONE amendment 27): human text, or a JSON event in --json mode. Recorded nowhere. */
|
|
40
40
|
export declare function printRiskLine(io: VerbIo): void;
|
|
41
|
+
/** "<org name> (<org_id>)", or the bare id when the api sent no name. */
|
|
42
|
+
export declare function joinTarget(orgId: string, orgName: string | null): string;
|
|
41
43
|
/** The device step: start, show the code, poll, write the state file. Returns an exit code. */
|
|
42
44
|
export declare function enrollDevice(io: VerbIo, opts?: {
|
|
43
45
|
label?: string;
|
package/dist/host/verbs.js
CHANGED
|
@@ -59,6 +59,10 @@ export function printRiskLine(io) {
|
|
|
59
59
|
else
|
|
60
60
|
io.out(RISK_LINE_TEXT);
|
|
61
61
|
}
|
|
62
|
+
/** "<org name> (<org_id>)", or the bare id when the api sent no name. */
|
|
63
|
+
export function joinTarget(orgId, orgName) {
|
|
64
|
+
return orgName ? `${orgName} (${orgId})` : orgId;
|
|
65
|
+
}
|
|
62
66
|
/** The device step: start, show the code, poll, write the state file. Returns an exit code. */
|
|
63
67
|
export async function enrollDevice(io, opts = {}) {
|
|
64
68
|
const { paths } = ctx(io);
|
|
@@ -85,6 +89,13 @@ export async function enrollDevice(io, opts = {}) {
|
|
|
85
89
|
return 1;
|
|
86
90
|
}
|
|
87
91
|
printRiskLine(io);
|
|
92
|
+
// mnemom-test #972: name the org the machine will join before the device step. The human
|
|
93
|
+
// line is on stderr in every mode, so --json stdout stays JSON; --json adds one stdout event.
|
|
94
|
+
if (start.orgId) {
|
|
95
|
+
io.err(`This machine will join ${joinTarget(start.orgId, start.orgName)}`);
|
|
96
|
+
if (io.json)
|
|
97
|
+
io.out(JSON.stringify({ event: "enroll_org", org_id: start.orgId, org_name: start.orgName }));
|
|
98
|
+
}
|
|
88
99
|
if (io.json)
|
|
89
100
|
io.out(JSON.stringify({
|
|
90
101
|
event: "enroll_started",
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { protectionShowCommand, protectionPublishCommand, protectionValidateComm
|
|
|
15
15
|
import { agentsListCommand, agentsClaimCommand, agentsMoveCommand } from "./commands/agents.js";
|
|
16
16
|
import { agentExportCommand, liveExportDeps } from "./commands/agent-export.js";
|
|
17
17
|
import { agentShareCommand, liveAgentShareDeps } from "./commands/agent-share.js";
|
|
18
|
-
import {
|
|
18
|
+
import { selectAgentShareDeps } from "./commands/agent-share-test-issuer.js";
|
|
19
19
|
import { agentSessionsCommand, agentShowCommand, liveSessionDeps, } from "./commands/agent-sessions.js";
|
|
20
20
|
import { agentGoalPinCommand, agentGoalResetCommand, agentGoalShowCommand, liveGoalDeps, } from "./commands/agent-goal.js";
|
|
21
21
|
import { agentFuelCommand, liveFuelDeps } from "./commands/agent-fuel.js";
|
|
@@ -37,7 +37,7 @@ import { agentDoctorCommand } from "./commands/agent-doctor.js";
|
|
|
37
37
|
import { agentSetupCommand, parseGovernanceFlag } from "./commands/agent-setup.js";
|
|
38
38
|
import { agentInviteCommand, runAgentInvocation } from "./commands/agent-invite.js";
|
|
39
39
|
import { agentFirstRunCommand, offerFirstRunOnLaunch } from "./commands/agent-first-run.js";
|
|
40
|
-
import { applyConfigToEnv, launcherEntries, loadLauncherConfig, withoutDisabledExperimental, } from "./lib/launcher-config.js";
|
|
40
|
+
import { applyConfigToEnv, durableLauncherKey, launcherEntries, loadLauncherConfig, withoutDisabledExperimental, } from "./lib/launcher-config.js";
|
|
41
41
|
import { loadCliConfig, loadExperimentalState } from "./lib/cli-config.js";
|
|
42
42
|
import { isSeatProxyArgv, runSeatProxyEntry } from "./durable/seat-proxy-entry.js";
|
|
43
43
|
import { assertEnabled, EXPERIMENTAL_CONTRACT, gateCheck, ignoredSettingLines, reportCliError, } from "./lib/experimental.js";
|
|
@@ -355,6 +355,7 @@ const agentCmd = program
|
|
|
355
355
|
await agentSetupCommand({
|
|
356
356
|
...selectLaunchAgent({
|
|
357
357
|
flag: program.opts().agent,
|
|
358
|
+
env: process.env.MNEMOM_AGENT,
|
|
358
359
|
configAgent: cfg.agent,
|
|
359
360
|
configAgentId: cfg.agentId,
|
|
360
361
|
}),
|
|
@@ -387,7 +388,8 @@ const agentCmd = program
|
|
|
387
388
|
// Durable (gate on only, BACKBONE C1): refusals decided before any registry call or spawn
|
|
388
389
|
// (nested launch, --server, the S1 name grammar); the registry plan runs in agentCommand.
|
|
389
390
|
const durableOn = experimental.includes("durable");
|
|
390
|
-
|
|
391
|
+
// MNEMOM_AGENT_DURABLE wins over launcher.toml `durable` for this launch.
|
|
392
|
+
const durableKey = durableLauncherKey(process.env, cfg.durable);
|
|
391
393
|
if (durableOn) {
|
|
392
394
|
const refusal = durablePreflight({
|
|
393
395
|
name: scenario,
|
|
@@ -413,9 +415,10 @@ const agentCmd = program
|
|
|
413
415
|
maxTurns: opts.maxTurns ?? (contractIntended ? numToStr(cfg.guardrails?.maxTurns) : undefined),
|
|
414
416
|
stall: opts.stall ?? (contractIntended ? numToStr(cfg.guardrails?.stallTurns) : undefined),
|
|
415
417
|
mode: opts.mode,
|
|
416
|
-
// --agent wins; else launcher.toml agent_id (a pin) over agent (a name).
|
|
418
|
+
// --agent wins, then MNEMOM_AGENT; else launcher.toml agent_id (a pin) over agent (a name).
|
|
417
419
|
...selectLaunchAgent({
|
|
418
420
|
flag: parentOpts.agent,
|
|
421
|
+
env: process.env.MNEMOM_AGENT,
|
|
419
422
|
configAgent: cfg.agent,
|
|
420
423
|
configAgentId: cfg.agentId,
|
|
421
424
|
}),
|
|
@@ -946,10 +949,11 @@ agentCmd
|
|
|
946
949
|
.action(async (idOrName, opts) => {
|
|
947
950
|
process.exitCode = await agentExportCommand(idOrName, opts, liveExportDeps);
|
|
948
951
|
});
|
|
949
|
-
// Hidden
|
|
952
|
+
// Hidden from help; runnable only with MNEMOM_AGENT_SHARE=1 (MNE-9037) or when the CLI targets the
|
|
953
|
+
// test cell (MNEMOM_RING=test / test API + website): otherwise it behaves as unknown.
|
|
950
954
|
agentCmd
|
|
951
955
|
.command("share", { hidden: true })
|
|
952
|
-
.description("Build a shareable card from lines of your signed statements and print the share-page link (card in the URL fragment; nothing is posted). Needs MNEMOM_AGENT_SHARE=1")
|
|
956
|
+
.description("Build a shareable card from lines of your signed statements and print the share-page link (card in the URL fragment; nothing is posted). Needs MNEMOM_AGENT_SHARE=1 (not needed on the test ring)")
|
|
953
957
|
.option("--lines <ids>", "Comma-separated line ids to include (skips the picker)")
|
|
954
958
|
.option("--period", "Period card: always include reverts (refused if none are available)")
|
|
955
959
|
.option("--list", "List the lines available to share and exit")
|
|
@@ -957,9 +961,9 @@ agentCmd
|
|
|
957
961
|
.action(async (opts) => {
|
|
958
962
|
// MNEMOM_AGENT_SHARE_FIXTURE=1 (with MNEMOM_AGENT_SHARE=1) swaps in the test-only fixture
|
|
959
963
|
// source/issuer; otherwise the not-yet-connected live deps are used, unchanged.
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
964
|
+
// Against the TEST cell the test-key issuer is selected by the target, not a flag; the
|
|
965
|
+
// fixture flag is refused anywhere else (agent-share-test-issuer.ts).
|
|
966
|
+
const deps = selectAgentShareDeps(liveAgentShareDeps, process.env);
|
|
963
967
|
process.exitCode = await agentShareCommand(opts, deps);
|
|
964
968
|
});
|
|
965
969
|
const agentSessionsCmd = agentCmd
|
package/dist/lib/agent.d.ts
CHANGED
|
@@ -171,13 +171,15 @@ export type AgentRef = {
|
|
|
171
171
|
*/
|
|
172
172
|
export declare function parseAgentRef(raw: string): AgentRef;
|
|
173
173
|
/**
|
|
174
|
-
* Which agent a launch asks for, from the
|
|
174
|
+
* Which agent a launch asks for, from the four places it can be named. `--agent` (flag) wins
|
|
175
175
|
* outright — an id there pins, a name there launches by name even when launcher.toml pins;
|
|
176
|
-
*
|
|
177
|
-
*
|
|
176
|
+
* then MNEMOM_AGENT (env), read exactly as the flag (the global `--agent`'s documented
|
|
177
|
+
* alternative); otherwise launcher.toml `agent_id` pins, and wins over launcher.toml `agent`.
|
|
178
|
+
* Returns the `--agent`-shaped value (`agent`) and the config pin (`agentId`) for AgentOptions.
|
|
178
179
|
*/
|
|
179
180
|
export declare function selectLaunchAgent(sources: {
|
|
180
181
|
flag?: string;
|
|
182
|
+
env?: string;
|
|
181
183
|
configAgent?: string;
|
|
182
184
|
configAgentId?: string;
|
|
183
185
|
}): {
|
package/dist/lib/agent.js
CHANGED
|
@@ -150,14 +150,17 @@ export function parseAgentRef(raw) {
|
|
|
150
150
|
return { kind: "name", name: v };
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
|
-
* Which agent a launch asks for, from the
|
|
153
|
+
* Which agent a launch asks for, from the four places it can be named. `--agent` (flag) wins
|
|
154
154
|
* outright — an id there pins, a name there launches by name even when launcher.toml pins;
|
|
155
|
-
*
|
|
156
|
-
*
|
|
155
|
+
* then MNEMOM_AGENT (env), read exactly as the flag (the global `--agent`'s documented
|
|
156
|
+
* alternative); otherwise launcher.toml `agent_id` pins, and wins over launcher.toml `agent`.
|
|
157
|
+
* Returns the `--agent`-shaped value (`agent`) and the config pin (`agentId`) for AgentOptions.
|
|
157
158
|
*/
|
|
158
159
|
export function selectLaunchAgent(sources) {
|
|
159
160
|
if (sources.flag?.trim())
|
|
160
161
|
return { agent: sources.flag };
|
|
162
|
+
if (sources.env?.trim())
|
|
163
|
+
return { agent: sources.env };
|
|
161
164
|
if (sources.configAgentId?.trim())
|
|
162
165
|
return { agentId: sources.configAgentId };
|
|
163
166
|
return sources.configAgent?.trim() ? { agent: sources.configAgent } : {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which claude to run, shared by the host (seats) and `agent run` (served phases) so both pick
|
|
3
|
+
* the same binary. Kept apart from host/run.ts so the runner does not load the host daemon.
|
|
4
|
+
*/
|
|
5
|
+
/** The pinned claude: MNEMOM_AGENT_CLI when absolute, else `claude` on PATH, else null. */
|
|
6
|
+
export declare function resolveClaudeBin(env: Readonly<Record<string, string | undefined>>): string | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which claude to run, shared by the host (seats) and `agent run` (served phases) so both pick
|
|
3
|
+
* the same binary. Kept apart from host/run.ts so the runner does not load the host daemon.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
/** The pinned claude: MNEMOM_AGENT_CLI when absolute, else `claude` on PATH, else null. */
|
|
8
|
+
export function resolveClaudeBin(env) {
|
|
9
|
+
const v = (env.MNEMOM_AGENT_CLI ?? "").trim();
|
|
10
|
+
if (v.startsWith("/"))
|
|
11
|
+
return v;
|
|
12
|
+
const name = v || "claude";
|
|
13
|
+
for (const dir of (env.PATH ?? "").split(":")) {
|
|
14
|
+
if (!dir)
|
|
15
|
+
continue;
|
|
16
|
+
const p = path.join(dir, name);
|
|
17
|
+
try {
|
|
18
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
19
|
+
return p;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* next */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* reported, trailer or not: a commit is bound when the index holds its sha or
|
|
20
20
|
* patch-id, never by its trailer alone.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* ON BY DEFAULT, stable and prerelease alike, so a PR from any `mnemom agent`
|
|
23
|
+
* session gets its commits bound in the record. MNEMOM_AGENT_COMMIT_REPORT=off
|
|
24
|
+
* (or 0, false, no) turns it off; any other value reads on.
|
|
24
25
|
*
|
|
25
26
|
* FAIL OPEN. Nothing here throws into the launch or changes its exit code. A
|
|
26
27
|
* dark route (404) stops reporting for the session; a failed POST is retried on
|
|
@@ -36,8 +37,8 @@ export declare const CC_DEFAULT_COMMIT_ATTRIBUTION = "Co-Authored-By: Claude <no
|
|
|
36
37
|
export declare const COMMIT_POLL_MS = 5000;
|
|
37
38
|
/** The final sweep at exit shares W1.3's 2 s exit budget with the conduct receipt. */
|
|
38
39
|
export declare const COMMIT_FINAL_DEADLINE_MS = 1500;
|
|
39
|
-
/**
|
|
40
|
-
export declare function commitReportEnabled(raw: string | undefined
|
|
40
|
+
/** On unless MNEMOM_AGENT_COMMIT_REPORT says off (off, 0, false, no). */
|
|
41
|
+
export declare function commitReportEnabled(raw: string | undefined): boolean;
|
|
41
42
|
/**
|
|
42
43
|
* The `--settings` fragment carrying the trailer. `user` is the commit attribution
|
|
43
44
|
* the user's settings files give (highest precedence first; `false` hides all
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* reported, trailer or not: a commit is bound when the index holds its sha or
|
|
20
20
|
* patch-id, never by its trailer alone.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* ON BY DEFAULT, stable and prerelease alike, so a PR from any `mnemom agent`
|
|
23
|
+
* session gets its commits bound in the record. MNEMOM_AGENT_COMMIT_REPORT=off
|
|
24
|
+
* (or 0, false, no) turns it off; any other value reads on.
|
|
24
25
|
*
|
|
25
26
|
* FAIL OPEN. Nothing here throws into the launch or changes its exit code. A
|
|
26
27
|
* dark route (404) stops reporting for the session; a failed POST is retried on
|
|
@@ -31,7 +32,6 @@
|
|
|
31
32
|
import { spawnSync } from "node:child_process";
|
|
32
33
|
import { postRecordCommit } from "./api.js";
|
|
33
34
|
import { readLineage } from "./statusline-feed.js";
|
|
34
|
-
import { isPrerelease } from "./update/semver.js";
|
|
35
35
|
export const COMMIT_REPORT_ENV = "MNEMOM_AGENT_COMMIT_REPORT";
|
|
36
36
|
export const TRAILER_KEY = "Mnemom-Conversation";
|
|
37
37
|
/** Claude Code's own commit attribution when none is configured (settings reference, `attribution`). */
|
|
@@ -43,12 +43,9 @@ export const COMMIT_FINAL_DEADLINE_MS = 1_500;
|
|
|
43
43
|
const HEX40 = /^[0-9a-f]{40}$/;
|
|
44
44
|
const REPO_SLUG = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
|
|
45
45
|
const BRANCH_MAX = 255;
|
|
46
|
-
/**
|
|
47
|
-
export function commitReportEnabled(raw
|
|
48
|
-
|
|
49
|
-
if (v === "")
|
|
50
|
-
return isPrerelease(version);
|
|
51
|
-
return ["on", "1", "true", "yes"].includes(v);
|
|
46
|
+
/** On unless MNEMOM_AGENT_COMMIT_REPORT says off (off, 0, false, no). */
|
|
47
|
+
export function commitReportEnabled(raw) {
|
|
48
|
+
return !["off", "0", "false", "no"].includes((raw ?? "").trim().toLowerCase());
|
|
52
49
|
}
|
|
53
50
|
/**
|
|
54
51
|
* The `--settings` fragment carrying the trailer. `user` is the commit attribution
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class RetiredEnvironmentError extends Error {
|
|
|
30
30
|
* Defaults to production. `staging` throws (see RetiredEnvironmentError).
|
|
31
31
|
*/
|
|
32
32
|
export declare function getEnvironment(): Environment;
|
|
33
|
-
export type CellRing = "us-1";
|
|
33
|
+
export type CellRing = "us-1" | "test";
|
|
34
34
|
/** Explicitly select a ring for this process (clears on undefined). Wins over MNEMOM_RING. */
|
|
35
35
|
export declare function setRing(ring: CellRing | undefined): void;
|
|
36
36
|
/**
|
|
@@ -45,3 +45,10 @@ export declare function setApiUrlOverride(url: string | undefined): void;
|
|
|
45
45
|
export declare function getApiUrl(): string;
|
|
46
46
|
export declare function getGatewayUrl(): string;
|
|
47
47
|
export declare function getWebsiteUrl(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Website base for links the CLI PRINTS (e.g. `agent share`). Same as getWebsiteUrl(), except
|
|
50
|
+
* when the CLI targets the test cell through its API alone (MNEMOM_API_URL / `--api`) and no
|
|
51
|
+
* website was chosen explicitly: then the link points at test.mnemom.ai, never www. An explicit
|
|
52
|
+
* MNEMOM_WEBSITE_URL or ring always wins; prod output is unchanged.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getShareWebsiteUrl(): string;
|
package/dist/lib/config.js
CHANGED
|
@@ -60,6 +60,13 @@ const RING_URLS = {
|
|
|
60
60
|
gateway: "https://gateway-us1.mnemom.ai",
|
|
61
61
|
website: "https://preview.mnemom.ai",
|
|
62
62
|
},
|
|
63
|
+
// The durable Azure TEST cell (tools/gateway-tester/README.md: api-az-test / gateway-az-test;
|
|
64
|
+
// test.mnemom.ai serves the website). Never a prod host.
|
|
65
|
+
test: {
|
|
66
|
+
api: "https://api-az-test.mnemom.ai",
|
|
67
|
+
gateway: "https://gateway-az-test.mnemom.ai",
|
|
68
|
+
website: "https://test.mnemom.ai",
|
|
69
|
+
},
|
|
63
70
|
};
|
|
64
71
|
function isCellRing(value) {
|
|
65
72
|
return Object.prototype.hasOwnProperty.call(RING_URLS, value);
|
|
@@ -115,3 +122,24 @@ export function getGatewayUrl() {
|
|
|
115
122
|
export function getWebsiteUrl() {
|
|
116
123
|
return envBase("MNEMOM_WEBSITE_URL") ?? ringUrls()?.website ?? WEBSITE_URLS[getEnvironment()];
|
|
117
124
|
}
|
|
125
|
+
const TEST_CELL_API_HOST = "api-az-test.mnemom.ai";
|
|
126
|
+
/**
|
|
127
|
+
* Website base for links the CLI PRINTS (e.g. `agent share`). Same as getWebsiteUrl(), except
|
|
128
|
+
* when the CLI targets the test cell through its API alone (MNEMOM_API_URL / `--api`) and no
|
|
129
|
+
* website was chosen explicitly: then the link points at test.mnemom.ai, never www. An explicit
|
|
130
|
+
* MNEMOM_WEBSITE_URL or ring always wins; prod output is unchanged.
|
|
131
|
+
*/
|
|
132
|
+
export function getShareWebsiteUrl() {
|
|
133
|
+
const website = getWebsiteUrl();
|
|
134
|
+
if (envBase("MNEMOM_WEBSITE_URL") || getRing())
|
|
135
|
+
return website;
|
|
136
|
+
try {
|
|
137
|
+
if (new URL(getApiUrl()).hostname.toLowerCase() === TEST_CELL_API_HOST) {
|
|
138
|
+
return RING_URLS.test.website;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* unparseable API base: keep the default */
|
|
143
|
+
}
|
|
144
|
+
return website;
|
|
145
|
+
}
|
|
@@ -130,25 +130,42 @@ export declare function preReadRegistry(d: RegistryReads, name: string): Promise
|
|
|
130
130
|
export declare function explicitCidRefusal(rows: RegistryRow[], explicitCid: string | null, name: string): DurableRefusal | null;
|
|
131
131
|
export declare function planDurableLaunch(i: PlanInput, deps: PlanDeps): Promise<DurableLaunchPlan>;
|
|
132
132
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
133
|
+
* The one stderr line a gate-on launch prints before its tracked Claude Code spawn when stdin is
|
|
134
|
+
* not a TTY and the passthrough has no -p/--print: Claude Code started that way waits forever and
|
|
135
|
+
* draws nothing. The spawn itself stays as today (C02-R23 a, b, f fallback); gate off prints
|
|
136
|
+
* nothing (C17-R11). No command line in it, so no placeholder (C11-R27). Null: print nothing.
|
|
137
|
+
*/
|
|
138
|
+
export declare function trackedNoTtyLine(i: {
|
|
139
|
+
name: string;
|
|
140
|
+
gateOn: boolean;
|
|
141
|
+
stdinIsTTY: boolean;
|
|
142
|
+
cliName: string;
|
|
143
|
+
passthrough: readonly string[];
|
|
144
|
+
}): string | null;
|
|
145
|
+
/**
|
|
146
|
+
* What a launcher does after its lock attempt refused: it exits non-zero with the refusal's own fix
|
|
147
|
+
* line and never falls back to an attended (tracked) spawn, for every agent (Durable lead ruling
|
|
148
|
+
* 2026-10-30 on C02-R23 c, e). A durable-only agent gets the durable_only_agent refusal (C02-R24,
|
|
149
|
+
* final F02); any other agent prints the lock refusal itself.
|
|
136
150
|
*/
|
|
137
151
|
export declare function afterLockRefusal(refusal: DurableRefusal, durableOnly: boolean, launcherKey: LauncherKey, name: string): {
|
|
138
152
|
print: DurableRefusal;
|
|
139
|
-
exit: number
|
|
153
|
+
exit: number;
|
|
140
154
|
};
|
|
141
155
|
/**
|
|
142
|
-
* A lock on a NEW Claude Code session (no transcript yet)
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
156
|
+
* A lock on a NEW Claude Code session (no transcript yet) is a create-and-lock (ruling A, which
|
|
157
|
+
* widens B5a to every agent): the session is born locked and its seat starts the transcript, so no
|
|
158
|
+
* attended claude ever runs first. A continued session already has its transcript and locks its
|
|
159
|
+
* registered row instead of spawning, as before.
|
|
146
160
|
*/
|
|
147
|
-
export declare function
|
|
161
|
+
export declare function locksAtCreate(plan: Extract<DurableLaunchPlan, {
|
|
148
162
|
kind: "spawn";
|
|
149
163
|
}>): boolean;
|
|
150
|
-
/**
|
|
151
|
-
|
|
164
|
+
/**
|
|
165
|
+
* The registered row of a tracked launch, naming the CC session this launch pins: what a later lock
|
|
166
|
+
* from another terminal hands over (C02-R26).
|
|
167
|
+
*/
|
|
168
|
+
export declare function launchRow(row: RegistryRow, plan: Extract<DurableLaunchPlan, {
|
|
152
169
|
kind: "spawn";
|
|
153
170
|
}>): RegistryRow;
|
|
154
171
|
/** The create body the CLI sends (A2 owns the route; see notes-c1.md). */
|