@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,533 @@
|
|
|
1
|
+
// GENERATED from shared/durable-contract/src/admission.ts by cli/scripts/sync-durable-contract.mjs; do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* Gateway admission for mnm_ds_ keys: one total pure function over the resolver's `durable`
|
|
4
|
+
* object plus the request pin and posture (design §3.6 steps 1-4; C06-R14/R15; C08-R13/R14;
|
|
5
|
+
* C09-R08/R11; C11-R33; rulings I03, I12, I20, r3 I01). Data, not branches, wherever possible:
|
|
6
|
+
* ADMISSION_PRECEDENCE is the order the oracle reads, and `admissionTruthTable()` enumerates the
|
|
7
|
+
* cross product the eval compares against.
|
|
8
|
+
*
|
|
9
|
+
* Precedence, first match wins:
|
|
10
|
+
* 1 unknown or invalid key -> the baseline unknown-key answer for the path and mode
|
|
11
|
+
* 2 resolver fault or bad schema -> 503 durable_admission_unavailable
|
|
12
|
+
* 3 key_state revoked -> 401 durable_key_revoked
|
|
13
|
+
* 4 pin absent or mismatched -> 409 durable_identity_mismatch
|
|
14
|
+
* 5 durable_mode tracked -> 410 durable_unlocked
|
|
15
|
+
* 6 effective terminal -> 410 durable_session_ended {state, state_reason}
|
|
16
|
+
* 7 fenced (unless nofence) -> 409 durable_lease_fenced
|
|
17
|
+
* 8 paused or non-admitting suspended -> 423 durable_session_stopped {state, state_reason}
|
|
18
|
+
* 9 posture: attended admits; unattended -> 503 durable_guard_unavailable on a guard fault,
|
|
19
|
+
* else 503 durable_unattended_unavailable (M1 refuses all unattended work).
|
|
20
|
+
* The one exception (wave-0 fix ruling 22): a POST /v1/messages/count_tokens with no chain= (header
|
|
21
|
+
* absent, or no chain on the newest entry) runs steps 1-8 and skips step 9: it is admitted as
|
|
22
|
+
* admit_unchained, with no chain row and no posture insert. Every other request is unchanged.
|
|
23
|
+
* Hello transition: only an admitted attended request moves an effectively resuming session to
|
|
24
|
+
* running (helloTransition). admit_unchained never does and writes nothing (a resuming session
|
|
25
|
+
* stays resuming).
|
|
26
|
+
* The door (normalizeCellId(CELL_ID) vs home_cell) is diagnostic only in M1.
|
|
27
|
+
* An attended guard fault is admitted (today's behaviour) and reported (C08-R14).
|
|
28
|
+
*/
|
|
29
|
+
import { canonicalJson, sha256Hex } from "./canonical.js";
|
|
30
|
+
import { deriveEffectiveState, EFFECTIVE_STATES, isEffectiveStateConsistent, isValidStateReason, endedBody, SESSION_STATES, STATE_REASONS, } from "./states.js";
|
|
31
|
+
import { NOFENCE_ADMIT_VALUE } from "./constants.js";
|
|
32
|
+
import { CANONICAL_CELL_IDS } from "./cell-id.js";
|
|
33
|
+
export const KEY_STATES = ["active", "revoked"];
|
|
34
|
+
export const POSTURES = ["attended", "unattended"];
|
|
35
|
+
export const EXECUTOR_KINDS = ["host", "fleet", "container"];
|
|
36
|
+
export const OUTAGE_MODES = ["stop", "continue"];
|
|
37
|
+
/**
|
|
38
|
+
* The resolver per-field schema. A missing field, wrong type, unknown enum value, or null on a
|
|
39
|
+
* non-nullable field is a schema fault: 503 durable_admission_unavailable, never a throw.
|
|
40
|
+
*/
|
|
41
|
+
export const RESOLVER_SCHEMA = {
|
|
42
|
+
key_state: { type: "enum", nullable: false, values: KEY_STATES },
|
|
43
|
+
session_id: { type: "uuid", nullable: false },
|
|
44
|
+
aip_session_id: { type: "string", nullable: false },
|
|
45
|
+
durable_mode: { type: "enum", nullable: false, values: ["tracked", "locked"] },
|
|
46
|
+
state: { type: "enum", nullable: false, values: SESSION_STATES },
|
|
47
|
+
effective_state: { type: "enum", nullable: false, values: EFFECTIVE_STATES },
|
|
48
|
+
state_reason: {
|
|
49
|
+
type: "string",
|
|
50
|
+
nullable: true,
|
|
51
|
+
nullMeans: "no reason (running, parked, resuming, owner kill)",
|
|
52
|
+
},
|
|
53
|
+
epoch: { type: "integer", nullable: false },
|
|
54
|
+
fenced: { type: "boolean", nullable: false },
|
|
55
|
+
agent_id: { type: "uuid", nullable: false },
|
|
56
|
+
home_cell: { type: "enum", nullable: false, values: CANONICAL_CELL_IDS },
|
|
57
|
+
posture: {
|
|
58
|
+
type: "enum",
|
|
59
|
+
nullable: true,
|
|
60
|
+
values: POSTURES,
|
|
61
|
+
nullMeans: "no chain row yet: unattended",
|
|
62
|
+
},
|
|
63
|
+
executor_kind: { type: "enum", nullable: false, values: EXECUTOR_KINDS },
|
|
64
|
+
host_unattended: { type: "boolean", nullable: false },
|
|
65
|
+
unattended_enabled: { type: "boolean", nullable: false },
|
|
66
|
+
grant_live: { type: "boolean", nullable: false },
|
|
67
|
+
expires_at: { type: "timestamp", nullable: true, nullMeans: "never expires (always NULL in M1)" },
|
|
68
|
+
lease_expires_at: {
|
|
69
|
+
type: "timestamp",
|
|
70
|
+
nullable: true,
|
|
71
|
+
nullMeans: "lease cleared by pause, kill, revoke or unlock",
|
|
72
|
+
},
|
|
73
|
+
lease_executor_id: { type: "uuid", nullable: true, nullMeans: "lease cleared" },
|
|
74
|
+
attach_until: { type: "timestamp", nullable: true, nullMeans: "no attach (P0b)" },
|
|
75
|
+
attach_live: { type: "boolean", nullable: true, nullMeans: "not applicable on a host executor" },
|
|
76
|
+
sealed: { type: "boolean", nullable: false },
|
|
77
|
+
outage_mode: { type: "enum", nullable: true, values: OUTAGE_MODES, nullMeans: "no opt-in: stop" },
|
|
78
|
+
charter_hash: { type: "string", nullable: true, nullMeans: "tracked: no charter" },
|
|
79
|
+
guard_state: { type: "object", nullable: true, nullMeans: "guard fault for a locked session" },
|
|
80
|
+
has_pointer: { type: "boolean", nullable: false },
|
|
81
|
+
};
|
|
82
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
83
|
+
const TS_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|[+-]\d{2}(:?\d{2})?)$/;
|
|
84
|
+
/** One resolver field against its spec (pure; exported for its direct test). */
|
|
85
|
+
export function fieldOk(spec, v) {
|
|
86
|
+
if (v === null)
|
|
87
|
+
return spec.nullable;
|
|
88
|
+
switch (spec.type) {
|
|
89
|
+
case "uuid":
|
|
90
|
+
return typeof v === "string" && UUID_RE.test(v);
|
|
91
|
+
case "string":
|
|
92
|
+
return typeof v === "string" && v.length > 0;
|
|
93
|
+
case "boolean":
|
|
94
|
+
return typeof v === "boolean";
|
|
95
|
+
case "integer":
|
|
96
|
+
return Number.isSafeInteger(v) && v >= 0;
|
|
97
|
+
case "timestamp":
|
|
98
|
+
return typeof v === "string" && TS_RE.test(v);
|
|
99
|
+
case "object":
|
|
100
|
+
return typeof v === "object" && !Array.isArray(v);
|
|
101
|
+
case "enum":
|
|
102
|
+
return typeof v === "string" && (spec.values ?? []).includes(v);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validate a resolver answer. Returns the typed object, or null for any schema fault (missing
|
|
107
|
+
* field, wrong type, unknown enum, null on non-nullable, an illegal state_reason for the state, an
|
|
108
|
+
* effective state the derivation cannot produce, or a locked row with no charter).
|
|
109
|
+
*/
|
|
110
|
+
export function parseResolverDurable(raw) {
|
|
111
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
112
|
+
return null;
|
|
113
|
+
const r = raw;
|
|
114
|
+
for (const [field, spec] of Object.entries(RESOLVER_SCHEMA)) {
|
|
115
|
+
if (!(field in r) || !fieldOk(spec, r[field]))
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const d = r;
|
|
119
|
+
// state and effective_state are already closed enums in RESOLVER_SCHEMA (checked above).
|
|
120
|
+
if (!isValidStateReason(d.state, d.state_reason))
|
|
121
|
+
return null;
|
|
122
|
+
if (!isEffectiveStateConsistent(d.state, d.state_reason, d.effective_state))
|
|
123
|
+
return null;
|
|
124
|
+
if (d.durable_mode === "locked" && d.charter_hash === null)
|
|
125
|
+
return null;
|
|
126
|
+
return d;
|
|
127
|
+
}
|
|
128
|
+
/** Whether a locked session's guard_state is usable (C08-R13): an object carrying numeric spend_usd. */
|
|
129
|
+
export function guardStateOk(guard) {
|
|
130
|
+
return (guard !== null &&
|
|
131
|
+
typeof guard.spend_usd === "number" &&
|
|
132
|
+
Number.isFinite(guard.spend_usd) &&
|
|
133
|
+
guard.spend_usd >= 0);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Canonical guard_state fixtures for the case datasets (`$S1.GUARD_STATE_OK` / `$S1.GUARD_STATE_FAULT`,
|
|
137
|
+
* written as jsonb): OK passes guardStateOk; FAULT is a present object of the wrong shape (spend_usd
|
|
138
|
+
* not a number), the C08-R13 / claim check 7 fault, never null so it exercises the shape check.
|
|
139
|
+
*/
|
|
140
|
+
export const GUARD_STATE_OK = Object.freeze({ spend_usd: 0 });
|
|
141
|
+
export const GUARD_STATE_FAULT = Object.freeze({
|
|
142
|
+
spend_usd: "unparseable",
|
|
143
|
+
});
|
|
144
|
+
/** Whether DURABLE_ADMIT is honoured: only the exact bytes "nofence" (NOFENCE_M1, r3 I01). */
|
|
145
|
+
export function nofenceHonoured(durableAdmit) {
|
|
146
|
+
return durableAdmit === NOFENCE_ADMIT_VALUE;
|
|
147
|
+
}
|
|
148
|
+
/** The order the oracle reads (C11-R33). */
|
|
149
|
+
export const ADMISSION_PRECEDENCE = [
|
|
150
|
+
"unknown_key",
|
|
151
|
+
"resolver_fault",
|
|
152
|
+
"revoked",
|
|
153
|
+
"identity",
|
|
154
|
+
"tracked",
|
|
155
|
+
"terminal",
|
|
156
|
+
"fenced",
|
|
157
|
+
"stopped",
|
|
158
|
+
"posture",
|
|
159
|
+
];
|
|
160
|
+
export const ADMISSION_STEP_CODE = {
|
|
161
|
+
resolver_fault: "durable_admission_unavailable",
|
|
162
|
+
revoked: "durable_key_revoked",
|
|
163
|
+
identity: "durable_identity_mismatch",
|
|
164
|
+
tracked: "durable_unlocked",
|
|
165
|
+
terminal: "durable_session_ended",
|
|
166
|
+
fenced: "durable_lease_fenced",
|
|
167
|
+
stopped: "durable_session_stopped",
|
|
168
|
+
};
|
|
169
|
+
/** Persisted or effective states that admit an attended chain (design §3.6 step 4 table). */
|
|
170
|
+
export function attendedAdmits(effective, reason) {
|
|
171
|
+
switch (effective) {
|
|
172
|
+
case "running":
|
|
173
|
+
case "resuming":
|
|
174
|
+
case "detached":
|
|
175
|
+
case "parked":
|
|
176
|
+
case "suspended_grant_expired":
|
|
177
|
+
return true;
|
|
178
|
+
case "suspended":
|
|
179
|
+
return reason === "budget_reached" || reason === "grant_expired";
|
|
180
|
+
default:
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** The count_tokens route (wave-0 fix ruling 22), path without a query string, as the proxy sends it upstream. */
|
|
185
|
+
export const COUNT_TOKENS_PATH = "/v1/messages/count_tokens";
|
|
186
|
+
/** The same route at the gateway door (provider-prefixed). */
|
|
187
|
+
export const GATEWAY_COUNT_TOKENS_PATH = "/anthropic/v1/messages/count_tokens";
|
|
188
|
+
/** Whether a path (no query string) is the count_tokens route, bare or at the gateway door. */
|
|
189
|
+
export function isCountTokensPath(path) {
|
|
190
|
+
return path === COUNT_TOKENS_PATH || path === GATEWAY_COUNT_TOKENS_PATH;
|
|
191
|
+
}
|
|
192
|
+
/** Whether a request skips the posture step: POST /v1/messages/count_tokens with no chain (ruling 22). */
|
|
193
|
+
export function isUnchainedCountTokens(r) {
|
|
194
|
+
return r.method === "POST" && isCountTokensPath(r.path) && !r.chain;
|
|
195
|
+
}
|
|
196
|
+
export const RESOLVER_FAULT = "fault";
|
|
197
|
+
/** The admission function. Total: every input maps to exactly one verdict; it never throws. */
|
|
198
|
+
export function admit(input) {
|
|
199
|
+
const fenceSource = nofenceHonoured(input.durableAdmit) ? "nofence" : "fresh";
|
|
200
|
+
const posture = input.posture ?? "unattended";
|
|
201
|
+
if (input.resolver === null)
|
|
202
|
+
return { kind: "baseline_unknown_key", step: "unknown_key" };
|
|
203
|
+
const d = input.resolver === RESOLVER_FAULT ? null : parseResolverDurable(input.resolver);
|
|
204
|
+
const refuse = (step, extra = {}) => ({
|
|
205
|
+
kind: "refuse",
|
|
206
|
+
step,
|
|
207
|
+
code: ADMISSION_STEP_CODE[step],
|
|
208
|
+
posture,
|
|
209
|
+
fenceSource,
|
|
210
|
+
...extra,
|
|
211
|
+
});
|
|
212
|
+
if (d === null)
|
|
213
|
+
return refuse("resolver_fault");
|
|
214
|
+
if (d.key_state === "revoked")
|
|
215
|
+
return refuse("revoked");
|
|
216
|
+
if (input.pinAgentId === null || input.pinAgentId !== d.agent_id)
|
|
217
|
+
return refuse("identity");
|
|
218
|
+
if (d.durable_mode === "tracked")
|
|
219
|
+
return refuse("tracked");
|
|
220
|
+
const ended = endedBody(d.effective_state, d.state_reason);
|
|
221
|
+
if (ended !== null)
|
|
222
|
+
return refuse("terminal", { state: ended.state, state_reason: ended.state_reason });
|
|
223
|
+
if (d.fenced && fenceSource === "fresh")
|
|
224
|
+
return refuse("fenced");
|
|
225
|
+
if (!attendedAdmits(d.effective_state, d.state_reason)) {
|
|
226
|
+
return refuse("stopped", {
|
|
227
|
+
state: d.effective_state,
|
|
228
|
+
state_reason: d.state_reason,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const guardFault = !guardStateOk(d.guard_state);
|
|
232
|
+
if (input.request !== undefined && isUnchainedCountTokens(input.request))
|
|
233
|
+
return {
|
|
234
|
+
kind: "admit_unchained",
|
|
235
|
+
step: "admit",
|
|
236
|
+
posture: null,
|
|
237
|
+
guardFault,
|
|
238
|
+
fenceSource,
|
|
239
|
+
helloTransition: false,
|
|
240
|
+
writes: false,
|
|
241
|
+
};
|
|
242
|
+
if (posture === "unattended") {
|
|
243
|
+
return {
|
|
244
|
+
kind: "refuse",
|
|
245
|
+
step: "posture",
|
|
246
|
+
code: guardFault ? "durable_guard_unavailable" : "durable_unattended_unavailable",
|
|
247
|
+
posture,
|
|
248
|
+
fenceSource,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
kind: "admit",
|
|
253
|
+
step: "admit",
|
|
254
|
+
posture: "attended",
|
|
255
|
+
guardFault,
|
|
256
|
+
fenceSource,
|
|
257
|
+
helloTransition: d.effective_state === "resuming",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
// ---- Truth table (data the eval diffs against its own oracle) ---------------------------------
|
|
261
|
+
export const ADMISSION_REQUEST_KINDS = ["standard", "count_tokens_unchained"];
|
|
262
|
+
const REQUEST_OF = {
|
|
263
|
+
standard: { method: "POST", path: "/v1/messages", chain: false },
|
|
264
|
+
count_tokens_unchained: { method: "POST", path: COUNT_TOKENS_PATH, chain: false },
|
|
265
|
+
};
|
|
266
|
+
/** HTTP status of a truth-table outcome (null = the baseline unknown-key answer); throws on an unknown outcome. */
|
|
267
|
+
export function admissionStatusOf(outcome) {
|
|
268
|
+
if (!Object.prototype.hasOwnProperty.call(STATUS, outcome))
|
|
269
|
+
throw new RangeError(`unknown admission outcome: ${outcome}`);
|
|
270
|
+
return STATUS[outcome];
|
|
271
|
+
}
|
|
272
|
+
const STATUS = {
|
|
273
|
+
admit: 200,
|
|
274
|
+
admit_unchained: 200,
|
|
275
|
+
baseline_unknown_key: null,
|
|
276
|
+
durable_admission_unavailable: 503,
|
|
277
|
+
durable_key_revoked: 401,
|
|
278
|
+
durable_identity_mismatch: 409,
|
|
279
|
+
durable_unlocked: 410,
|
|
280
|
+
durable_session_ended: 410,
|
|
281
|
+
durable_lease_fenced: 409,
|
|
282
|
+
durable_session_stopped: 423,
|
|
283
|
+
durable_guard_unavailable: 503,
|
|
284
|
+
durable_unattended_unavailable: 503,
|
|
285
|
+
};
|
|
286
|
+
const FIXTURE_AGENT = "00000000-0000-4000-8000-000000000001";
|
|
287
|
+
const OTHER_AGENT = "00000000-0000-4000-8000-000000000002";
|
|
288
|
+
/** A well-formed resolver object for a row of the truth table. */
|
|
289
|
+
export function fixtureResolver(over = {}) {
|
|
290
|
+
return {
|
|
291
|
+
key_state: "active",
|
|
292
|
+
session_id: "00000000-0000-4000-8000-0000000000aa",
|
|
293
|
+
aip_session_id: "smolt-0000aaaa-c00000000000000000000000000000000",
|
|
294
|
+
durable_mode: "locked",
|
|
295
|
+
state: "running",
|
|
296
|
+
effective_state: "running",
|
|
297
|
+
state_reason: null,
|
|
298
|
+
epoch: 1,
|
|
299
|
+
fenced: false,
|
|
300
|
+
agent_id: FIXTURE_AGENT,
|
|
301
|
+
home_cell: "test",
|
|
302
|
+
posture: "attended",
|
|
303
|
+
executor_kind: "host",
|
|
304
|
+
host_unattended: true,
|
|
305
|
+
unattended_enabled: false,
|
|
306
|
+
grant_live: true,
|
|
307
|
+
expires_at: null,
|
|
308
|
+
lease_expires_at: "2026-10-16T12:00:00.000000Z",
|
|
309
|
+
lease_executor_id: "00000000-0000-4000-8000-0000000000ee",
|
|
310
|
+
attach_until: null,
|
|
311
|
+
attach_live: null,
|
|
312
|
+
sealed: true,
|
|
313
|
+
outage_mode: null,
|
|
314
|
+
charter_hash: "a".repeat(64),
|
|
315
|
+
guard_state: { spend_usd: 0 },
|
|
316
|
+
has_pointer: false,
|
|
317
|
+
...over,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/** Every (persisted state, reason) pair, with its persisted effective state and the derived ones. */
|
|
321
|
+
export function stateRows() {
|
|
322
|
+
const rows = [];
|
|
323
|
+
for (const state of SESSION_STATES) {
|
|
324
|
+
for (const reason of STATE_REASONS[state]) {
|
|
325
|
+
const candidates = [
|
|
326
|
+
"resuming",
|
|
327
|
+
"running",
|
|
328
|
+
"detached",
|
|
329
|
+
"parked",
|
|
330
|
+
"suspended",
|
|
331
|
+
"suspended_grant_expired",
|
|
332
|
+
"paused",
|
|
333
|
+
"completed",
|
|
334
|
+
"failed",
|
|
335
|
+
"killed",
|
|
336
|
+
"expired",
|
|
337
|
+
];
|
|
338
|
+
for (const eff of candidates) {
|
|
339
|
+
if (isEffectiveStateConsistent(state, reason, eff))
|
|
340
|
+
rows.push({ state, state_reason: reason, effective_state: eff });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return rows;
|
|
345
|
+
}
|
|
346
|
+
/** The full cross product over the admission dimensions, each row decided by `admit`. */
|
|
347
|
+
export function admissionTruthTable() {
|
|
348
|
+
const out = [];
|
|
349
|
+
for (const s of stateRows()) {
|
|
350
|
+
for (const fenced of [false, true]) {
|
|
351
|
+
for (const key_state of KEY_STATES) {
|
|
352
|
+
for (const durable_mode of ["tracked", "locked"]) {
|
|
353
|
+
for (const posture of POSTURES) {
|
|
354
|
+
for (const guard_ok of [true, false]) {
|
|
355
|
+
for (const nofence of [false, true]) {
|
|
356
|
+
for (const pin of ["match", "absent", "mismatch"]) {
|
|
357
|
+
for (const request of ADMISSION_REQUEST_KINDS) {
|
|
358
|
+
const resolver = fixtureResolver({
|
|
359
|
+
...s,
|
|
360
|
+
fenced,
|
|
361
|
+
key_state,
|
|
362
|
+
durable_mode,
|
|
363
|
+
posture,
|
|
364
|
+
guard_state: guard_ok ? { spend_usd: 0 } : null,
|
|
365
|
+
});
|
|
366
|
+
const v = admit({
|
|
367
|
+
resolver,
|
|
368
|
+
pinAgentId: pin === "match" ? FIXTURE_AGENT : pin === "mismatch" ? OTHER_AGENT : null,
|
|
369
|
+
posture,
|
|
370
|
+
durableAdmit: nofence ? NOFENCE_ADMIT_VALUE : undefined,
|
|
371
|
+
request: REQUEST_OF[request],
|
|
372
|
+
});
|
|
373
|
+
const outcome = v.kind === "refuse" ? v.code : v.kind;
|
|
374
|
+
out.push({
|
|
375
|
+
...s,
|
|
376
|
+
fenced,
|
|
377
|
+
key_state,
|
|
378
|
+
durable_mode,
|
|
379
|
+
posture,
|
|
380
|
+
guard_ok,
|
|
381
|
+
nofence,
|
|
382
|
+
pin,
|
|
383
|
+
request,
|
|
384
|
+
hello_transition: v.kind === "admit" && v.helloTransition,
|
|
385
|
+
outcome,
|
|
386
|
+
status: admissionStatusOf(outcome),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return out;
|
|
398
|
+
}
|
|
399
|
+
// ---- admit sweep digest (rulings 24.11, 24.11.1; C06-SWEEP-001/002) ----------------------------
|
|
400
|
+
/** Golden vectors: a 3-dim toy sweep (every line) and C06 SWEEP_A (count, first 3 lines, digest). */
|
|
401
|
+
export const ADMIT_SWEEP_VECTORS_PATH = "src/vectors/admit-sweep-vectors.json";
|
|
402
|
+
/**
|
|
403
|
+
* The closed value set of each C06 sweep dimension (gen/fam-a.mjs SWEEP_A / SWEEP_B vocabulary).
|
|
404
|
+
* A null on a non-nullable resolver field reaches admit() as that null (a schema fault, 503).
|
|
405
|
+
* stored_state is "state" or "state:reason" ("null" for a NULL reason), state a SESSION_STATES value.
|
|
406
|
+
*/
|
|
407
|
+
export const ADMIT_SWEEP_VALUES = Object.freeze({
|
|
408
|
+
durable_mode: ["locked", "tracked", null],
|
|
409
|
+
key_state: ["active", "revoked", null],
|
|
410
|
+
fenced: [false, true, null],
|
|
411
|
+
pin: ["ok", "absent", "mismatch"],
|
|
412
|
+
chain_posture: ["attended", "unattended", null],
|
|
413
|
+
expires_past: [false, true],
|
|
414
|
+
grant_live: [true, false, null],
|
|
415
|
+
lease_expired: [false, true],
|
|
416
|
+
guard_state: ["ok", "fault"],
|
|
417
|
+
unattended_enabled: [false, true, null],
|
|
418
|
+
executor_kind: ["host", "fleet", "container", null],
|
|
419
|
+
outage_mode: [null, "stop", "continue"],
|
|
420
|
+
host_unattended: [true, false, null],
|
|
421
|
+
attach_live: [true, false, null],
|
|
422
|
+
});
|
|
423
|
+
/** The sweep clock: expires_past / lease_expired place the timestamp an hour before or after it. */
|
|
424
|
+
const SWEEP_NOW = "2026-10-16T12:00:00.000000Z";
|
|
425
|
+
const SWEEP_PAST = "2026-10-16T11:00:00.000000Z";
|
|
426
|
+
const SWEEP_FUTURE = "2026-10-16T13:00:00.000000Z";
|
|
427
|
+
function parseStoredState(v) {
|
|
428
|
+
if (typeof v !== "string")
|
|
429
|
+
throw new RangeError(`stored_state: unknown value ${JSON.stringify(v)}`);
|
|
430
|
+
const i = v.indexOf(":");
|
|
431
|
+
const state = i < 0 ? v : v.slice(0, i);
|
|
432
|
+
const reason = i < 0 || v.slice(i + 1) === "null" ? null : v.slice(i + 1);
|
|
433
|
+
if (!SESSION_STATES.includes(state))
|
|
434
|
+
throw new RangeError(`stored_state: unknown value ${JSON.stringify(v)}`);
|
|
435
|
+
return { state, state_reason: reason };
|
|
436
|
+
}
|
|
437
|
+
function checkSweep(sweep) {
|
|
438
|
+
if (!Array.isArray(sweep?.dims))
|
|
439
|
+
throw new TypeError(`admit sweep ${String(sweep?.name)}: dims must be an array of {name, values}`);
|
|
440
|
+
const seen = new Set();
|
|
441
|
+
for (const d of sweep.dims) {
|
|
442
|
+
const name = d?.name;
|
|
443
|
+
if (name !== "stored_state" && !Object.prototype.hasOwnProperty.call(ADMIT_SWEEP_VALUES, name))
|
|
444
|
+
throw new RangeError(`unknown sweep dimension ${String(name)}`);
|
|
445
|
+
if (seen.has(name))
|
|
446
|
+
throw new RangeError(`repeated sweep dimension ${name}`);
|
|
447
|
+
seen.add(name);
|
|
448
|
+
if (!Array.isArray(d.values) || d.values.length === 0)
|
|
449
|
+
throw new RangeError(`dimension ${name} must be a non-empty array`);
|
|
450
|
+
for (const x of d.values) {
|
|
451
|
+
if (name === "stored_state")
|
|
452
|
+
parseStoredState(x);
|
|
453
|
+
else if (!ADMIT_SWEEP_VALUES[name].includes(x))
|
|
454
|
+
throw new RangeError(`dimension ${name}: unknown value ${JSON.stringify(x)}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/** One tuple's verdict line: canonicalJson({code, state_reason, status}), absent fields null. */
|
|
459
|
+
function sweepLine(t) {
|
|
460
|
+
const stored = parseStoredState(t.stored_state ?? "running");
|
|
461
|
+
const has = (k) => Object.prototype.hasOwnProperty.call(t, k);
|
|
462
|
+
const expires_at = t.expires_past === true ? SWEEP_PAST : null;
|
|
463
|
+
const lease_expires_at = t.lease_expired === true ? SWEEP_PAST : SWEEP_FUTURE;
|
|
464
|
+
const grant_live = has("grant_live") ? t.grant_live : true;
|
|
465
|
+
const effective_state = deriveEffectiveState({
|
|
466
|
+
state: stored.state,
|
|
467
|
+
state_reason: stored.state_reason,
|
|
468
|
+
expires_at,
|
|
469
|
+
grant_live: grant_live === true,
|
|
470
|
+
lease_expires_at,
|
|
471
|
+
}, Date.parse(SWEEP_NOW));
|
|
472
|
+
const posture = (has("chain_posture") ? t.chain_posture : "attended");
|
|
473
|
+
const over = {
|
|
474
|
+
state: stored.state,
|
|
475
|
+
state_reason: stored.state_reason,
|
|
476
|
+
effective_state,
|
|
477
|
+
expires_at,
|
|
478
|
+
lease_expires_at,
|
|
479
|
+
grant_live,
|
|
480
|
+
posture,
|
|
481
|
+
guard_state: t.guard_state === "fault" ? GUARD_STATE_FAULT : GUARD_STATE_OK,
|
|
482
|
+
};
|
|
483
|
+
for (const k of [
|
|
484
|
+
"durable_mode",
|
|
485
|
+
"key_state",
|
|
486
|
+
"fenced",
|
|
487
|
+
"unattended_enabled",
|
|
488
|
+
"executor_kind",
|
|
489
|
+
"outage_mode",
|
|
490
|
+
"host_unattended",
|
|
491
|
+
"attach_live",
|
|
492
|
+
])
|
|
493
|
+
if (has(k))
|
|
494
|
+
over[k] = t[k];
|
|
495
|
+
const pin = t.pin ?? "ok";
|
|
496
|
+
const v = admit({
|
|
497
|
+
resolver: fixtureResolver(over),
|
|
498
|
+
pinAgentId: pin === "ok" ? FIXTURE_AGENT : pin === "mismatch" ? OTHER_AGENT : null,
|
|
499
|
+
posture,
|
|
500
|
+
durableAdmit: undefined,
|
|
501
|
+
});
|
|
502
|
+
const code = v.kind === "refuse" ? v.code : null;
|
|
503
|
+
const state_reason = v.kind === "refuse" ? (v.state_reason ?? null) : null;
|
|
504
|
+
const status = admissionStatusOf(v.kind === "refuse" ? v.code : v.kind);
|
|
505
|
+
return canonicalJson({ code, state_reason, status });
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* The sweep's verdict text: an odometer over `dims` (first entry slowest, values in array order),
|
|
509
|
+
* one sweepLine per tuple, each followed by LF (also after the last). Throws on a bad sweep.
|
|
510
|
+
*/
|
|
511
|
+
export function admitSweepText(sweep) {
|
|
512
|
+
checkSweep(sweep);
|
|
513
|
+
const dims = sweep.dims;
|
|
514
|
+
const idx = dims.map(() => 0);
|
|
515
|
+
const out = [];
|
|
516
|
+
for (;;) {
|
|
517
|
+
const t = {};
|
|
518
|
+
dims.forEach((d, j) => {
|
|
519
|
+
t[d.name] = d.values[idx[j]];
|
|
520
|
+
});
|
|
521
|
+
out.push(sweepLine(t), "\n");
|
|
522
|
+
let j = dims.length - 1;
|
|
523
|
+
while (j >= 0 && ++idx[j] === dims[j].values.length)
|
|
524
|
+
idx[j--] = 0;
|
|
525
|
+
if (j < 0)
|
|
526
|
+
return { tuples: out.length / 2, text: out.join("") };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/** verdicts_sha256: lowercase hex sha256 of the UTF-8 bytes of admitSweepText(sweep).text. */
|
|
530
|
+
export async function admitSweepDigest(sweep) {
|
|
531
|
+
const { tuples, text } = admitSweepText(sweep);
|
|
532
|
+
return { tuples, verdicts_sha256: await sha256Hex(text) };
|
|
533
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The x-mnemom-durable-arm header grammar (design §4 I2; BACKBONE G1/C3; C06-R23, C13).
|
|
3
|
+
*
|
|
4
|
+
* <ver>;hold=on|off;fence=on|off;posture=attended|unattended|none
|
|
5
|
+
*
|
|
6
|
+
* posture=none (wave-0 fix ruling 22.1) is the arm of an admit_unchained answer (a count_tokens
|
|
7
|
+
* with no chain): valid only on a count_tokens response (armValidFor); on any other path it is
|
|
8
|
+
* invalid and drops, and armForwards (no path) never forwards it.
|
|
9
|
+
*
|
|
10
|
+
* Frozen strictness (C13 OQ, "anchored exact grammar, fixed key order, lowercase, single header
|
|
11
|
+
* line"): ver is the literal `1`; keys in exactly this order; lowercase; no whitespace anywhere;
|
|
12
|
+
* no trailing `;`. A value carrying `,` (Node's join of duplicate header lines) is unparseable, so
|
|
13
|
+
* duplicate lines (conflicting or not) drop the response, as do a missing header and a trailer.
|
|
14
|
+
* The same bytes are the body of GET /internal/durable/arm.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ARM_HEADER = "x-mnemom-durable-arm";
|
|
17
|
+
export declare const ARM_VERSION = "1";
|
|
18
|
+
export declare const ARM_ROUTE = "/internal/durable/arm";
|
|
19
|
+
export interface ArmState {
|
|
20
|
+
hold: "on" | "off";
|
|
21
|
+
fence: "on" | "off";
|
|
22
|
+
posture: "attended" | "unattended" | "none";
|
|
23
|
+
}
|
|
24
|
+
export declare const ARM_RE: RegExp;
|
|
25
|
+
export declare function formatArm(a: ArmState): string;
|
|
26
|
+
/** Parse one header value; null when absent or not in the exact grammar. */
|
|
27
|
+
export declare function parseArm(value: string | null | undefined): ArmState | null;
|
|
28
|
+
/**
|
|
29
|
+
* The executor/proxy decision (C3, C13): forward only a parseable header, and never an unattended
|
|
30
|
+
* one without hold=on and fence=on. In M1 hold is always off, so every unattended answer drops.
|
|
31
|
+
*/
|
|
32
|
+
export declare function armForwards(value: string | null | undefined): boolean;
|
|
33
|
+
/** The arm of a response on `path` (no query string), or null when absent, unparseable, or posture=none off count_tokens. */
|
|
34
|
+
export declare function armValidFor(path: string, value: string | null | undefined): ArmState | null;
|
|
35
|
+
/** The C3 decision on `path`: posture=none forwards on count_tokens only; every other arm as armForwards. */
|
|
36
|
+
export declare function armForwardsFor(path: string, value: string | null | undefined): boolean;
|
|
37
|
+
/** The M1 gateway's arm: hold=off always (no hold code), fence=off only for the exact bytes `nofence`. */
|
|
38
|
+
export declare function m1Arm(durableAdmit: string | undefined, posture: ArmState["posture"]): ArmState;
|
|
39
|
+
/** The proxy's local drop answer when the arm check fails (frozen template, zero upstream bytes). */
|
|
40
|
+
export declare const ARM_DROP_STATUS = 502;
|
|
41
|
+
export declare const ARM_DROP_HEADERS: Readonly<{
|
|
42
|
+
"content-type": "application/json";
|
|
43
|
+
"x-should-retry": "false";
|
|
44
|
+
}>;
|
|
45
|
+
export declare const ARM_DROP_BODY = "{\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"durable arm check failed: response dropped\"}}";
|
|
46
|
+
/** Golden vectors: header value -> parse result (null = drop). */
|
|
47
|
+
export declare const ARM_VECTORS: readonly {
|
|
48
|
+
value: string | null;
|
|
49
|
+
parsed: ArmState | null;
|
|
50
|
+
forwards: boolean;
|
|
51
|
+
}[];
|