@mnemom/mnemom 0.17.33 → 0.17.34-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-url-proxy.mjs +18 -4
- package/dist/commands/agent-config.js +5 -1
- package/dist/commands/agent-doctor.d.ts +9 -0
- package/dist/commands/agent-doctor.js +4 -0
- package/dist/commands/agent-session.d.ts +1 -2
- package/dist/commands/agent-session.js +1 -2
- package/dist/commands/agent-setup.d.ts +6 -0
- package/dist/commands/agent-setup.js +12 -1
- package/dist/commands/agent-share-render.d.ts +1 -1
- package/dist/commands/agent-share-render.js +1 -1
- package/dist/commands/agent.d.ts +26 -0
- package/dist/commands/agent.js +228 -53
- package/dist/commands/durable-doctor.d.ts +50 -0
- package/dist/commands/durable-doctor.js +186 -0
- package/dist/commands/durable-lock.d.ts +254 -0
- package/dist/commands/durable-lock.js +595 -0
- package/dist/commands/durable-sessions.d.ts +87 -0
- package/dist/commands/durable-sessions.js +303 -0
- package/dist/commands/durable-verbs.d.ts +116 -0
- package/dist/commands/durable-verbs.js +393 -0
- package/dist/commands/protection.js +1 -1
- package/dist/commands/scan.d.ts +97 -0
- package/dist/commands/scan.js +238 -0
- package/dist/durable/flock.d.ts +26 -0
- package/dist/durable/flock.js +80 -0
- package/dist/durable/launch-spec.d.ts +7 -1
- package/dist/durable/launch-spec.js +10 -3
- package/dist/durable/seat-proxy-entry.d.ts +11 -0
- package/dist/durable/seat-proxy-entry.js +25 -0
- package/dist/durable/seat-proxy.d.ts +107 -0
- package/dist/durable/seat-proxy.js +599 -0
- package/dist/durable/seat-socket.d.ts +46 -0
- package/dist/durable/seat-socket.js +179 -0
- package/dist/durable/seat-spec-validator.d.ts +28 -0
- package/dist/durable/seat-spec-validator.js +59 -0
- package/dist/durable/test-mode.mjs +81 -0
- package/dist/durable/turn-chain.d.ts +55 -0
- package/dist/durable/turn-chain.js +178 -0
- package/dist/host/auto-continue.d.ts +67 -0
- package/dist/host/auto-continue.js +118 -0
- package/dist/host/bootid.d.ts +13 -0
- package/dist/host/bootid.js +41 -0
- package/dist/host/client.d.ts +123 -0
- package/dist/host/client.js +171 -0
- package/dist/host/commands.d.ts +2 -0
- package/dist/host/commands.js +122 -0
- package/dist/host/control.d.ts +33 -0
- package/dist/host/control.js +144 -0
- package/dist/host/daemon.d.ts +217 -0
- package/dist/host/daemon.js +606 -0
- package/dist/host/gate.d.ts +46 -0
- package/dist/host/gate.js +82 -0
- package/dist/host/grant.d.ts +24 -0
- package/dist/host/grant.js +24 -0
- package/dist/host/instance.d.ts +35 -0
- package/dist/host/instance.js +62 -0
- package/dist/host/integrity.d.ts +35 -0
- package/dist/host/integrity.js +115 -0
- package/dist/host/launchd.d.ts +16 -0
- package/dist/host/launchd.js +45 -0
- package/dist/host/lock.d.ts +20 -0
- package/dist/host/lock.js +63 -0
- package/dist/host/owner.d.ts +55 -0
- package/dist/host/owner.js +121 -0
- package/dist/host/plist.d.ts +16 -0
- package/dist/host/plist.js +85 -0
- package/dist/host/procs.d.ts +27 -0
- package/dist/host/procs.js +105 -0
- package/dist/host/run.d.ts +66 -0
- package/dist/host/run.js +314 -0
- package/dist/host/runtime.d.ts +8 -0
- package/dist/host/runtime.js +22 -0
- package/dist/host/s1-names.d.ts +22 -0
- package/dist/host/s1-names.js +21 -0
- package/dist/host/seat-credential.d.ts +27 -0
- package/dist/host/seat-credential.js +41 -0
- package/dist/host/seat-launch-main.d.ts +1 -0
- package/dist/host/seat-launch-main.js +59 -0
- package/dist/host/seat-launch.d.ts +116 -0
- package/dist/host/seat-launch.js +302 -0
- package/dist/host/seat.d.ts +48 -0
- package/dist/host/seat.js +149 -0
- package/dist/host/state.d.ts +33 -0
- package/dist/host/state.js +117 -0
- package/dist/host/status.d.ts +101 -0
- package/dist/host/status.js +307 -0
- package/dist/host/tmux.d.ts +56 -0
- package/dist/host/tmux.js +182 -0
- package/dist/host/transcript.d.ts +30 -0
- package/dist/host/transcript.js +130 -0
- package/dist/host/verbs.d.ts +50 -0
- package/dist/host/verbs.js +271 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +232 -35
- package/dist/lib/agent.d.ts +8 -26
- package/dist/lib/agent.js +11 -26
- package/dist/lib/api.d.ts +13 -0
- package/dist/lib/api.js +8 -0
- package/dist/lib/claude-plugin.d.ts +11 -2
- package/dist/lib/claude-plugin.js +10 -1
- package/dist/lib/durable/api.d.ts +136 -0
- package/dist/lib/durable/api.js +220 -0
- package/dist/lib/durable/codes.d.ts +8 -0
- package/dist/lib/durable/codes.js +2 -0
- package/dist/lib/durable/first-turn.d.ts +50 -0
- package/dist/lib/durable/first-turn.js +102 -0
- package/dist/lib/durable/gate.d.ts +48 -0
- package/dist/lib/durable/gate.js +122 -0
- package/dist/lib/durable/handover.d.ts +101 -0
- package/dist/lib/durable/handover.js +175 -0
- package/dist/lib/durable/launch.d.ts +182 -0
- package/dist/lib/durable/launch.js +443 -0
- package/dist/lib/durable/launching.d.ts +15 -0
- package/dist/lib/durable/launching.js +70 -0
- package/dist/lib/durable/live.d.ts +194 -0
- package/dist/lib/durable/live.js +635 -0
- package/dist/lib/durable/refusal.d.ts +57 -0
- package/dist/lib/durable/refusal.js +78 -0
- package/dist/lib/durable/view.d.ts +83 -0
- package/dist/lib/durable/view.js +244 -0
- package/dist/lib/experimental.d.ts +0 -9
- package/dist/lib/experimental.js +18 -17
- package/dist/lib/launcher-config.d.ts +7 -0
- package/dist/lib/launcher-config.js +21 -0
- package/dist/lib/scan/caps.d.ts +35 -0
- package/dist/lib/scan/caps.js +67 -0
- package/dist/lib/scan/credential.d.ts +37 -0
- package/dist/lib/scan/credential.js +104 -0
- package/dist/lib/scan/failure.d.ts +10 -0
- package/dist/lib/scan/failure.js +43 -0
- package/dist/lib/scan/pr-source.d.ts +57 -0
- package/dist/lib/scan/pr-source.js +130 -0
- package/dist/lib/scan/record.d.ts +49 -0
- package/dist/lib/scan/record.js +85 -0
- package/dist/lib/scan/report-email.d.ts +92 -0
- package/dist/lib/scan/report-email.js +144 -0
- package/dist/lib/scan/report.d.ts +13 -0
- package/dist/lib/scan/report.js +30 -0
- package/dist/lib/scan/scan.d.ts +64 -0
- package/dist/lib/scan/scan.js +182 -0
- package/dist/lib/scan/share.d.ts +38 -0
- package/dist/lib/scan/share.js +47 -0
- package/dist/lib/scan/spend/ai-tool-detect.d.ts +6 -0
- package/dist/lib/scan/spend/ai-tool-detect.js +18 -0
- package/dist/lib/scan/spend/anthropic-usage.d.ts +57 -0
- package/dist/lib/scan/spend/anthropic-usage.js +244 -0
- package/dist/lib/scan/spend/cost-corpus.v1.d.ts +56 -0
- package/dist/lib/scan/spend/cost-corpus.v1.js +64 -0
- package/dist/lib/scan/spend/prices.d.ts +33 -0
- package/dist/lib/scan/spend/prices.js +57 -0
- package/dist/lib/scan/spend/spend-estimate.d.ts +30 -0
- package/dist/lib/scan/spend/spend-estimate.js +70 -0
- package/dist/lib/scan/spend/spend-tiers.d.ts +25 -0
- package/dist/lib/scan/spend/spend-tiers.js +42 -0
- package/dist/lib/scan/spend/token-rate.d.ts +30 -0
- package/dist/lib/scan/spend/token-rate.js +54 -0
- package/dist/lib/session-key.d.ts +41 -0
- package/dist/lib/session-key.js +124 -0
- package/dist/lib/subscription-login.d.ts +15 -0
- package/dist/lib/subscription-login.js +28 -1
- package/dist/rc-key-401.mjs +75 -0
- package/dist/rc-proxy.mjs +221 -25
- package/dist/vendor/durable-contract/index.js +240 -18
- package/dist/vendor/shared/durable-contract/admission.d.ts +240 -0
- package/dist/vendor/shared/durable-contract/admission.js +533 -0
- package/dist/vendor/shared/durable-contract/arm.d.ts +51 -0
- package/dist/vendor/shared/durable-contract/arm.js +133 -0
- package/dist/vendor/shared/durable-contract/canonical.d.ts +29 -0
- package/dist/vendor/shared/durable-contract/canonical.js +98 -0
- package/dist/vendor/shared/durable-contract/cell-id.d.ts +22 -0
- package/dist/vendor/shared/durable-contract/cell-id.js +38 -0
- package/dist/vendor/shared/durable-contract/cli-output.d.ts +342 -0
- package/dist/vendor/shared/durable-contract/cli-output.js +201 -0
- package/dist/vendor/shared/durable-contract/cli-surface.d.ts +119 -0
- package/dist/vendor/shared/durable-contract/cli-surface.js +270 -0
- package/dist/vendor/shared/durable-contract/codes.d.ts +537 -0
- package/dist/vendor/shared/durable-contract/codes.js +475 -0
- package/dist/vendor/shared/durable-contract/constants.d.ts +153 -0
- package/dist/vendor/shared/durable-contract/constants.js +179 -0
- package/dist/vendor/shared/durable-contract/doors.d.ts +119 -0
- package/dist/vendor/shared/durable-contract/doors.js +249 -0
- package/dist/vendor/shared/durable-contract/envelope.d.ts +103 -0
- package/dist/vendor/shared/durable-contract/envelope.js +317 -0
- package/dist/vendor/shared/durable-contract/events.d.ts +147 -0
- package/dist/vendor/shared/durable-contract/events.js +630 -0
- package/dist/vendor/shared/durable-contract/executor-state.d.ts +41 -0
- package/dist/vendor/shared/durable-contract/executor-state.js +32 -0
- package/dist/vendor/shared/durable-contract/fixlines.d.ts +318 -0
- package/dist/vendor/shared/durable-contract/fixlines.js +483 -0
- package/dist/vendor/shared/durable-contract/freeze-record.d.ts +787 -0
- package/dist/vendor/shared/durable-contract/freeze-record.js +361 -0
- package/dist/vendor/shared/durable-contract/index.d.ts +36 -0
- package/dist/vendor/shared/durable-contract/index.js +37 -0
- package/dist/vendor/shared/durable-contract/journal-approvals.d.ts +29 -0
- package/dist/vendor/shared/durable-contract/journal-approvals.js +60 -0
- package/dist/vendor/shared/durable-contract/keys.d.ts +24 -0
- package/dist/vendor/shared/durable-contract/keys.js +66 -0
- package/dist/vendor/shared/durable-contract/lock.d.ts +128 -0
- package/dist/vendor/shared/durable-contract/lock.js +151 -0
- package/dist/vendor/shared/durable-contract/names.d.ts +25 -0
- package/dist/vendor/shared/durable-contract/names.js +78 -0
- package/dist/vendor/shared/durable-contract/owner-verbs.d.ts +175 -0
- package/dist/vendor/shared/durable-contract/owner-verbs.js +403 -0
- package/dist/vendor/shared/durable-contract/paths.d.ts +23 -0
- package/dist/vendor/shared/durable-contract/paths.js +35 -0
- package/dist/vendor/shared/durable-contract/pure-fn.d.ts +48 -0
- package/dist/vendor/shared/durable-contract/pure-fn.js +89 -0
- package/dist/vendor/shared/durable-contract/routes.d.ts +205 -0
- package/dist/vendor/shared/durable-contract/routes.js +315 -0
- package/dist/vendor/shared/durable-contract/schema.d.ts +126 -0
- package/dist/vendor/shared/durable-contract/schema.js +441 -0
- package/dist/vendor/shared/durable-contract/seal.d.ts +54 -0
- package/dist/vendor/shared/durable-contract/seal.js +99 -0
- package/dist/vendor/shared/durable-contract/secret-scope.d.ts +28 -0
- package/dist/vendor/shared/durable-contract/secret-scope.js +76 -0
- package/dist/vendor/shared/durable-contract/session-id.d.ts +21 -0
- package/dist/vendor/shared/durable-contract/session-id.js +58 -0
- package/dist/vendor/shared/durable-contract/spec.d.ts +162 -0
- package/dist/vendor/shared/durable-contract/spec.js +286 -0
- package/dist/vendor/shared/durable-contract/states.d.ts +96 -0
- package/dist/vendor/shared/durable-contract/states.js +218 -0
- package/dist/vendor/shared/durable-contract/telemetry.d.ts +94 -0
- package/dist/vendor/shared/durable-contract/telemetry.js +180 -0
- package/dist/vendor/shared/durable-contract/test-mode.d.ts +62 -0
- package/dist/vendor/shared/durable-contract/test-mode.js +140 -0
- package/dist/vendor/shared/durable-contract/turn.d.ts +57 -0
- package/dist/vendor/shared/durable-contract/turn.js +81 -0
- package/dist/vendor/shared/durable-contract/vectors.d.ts +191 -0
- package/dist/vendor/shared/durable-contract/vectors.js +208 -0
- package/package.json +4 -3
- package/plugins/integrity-manifest.json +35 -0
- package/plugins/mnemom/hooks/hooks.json +15 -1
- package/plugins/mnemom/hooks/register.tsx +60 -2
- package/plugins/mnemom/hooks/seat-hello.mjs +116 -0
- package/dist/commands/agent-implicit-notice.d.ts +0 -31
- package/dist/commands/agent-implicit-notice.js +0 -73
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock preconditions (C02-R01..R27, C02 open question 1; rulings I03, r3 U02, final F06; BACKBONE
|
|
3
|
+
* A2 lock executor precondition). One order, first failure wins:
|
|
4
|
+
*
|
|
5
|
+
* step answer on failure external probe
|
|
6
|
+
* machine_principal 403 machine_principal_refused no
|
|
7
|
+
* visible 404 agent_not_found (byte-identical to a random cid) no
|
|
8
|
+
* owner 403 durable_not_owner {owner} no
|
|
9
|
+
* terminal 410 durable_session_ended {state, state_reason} no
|
|
10
|
+
* locked already locked: equal spec hash -> 200 {changed:false};
|
|
11
|
+
* different -> 422 durable_unsupported {feature:'launch_spec_change'}
|
|
12
|
+
* spec 422 durable_unsupported {feature:'remote_control' | 'tool_classes'}
|
|
13
|
+
* entitlement 403 durable_lock_not_entitled yes (entitlement lookup, schema probe)
|
|
14
|
+
* executor 409 durable_no_executor yes (executor lookup)
|
|
15
|
+
* mfa 403 durable_lock_mfa_required yes (IdP user/admin lookup)
|
|
16
|
+
* protection 409 durable_lock_branch_unprotected {refusals} yes (GitHub)
|
|
17
|
+
* sharing 409 durable_lock_agent_shared {reason} no (DB, under the lock-serialising row lock)
|
|
18
|
+
*
|
|
19
|
+
* Skipping: steps run strictly in this order and stop at the first non-pass. A later step is not
|
|
20
|
+
* evaluated, so its probe is never called (no GitHub or IdP traffic after an earlier refusal).
|
|
21
|
+
* Unavailable: a probe step whose source is down answers 503 durable_precondition_unavailable
|
|
22
|
+
* {retryable:true} at its own position (earlier refusals still win; later steps are skipped); it is
|
|
23
|
+
* never read as pass, 'MFA missing', 'unprotected' or 'not entitled'.
|
|
24
|
+
* Protection is the one step that does not short-circuit inside itself: every pushable repo of the
|
|
25
|
+
* server-held repo set is checked, and refusals[] names each failing repo (protectionRefusals).
|
|
26
|
+
*/
|
|
27
|
+
import { TOOL_CLASS_VALUES } from "./envelope.js";
|
|
28
|
+
import { type LaunchSpec } from "./spec.js";
|
|
29
|
+
export declare const LOCK_STEPS: readonly ["machine_principal", "visible", "owner", "terminal", "locked", "spec", "entitlement", "executor", "mfa", "protection", "sharing"];
|
|
30
|
+
export type LockStep = (typeof LOCK_STEPS)[number];
|
|
31
|
+
/** Steps that call an external source and may therefore be unavailable. */
|
|
32
|
+
export declare const LOCK_PROBE_STEPS: readonly ["entitlement", "executor", "mfa", "protection"];
|
|
33
|
+
export declare const LOCK_REFUSAL_REASONS: readonly ["unprotected", "unsupported_host"];
|
|
34
|
+
export type LockRefusalReason = (typeof LOCK_REFUSAL_REASONS)[number];
|
|
35
|
+
/** One durable_lock_branch_unprotected refusals[] element. */
|
|
36
|
+
export interface LockRefusalItem {
|
|
37
|
+
/** "<owner>/<name>" for GitHub; the remote host and path otherwise. */
|
|
38
|
+
repo: string;
|
|
39
|
+
reason: LockRefusalReason;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* What a repo looks like to the protection probe. Protected means the default branch is covered by
|
|
43
|
+
* an active ruleset (enforcement "active"; "evaluate" does not count) or by classic branch protection.
|
|
44
|
+
*/
|
|
45
|
+
export interface RepoProtectionFacts {
|
|
46
|
+
repo: string;
|
|
47
|
+
/** The user can push; read-only repos are ignored. */
|
|
48
|
+
pushable: boolean;
|
|
49
|
+
/** Only github.com remotes are probed; anything else fails closed. */
|
|
50
|
+
host: "github" | "other";
|
|
51
|
+
activeRulesetOnDefault: boolean;
|
|
52
|
+
classicProtectionOnDefault: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Every failing pushable repo, sorted by repo; empty means the step passes. */
|
|
55
|
+
export declare function protectionRefusals(repos: readonly RepoProtectionFacts[]): LockRefusalItem[];
|
|
56
|
+
export type LockStepResult = {
|
|
57
|
+
kind: "pass";
|
|
58
|
+
} | {
|
|
59
|
+
kind: "noop";
|
|
60
|
+
} | {
|
|
61
|
+
kind: "refuse";
|
|
62
|
+
status: number;
|
|
63
|
+
code: string;
|
|
64
|
+
body?: Record<string, unknown>;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "unavailable";
|
|
67
|
+
};
|
|
68
|
+
export type LockDecision = {
|
|
69
|
+
kind: "lock";
|
|
70
|
+
evaluated: LockStep[];
|
|
71
|
+
} | {
|
|
72
|
+
kind: "noop";
|
|
73
|
+
status: 200;
|
|
74
|
+
step: LockStep;
|
|
75
|
+
evaluated: LockStep[];
|
|
76
|
+
} | {
|
|
77
|
+
kind: "refuse";
|
|
78
|
+
step: LockStep;
|
|
79
|
+
status: number;
|
|
80
|
+
code: string;
|
|
81
|
+
body?: Record<string, unknown>;
|
|
82
|
+
evaluated: LockStep[];
|
|
83
|
+
};
|
|
84
|
+
/** Run the steps in order through an injected evaluator; stops at the first non-pass. */
|
|
85
|
+
export declare function runLockPreconditions(evaluate: (step: LockStep) => Promise<LockStepResult>): Promise<LockDecision>;
|
|
86
|
+
/**
|
|
87
|
+
* The closed own_lock request body. It carries no contract: the goal (text and ceilings) is sealed
|
|
88
|
+
* by the gateway before lock from a turn's x-mnemom-contract header, and lock commits that seal
|
|
89
|
+
* (C07-R02 allowed carriers). spec is the LaunchSpec, executor_id this machine's executor uuid,
|
|
90
|
+
* own_agent the --own-agent flag, tool_classes the optional tool -> class map.
|
|
91
|
+
*/
|
|
92
|
+
export declare const LOCK_BODY_KEYS: readonly ["spec", "executor_id", "own_agent", "tool_classes"];
|
|
93
|
+
export interface LockBody {
|
|
94
|
+
spec: LaunchSpec;
|
|
95
|
+
executor_id: string;
|
|
96
|
+
own_agent?: boolean;
|
|
97
|
+
tool_classes?: Record<string, (typeof TOOL_CLASS_VALUES)[number]>;
|
|
98
|
+
}
|
|
99
|
+
export type LockBodyResult = {
|
|
100
|
+
ok: true;
|
|
101
|
+
body: LockBody;
|
|
102
|
+
} | {
|
|
103
|
+
ok: false;
|
|
104
|
+
code: "durable_unsupported";
|
|
105
|
+
details: {
|
|
106
|
+
feature: "remote_control" | "tool_classes";
|
|
107
|
+
};
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
code: "durable_lock_body_invalid";
|
|
111
|
+
details: {
|
|
112
|
+
field: string | null;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Validate an own_lock body before any write. First match wins:
|
|
117
|
+
* 1 a remote-control field anywhere (hasRemoteControl) -> 422 durable_unsupported {feature:'remote_control'}
|
|
118
|
+
* 2 not a JSON object -> 422 durable_lock_body_invalid {field:null}
|
|
119
|
+
* 3 a key outside LOCK_BODY_KEYS (contract and limit
|
|
120
|
+
* fields included; the first in sorted order) -> 422 durable_lock_body_invalid {field:<key>}
|
|
121
|
+
* 4 spec absent or not a LaunchSpec -> 422 durable_lock_body_invalid {field:'spec'}
|
|
122
|
+
* 5 executor_id absent or not a lowercase uuid -> 422 durable_lock_body_invalid {field:'executor_id'}
|
|
123
|
+
* 6 own_agent present and not a boolean -> 422 durable_lock_body_invalid {field:'own_agent'}
|
|
124
|
+
* 7 tool_classes present and not a tool -> TOOL_CLASS_VALUES map
|
|
125
|
+
* -> 422 durable_lock_body_invalid {field:'tool_classes'}
|
|
126
|
+
* 8 tool_classes naming safe or idempotent -> 422 durable_unsupported {feature:'tool_classes'}
|
|
127
|
+
*/
|
|
128
|
+
export declare function parseLockBody(raw: unknown): LockBodyResult;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// GENERATED from shared/durable-contract/src/lock.ts by cli/scripts/sync-durable-contract.mjs; do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* Lock preconditions (C02-R01..R27, C02 open question 1; rulings I03, r3 U02, final F06; BACKBONE
|
|
4
|
+
* A2 lock executor precondition). One order, first failure wins:
|
|
5
|
+
*
|
|
6
|
+
* step answer on failure external probe
|
|
7
|
+
* machine_principal 403 machine_principal_refused no
|
|
8
|
+
* visible 404 agent_not_found (byte-identical to a random cid) no
|
|
9
|
+
* owner 403 durable_not_owner {owner} no
|
|
10
|
+
* terminal 410 durable_session_ended {state, state_reason} no
|
|
11
|
+
* locked already locked: equal spec hash -> 200 {changed:false};
|
|
12
|
+
* different -> 422 durable_unsupported {feature:'launch_spec_change'}
|
|
13
|
+
* spec 422 durable_unsupported {feature:'remote_control' | 'tool_classes'}
|
|
14
|
+
* entitlement 403 durable_lock_not_entitled yes (entitlement lookup, schema probe)
|
|
15
|
+
* executor 409 durable_no_executor yes (executor lookup)
|
|
16
|
+
* mfa 403 durable_lock_mfa_required yes (IdP user/admin lookup)
|
|
17
|
+
* protection 409 durable_lock_branch_unprotected {refusals} yes (GitHub)
|
|
18
|
+
* sharing 409 durable_lock_agent_shared {reason} no (DB, under the lock-serialising row lock)
|
|
19
|
+
*
|
|
20
|
+
* Skipping: steps run strictly in this order and stop at the first non-pass. A later step is not
|
|
21
|
+
* evaluated, so its probe is never called (no GitHub or IdP traffic after an earlier refusal).
|
|
22
|
+
* Unavailable: a probe step whose source is down answers 503 durable_precondition_unavailable
|
|
23
|
+
* {retryable:true} at its own position (earlier refusals still win; later steps are skipped); it is
|
|
24
|
+
* never read as pass, 'MFA missing', 'unprotected' or 'not entitled'.
|
|
25
|
+
* Protection is the one step that does not short-circuit inside itself: every pushable repo of the
|
|
26
|
+
* server-held repo set is checked, and refusals[] names each failing repo (protectionRefusals).
|
|
27
|
+
*/
|
|
28
|
+
import { UUID_RE } from "./canonical.js";
|
|
29
|
+
import { TOOL_CLASS_VALUES, toolClassesSupportedM1 } from "./envelope.js";
|
|
30
|
+
import { hasRemoteControl, parseLaunchSpec } from "./spec.js";
|
|
31
|
+
export const LOCK_STEPS = [
|
|
32
|
+
"machine_principal",
|
|
33
|
+
"visible",
|
|
34
|
+
"owner",
|
|
35
|
+
"terminal",
|
|
36
|
+
"locked",
|
|
37
|
+
"spec",
|
|
38
|
+
"entitlement",
|
|
39
|
+
"executor",
|
|
40
|
+
"mfa",
|
|
41
|
+
"protection",
|
|
42
|
+
"sharing",
|
|
43
|
+
];
|
|
44
|
+
/** Steps that call an external source and may therefore be unavailable. */
|
|
45
|
+
export const LOCK_PROBE_STEPS = [
|
|
46
|
+
"entitlement",
|
|
47
|
+
"executor",
|
|
48
|
+
"mfa",
|
|
49
|
+
"protection",
|
|
50
|
+
];
|
|
51
|
+
export const LOCK_REFUSAL_REASONS = ["unprotected", "unsupported_host"];
|
|
52
|
+
/** Every failing pushable repo, sorted by repo; empty means the step passes. */
|
|
53
|
+
export function protectionRefusals(repos) {
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const r of repos) {
|
|
56
|
+
if (!r.pushable)
|
|
57
|
+
continue;
|
|
58
|
+
if (r.host !== "github")
|
|
59
|
+
out.push({ repo: r.repo, reason: "unsupported_host" });
|
|
60
|
+
else if (!r.activeRulesetOnDefault && !r.classicProtectionOnDefault)
|
|
61
|
+
out.push({ repo: r.repo, reason: "unprotected" });
|
|
62
|
+
}
|
|
63
|
+
return out.sort((a, b) => (a.repo < b.repo ? -1 : a.repo > b.repo ? 1 : 0));
|
|
64
|
+
}
|
|
65
|
+
/** Run the steps in order through an injected evaluator; stops at the first non-pass. */
|
|
66
|
+
export async function runLockPreconditions(evaluate) {
|
|
67
|
+
const evaluated = [];
|
|
68
|
+
for (const step of LOCK_STEPS) {
|
|
69
|
+
evaluated.push(step);
|
|
70
|
+
const r = await evaluate(step);
|
|
71
|
+
switch (r.kind) {
|
|
72
|
+
case "pass":
|
|
73
|
+
continue;
|
|
74
|
+
case "noop":
|
|
75
|
+
if (step !== "locked")
|
|
76
|
+
throw new RangeError(`only the locked step can no-op, not ${step}`);
|
|
77
|
+
return { kind: "noop", status: 200, step, evaluated };
|
|
78
|
+
case "unavailable":
|
|
79
|
+
if (!LOCK_PROBE_STEPS.includes(step))
|
|
80
|
+
throw new RangeError(`${step} has no external source to be unavailable`);
|
|
81
|
+
return {
|
|
82
|
+
kind: "refuse",
|
|
83
|
+
step,
|
|
84
|
+
status: 503,
|
|
85
|
+
code: "durable_precondition_unavailable",
|
|
86
|
+
body: { retryable: true },
|
|
87
|
+
evaluated,
|
|
88
|
+
};
|
|
89
|
+
case "refuse":
|
|
90
|
+
return { kind: "refuse", step, status: r.status, code: r.code, body: r.body, evaluated };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { kind: "lock", evaluated };
|
|
94
|
+
}
|
|
95
|
+
// ---- lock request body (ruling 33.24(1)) ------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* The closed own_lock request body. It carries no contract: the goal (text and ceilings) is sealed
|
|
98
|
+
* by the gateway before lock from a turn's x-mnemom-contract header, and lock commits that seal
|
|
99
|
+
* (C07-R02 allowed carriers). spec is the LaunchSpec, executor_id this machine's executor uuid,
|
|
100
|
+
* own_agent the --own-agent flag, tool_classes the optional tool -> class map.
|
|
101
|
+
*/
|
|
102
|
+
export const LOCK_BODY_KEYS = ["spec", "executor_id", "own_agent", "tool_classes"];
|
|
103
|
+
const TOOL_NAME_RE = /^[A-Za-z0-9_:.-]{1,128}$/;
|
|
104
|
+
/**
|
|
105
|
+
* Validate an own_lock body before any write. First match wins:
|
|
106
|
+
* 1 a remote-control field anywhere (hasRemoteControl) -> 422 durable_unsupported {feature:'remote_control'}
|
|
107
|
+
* 2 not a JSON object -> 422 durable_lock_body_invalid {field:null}
|
|
108
|
+
* 3 a key outside LOCK_BODY_KEYS (contract and limit
|
|
109
|
+
* fields included; the first in sorted order) -> 422 durable_lock_body_invalid {field:<key>}
|
|
110
|
+
* 4 spec absent or not a LaunchSpec -> 422 durable_lock_body_invalid {field:'spec'}
|
|
111
|
+
* 5 executor_id absent or not a lowercase uuid -> 422 durable_lock_body_invalid {field:'executor_id'}
|
|
112
|
+
* 6 own_agent present and not a boolean -> 422 durable_lock_body_invalid {field:'own_agent'}
|
|
113
|
+
* 7 tool_classes present and not a tool -> TOOL_CLASS_VALUES map
|
|
114
|
+
* -> 422 durable_lock_body_invalid {field:'tool_classes'}
|
|
115
|
+
* 8 tool_classes naming safe or idempotent -> 422 durable_unsupported {feature:'tool_classes'}
|
|
116
|
+
*/
|
|
117
|
+
export function parseLockBody(raw) {
|
|
118
|
+
const invalid = (field) => ({
|
|
119
|
+
ok: false,
|
|
120
|
+
code: "durable_lock_body_invalid",
|
|
121
|
+
details: { field },
|
|
122
|
+
});
|
|
123
|
+
if (hasRemoteControl(raw))
|
|
124
|
+
return { ok: false, code: "durable_unsupported", details: { feature: "remote_control" } };
|
|
125
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
126
|
+
return invalid(null);
|
|
127
|
+
const r = raw;
|
|
128
|
+
const unknown = Object.keys(r)
|
|
129
|
+
.filter((k) => !LOCK_BODY_KEYS.includes(k))
|
|
130
|
+
.sort();
|
|
131
|
+
if (unknown.length > 0)
|
|
132
|
+
return invalid(unknown[0]);
|
|
133
|
+
if (parseLaunchSpec(r.spec) === null)
|
|
134
|
+
return invalid("spec");
|
|
135
|
+
if (typeof r.executor_id !== "string" || !UUID_RE.test(r.executor_id))
|
|
136
|
+
return invalid("executor_id");
|
|
137
|
+
if ("own_agent" in r && typeof r.own_agent !== "boolean")
|
|
138
|
+
return invalid("own_agent");
|
|
139
|
+
if ("tool_classes" in r) {
|
|
140
|
+
const tc = r.tool_classes;
|
|
141
|
+
if (tc === null || typeof tc !== "object" || Array.isArray(tc))
|
|
142
|
+
return invalid("tool_classes");
|
|
143
|
+
for (const [tool, cls] of Object.entries(tc)) {
|
|
144
|
+
if (!TOOL_NAME_RE.test(tool) || !TOOL_CLASS_VALUES.includes(cls))
|
|
145
|
+
return invalid("tool_classes");
|
|
146
|
+
}
|
|
147
|
+
if (!toolClassesSupportedM1(tc))
|
|
148
|
+
return { ok: false, code: "durable_unsupported", details: { feature: "tool_classes" } };
|
|
149
|
+
}
|
|
150
|
+
return { ok: true, body: r };
|
|
151
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-name grammar (C01-R19, C01 open question 4; rulings r2 on `containment`, U20 on `host`).
|
|
3
|
+
* One pure function decides; the API answers a refusal with 400 invalid_session_name and the CLI
|
|
4
|
+
* refuses before any spawn.
|
|
5
|
+
*
|
|
6
|
+
* Normalisation (applied before every check, and the stored form): trim leading and trailing
|
|
7
|
+
* whitespace, then Unicode NFC. Case is kept as typed; names compare by exact code points.
|
|
8
|
+
* Refusals, first match wins:
|
|
9
|
+
* empty nothing left after normalisation
|
|
10
|
+
* too_long more than SESSION_NAME_MAX_CHARS code points
|
|
11
|
+
* control NUL, any other C0 control, DEL, any C1 control, or U+2028 / U+2029
|
|
12
|
+
* bidi a bidi override or isolate (U+061C, U+200E, U+200F, U+202A..U+202E, U+2066..U+2069)
|
|
13
|
+
* markup contains "<mnemom-" (any case): the injected-block prefix the gateway strips
|
|
14
|
+
* leading_dash starts with "-": the CLI would read it as a flag
|
|
15
|
+
* reserved equals a RESERVED_SESSION_VERBS word, compared case-insensitively, so a session
|
|
16
|
+
* can never shadow a verb or a fix line (`host`, `containment` included)
|
|
17
|
+
*/
|
|
18
|
+
export declare const SESSION_NAME_MAX_CHARS = 64;
|
|
19
|
+
export declare const RESERVED_SESSION_VERBS: readonly ["sessions", "show", "setup", "doctor", "config", "list", "claim", "move", "export", "help", "invite", "lock", "done", "kill", "pause", "resume", "amend", "host", "containment"];
|
|
20
|
+
export declare const SESSION_NAME_REFUSALS: readonly ["empty", "too_long", "control", "bidi", "markup", "leading_dash", "reserved"];
|
|
21
|
+
export type SessionNameRefusal = (typeof SESSION_NAME_REFUSALS)[number];
|
|
22
|
+
export declare function normalizeSessionName(raw: string): string;
|
|
23
|
+
/** The first refusal class for a raw name, or null when the name is acceptable. */
|
|
24
|
+
export declare function sessionNameRefusal(raw: string): SessionNameRefusal | null;
|
|
25
|
+
export declare function isValidSessionName(raw: string): boolean;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// GENERATED from shared/durable-contract/src/names.ts by cli/scripts/sync-durable-contract.mjs; do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* Session-name grammar (C01-R19, C01 open question 4; rulings r2 on `containment`, U20 on `host`).
|
|
4
|
+
* One pure function decides; the API answers a refusal with 400 invalid_session_name and the CLI
|
|
5
|
+
* refuses before any spawn.
|
|
6
|
+
*
|
|
7
|
+
* Normalisation (applied before every check, and the stored form): trim leading and trailing
|
|
8
|
+
* whitespace, then Unicode NFC. Case is kept as typed; names compare by exact code points.
|
|
9
|
+
* Refusals, first match wins:
|
|
10
|
+
* empty nothing left after normalisation
|
|
11
|
+
* too_long more than SESSION_NAME_MAX_CHARS code points
|
|
12
|
+
* control NUL, any other C0 control, DEL, any C1 control, or U+2028 / U+2029
|
|
13
|
+
* bidi a bidi override or isolate (U+061C, U+200E, U+200F, U+202A..U+202E, U+2066..U+2069)
|
|
14
|
+
* markup contains "<mnemom-" (any case): the injected-block prefix the gateway strips
|
|
15
|
+
* leading_dash starts with "-": the CLI would read it as a flag
|
|
16
|
+
* reserved equals a RESERVED_SESSION_VERBS word, compared case-insensitively, so a session
|
|
17
|
+
* can never shadow a verb or a fix line (`host`, `containment` included)
|
|
18
|
+
*/
|
|
19
|
+
export const SESSION_NAME_MAX_CHARS = 64;
|
|
20
|
+
export const RESERVED_SESSION_VERBS = [
|
|
21
|
+
"sessions",
|
|
22
|
+
"show",
|
|
23
|
+
"setup",
|
|
24
|
+
"doctor",
|
|
25
|
+
"config",
|
|
26
|
+
"list",
|
|
27
|
+
"claim",
|
|
28
|
+
"move",
|
|
29
|
+
"export",
|
|
30
|
+
"help",
|
|
31
|
+
"invite",
|
|
32
|
+
"lock",
|
|
33
|
+
"done",
|
|
34
|
+
"kill",
|
|
35
|
+
"pause",
|
|
36
|
+
"resume",
|
|
37
|
+
"amend",
|
|
38
|
+
"host",
|
|
39
|
+
"containment",
|
|
40
|
+
];
|
|
41
|
+
export const SESSION_NAME_REFUSALS = [
|
|
42
|
+
"empty",
|
|
43
|
+
"too_long",
|
|
44
|
+
"control",
|
|
45
|
+
"bidi",
|
|
46
|
+
"markup",
|
|
47
|
+
"leading_dash",
|
|
48
|
+
"reserved",
|
|
49
|
+
];
|
|
50
|
+
// eslint-disable-next-line no-control-regex
|
|
51
|
+
const CONTROL_RE = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
|
|
52
|
+
const BIDI_RE = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/;
|
|
53
|
+
const MARKUP = "<mnemom-";
|
|
54
|
+
export function normalizeSessionName(raw) {
|
|
55
|
+
return raw.trim().normalize("NFC");
|
|
56
|
+
}
|
|
57
|
+
/** The first refusal class for a raw name, or null when the name is acceptable. */
|
|
58
|
+
export function sessionNameRefusal(raw) {
|
|
59
|
+
const name = normalizeSessionName(raw);
|
|
60
|
+
if (name.length === 0)
|
|
61
|
+
return "empty";
|
|
62
|
+
if ([...name].length > SESSION_NAME_MAX_CHARS)
|
|
63
|
+
return "too_long";
|
|
64
|
+
if (CONTROL_RE.test(name))
|
|
65
|
+
return "control";
|
|
66
|
+
if (BIDI_RE.test(name))
|
|
67
|
+
return "bidi";
|
|
68
|
+
if (name.toLowerCase().includes(MARKUP))
|
|
69
|
+
return "markup";
|
|
70
|
+
if (name.startsWith("-"))
|
|
71
|
+
return "leading_dash";
|
|
72
|
+
if (RESERVED_SESSION_VERBS.includes(name.toLowerCase()))
|
|
73
|
+
return "reserved";
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
export function isValidSessionName(raw) {
|
|
77
|
+
return sessionNameRefusal(raw) === null;
|
|
78
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The owner-verb table (BACKBONE amendments 35, 45; C11-R04..R11, R17, R23, R26, R34, R36;
|
|
3
|
+
* rulings r3 U04, I03; final F04, F06). One total pure function, first match wins:
|
|
4
|
+
*
|
|
5
|
+
* 1 machine principal -> 403 machine_principal_refused
|
|
6
|
+
* 2 unknown session or another org -> 404 agent_not_found (the existing answer)
|
|
7
|
+
* 3 caller is not the owner -> 403 durable_not_owner {owner}
|
|
8
|
+
* 4 effective terminal (expired included) -> 410 durable_session_ended {state, state_reason}, every verb
|
|
9
|
+
* 5 resume of paused(agent_contained) while the agent is still paused -> 409 durable_agent_contained
|
|
10
|
+
* 6 pause on suspended(identity_mismatch) -> 409 durable_invalid_transition (done then --new)
|
|
11
|
+
* 7 no-op repeats -> 200 {changed:false}
|
|
12
|
+
* 8 anything else not in the §3.4 table -> 409 durable_invalid_transition {from_state, state_reason, verb}
|
|
13
|
+
*
|
|
14
|
+
* Applied rows bump the epoch FIRST in the same transaction where they fence (pause, complete,
|
|
15
|
+
* kill); resume to parked does not bump (the home executor's next claim does). Device-step verbs
|
|
16
|
+
* (unlock, budget_raise, and resume of paused(agent_contained) once the agent is active) answer
|
|
17
|
+
* 202 and change nothing until the approval is consumed; with an API-key-only credential they are
|
|
18
|
+
* refused before any approval row (F06).
|
|
19
|
+
*/
|
|
20
|
+
import type { DurableCode } from "./codes.js";
|
|
21
|
+
import { type FixKey } from "./fixlines.js";
|
|
22
|
+
import type { DurableEventKind } from "./events.js";
|
|
23
|
+
import { type DurableMode, type EffectiveState, type SessionState, type StateReason } from "./states.js";
|
|
24
|
+
/**
|
|
25
|
+
* The owner verbs BACKBONE names for this table: pause, resume, complete, kill, amend. The first
|
|
26
|
+
* four are decided by `ownerVerbDecision`; `amend` is decided by `amendDecision`, which runs the
|
|
27
|
+
* same precedence rows 1-6 and then splits by direction (C07-R07, C16-R04): a budget raise goes to
|
|
28
|
+
* the device-step verb `budget_raise`, any other raise or loosening (goal text edits included, and
|
|
29
|
+
* a budget raise mixed with one) is 409 durable_amend_raise_refused {field}, and a lowering applies
|
|
30
|
+
* with journal `charter.amended {change:'lowering'}` and no state transition.
|
|
31
|
+
*/
|
|
32
|
+
export declare const OWNER_VERBS: readonly ["pause", "resume", "complete", "kill", "amend"];
|
|
33
|
+
export type OwnerVerb = (typeof OWNER_VERBS)[number];
|
|
34
|
+
/**
|
|
35
|
+
* The verbs `ownerVerbDecision` takes: the four table verbs plus the two device-step verbs that
|
|
36
|
+
* run the same precedence (`unlock`, and `budget_raise`, which is amend's raise leg).
|
|
37
|
+
*/
|
|
38
|
+
export declare const OWNER_TABLE_VERBS: readonly ["pause", "resume", "complete", "kill", "unlock", "budget_raise"];
|
|
39
|
+
export type OwnerTableVerb = (typeof OWNER_TABLE_VERBS)[number];
|
|
40
|
+
/** Verbs that run the owner_mutation device step (and so need a signed-in owner, F06). */
|
|
41
|
+
export declare const DEVICE_STEP_VERBS: readonly ["unlock", "budget_raise"];
|
|
42
|
+
export declare const CALLER_CREDENTIALS: readonly ["jwt", "owner_api_key", "machine"];
|
|
43
|
+
export type CallerCredential = (typeof CALLER_CREDENTIALS)[number];
|
|
44
|
+
export interface OwnerVerbInput {
|
|
45
|
+
verb: OwnerTableVerb;
|
|
46
|
+
credential: CallerCredential;
|
|
47
|
+
/** The session exists and is in the caller's org. */
|
|
48
|
+
visible: boolean;
|
|
49
|
+
/** The caller is the session's created_by. */
|
|
50
|
+
isOwner: boolean;
|
|
51
|
+
/** The caller is an org admin (only used for the durable_not_owner fix). */
|
|
52
|
+
callerIsAdmin: boolean;
|
|
53
|
+
durable_mode: DurableMode;
|
|
54
|
+
state: SessionState;
|
|
55
|
+
state_reason: StateReason | null;
|
|
56
|
+
effective_state: EffectiveState;
|
|
57
|
+
/** agents.containment_status of the session's agent. */
|
|
58
|
+
agentContainment: "active" | "paused" | "killed";
|
|
59
|
+
/** budget_raise only: the committed budget and the requested one (requested > committed; lowering is amend's). */
|
|
60
|
+
committedBudgetUsd?: number;
|
|
61
|
+
requestedBudgetUsd?: number;
|
|
62
|
+
}
|
|
63
|
+
export type OwnerVerbDecision = {
|
|
64
|
+
kind: "refuse";
|
|
65
|
+
row: 1 | 2 | 3 | 4 | 5 | 6 | 8;
|
|
66
|
+
status: number;
|
|
67
|
+
code: DurableCode | "agent_not_found";
|
|
68
|
+
fix: FixKey | null;
|
|
69
|
+
body?: Record<string, unknown>;
|
|
70
|
+
} | {
|
|
71
|
+
kind: "noop";
|
|
72
|
+
row: 7;
|
|
73
|
+
status: 200;
|
|
74
|
+
fix: FixKey | null;
|
|
75
|
+
} | {
|
|
76
|
+
kind: "device_step";
|
|
77
|
+
status: 202;
|
|
78
|
+
subject: "unlock" | "unpause_contained" | "budget_raise";
|
|
79
|
+
} | {
|
|
80
|
+
kind: "apply";
|
|
81
|
+
status: 200;
|
|
82
|
+
to: {
|
|
83
|
+
state: SessionState | "tracked";
|
|
84
|
+
state_reason: StateReason | null;
|
|
85
|
+
} | "unchanged";
|
|
86
|
+
event: DurableEventKind | null;
|
|
87
|
+
epochBump: boolean;
|
|
88
|
+
/** Lease nonce, executor and attach_until cleared; earlier-epoch keys fenced (revoked_at stays NULL). */
|
|
89
|
+
clearsLease: boolean;
|
|
90
|
+
/** resume_failures := 0 in the same transaction (owner resume from suspended(respawn_failed|attestation_failed), BACKBONE claim rules, C20). */
|
|
91
|
+
resetsResumeFailures: boolean;
|
|
92
|
+
/** refs.change of a charter.amended event. */
|
|
93
|
+
change?: "budget_raise" | "lowering";
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The catalogue fix for a bare row-8 refusal (wave-0 fix ruling 4). Locked rows key on the
|
|
97
|
+
* EFFECTIVE state through STOPPED_FIX (so a lapsed grant over any stored state gets grant_expired)
|
|
98
|
+
* and are never null: budget_raise on paused(owner) gets budget_while_paused, every other locked
|
|
99
|
+
* row-8 the stopped line for its state. A locked row 8 only arises on a stopped effective state;
|
|
100
|
+
* any other state, or an effective state the stored row cannot derive, throws. Tracked rows keep
|
|
101
|
+
* their tracked lines.
|
|
102
|
+
*/
|
|
103
|
+
export declare function invalidTransitionFix(state: SessionState, reason: StateReason | null, effective: EffectiveState, durableMode: DurableMode, verb: OwnerTableVerb, committedBudgetUsd?: number): FixKey | null;
|
|
104
|
+
/** The (from_state, state_reason) a row-8 body reports: the effective pair for a lapsed grant. */
|
|
105
|
+
export declare function row8FromPair(state: SessionState, reason: StateReason | null, effective: EffectiveState): {
|
|
106
|
+
from_state: SessionState;
|
|
107
|
+
state_reason: StateReason | null;
|
|
108
|
+
};
|
|
109
|
+
export declare function ownerVerbDecision(i: OwnerVerbInput): OwnerVerbDecision;
|
|
110
|
+
/**
|
|
111
|
+
* The old contract route (POST /v1/agent/sessions/{cid}/contract) and MCP update_contract on a
|
|
112
|
+
* LOCKED session (C07-R08; wave-0 fix ruling 1.20, Wassim option 1): a lowering is applied as a
|
|
113
|
+
* chained charter amend, exactly `amendDecision` (charter.amended {change:'lowering'}); every raise
|
|
114
|
+
* or loosening is 409 durable_amend_raise_refused {field}. The gateway picks the new charter up
|
|
115
|
+
* lazily, on the next request (C07-R06); nothing is pushed. Constants only here; the route
|
|
116
|
+
* behaviour lands in Wave 1.
|
|
117
|
+
*/
|
|
118
|
+
export declare const OLD_ROUTE_BRANCH: "amend";
|
|
119
|
+
export declare const OLD_ROUTE_REFRESH: "lazy";
|
|
120
|
+
/** The charter field an amend raises budget through; every other field can only be lowered. */
|
|
121
|
+
export declare const AMEND_BUDGET_FIELD = "budget";
|
|
122
|
+
/**
|
|
123
|
+
* One field an amend names, with its direction ("loosen" covers goal-text edits and widened paths;
|
|
124
|
+
* "equal" is a field set to the value it already has).
|
|
125
|
+
*/
|
|
126
|
+
export interface AmendChange {
|
|
127
|
+
field: string;
|
|
128
|
+
direction: "raise" | "lower" | "loosen" | "equal";
|
|
129
|
+
}
|
|
130
|
+
export interface AmendInput extends Omit<OwnerVerbInput, "verb"> {
|
|
131
|
+
/** The fields this amend changes; at least one. A budget raise also needs committed/requestedBudgetUsd. */
|
|
132
|
+
changes: readonly AmendChange[];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The owner `amend` verb (BACKBONE owner-verb table; C07-R07, C16-R04, C11 final F04). First match:
|
|
136
|
+
*
|
|
137
|
+
* rows 1-4 as for every verb (rows 5 and 6 are resume- and pause-only, so never match amend)
|
|
138
|
+
* row 7 every change is "equal" (the new value is the current one) -> 200 {changed:false},
|
|
139
|
+
* nothing journaled, in either mode; "equal" changes beside others are ignored
|
|
140
|
+
* raise-refused any non-budget raise or loosening, alone or mixed with a budget raise
|
|
141
|
+
* -> 409 durable_amend_raise_refused {field} (the first such field); refused whole
|
|
142
|
+
* budget raise -> exactly the budget_raise row (tracked line, raisable states, 422, device step)
|
|
143
|
+
* tracked a lowering on a tracked session -> 409 durable_invalid_transition with the tracked budget line
|
|
144
|
+
* lowering -> 200 applied in place: charter.amended {change:'lowering'}, no state, epoch or lease change
|
|
145
|
+
*/
|
|
146
|
+
export declare function amendDecision(i: AmendInput): OwnerVerbDecision;
|
|
147
|
+
/**
|
|
148
|
+
* What an applied owner_mutation does on consume (C11-R07, R17, R36; C16-R29). budget_raise from
|
|
149
|
+
* suspended(budget_reached) parks; elsewhere it raises the ceiling in place.
|
|
150
|
+
*/
|
|
151
|
+
export declare function applyDeviceStep(subject: "unlock" | "unpause_contained" | "budget_raise", state: SessionState, reason: StateReason | null): Extract<OwnerVerbDecision, {
|
|
152
|
+
kind: "apply";
|
|
153
|
+
}>;
|
|
154
|
+
/**
|
|
155
|
+
* Revocation table for pre-bump keys (rulings I03, r2 I01): which transitions REVOKE (revoked_at
|
|
156
|
+
* set, a pre-bump key then answers 401 durable_key_revoked) versus only fence (revoked_at stays
|
|
157
|
+
* NULL; the key answers 410 durable_session_ended or 409 durable_lease_fenced). Exactly four
|
|
158
|
+
* transitions revoke, each in the same transaction as its epoch bump (a fault between the bump
|
|
159
|
+
* and the revoke rolls back both). The M1 sweep never writes revoked_at.
|
|
160
|
+
*/
|
|
161
|
+
export declare const KEY_EFFECT_OF_TRANSITION: {
|
|
162
|
+
readonly pause: "fence";
|
|
163
|
+
readonly complete: "fence";
|
|
164
|
+
readonly kill: "fence";
|
|
165
|
+
readonly expire: "fence";
|
|
166
|
+
readonly fail: "fence";
|
|
167
|
+
readonly containment: "fence";
|
|
168
|
+
readonly unlock: "revoke";
|
|
169
|
+
readonly claim_supersede: "revoke";
|
|
170
|
+
readonly executor_revoke: "revoke";
|
|
171
|
+
readonly respawn_failed_suspend: "revoke";
|
|
172
|
+
};
|
|
173
|
+
export type KeyTransition = keyof typeof KEY_EFFECT_OF_TRANSITION;
|
|
174
|
+
/** The four revoking transitions, in ruling order (I03). */
|
|
175
|
+
export declare const REVOKING_TRANSITIONS: readonly ["claim_supersede", "executor_revoke", "unlock", "respawn_failed_suspend"];
|