@mnemom/mnemom 0.17.38-next.2 → 0.17.38
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.js +6 -1
- package/dist/index.js +1 -0
- package/dist/lib/runner/loop.d.ts +7 -2
- package/dist/lib/runner/loop.js +15 -3
- package/dist/lib/runner/options.d.ts +7 -0
- package/dist/lib/runner/options.js +11 -0
- package/dist/vendor/durable-contract/index.js +19 -2
- package/dist/vendor/shared/durable-contract/cli-surface.js +2 -1
- package/dist/vendor/shared/durable-contract/codes.d.ts +12 -0
- package/dist/vendor/shared/durable-contract/codes.js +11 -0
- package/dist/vendor/shared/durable-contract/constants.d.ts +7 -1
- package/dist/vendor/shared/durable-contract/constants.js +7 -1
- package/dist/vendor/shared/durable-contract/doors.d.ts +9 -1
- package/dist/vendor/shared/durable-contract/doors.js +13 -0
- package/dist/vendor/shared/durable-contract/freeze-record.d.ts +1 -1
- package/dist/vendor/shared/durable-contract/schema.d.ts +9 -0
- package/dist/vendor/shared/durable-contract/schema.js +11 -0
- package/package.json +1 -1
|
@@ -92,7 +92,12 @@ export async function agentRunCommand(flags, deps) {
|
|
|
92
92
|
executorId = executorIdFromState(await deps.readExecutorState(), executorKey) ?? executorId;
|
|
93
93
|
}
|
|
94
94
|
const api = runnerApi({ apiUrl: deps.apiUrl, executorKey, fetch: deps.fetch });
|
|
95
|
-
const loop = {
|
|
95
|
+
const loop = {
|
|
96
|
+
failMode: plan.failMode,
|
|
97
|
+
stop: deps.stop,
|
|
98
|
+
emit: deps.emit,
|
|
99
|
+
...(plan.sessionId !== undefined ? { sessionId: plan.sessionId } : {}),
|
|
100
|
+
};
|
|
96
101
|
const job = deps.jobDeps(api, executorId, gh.bases, modelKey, plan.credential.kind === "oidc");
|
|
97
102
|
return plan.form === "once" ? runOnce(job, loop) : runWatch(job, loop);
|
|
98
103
|
}
|
package/dist/index.js
CHANGED
|
@@ -1220,6 +1220,7 @@ agentCmd
|
|
|
1220
1220
|
.option("--fail-mode <mode>", "closed (default) or open; also MNEMOM_RUNNER_FAIL_MODE")
|
|
1221
1221
|
.option("--json", "Print only outcome lines on stdout; logs go to stderr")
|
|
1222
1222
|
.option("--oidc", "With --once: exchange the GitHub Actions ID token for a one-job key")
|
|
1223
|
+
.option("--session <uuid>", "With --once: claim only this session (a wake-up's own); never take another from the pool")
|
|
1223
1224
|
.hook("preAction", () => requireExperimental("`mnemom agent run`", "dispatch"))
|
|
1224
1225
|
.action(async (opts) => {
|
|
1225
1226
|
process.exitCode = await runAgentRun(opts);
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* --once: list the ready pool sessions (exec_ready_list, oldest first), try each in order, and run
|
|
5
5
|
* at most one job. A lost race, or an ineligible waiting / not_pool_member, moves to the next
|
|
6
|
-
* candidate; an empty list, or every candidate lost, ends no_job. --
|
|
7
|
-
*
|
|
6
|
+
* candidate; an empty list, or every candidate lost, ends no_job. --once --session <id> (the
|
|
7
|
+
* wake-up's own session, #1329): try exactly that session and never list the pool; a 404 attest, a
|
|
8
|
+
* lost race or a skippable ineligible ends no_job, and no other session is tried, so a wake-up
|
|
9
|
+
* dispatched for one repo's session never works another's. --watch: the same as plain --once, again
|
|
10
|
+
* and again, until SIGTERM or SIGINT. Every job prints exactly one outcome line (none when stopped
|
|
8
11
|
* mid-job), and the process exit code is the --once outcome's, or 0 for --watch stopped.
|
|
9
12
|
*
|
|
10
13
|
* Every side effect is in `JobDeps` (plus `emit`), so each arm is tested; `liveJobDeps`
|
|
@@ -21,6 +24,8 @@ export interface LoopOptions {
|
|
|
21
24
|
emit(line: string): void;
|
|
22
25
|
/** --watch: wait between empty polls, ms. */
|
|
23
26
|
idlePollMs?: number;
|
|
27
|
+
/** --once --session: the one session to try; the ready list is never read. */
|
|
28
|
+
sessionId?: string;
|
|
24
29
|
}
|
|
25
30
|
export declare const WATCH_IDLE_POLL_MS = 15000;
|
|
26
31
|
/** --watch backs off on a ready-list the api cannot answer, up to this, ms. */
|
package/dist/lib/runner/loop.js
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* --once: list the ready pool sessions (exec_ready_list, oldest first), try each in order, and run
|
|
5
5
|
* at most one job. A lost race, or an ineligible waiting / not_pool_member, moves to the next
|
|
6
|
-
* candidate; an empty list, or every candidate lost, ends no_job. --
|
|
7
|
-
*
|
|
6
|
+
* candidate; an empty list, or every candidate lost, ends no_job. --once --session <id> (the
|
|
7
|
+
* wake-up's own session, #1329): try exactly that session and never list the pool; a 404 attest, a
|
|
8
|
+
* lost race or a skippable ineligible ends no_job, and no other session is tried, so a wake-up
|
|
9
|
+
* dispatched for one repo's session never works another's. --watch: the same as plain --once, again
|
|
10
|
+
* and again, until SIGTERM or SIGINT. Every job prints exactly one outcome line (none when stopped
|
|
8
11
|
* mid-job), and the process exit code is the --once outcome's, or 0 for --watch stopped.
|
|
9
12
|
*
|
|
10
13
|
* Every side effect is in `JobDeps` (plus `emit`), so each arm is tested; `liveJobDeps`
|
|
@@ -58,9 +61,18 @@ async function onePass(deps, opts) {
|
|
|
58
61
|
}
|
|
59
62
|
return { kind: "none" };
|
|
60
63
|
}
|
|
64
|
+
/** --once --session: that session's outcome, or none; never another session. */
|
|
65
|
+
async function sessionPass(deps, opts, id) {
|
|
66
|
+
if (opts.stop.aborted)
|
|
67
|
+
return { kind: "stopped" };
|
|
68
|
+
const r = await runJob(id, { failMode: opts.failMode, stop: opts.stop }, deps);
|
|
69
|
+
return r.kind === "next" ? { kind: "none" } : r;
|
|
70
|
+
}
|
|
61
71
|
/** --once: at most one job. Resolves the process exit code. */
|
|
62
72
|
export async function runOnce(deps, opts) {
|
|
63
|
-
const p =
|
|
73
|
+
const p = opts.sessionId === undefined
|
|
74
|
+
? await onePass(deps, opts)
|
|
75
|
+
: await sessionPass(deps, opts, opts.sessionId);
|
|
64
76
|
if (p.kind === "stopped")
|
|
65
77
|
return 0;
|
|
66
78
|
const outcome = p.kind === "outcome" ? p.outcome : noJobOutcome();
|
|
@@ -16,6 +16,8 @@ export interface RunFlags {
|
|
|
16
16
|
failMode?: string;
|
|
17
17
|
json?: boolean;
|
|
18
18
|
oidc?: boolean;
|
|
19
|
+
/** --session <uuid>: with --once, claim exactly this session and never list the pool. */
|
|
20
|
+
session?: string;
|
|
19
21
|
}
|
|
20
22
|
export type RunCredential = {
|
|
21
23
|
kind: "executor_key";
|
|
@@ -34,6 +36,11 @@ export interface RunPlan {
|
|
|
34
36
|
executorId: string | null;
|
|
35
37
|
/** Warnings to print on stderr (an unknown fail mode reads as closed). */
|
|
36
38
|
warnings: string[];
|
|
39
|
+
/**
|
|
40
|
+
* --session: the one session this --once run may claim (the wake-up's own, #1329). Absent: the
|
|
41
|
+
* pool's ready list, oldest first, as before.
|
|
42
|
+
*/
|
|
43
|
+
sessionId?: string;
|
|
37
44
|
}
|
|
38
45
|
export type RunPlanResult = {
|
|
39
46
|
ok: true;
|
|
@@ -22,6 +22,16 @@ export function resolveRunPlan(flags, env) {
|
|
|
22
22
|
if (flags.oidc && form === "watch") {
|
|
23
23
|
return usage("mnemom agent run: --oidc is valid only with --once");
|
|
24
24
|
}
|
|
25
|
+
let sessionId;
|
|
26
|
+
if (flags.session !== undefined) {
|
|
27
|
+
if (form === "watch") {
|
|
28
|
+
return usage("mnemom agent run: --session is valid only with --once");
|
|
29
|
+
}
|
|
30
|
+
if (!UUID_RE.test(flags.session)) {
|
|
31
|
+
return usage("mnemom agent run: --session is not a lowercase uuid");
|
|
32
|
+
}
|
|
33
|
+
sessionId = flags.session;
|
|
34
|
+
}
|
|
25
35
|
const fail = resolveFailMode(flags.failMode, env[RUNNER_FAIL_MODE_ENV]);
|
|
26
36
|
const warnings = fail.warning ? [`mnemom agent run: ${fail.warning}`] : [];
|
|
27
37
|
let credential;
|
|
@@ -60,6 +70,7 @@ export function resolveRunPlan(flags, env) {
|
|
|
60
70
|
credential,
|
|
61
71
|
executorId,
|
|
62
72
|
warnings,
|
|
73
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
63
74
|
},
|
|
64
75
|
};
|
|
65
76
|
}
|
|
@@ -1065,6 +1065,7 @@ var CLAIM_UNAVAILABLE_CAUSES = [
|
|
|
1065
1065
|
/** A pool claim with no valid registry default cap (ruling 34.05.1 (3)). */
|
|
1066
1066
|
"lease_cap_config"
|
|
1067
1067
|
];
|
|
1068
|
+
var SEAL_REPO_MISMATCH_CAUSES = ["no_repo_claim", "no_seal_repo", "repo_mismatch"];
|
|
1068
1069
|
var UNSUPPORTED_FEATURES = [
|
|
1069
1070
|
"tool_classes",
|
|
1070
1071
|
"launch_spec_change",
|
|
@@ -1173,6 +1174,11 @@ var DURABLE_CODES = {
|
|
|
1173
1174
|
// The per-org claim rate limit (ruling 34.24): nothing written, no key minted. retry_after_s is
|
|
1174
1175
|
// the number of seconds to wait (claimRateLimitedDetailsOk).
|
|
1175
1176
|
durable_claim_rate_limited: { status: 429, surfaces: ["api"], fields: ["retry_after_s"] },
|
|
1177
|
+
// An OIDC executor key (bound to the repo whose GitHub Actions token minted it) claiming a session
|
|
1178
|
+
// not sealed for that repo (mnemom-test#1329; mnemom-api#4530). Its own code, not a
|
|
1179
|
+
// durable_claim_ineligible reason: the session is claimable, only not by this executor, and a
|
|
1180
|
+
// runner never skips past it to another session. cause is a SEAL_REPO_MISMATCH_CAUSES value.
|
|
1181
|
+
seal_repo_mismatch: { status: 403, surfaces: ["api"], fields: ["cause"] },
|
|
1176
1182
|
// ---- api journal (C10-R0 (i), (j), (k)) ----
|
|
1177
1183
|
/** Lost CAS race on (session_id, seq). */
|
|
1178
1184
|
durable_append_conflict: { status: 409, surfaces: ["api"], fields: ["tip_seq"] },
|
|
@@ -1670,7 +1676,8 @@ var M1_CLI_SURFACE = Object.freeze([
|
|
|
1670
1676
|
entry(["agent", "pause"], ["--json"]),
|
|
1671
1677
|
entry(["agent", "resume"], ["--json"]),
|
|
1672
1678
|
// Doors thin runner (W-L13), ruling 34.12 (1): --watch or --once, --fail-mode, --json, --oidc.
|
|
1673
|
-
|
|
1679
|
+
// --session (#1329, approved by the Durable lead): with --once, claim only the wake-up's session.
|
|
1680
|
+
entry(["agent", "run"], ["--fail-mode", "--json", "--oidc", "--once", "--session", "--watch"]),
|
|
1674
1681
|
// BACKBONE C4 (line 147: buildDurableLaunchSpec serves 'sessions adopt'); options: C14 spec
|
|
1675
1682
|
// (`sessions adopt --dry-run --json`, `sessions adopt --own-agent`); ruling 32.33.
|
|
1676
1683
|
entry(["agent", "sessions", "adopt"], ["--dry-run", "--json", "--own-agent"]),
|
|
@@ -2393,12 +2400,18 @@ var DISPATCH_SESSION_CID_PREFIX = "dispatch-";
|
|
|
2393
2400
|
var DISPATCH_SESSION_NAME_PREFIX = "job-";
|
|
2394
2401
|
var CREATE_READY_SESSION_REFUSALS = [
|
|
2395
2402
|
"no_dispatch_agent",
|
|
2403
|
+
// amendment 63 (signed off by Wassim 2026-11-05): neither the seal's confirmer nor the dispatch agent's
|
|
2404
|
+
// creator is a user (NULL, empty or scheme-prefixed, such as github:<login>); nothing written
|
|
2405
|
+
"dispatch_owner_missing",
|
|
2396
2406
|
"seal_not_found",
|
|
2397
2407
|
"cell_unmapped",
|
|
2398
2408
|
// 34.10.1 (4): the api-built charter v1 or its envelope does not match the seal; nothing written
|
|
2399
2409
|
"charter_mismatch"
|
|
2400
2410
|
];
|
|
2401
2411
|
var CREATE_READY_SESSION_OUTCOMES = ["created", "existing", "refused"];
|
|
2412
|
+
function isUserCreator(createdBy) {
|
|
2413
|
+
return typeof createdBy === "string" && createdBy !== "" && !createdBy.includes(":");
|
|
2414
|
+
}
|
|
2402
2415
|
var CREATE_READY_SESSION_ANSWERS = Object.freeze({
|
|
2403
2416
|
created: ["outcome", "session_id"],
|
|
2404
2417
|
existing: ["outcome", "session_id"],
|
|
@@ -4092,7 +4105,9 @@ var M1_COLUMNS = {
|
|
|
4092
4105
|
"last_seen_at",
|
|
4093
4106
|
"risk_line_version",
|
|
4094
4107
|
"created_at",
|
|
4095
|
-
"scope"
|
|
4108
|
+
"scope",
|
|
4109
|
+
"oidc_repo",
|
|
4110
|
+
"oidc_bound"
|
|
4096
4111
|
],
|
|
4097
4112
|
agent_durable_profiles: [
|
|
4098
4113
|
"id",
|
|
@@ -6572,6 +6587,7 @@ export {
|
|
|
6572
6587
|
RUNNER_OUTCOME_FIELDS,
|
|
6573
6588
|
RUNNER_REFUSED_REASONS,
|
|
6574
6589
|
SEALED_CONTRACT_FIELDS,
|
|
6590
|
+
SEAL_REPO_MISMATCH_CAUSES,
|
|
6575
6591
|
SEAL_VECTORS,
|
|
6576
6592
|
SEAT_DAEMON_FLAGS,
|
|
6577
6593
|
SEAT_MAX_ATTEMPTS_NONRETRY,
|
|
@@ -6776,6 +6792,7 @@ export {
|
|
|
6776
6792
|
isTerminalState,
|
|
6777
6793
|
isTestAllowlisted,
|
|
6778
6794
|
isUnchainedCountTokens,
|
|
6795
|
+
isUserCreator,
|
|
6779
6796
|
isValidCommitment,
|
|
6780
6797
|
isValidSessionName,
|
|
6781
6798
|
isValidStateReason,
|
|
@@ -60,7 +60,8 @@ export const M1_CLI_SURFACE = Object.freeze([
|
|
|
60
60
|
entry(["agent", "pause"], ["--json"]),
|
|
61
61
|
entry(["agent", "resume"], ["--json"]),
|
|
62
62
|
// Doors thin runner (W-L13), ruling 34.12 (1): --watch or --once, --fail-mode, --json, --oidc.
|
|
63
|
-
|
|
63
|
+
// --session (#1329, approved by the Durable lead): with --once, claim only the wake-up's session.
|
|
64
|
+
entry(["agent", "run"], ["--fail-mode", "--json", "--oidc", "--once", "--session", "--watch"]),
|
|
64
65
|
// BACKBONE C4 (line 147: buildDurableLaunchSpec serves 'sessions adopt'); options: C14 spec
|
|
65
66
|
// (`sessions adopt --dry-run --json`, `sessions adopt --own-agent`); ruling 32.33.
|
|
66
67
|
entry(["agent", "sessions", "adopt"], ["--dry-run", "--json", "--own-agent"]),
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const CLAIM_UNAVAILABLE_CAUSES: readonly ["schema", "arm_probe", "entitlement_lookup", "protection_probe", "guard", "lease_cap_config"];
|
|
10
10
|
export type ClaimUnavailableCause = (typeof CLAIM_UNAVAILABLE_CAUSES)[number];
|
|
11
|
+
/**
|
|
12
|
+
* seal_repo_mismatch details.cause (mnemom-test#1329; mnemom-api#4530 claim.ts oidcRepoBinding): the
|
|
13
|
+
* bound executor names no repo, the session has no dispatch seal naming a repo, or the seal names
|
|
14
|
+
* another repo than the executor's (owner/name, case-insensitive).
|
|
15
|
+
*/
|
|
16
|
+
export declare const SEAL_REPO_MISMATCH_CAUSES: readonly ["no_repo_claim", "no_seal_repo", "repo_mismatch"];
|
|
17
|
+
export type SealRepoMismatchCause = (typeof SEAL_REPO_MISMATCH_CAUSES)[number];
|
|
11
18
|
export declare const UNSUPPORTED_FEATURES: readonly ["tool_classes", "launch_spec_change", "above_limit_raise", "remote_control"];
|
|
12
19
|
export type UnsupportedFeature = (typeof UNSUPPORTED_FEATURES)[number];
|
|
13
20
|
export declare const CWD_KINDS: readonly ["subdirectory", "not_git", "symlink_transcript"];
|
|
@@ -261,6 +268,11 @@ export declare const DURABLE_CODES: {
|
|
|
261
268
|
readonly surfaces: readonly ["api"];
|
|
262
269
|
readonly fields: readonly ["retry_after_s"];
|
|
263
270
|
};
|
|
271
|
+
readonly seal_repo_mismatch: {
|
|
272
|
+
readonly status: 403;
|
|
273
|
+
readonly surfaces: readonly ["api"];
|
|
274
|
+
readonly fields: readonly ["cause"];
|
|
275
|
+
};
|
|
264
276
|
/** Lost CAS race on (session_id, seq). */
|
|
265
277
|
readonly durable_append_conflict: {
|
|
266
278
|
readonly status: 409;
|
|
@@ -18,6 +18,12 @@ export const CLAIM_UNAVAILABLE_CAUSES = [
|
|
|
18
18
|
/** A pool claim with no valid registry default cap (ruling 34.05.1 (3)). */
|
|
19
19
|
"lease_cap_config",
|
|
20
20
|
];
|
|
21
|
+
/**
|
|
22
|
+
* seal_repo_mismatch details.cause (mnemom-test#1329; mnemom-api#4530 claim.ts oidcRepoBinding): the
|
|
23
|
+
* bound executor names no repo, the session has no dispatch seal naming a repo, or the seal names
|
|
24
|
+
* another repo than the executor's (owner/name, case-insensitive).
|
|
25
|
+
*/
|
|
26
|
+
export const SEAL_REPO_MISMATCH_CAUSES = ["no_repo_claim", "no_seal_repo", "repo_mismatch"];
|
|
21
27
|
export const UNSUPPORTED_FEATURES = [
|
|
22
28
|
"tool_classes",
|
|
23
29
|
"launch_spec_change",
|
|
@@ -134,6 +140,11 @@ export const DURABLE_CODES = {
|
|
|
134
140
|
// The per-org claim rate limit (ruling 34.24): nothing written, no key minted. retry_after_s is
|
|
135
141
|
// the number of seconds to wait (claimRateLimitedDetailsOk).
|
|
136
142
|
durable_claim_rate_limited: { status: 429, surfaces: ["api"], fields: ["retry_after_s"] },
|
|
143
|
+
// An OIDC executor key (bound to the repo whose GitHub Actions token minted it) claiming a session
|
|
144
|
+
// not sealed for that repo (mnemom-test#1329; mnemom-api#4530). Its own code, not a
|
|
145
|
+
// durable_claim_ineligible reason: the session is claimable, only not by this executor, and a
|
|
146
|
+
// runner never skips past it to another session. cause is a SEAL_REPO_MISMATCH_CAUSES value.
|
|
147
|
+
seal_repo_mismatch: { status: 403, surfaces: ["api"], fields: ["cause"] },
|
|
137
148
|
// ---- api journal (C10-R0 (i), (j), (k)) ----
|
|
138
149
|
/** Lost CAS race on (session_id, seq). */
|
|
139
150
|
durable_append_conflict: { status: 409, surfaces: ["api"], fields: ["tip_seq"] },
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
* freeze record" are chosen here; each carries its reason. `freeze-record.ts` copies them into
|
|
5
5
|
* the JSON record the eval runner reads as `$S1.<NAME>`.
|
|
6
6
|
*/
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* The contract version of this package. The per-file hashes in FREEZE.json are the change signal
|
|
9
|
+
* for S1; this number moves only on a breaking change to the contract's shape, by ruling.
|
|
10
|
+
* Precedent: amendments 59, 60 and 34.03.4 all changed FREEZE.json at 1.0.0 (lead ruling 34.35.1,
|
|
11
|
+
* 2026-11-05: the contract version is held at 1.0.0 while FREEZE hashes carry change; a bump is a
|
|
12
|
+
* separate ruling).
|
|
13
|
+
*/
|
|
8
14
|
export declare const DURABLE_CONTRACT_VERSION = "1.0.0";
|
|
9
15
|
/**
|
|
10
16
|
* `durable_schema_version()` must return exactly this. A mismatch makes the entitlement `durable`
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
* freeze record" are chosen here; each carries its reason. `freeze-record.ts` copies them into
|
|
6
6
|
* the JSON record the eval runner reads as `$S1.<NAME>`.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* The contract version of this package. The per-file hashes in FREEZE.json are the change signal
|
|
10
|
+
* for S1; this number moves only on a breaking change to the contract's shape, by ruling.
|
|
11
|
+
* Precedent: amendments 59, 60 and 34.03.4 all changed FREEZE.json at 1.0.0 (lead ruling 34.35.1,
|
|
12
|
+
* 2026-11-05: the contract version is held at 1.0.0 while FREEZE hashes carry change; a bump is a
|
|
13
|
+
* separate ruling).
|
|
14
|
+
*/
|
|
9
15
|
export const DURABLE_CONTRACT_VERSION = "1.0.0";
|
|
10
16
|
/**
|
|
11
17
|
* `durable_schema_version()` must return exactly this. A mismatch makes the entitlement `durable`
|
|
@@ -60,8 +60,16 @@ export declare function effectiveLeaseCap(orgLimits: {
|
|
|
60
60
|
} | null, defaultCap: number | null): number;
|
|
61
61
|
export declare const DISPATCH_SESSION_CID_PREFIX = "dispatch-";
|
|
62
62
|
export declare const DISPATCH_SESSION_NAME_PREFIX = "job-";
|
|
63
|
-
export declare const CREATE_READY_SESSION_REFUSALS: readonly ["no_dispatch_agent", "seal_not_found", "cell_unmapped", "charter_mismatch"];
|
|
63
|
+
export declare const CREATE_READY_SESSION_REFUSALS: readonly ["no_dispatch_agent", "dispatch_owner_missing", "seal_not_found", "cell_unmapped", "charter_mismatch"];
|
|
64
64
|
export declare const CREATE_READY_SESSION_OUTCOMES: readonly ["created", "existing", "refused"];
|
|
65
|
+
/**
|
|
66
|
+
* Amendment 63 (ruling 34.35): the one test for whether a session creator is a user, shared by the
|
|
67
|
+
* api (owner choice at dispatch, the claim's 403 not_owner, the repair) and its SQL twin
|
|
68
|
+
* public.durable_is_user_creator. A creator is a user unless it is null, empty or scheme-prefixed
|
|
69
|
+
* (contains ':', as github:<login> does). No id shape is read: uuids, Zitadel numeric ids and
|
|
70
|
+
* test-mode harness-<x> are users, so an identity-provider change does not move it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isUserCreator(createdBy: unknown): boolean;
|
|
65
73
|
/** durable_create_ready_session's answer keys per outcome, closed (34.10 (g)(1)). */
|
|
66
74
|
export declare const CREATE_READY_SESSION_ANSWERS: Readonly<{
|
|
67
75
|
readonly created: readonly ["outcome", "session_id"];
|
|
@@ -91,12 +91,25 @@ export const DISPATCH_SESSION_CID_PREFIX = "dispatch-";
|
|
|
91
91
|
export const DISPATCH_SESSION_NAME_PREFIX = "job-";
|
|
92
92
|
export const CREATE_READY_SESSION_REFUSALS = [
|
|
93
93
|
"no_dispatch_agent",
|
|
94
|
+
// amendment 63 (signed off by Wassim 2026-11-05): neither the seal's confirmer nor the dispatch agent's
|
|
95
|
+
// creator is a user (NULL, empty or scheme-prefixed, such as github:<login>); nothing written
|
|
96
|
+
"dispatch_owner_missing",
|
|
94
97
|
"seal_not_found",
|
|
95
98
|
"cell_unmapped",
|
|
96
99
|
// 34.10.1 (4): the api-built charter v1 or its envelope does not match the seal; nothing written
|
|
97
100
|
"charter_mismatch",
|
|
98
101
|
];
|
|
99
102
|
export const CREATE_READY_SESSION_OUTCOMES = ["created", "existing", "refused"];
|
|
103
|
+
/**
|
|
104
|
+
* Amendment 63 (ruling 34.35): the one test for whether a session creator is a user, shared by the
|
|
105
|
+
* api (owner choice at dispatch, the claim's 403 not_owner, the repair) and its SQL twin
|
|
106
|
+
* public.durable_is_user_creator. A creator is a user unless it is null, empty or scheme-prefixed
|
|
107
|
+
* (contains ':', as github:<login> does). No id shape is read: uuids, Zitadel numeric ids and
|
|
108
|
+
* test-mode harness-<x> are users, so an identity-provider change does not move it.
|
|
109
|
+
*/
|
|
110
|
+
export function isUserCreator(createdBy) {
|
|
111
|
+
return typeof createdBy === "string" && createdBy !== "" && !createdBy.includes(":");
|
|
112
|
+
}
|
|
100
113
|
/** durable_create_ready_session's answer keys per outcome, closed (34.10 (g)(1)). */
|
|
101
114
|
export const CREATE_READY_SESSION_ANSWERS = Object.freeze({
|
|
102
115
|
created: ["outcome", "session_id"],
|
|
@@ -737,7 +737,7 @@ export declare const FREEZE_STRUCTURED: Readonly<{
|
|
|
737
737
|
OIDC_EXCHANGE_REFUSALS: readonly ["bad_issuer", "repo_not_enrolled", "ref_not_allowed", "pool_flag_off"];
|
|
738
738
|
ORG_POLICY_LIMIT_KEYS: readonly ["per_session_budget_usd", "live_lease_cap"];
|
|
739
739
|
POOL_CLAIM_FIELDS: readonly ["contract_sha256", "contract_version", "cap"];
|
|
740
|
-
CREATE_READY_SESSION_REFUSALS: readonly ["no_dispatch_agent", "seal_not_found", "cell_unmapped", "charter_mismatch"];
|
|
740
|
+
CREATE_READY_SESSION_REFUSALS: readonly ["no_dispatch_agent", "dispatch_owner_missing", "seal_not_found", "cell_unmapped", "charter_mismatch"];
|
|
741
741
|
CREATE_READY_SESSION_OUTCOMES: readonly ["created", "existing", "refused"];
|
|
742
742
|
CREATE_READY_SESSION_ANSWERS: Readonly<{
|
|
743
743
|
readonly created: readonly ["outcome", "session_id"];
|
|
@@ -27,6 +27,15 @@
|
|
|
27
27
|
* - agent_sessions.job_failures (INTEGER NOT NULL DEFAULT 0, CHECK >= 0): the pool job's accepted
|
|
28
28
|
* runner_failed count, never reset. Separate from resume_failures, so a lease lapse never spends
|
|
29
29
|
* a job retry and a job retry never counts towards respawn_failed.
|
|
30
|
+
*
|
|
31
|
+
* OIDC repo binding (mnemom-test#1329; S1 amendment approved by the Durable lead; mnemom-api#4530
|
|
32
|
+
* migration durable_m1_doors_48_w17_oidc_repo_binding), appended to its table:
|
|
33
|
+
* - agent_executors.oidc_repo (TEXT NULL): the owner/name repo whose GitHub Actions token minted
|
|
34
|
+
* the executor in the OIDC exchange (durable_apply_oidc_enrollment); NULL on every other row;
|
|
35
|
+
* - agent_executors.oidc_bound (BOOLEAN NOT NULL DEFAULT false): true only on a row the OIDC
|
|
36
|
+
* exchange wrote with oidc_repo. A bound executor claims only sessions whose dispatch seal names
|
|
37
|
+
* that repo, else 403 seal_repo_mismatch (SEAL_REPO_MISMATCH_CAUSES); an unbound row (every
|
|
38
|
+
* row written before the migration) is not checked.
|
|
30
39
|
*/
|
|
31
40
|
export declare const M1_TABLES: readonly ["agent_sessions", "durable_lease_keys", "agent_session_charters", "agent_session_events", "durable_event_kinds", "agent_session_chains", "agent_executors", "agent_durable_profiles", "durable_cell_state", "durable_org_policy", "durable_user_policy", "durable_approvals", "durable_approval_attestations"];
|
|
32
41
|
export type M1Table = (typeof M1_TABLES)[number];
|
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
* - agent_sessions.job_failures (INTEGER NOT NULL DEFAULT 0, CHECK >= 0): the pool job's accepted
|
|
29
29
|
* runner_failed count, never reset. Separate from resume_failures, so a lease lapse never spends
|
|
30
30
|
* a job retry and a job retry never counts towards respawn_failed.
|
|
31
|
+
*
|
|
32
|
+
* OIDC repo binding (mnemom-test#1329; S1 amendment approved by the Durable lead; mnemom-api#4530
|
|
33
|
+
* migration durable_m1_doors_48_w17_oidc_repo_binding), appended to its table:
|
|
34
|
+
* - agent_executors.oidc_repo (TEXT NULL): the owner/name repo whose GitHub Actions token minted
|
|
35
|
+
* the executor in the OIDC exchange (durable_apply_oidc_enrollment); NULL on every other row;
|
|
36
|
+
* - agent_executors.oidc_bound (BOOLEAN NOT NULL DEFAULT false): true only on a row the OIDC
|
|
37
|
+
* exchange wrote with oidc_repo. A bound executor claims only sessions whose dispatch seal names
|
|
38
|
+
* that repo, else 403 seal_repo_mismatch (SEAL_REPO_MISMATCH_CAUSES); an unbound row (every
|
|
39
|
+
* row written before the migration) is not checked.
|
|
31
40
|
*/
|
|
32
41
|
import { canonicalJson, sha256Hex, UUID_RE } from "./canonical.js";
|
|
33
42
|
import { CANONICAL_CELL_IDS } from "./cell-id.js";
|
|
@@ -150,6 +159,8 @@ export const M1_COLUMNS = {
|
|
|
150
159
|
"risk_line_version",
|
|
151
160
|
"created_at",
|
|
152
161
|
"scope",
|
|
162
|
+
"oidc_repo",
|
|
163
|
+
"oidc_bound",
|
|
153
164
|
],
|
|
154
165
|
agent_durable_profiles: [
|
|
155
166
|
"id",
|