@mnemom/mnemom 0.17.34-next.1 → 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.
Files changed (195) hide show
  1. package/dist/base-url-proxy.mjs +18 -4
  2. package/dist/commands/agent-config.js +5 -1
  3. package/dist/commands/agent-doctor.d.ts +9 -0
  4. package/dist/commands/agent-doctor.js +4 -0
  5. package/dist/commands/agent-session.d.ts +1 -2
  6. package/dist/commands/agent-session.js +1 -2
  7. package/dist/commands/agent-setup.d.ts +6 -0
  8. package/dist/commands/agent-setup.js +12 -1
  9. package/dist/commands/agent.d.ts +11 -0
  10. package/dist/commands/agent.js +145 -46
  11. package/dist/commands/durable-doctor.d.ts +50 -0
  12. package/dist/commands/durable-doctor.js +186 -0
  13. package/dist/commands/durable-lock.d.ts +254 -0
  14. package/dist/commands/durable-lock.js +595 -0
  15. package/dist/commands/durable-sessions.d.ts +87 -0
  16. package/dist/commands/durable-sessions.js +303 -0
  17. package/dist/commands/durable-verbs.d.ts +116 -0
  18. package/dist/commands/durable-verbs.js +393 -0
  19. package/dist/durable/flock.d.ts +26 -0
  20. package/dist/durable/flock.js +80 -0
  21. package/dist/durable/launch-spec.d.ts +7 -1
  22. package/dist/durable/launch-spec.js +10 -3
  23. package/dist/durable/seat-proxy-entry.d.ts +11 -0
  24. package/dist/durable/seat-proxy-entry.js +25 -0
  25. package/dist/durable/seat-proxy.d.ts +107 -0
  26. package/dist/durable/seat-proxy.js +599 -0
  27. package/dist/durable/seat-socket.d.ts +46 -0
  28. package/dist/durable/seat-socket.js +179 -0
  29. package/dist/durable/seat-spec-validator.d.ts +28 -0
  30. package/dist/durable/seat-spec-validator.js +59 -0
  31. package/dist/durable/test-mode.mjs +81 -0
  32. package/dist/durable/turn-chain.d.ts +55 -0
  33. package/dist/durable/turn-chain.js +178 -0
  34. package/dist/host/auto-continue.d.ts +67 -0
  35. package/dist/host/auto-continue.js +118 -0
  36. package/dist/host/bootid.d.ts +13 -0
  37. package/dist/host/bootid.js +41 -0
  38. package/dist/host/client.d.ts +123 -0
  39. package/dist/host/client.js +171 -0
  40. package/dist/host/commands.d.ts +2 -0
  41. package/dist/host/commands.js +122 -0
  42. package/dist/host/control.d.ts +33 -0
  43. package/dist/host/control.js +144 -0
  44. package/dist/host/daemon.d.ts +217 -0
  45. package/dist/host/daemon.js +606 -0
  46. package/dist/host/gate.d.ts +46 -0
  47. package/dist/host/gate.js +82 -0
  48. package/dist/host/grant.d.ts +24 -0
  49. package/dist/host/grant.js +24 -0
  50. package/dist/host/instance.d.ts +35 -0
  51. package/dist/host/instance.js +62 -0
  52. package/dist/host/integrity.d.ts +35 -0
  53. package/dist/host/integrity.js +115 -0
  54. package/dist/host/launchd.d.ts +16 -0
  55. package/dist/host/launchd.js +45 -0
  56. package/dist/host/lock.d.ts +20 -0
  57. package/dist/host/lock.js +63 -0
  58. package/dist/host/owner.d.ts +55 -0
  59. package/dist/host/owner.js +121 -0
  60. package/dist/host/plist.d.ts +16 -0
  61. package/dist/host/plist.js +85 -0
  62. package/dist/host/procs.d.ts +27 -0
  63. package/dist/host/procs.js +105 -0
  64. package/dist/host/run.d.ts +66 -0
  65. package/dist/host/run.js +314 -0
  66. package/dist/host/runtime.d.ts +8 -0
  67. package/dist/host/runtime.js +22 -0
  68. package/dist/host/s1-names.d.ts +22 -0
  69. package/dist/host/s1-names.js +21 -0
  70. package/dist/host/seat-credential.d.ts +27 -0
  71. package/dist/host/seat-credential.js +41 -0
  72. package/dist/host/seat-launch-main.d.ts +1 -0
  73. package/dist/host/seat-launch-main.js +59 -0
  74. package/dist/host/seat-launch.d.ts +116 -0
  75. package/dist/host/seat-launch.js +302 -0
  76. package/dist/host/seat.d.ts +48 -0
  77. package/dist/host/seat.js +149 -0
  78. package/dist/host/state.d.ts +33 -0
  79. package/dist/host/state.js +117 -0
  80. package/dist/host/status.d.ts +101 -0
  81. package/dist/host/status.js +307 -0
  82. package/dist/host/tmux.d.ts +56 -0
  83. package/dist/host/tmux.js +182 -0
  84. package/dist/host/transcript.d.ts +30 -0
  85. package/dist/host/transcript.js +130 -0
  86. package/dist/host/verbs.d.ts +50 -0
  87. package/dist/host/verbs.js +271 -0
  88. package/dist/index.d.ts +8 -6
  89. package/dist/index.js +197 -35
  90. package/dist/lib/agent.d.ts +8 -26
  91. package/dist/lib/agent.js +11 -26
  92. package/dist/lib/claude-plugin.d.ts +11 -2
  93. package/dist/lib/claude-plugin.js +10 -1
  94. package/dist/lib/durable/api.d.ts +136 -0
  95. package/dist/lib/durable/api.js +220 -0
  96. package/dist/lib/durable/codes.d.ts +8 -0
  97. package/dist/lib/durable/codes.js +2 -0
  98. package/dist/lib/durable/first-turn.d.ts +50 -0
  99. package/dist/lib/durable/first-turn.js +102 -0
  100. package/dist/lib/durable/gate.d.ts +48 -0
  101. package/dist/lib/durable/gate.js +122 -0
  102. package/dist/lib/durable/handover.d.ts +101 -0
  103. package/dist/lib/durable/handover.js +175 -0
  104. package/dist/lib/durable/launch.d.ts +182 -0
  105. package/dist/lib/durable/launch.js +443 -0
  106. package/dist/lib/durable/launching.d.ts +15 -0
  107. package/dist/lib/durable/launching.js +70 -0
  108. package/dist/lib/durable/live.d.ts +194 -0
  109. package/dist/lib/durable/live.js +635 -0
  110. package/dist/lib/durable/refusal.d.ts +57 -0
  111. package/dist/lib/durable/refusal.js +78 -0
  112. package/dist/lib/durable/view.d.ts +83 -0
  113. package/dist/lib/durable/view.js +244 -0
  114. package/dist/lib/experimental.d.ts +0 -9
  115. package/dist/lib/experimental.js +18 -17
  116. package/dist/lib/launcher-config.d.ts +7 -0
  117. package/dist/lib/launcher-config.js +21 -0
  118. package/dist/lib/scan/report-email.d.ts +1 -1
  119. package/dist/lib/scan/report-email.js +9 -1
  120. package/dist/lib/subscription-login.d.ts +15 -0
  121. package/dist/lib/subscription-login.js +28 -1
  122. package/dist/vendor/durable-contract/index.js +240 -18
  123. package/dist/vendor/shared/durable-contract/admission.d.ts +240 -0
  124. package/dist/vendor/shared/durable-contract/admission.js +533 -0
  125. package/dist/vendor/shared/durable-contract/arm.d.ts +51 -0
  126. package/dist/vendor/shared/durable-contract/arm.js +133 -0
  127. package/dist/vendor/shared/durable-contract/canonical.d.ts +29 -0
  128. package/dist/vendor/shared/durable-contract/canonical.js +98 -0
  129. package/dist/vendor/shared/durable-contract/cell-id.d.ts +22 -0
  130. package/dist/vendor/shared/durable-contract/cell-id.js +38 -0
  131. package/dist/vendor/shared/durable-contract/cli-output.d.ts +342 -0
  132. package/dist/vendor/shared/durable-contract/cli-output.js +201 -0
  133. package/dist/vendor/shared/durable-contract/cli-surface.d.ts +119 -0
  134. package/dist/vendor/shared/durable-contract/cli-surface.js +270 -0
  135. package/dist/vendor/shared/durable-contract/codes.d.ts +537 -0
  136. package/dist/vendor/shared/durable-contract/codes.js +475 -0
  137. package/dist/vendor/shared/durable-contract/constants.d.ts +153 -0
  138. package/dist/vendor/shared/durable-contract/constants.js +179 -0
  139. package/dist/vendor/shared/durable-contract/doors.d.ts +119 -0
  140. package/dist/vendor/shared/durable-contract/doors.js +249 -0
  141. package/dist/vendor/shared/durable-contract/envelope.d.ts +103 -0
  142. package/dist/vendor/shared/durable-contract/envelope.js +317 -0
  143. package/dist/vendor/shared/durable-contract/events.d.ts +147 -0
  144. package/dist/vendor/shared/durable-contract/events.js +630 -0
  145. package/dist/vendor/shared/durable-contract/executor-state.d.ts +41 -0
  146. package/dist/vendor/shared/durable-contract/executor-state.js +32 -0
  147. package/dist/vendor/shared/durable-contract/fixlines.d.ts +318 -0
  148. package/dist/vendor/shared/durable-contract/fixlines.js +483 -0
  149. package/dist/vendor/shared/durable-contract/freeze-record.d.ts +787 -0
  150. package/dist/vendor/shared/durable-contract/freeze-record.js +361 -0
  151. package/dist/vendor/shared/durable-contract/index.d.ts +36 -0
  152. package/dist/vendor/shared/durable-contract/index.js +37 -0
  153. package/dist/vendor/shared/durable-contract/journal-approvals.d.ts +29 -0
  154. package/dist/vendor/shared/durable-contract/journal-approvals.js +60 -0
  155. package/dist/vendor/shared/durable-contract/keys.d.ts +24 -0
  156. package/dist/vendor/shared/durable-contract/keys.js +66 -0
  157. package/dist/vendor/shared/durable-contract/lock.d.ts +128 -0
  158. package/dist/vendor/shared/durable-contract/lock.js +151 -0
  159. package/dist/vendor/shared/durable-contract/names.d.ts +25 -0
  160. package/dist/vendor/shared/durable-contract/names.js +78 -0
  161. package/dist/vendor/shared/durable-contract/owner-verbs.d.ts +175 -0
  162. package/dist/vendor/shared/durable-contract/owner-verbs.js +403 -0
  163. package/dist/vendor/shared/durable-contract/paths.d.ts +23 -0
  164. package/dist/vendor/shared/durable-contract/paths.js +35 -0
  165. package/dist/vendor/shared/durable-contract/pure-fn.d.ts +48 -0
  166. package/dist/vendor/shared/durable-contract/pure-fn.js +89 -0
  167. package/dist/vendor/shared/durable-contract/routes.d.ts +205 -0
  168. package/dist/vendor/shared/durable-contract/routes.js +315 -0
  169. package/dist/vendor/shared/durable-contract/schema.d.ts +126 -0
  170. package/dist/vendor/shared/durable-contract/schema.js +441 -0
  171. package/dist/vendor/shared/durable-contract/seal.d.ts +54 -0
  172. package/dist/vendor/shared/durable-contract/seal.js +99 -0
  173. package/dist/vendor/shared/durable-contract/secret-scope.d.ts +28 -0
  174. package/dist/vendor/shared/durable-contract/secret-scope.js +76 -0
  175. package/dist/vendor/shared/durable-contract/session-id.d.ts +21 -0
  176. package/dist/vendor/shared/durable-contract/session-id.js +58 -0
  177. package/dist/vendor/shared/durable-contract/spec.d.ts +162 -0
  178. package/dist/vendor/shared/durable-contract/spec.js +286 -0
  179. package/dist/vendor/shared/durable-contract/states.d.ts +96 -0
  180. package/dist/vendor/shared/durable-contract/states.js +218 -0
  181. package/dist/vendor/shared/durable-contract/telemetry.d.ts +94 -0
  182. package/dist/vendor/shared/durable-contract/telemetry.js +180 -0
  183. package/dist/vendor/shared/durable-contract/test-mode.d.ts +62 -0
  184. package/dist/vendor/shared/durable-contract/test-mode.js +140 -0
  185. package/dist/vendor/shared/durable-contract/turn.d.ts +57 -0
  186. package/dist/vendor/shared/durable-contract/turn.js +81 -0
  187. package/dist/vendor/shared/durable-contract/vectors.d.ts +191 -0
  188. package/dist/vendor/shared/durable-contract/vectors.js +208 -0
  189. package/package.json +3 -2
  190. package/plugins/integrity-manifest.json +35 -0
  191. package/plugins/mnemom/hooks/hooks.json +15 -1
  192. package/plugins/mnemom/hooks/register.tsx +60 -2
  193. package/plugins/mnemom/hooks/seat-hello.mjs +116 -0
  194. package/dist/commands/agent-implicit-notice.d.ts +0 -31
  195. package/dist/commands/agent-implicit-notice.js +0 -73
@@ -0,0 +1,317 @@
1
+ // GENERATED from shared/durable-contract/src/envelope.ts by cli/scripts/sync-durable-contract.mjs; do not edit.
2
+ /**
3
+ * Contract ciphertext envelope v1 and the charter commitment schema (C18-R01..R03; BACKBONE
4
+ * amendment 36; r3 U05). API only: no other repo decrypts.
5
+ *
6
+ * contract_ciphertext = "v1." + b64url(iv, 12 bytes) + "." + b64url(ciphertext || 16-byte tag)
7
+ * AES-256-GCM, per-session 32-byte DEK, random IV per write
8
+ * AAD = UTF-8 "durable-contract:v1:<session_id>:<charter_version>"
9
+ * plaintext = canonicalJson({statement, requirements, allowed_paths, forbidden_paths}), plus
10
+ * goal_id, work_card and shape when the seal carries them (amendment 55)
11
+ * wrapped_data_key = encryptBlob(dekWrapInput(DEK)) under the SESSION_COOKIE_KEY keyring (api)
12
+ * statement_hash = sha256(canonicalJson(statement)); requirements_hash = sha256(canonicalJson(requirements))
13
+ */
14
+ import { base64urlDecode, base64urlEncode, canonicalJson, sha256Hex, utf8, HEX64_RE, } from "./canonical.js";
15
+ import { CONTRACT_STATEMENT_MAX_CHARS } from "./constants.js";
16
+ import { canonicalEventTs, GENESIS_PREV_HASH } from "./events.js";
17
+ export const ENVELOPE_VERSION = "v1";
18
+ export const CIPHER_KEY_REFS = ["mnemom"];
19
+ export const DEK_BYTES = 32;
20
+ export const IV_BYTES = 12;
21
+ export const TAG_BYTES = 16;
22
+ /** The Contract's optional keys (amendment 55); absent unless the gateway seal carried them. */
23
+ export const CONTRACT_OPTIONAL_KEYS = ["goal_id", "work_card", "shape"];
24
+ export function contractAad(sessionId, charterVersion) {
25
+ return `durable-contract:v1:${sessionId}:${charterVersion}`;
26
+ }
27
+ /** Validate a submitted contract; null when malformed or over the statement limit. */
28
+ export function parseContract(raw) {
29
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
30
+ return null;
31
+ const r = raw;
32
+ const keys = ["statement", "requirements", "allowed_paths", "forbidden_paths"];
33
+ const known = [...keys, ...CONTRACT_OPTIONAL_KEYS];
34
+ if (Object.keys(r).some((k) => !known.includes(k)))
35
+ return null;
36
+ if (typeof r.statement !== "string" ||
37
+ r.statement.length === 0 ||
38
+ r.statement.length > CONTRACT_STATEMENT_MAX_CHARS)
39
+ return null;
40
+ for (const k of keys.slice(1)) {
41
+ const v = r[k] ?? [];
42
+ if (!Array.isArray(v) || v.some((s) => typeof s !== "string"))
43
+ return null;
44
+ }
45
+ if ("goal_id" in r && (typeof r.goal_id !== "string" || r.goal_id.length === 0))
46
+ return null;
47
+ if ("work_card" in r && r.work_card !== true)
48
+ return null;
49
+ if ("shape" in r && r.shape !== "duty")
50
+ return null;
51
+ const out = {
52
+ statement: r.statement,
53
+ requirements: (r.requirements ?? []),
54
+ allowed_paths: (r.allowed_paths ?? []),
55
+ forbidden_paths: (r.forbidden_paths ?? []),
56
+ };
57
+ return { ...out, ...optionalContractFields(r) };
58
+ }
59
+ function optionalContractFields(c) {
60
+ const out = {};
61
+ if (c.goal_id !== undefined)
62
+ out.goal_id = c.goal_id;
63
+ if (c.work_card !== undefined)
64
+ out.work_card = c.work_card;
65
+ if (c.shape !== undefined)
66
+ out.shape = c.shape;
67
+ return out;
68
+ }
69
+ export function contractPlaintext(c) {
70
+ return canonicalJson({
71
+ statement: c.statement,
72
+ requirements: c.requirements,
73
+ allowed_paths: c.allowed_paths,
74
+ forbidden_paths: c.forbidden_paths,
75
+ ...optionalContractFields(c),
76
+ });
77
+ }
78
+ export async function statementHash(statement) {
79
+ return sha256Hex(canonicalJson(statement));
80
+ }
81
+ export async function requirementsHash(requirements) {
82
+ return sha256Hex(canonicalJson(requirements));
83
+ }
84
+ /** What encryptBlob wraps: base64url of the raw DEK (so wrapped_data_key never decodes to 32 raw bytes). */
85
+ export function dekWrapInput(dek) {
86
+ if (dek.length !== DEK_BYTES)
87
+ throw new RangeError("DEK must be 32 bytes");
88
+ return base64urlEncode(dek);
89
+ }
90
+ export function dekFromWrapInput(text) {
91
+ const b = base64urlDecode(text);
92
+ return b !== null && b.length === DEK_BYTES ? b : null;
93
+ }
94
+ export function newDek(random = (n) => crypto.getRandomValues(new Uint8Array(n))) {
95
+ return random(DEK_BYTES);
96
+ }
97
+ async function importDek(dek) {
98
+ if (dek.length !== DEK_BYTES)
99
+ throw new RangeError("DEK must be 32 bytes");
100
+ return crypto.subtle.importKey("raw", dek, { name: "AES-GCM" }, false, [
101
+ "encrypt",
102
+ "decrypt",
103
+ ]);
104
+ }
105
+ /** Seal a contract. `random` is injectable for golden vectors only; production uses getRandomValues. */
106
+ export async function sealContract(dek, sessionId, charterVersion, contract, random = (n) => crypto.getRandomValues(new Uint8Array(n))) {
107
+ const iv = random(IV_BYTES);
108
+ const key = await importDek(dek);
109
+ const ct = await crypto.subtle.encrypt({
110
+ name: "AES-GCM",
111
+ iv: iv,
112
+ additionalData: utf8(contractAad(sessionId, charterVersion)),
113
+ tagLength: TAG_BYTES * 8,
114
+ }, key, utf8(contractPlaintext(contract)));
115
+ return `${ENVELOPE_VERSION}.${base64urlEncode(iv)}.${base64urlEncode(new Uint8Array(ct))}`;
116
+ }
117
+ export function parseEnvelope(text) {
118
+ const parts = text.split(".");
119
+ if (parts.length !== 3 || parts[0] !== ENVELOPE_VERSION)
120
+ return null;
121
+ const iv = base64urlDecode(parts[1]);
122
+ const ct = base64urlDecode(parts[2]);
123
+ if (iv === null || iv.length !== IV_BYTES || ct === null || ct.length < TAG_BYTES)
124
+ return null;
125
+ return { iv, ct };
126
+ }
127
+ /** Open an envelope; null on a malformed envelope, wrong key, wrong session or wrong version (AAD). */
128
+ export async function openContract(dek, sessionId, charterVersion, envelope) {
129
+ const env = parseEnvelope(envelope);
130
+ if (env === null)
131
+ return null;
132
+ try {
133
+ const key = await importDek(dek);
134
+ const pt = await crypto.subtle.decrypt({
135
+ name: "AES-GCM",
136
+ iv: env.iv,
137
+ additionalData: utf8(contractAad(sessionId, charterVersion)),
138
+ tagLength: TAG_BYTES * 8,
139
+ }, key, env.ct);
140
+ return parseContract(JSON.parse(new TextDecoder().decode(pt)));
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ }
146
+ // ---- charter commitment schema (closed) -------------------------------------------------------
147
+ export const COMMITMENT_KEYS = [
148
+ "statement_hash",
149
+ "requirements_hash",
150
+ "budget_usd",
151
+ "per_wake_cap_usd",
152
+ "max_wakes_per_day",
153
+ "max_turns",
154
+ "active_time_limit_s",
155
+ "pins",
156
+ "tool_classes",
157
+ "launch_spec",
158
+ // amendment 55 (C07-R02): what the gateway sealed beyond the ceilings, so a reload keeps it
159
+ "mode",
160
+ "stall_turns",
161
+ "contract_hash",
162
+ ];
163
+ /** The gateway's sealed-contract modes (MNE-8199); committed as `mode` by amendment 55. */
164
+ export const CONTRACT_MODE_VALUES = ["nudge", "enforce"];
165
+ export const TOOL_CLASS_VALUES = ["input_dependent", "safe", "idempotent"];
166
+ /**
167
+ * Charter pins (BACKBONE A3, rulings U13, C07-R23): card_content_hash is the agent's live /state
168
+ * content_hash read at lock; policy_engine_major is the API's policy-engine major at lock. Both
169
+ * required, never empty, inside the charter preimage.
170
+ */
171
+ export const PIN_KEYS = ["card_content_hash", "policy_engine_major"];
172
+ /** Validate a charter commitment object (keys a subset of COMMITMENT_KEYS; no text values). */
173
+ export function isValidCommitment(raw) {
174
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
175
+ return false;
176
+ const r = raw;
177
+ for (const [k, v] of Object.entries(r)) {
178
+ switch (k) {
179
+ case "statement_hash":
180
+ case "requirements_hash":
181
+ case "contract_hash":
182
+ if (typeof v !== "string" || !HEX64_RE.test(v))
183
+ return false;
184
+ break;
185
+ case "budget_usd":
186
+ case "per_wake_cap_usd":
187
+ if (typeof v !== "number" || !Number.isFinite(v) || v < 0)
188
+ return false;
189
+ break;
190
+ case "max_wakes_per_day":
191
+ case "max_turns":
192
+ case "active_time_limit_s":
193
+ case "stall_turns":
194
+ if (!Number.isSafeInteger(v) || v < 0)
195
+ return false;
196
+ break;
197
+ case "pins": {
198
+ if (v === null || typeof v !== "object" || Array.isArray(v))
199
+ return false;
200
+ const p = v;
201
+ if (Object.keys(p).some((x) => !PIN_KEYS.includes(x)))
202
+ return false;
203
+ if (typeof p.card_content_hash !== "string" ||
204
+ !HEX64_RE.test(p.card_content_hash) ||
205
+ !Number.isSafeInteger(p.policy_engine_major) ||
206
+ p.policy_engine_major < 0)
207
+ return false;
208
+ break;
209
+ }
210
+ case "tool_classes": {
211
+ if (v === null || typeof v !== "object" || Array.isArray(v))
212
+ return false;
213
+ for (const [tool, cls] of Object.entries(v)) {
214
+ if (!/^[A-Za-z0-9_:.-]{1,128}$/.test(tool) ||
215
+ !TOOL_CLASS_VALUES.includes(cls))
216
+ return false;
217
+ }
218
+ break;
219
+ }
220
+ case "mode":
221
+ if (!CONTRACT_MODE_VALUES.includes(v))
222
+ return false;
223
+ break;
224
+ case "launch_spec":
225
+ if (typeof v !== "object" || v === null || Array.isArray(v))
226
+ return false;
227
+ break;
228
+ default:
229
+ return false;
230
+ }
231
+ }
232
+ return true;
233
+ }
234
+ /** M1 accepts only input_dependent tool classes (safe/idempotent -> 422 durable_unsupported {feature:'tool_classes'}). */
235
+ export function toolClassesSupportedM1(toolClasses) {
236
+ return (toolClasses === undefined || Object.values(toolClasses).every((c) => c === "input_dependent"));
237
+ }
238
+ // ---- charter hash chain (C07-R04, R23; r3 U05) ------------------------------------------------
239
+ /**
240
+ * The charter row preimage. hash = sha256(canonicalJson(charterPreimage(row))); v(1).prev_hash =
241
+ * GENESIS_PREV_HASH (64 zeros) and v(n).prev_hash = v(n-1).hash. The preimage is exactly these
242
+ * fields (canonicalJson sorts keys):
243
+ * v CHARTER_PREIMAGE_VERSION (1)
244
+ * session_id the session uuid, lowercase text
245
+ * version the charter version, integer >= 1
246
+ * prev_hash 64 lowercase hex
247
+ * charter the commitment object (isValidCommitment; pins included)
248
+ * created_at canonicalEventTs(created_at): UTC, "T", 6 fraction digits, "Z" (a Postgres
249
+ * timestamptz text such as "2026-10-16 12:00:00.123+00" canonicalises the same).
250
+ * Amendment 55: v1's created_at IS the original seal's sealed_at (T0, the gateway's
251
+ * first sealed request), never the lock time: build it with charterV1CreatedAt. Every
252
+ * reader that reports a session's sealed_at reads v1's created_at. v(n>1)'s created_at
253
+ * is the amend time and never changes sealed_at.
254
+ * Every other agent_session_charters column is outside it (CHARTER_HASH_EXCLUDED_COLUMNS), so a KEK
255
+ * re-wrap (wrapped_data_key changes) and the contract purge (contract_ciphertext nulled,
256
+ * contract_purge_after stamped) leave every hash and agent_sessions.charter_hash unchanged.
257
+ */
258
+ export const CHARTER_PREIMAGE_VERSION = 1;
259
+ export const CHARTER_PREIMAGE_FIELDS = [
260
+ "v",
261
+ "session_id",
262
+ "version",
263
+ "prev_hash",
264
+ "charter",
265
+ "created_at",
266
+ ];
267
+ export const CHARTER_HASH_EXCLUDED_COLUMNS = [
268
+ "hash",
269
+ "contract_ciphertext",
270
+ "wrapped_data_key",
271
+ "cipher_key_ref",
272
+ "contract_purge_after",
273
+ "signature",
274
+ ];
275
+ /**
276
+ * Build the preimage from a charter row. Extra row columns (the excluded ones) are ignored. Throws on
277
+ * a bad version, a malformed prev_hash, a version-1 row that does not chain from genesis, or an
278
+ * invalid commitment.
279
+ */
280
+ export function charterPreimage(row) {
281
+ if (!Number.isSafeInteger(row.version) || row.version < 1)
282
+ throw new RangeError("charter version must be an integer >= 1");
283
+ if (!HEX64_RE.test(row.prev_hash))
284
+ throw new RangeError("prev_hash must be 64 lowercase hex");
285
+ if (row.version === 1 && row.prev_hash !== GENESIS_PREV_HASH)
286
+ throw new RangeError("charter v1 must chain from GENESIS_PREV_HASH");
287
+ if (!isValidCommitment(row.charter))
288
+ throw new RangeError("charter is not a valid commitment");
289
+ return {
290
+ v: CHARTER_PREIMAGE_VERSION,
291
+ session_id: row.session_id,
292
+ version: row.version,
293
+ prev_hash: row.prev_hash,
294
+ charter: row.charter,
295
+ created_at: canonicalEventTs(row.created_at),
296
+ };
297
+ }
298
+ /**
299
+ * Charter v1's created_at from the gateway seal's sealed_at (amendment 55). The seal may carry any
300
+ * ISO-8601 instant (UTC, or an offset such as "-07:00", at millisecond precision); the result is the same instant in
301
+ * canonicalEventTs form. Throws on text that is not an instant.
302
+ */
303
+ export function charterV1CreatedAt(sealedAt) {
304
+ try {
305
+ return canonicalEventTs(sealedAt); // UTC already: keeps every fraction digit
306
+ }
307
+ catch {
308
+ // an offset form: the same instant, in UTC
309
+ }
310
+ const ms = Date.parse(sealedAt);
311
+ if (!Number.isFinite(ms))
312
+ throw new RangeError(`not a seal instant: ${sealedAt}`);
313
+ return canonicalEventTs(new Date(ms).toISOString());
314
+ }
315
+ export async function charterHash(row) {
316
+ return sha256Hex(canonicalJson(charterPreimage(row)));
317
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The journal: the closed event vocabulary (design §3.7, BACKBONE "Event kinds list", C10-R1..R25),
3
+ * writer classes, refs schemas, the server-generated detail, idempotency keys and the hash
4
+ * preimage (C10-R0 (a)-(m)).
5
+ *
6
+ * C10-R0 decisions frozen here:
7
+ * (a) preimage fields: EVENT_PREIMAGE_FIELDS; hash = sha256(canonicalJson(preimage)).
8
+ * (b) genesis prev_hash: GENESIS_PREV_HASH (64 zeros), never null.
9
+ * (c) ts: stamped by the API process at append (Date, millisecond precision), stored timestamptz,
10
+ * hashed as canonicalEventTs(ts): UTC, exactly 6 fraction digits, "Z" (survives PostgREST's
11
+ * "+00:00" and psql's "+00" read-back forms).
12
+ * (d) principal_ref: a stored column, HMAC-SHA256(hmac_salt, principal_uid) hex, null when the
13
+ * writer has no principal (gateway, sweep, trigger).
14
+ * (e) writer classes: EVENT_WRITER; executor list exactly [executor.spawned], plus session.waiting
15
+ * from the Doors amendment (ruling 34.06) and executor.turn_continued (lead ruling 2026-10-28).
16
+ * (f) executor.enrolled is journaled into no session in M1 (the agent_executors row is the record).
17
+ * (h) an epoch-bump event is stamped with the NEW epoch.
18
+ * (i)(j) codes.ts: durable_append_conflict, durable_lease_fenced, durable_idempotency_conflict.
19
+ * (k) refs_schema is enforced inside durable_append_event (so a direct service_role RPC is refused).
20
+ * (l) the gateway's internal route is GATEWAY_EVENTS_ROUTE on the API.
21
+ * (m) DSR: phase user_erasure, treatment pseudonymize (nulls principal_uid, detail, refs).
22
+ */
23
+ export declare const EVENT_GROUPS: readonly ["lifecycle", "charter", "lease", "input", "human", "effects", "governance", "context"];
24
+ export type EventGroup = (typeof EVENT_GROUPS)[number];
25
+ /** The §3.7 table, all 88 kinds, by group. */
26
+ export declare const DESIGN_EVENT_KINDS: {
27
+ readonly [G in EventGroup]: readonly string[];
28
+ };
29
+ /** The 8 kinds L01 leaves out of the M1 seed: unknown kinds on every write path in M1. */
30
+ export declare const M1_EXCLUDED_KINDS: readonly ["intent.issued", "intent.committed", "intent.unknown", "intent.resolved", "intent.readmission_flagged", "context.note", "context.compacted", "inbox.acked"];
31
+ /**
32
+ * Every M1 kind (80) with its group, in §3.7 order, then the Doors amendment's three (rulings
33
+ * 34.02, 34.06), appended so no existing index moves. Mirrors durable_event_kinds (parity test).
34
+ */
35
+ export declare const DURABLE_EVENT_KINDS: readonly ["session.registered", "session.locked", "session.unlocked", "session.resumed", "session.detached", "session.parked", "session.suspended", "session.paused", "session.unpaused", "session.moved", "session.completed", "session.failed", "session.killed", "session.expired", "session.adopted", "seat.hello", "charter.sealed", "charter.amended", "charter.goal_sealed", "charter.card_drift", "profile.amended", "executor.upgraded", "attestation.passed", "attestation.failed", "lease.acquired", "lease.reclaimed", "lease.released", "lease.expired", "lease.fenced", "lease.zombie_call", "lease.stale_admit", "executor.enrolled", "executor.revoked", "executor.spawned", "executor.exited", "executor.contract_violation", "inbox.admitted", "inbox.rejected", "inbox.coalesced", "inbox.held_unsealed", "inbox.queued_for_home", "inbox.reverted", "inbox.delivered", "inbox.unmatched", "inbox.dead", "timer.set", "timer.fired", "timer.cancelled", "trigger.added", "trigger.removed", "trigger.fired", "trigger.suppressed", "trigger.over_cap", "wake.undelivered", "question.asked", "question.answered", "approval.requested", "approval.resolved", "approval.consumed", "approval.expired", "approval.superseded", "approval.single_person_override", "never.held", "chain.posture", "guard.refused", "guard.unavailable", "budget.reached", "grant.renewed", "grant.revoked", "verdict.violation", "identity.mismatch", "containment.pause", "containment.kill", "containment.auto_pause", "governance.degraded_admit", "governance.outage_mode_set", "policy.host_unattended_changed", "workspace.checkpointed", "rearm.recorded", "tip.signed", "session.ready", "session.waiting", "session.wait_cleared", "executor.turn_continued"];
36
+ export type DurableEventKind = (typeof DURABLE_EVENT_KINDS)[number];
37
+ export declare function isDurableEventKind(v: unknown): v is DurableEventKind;
38
+ /**
39
+ * The kinds amendments add, with their group: the Doors amendment (rulings 34.02, 34.06), and
40
+ * executor.turn_continued (lead ruling 2026-10-28: the seat sent its one automatic "continue" to a
41
+ * turn a lock interrupted). They are not in the design's §3.7 table, so DESIGN_EVENT_KINDS stays the
42
+ * design's table and these are listed here.
43
+ */
44
+ export declare const AMENDMENT_EVENT_GROUPS: Readonly<{
45
+ readonly "session.ready": "lifecycle";
46
+ readonly "session.waiting": "lifecycle";
47
+ readonly "session.wait_cleared": "lifecycle";
48
+ readonly "executor.turn_continued": "lease";
49
+ }>;
50
+ export declare function eventGroupOf(kind: DurableEventKind): EventGroup;
51
+ export declare const EVENT_WRITERS: readonly ["executor", "gateway", "api"];
52
+ export type EventWriter = (typeof EVENT_WRITERS)[number];
53
+ /**
54
+ * M1: exactly [executor.spawned] (L01); the Doors amendment adds session.waiting (ruling 34.06), and
55
+ * the seat's one automatic continue adds executor.turn_continued (lead ruling 2026-10-28).
56
+ */
57
+ export declare const EXECUTOR_WRITABLE_KINDS: readonly ["executor.spawned", "session.waiting", "executor.turn_continued"];
58
+ /** Gateway: no session state in M1, so no containment.*, charter.* or lifecycle kind. */
59
+ export declare const GATEWAY_WRITABLE_KINDS: readonly ["lease.zombie_call", "identity.mismatch", "guard.unavailable"];
60
+ /** The one writer class allowed to append a kind; everything not executor- or gateway-writable is API-writable. */
61
+ export declare function writerOf(kind: DurableEventKind): EventWriter;
62
+ /** Kinds with no M1 emitter: seeded for parity, never written by M1 code (C10-R4 static scan). */
63
+ export declare const NO_M1_EMITTER_KINDS: readonly ["session.resumed", "session.moved", "charter.goal_sealed", "profile.amended", "executor.upgraded", "lease.stale_admit", "executor.enrolled", "executor.contract_violation", "inbox.admitted", "inbox.rejected", "inbox.coalesced", "inbox.held_unsealed", "inbox.queued_for_home", "inbox.reverted", "inbox.delivered", "inbox.unmatched", "inbox.dead", "timer.set", "timer.fired", "timer.cancelled", "trigger.added", "trigger.removed", "trigger.fired", "trigger.suppressed", "trigger.over_cap", "wake.undelivered", "question.asked", "question.answered", "approval.superseded", "approval.single_person_override", "never.held", "verdict.violation", "containment.auto_pause", "governance.degraded_admit", "governance.outage_mode_set", "policy.host_unattended_changed", "workspace.checkpointed", "rearm.recorded", "tip.signed"];
64
+ export interface RefsProperty {
65
+ readonly type: "string" | "integer" | "boolean";
66
+ readonly enum?: readonly string[];
67
+ readonly pattern?: string;
68
+ readonly maxLength?: number;
69
+ readonly minimum?: number;
70
+ /** A boolean that may only take this value when present (ruling 34.14 (1): lease.acquired pool). */
71
+ readonly const?: boolean;
72
+ }
73
+ export interface RefsSchema {
74
+ readonly type: "object";
75
+ readonly additionalProperties: false;
76
+ readonly required: readonly string[];
77
+ readonly properties: Readonly<Record<string, RefsProperty>>;
78
+ /**
79
+ * if refs[if.field] === if.equals then `require` fields are required (C10-R5, R25) and `forbid`
80
+ * fields must be absent (ruling 34.14 (5)).
81
+ */
82
+ readonly if_then?: readonly {
83
+ readonly field: string;
84
+ readonly equals: string;
85
+ readonly require?: readonly string[];
86
+ readonly forbid?: readonly string[];
87
+ readonly enums?: Readonly<Record<string, readonly string[]>>;
88
+ }[];
89
+ }
90
+ /** durable_approvals.kind values, as the approval.* refs enums carry them. */
91
+ export declare const APPROVAL_KINDS: readonly ["question", "ask", "tool_confirmation", "intent_rerun", "card_drift", "owner_mutation", "grant_renewal", "limit_raise"];
92
+ /** session.ready refs.cause: how a session entered ready (ruling 34.02 (3)). */
93
+ export declare const READY_CAUSES: readonly ["dispatched", "lease_expired", "wait_cleared"];
94
+ /** refs_schema for every M1 kind; kinds without a specific schema take EMPTY (refs must be {}). */
95
+ export declare function refsSchemaOf(kind: DurableEventKind): RefsSchema;
96
+ /** One refs property against its schema (pure; exported for its direct test). An integer with no minimum accepts any safe integer. */
97
+ export declare function refsPropertyOk(p: RefsProperty, v: unknown): boolean;
98
+ /** Reference validator for refs against refsSchemaOf(kind) (the RPC enforces the same rules). */
99
+ export declare function validateRefs(kind: DurableEventKind, refs: unknown): boolean;
100
+ /** Server-generated detail (C10-R6, C18-R07): "<kind>" then " key=value" for each refs key, sorted. Content-free by construction. */
101
+ export declare function eventDetail(kind: DurableEventKind, refs: Readonly<Record<string, string | number | boolean | null>>): string;
102
+ /** The closed shape every stored detail matches (the C18 structural checker's regex). */
103
+ export declare const EVENT_DETAIL_RE: RegExp;
104
+ export declare const idempotencyKeys: {
105
+ readonly spawn: (sessionId: string, epoch: number, spawnId: string) => string;
106
+ readonly zombie: (requestId: string) => string;
107
+ readonly identityMismatch: (sessionId: string, epoch: number) => string;
108
+ readonly cardDrift: (sessionId: string, observedHash: string) => string;
109
+ /** API transitions: one event per (session, kind, new epoch); a retried transition replays the row. */
110
+ readonly transition: (sessionId: string, kind: DurableEventKind, epoch: number) => string;
111
+ };
112
+ export declare const GENESIS_PREV_HASH: string;
113
+ export declare const GATEWAY_EVENTS_ROUTE = "/internal/durable/events";
114
+ /**
115
+ * The journal kinds that mark a terminal transition, one per TERMINAL_STATES value in the same
116
+ * order (wave-0 fix ruling 1.06). contract_purge_after is stamped from the ts of this event
117
+ * (C18-R13).
118
+ */
119
+ export declare const TERMINAL_EVENT_KINDS: readonly ["session.completed", "session.failed", "session.killed", "session.expired"];
120
+ export declare const EVENT_DSR: {
121
+ readonly phase: "user_erasure";
122
+ readonly treatment: "pseudonymize";
123
+ };
124
+ /** Hashed fields, in no particular order (canonicalJson sorts). Raw detail, refs and principal_uid are never hashed. */
125
+ export declare const EVENT_PREIMAGE_FIELDS: readonly ["v", "session_id", "seq", "ts", "kind", "epoch", "idempotency_key", "principal_ref", "detail_sha256", "refs_sha256", "prev_hash"];
126
+ export interface EventPreimageInput {
127
+ session_id: string;
128
+ seq: number;
129
+ ts: string;
130
+ kind: DurableEventKind;
131
+ epoch: number;
132
+ idempotency_key: string;
133
+ principal_ref?: string | null;
134
+ detail_sha256: string;
135
+ refs_sha256: string;
136
+ prev_hash: string;
137
+ }
138
+ /** The canonical ts text: UTC, 6 fraction digits, "Z". Throws on a non-UTC or malformed value. */
139
+ export declare function canonicalEventTs(ts: string): string;
140
+ /** The API's stamper: millisecond Date, canonical form. */
141
+ export declare function stampEventTs(epochMs: number): string;
142
+ /** The preimage object; absent principal_ref hashes like explicit null (C10-R8). */
143
+ export declare function eventPreimage(input: EventPreimageInput): Record<string, unknown>;
144
+ export declare function eventHash(input: EventPreimageInput): Promise<string>;
145
+ export declare function refsSha256(refs: unknown): Promise<string>;
146
+ /** principal_ref = HMAC-SHA256(hmac_salt, principal_uid), lowercase hex (C10-R15). */
147
+ export declare function principalRef(hmacSalt: Uint8Array, principalUid: string): Promise<string>;