@mnemom/mnemom 0.17.34-next.1 → 0.17.34-next.3

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 (236) 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-run.d.ts +14 -6
  6. package/dist/commands/agent-run.js +44 -27
  7. package/dist/commands/agent-session.d.ts +1 -2
  8. package/dist/commands/agent-session.js +1 -2
  9. package/dist/commands/agent-setup.d.ts +6 -0
  10. package/dist/commands/agent-setup.js +12 -1
  11. package/dist/commands/agent-share-test-issuer.d.ts +67 -0
  12. package/dist/commands/agent-share-test-issuer.js +139 -0
  13. package/dist/commands/agent-share.d.ts +7 -1
  14. package/dist/commands/agent-share.js +14 -4
  15. package/dist/commands/agent.d.ts +18 -5
  16. package/dist/commands/agent.js +186 -59
  17. package/dist/commands/durable-doctor.d.ts +50 -0
  18. package/dist/commands/durable-doctor.js +186 -0
  19. package/dist/commands/durable-lock.d.ts +261 -0
  20. package/dist/commands/durable-lock.js +600 -0
  21. package/dist/commands/durable-sessions.d.ts +87 -0
  22. package/dist/commands/durable-sessions.js +303 -0
  23. package/dist/commands/durable-verbs.d.ts +116 -0
  24. package/dist/commands/durable-verbs.js +393 -0
  25. package/dist/commands/scan.d.ts +12 -1
  26. package/dist/commands/scan.js +22 -3
  27. package/dist/commands/wrap.js +1 -1
  28. package/dist/durable/adopt-live.js +5 -8
  29. package/dist/durable/adopt.d.ts +8 -0
  30. package/dist/durable/adopt.js +27 -6
  31. package/dist/durable/cwd-rule.d.ts +9 -1
  32. package/dist/durable/cwd-rule.js +18 -0
  33. package/dist/durable/flock.d.ts +26 -0
  34. package/dist/durable/flock.js +80 -0
  35. package/dist/durable/launch-spec.d.ts +9 -1
  36. package/dist/durable/launch-spec.js +11 -4
  37. package/dist/durable/seat-proxy-entry.d.ts +11 -0
  38. package/dist/durable/seat-proxy-entry.js +25 -0
  39. package/dist/durable/seat-proxy.d.ts +107 -0
  40. package/dist/durable/seat-proxy.js +599 -0
  41. package/dist/durable/seat-socket.d.ts +46 -0
  42. package/dist/durable/seat-socket.js +179 -0
  43. package/dist/durable/seat-spec-validator.d.ts +28 -0
  44. package/dist/durable/seat-spec-validator.js +59 -0
  45. package/dist/durable/test-mode.mjs +105 -0
  46. package/dist/durable/turn-chain.d.ts +55 -0
  47. package/dist/durable/turn-chain.js +178 -0
  48. package/dist/host/auto-continue.d.ts +67 -0
  49. package/dist/host/auto-continue.js +118 -0
  50. package/dist/host/bootid.d.ts +13 -0
  51. package/dist/host/bootid.js +41 -0
  52. package/dist/host/client.d.ts +125 -0
  53. package/dist/host/client.js +171 -0
  54. package/dist/host/commands.d.ts +2 -0
  55. package/dist/host/commands.js +122 -0
  56. package/dist/host/control.d.ts +33 -0
  57. package/dist/host/control.js +144 -0
  58. package/dist/host/daemon.d.ts +221 -0
  59. package/dist/host/daemon.js +616 -0
  60. package/dist/host/gate.d.ts +46 -0
  61. package/dist/host/gate.js +82 -0
  62. package/dist/host/grant.d.ts +24 -0
  63. package/dist/host/grant.js +24 -0
  64. package/dist/host/instance.d.ts +35 -0
  65. package/dist/host/instance.js +62 -0
  66. package/dist/host/integrity.d.ts +35 -0
  67. package/dist/host/integrity.js +115 -0
  68. package/dist/host/launchd.d.ts +16 -0
  69. package/dist/host/launchd.js +45 -0
  70. package/dist/host/lock.d.ts +20 -0
  71. package/dist/host/lock.js +63 -0
  72. package/dist/host/owner.d.ts +58 -0
  73. package/dist/host/owner.js +123 -0
  74. package/dist/host/plist.d.ts +16 -0
  75. package/dist/host/plist.js +85 -0
  76. package/dist/host/procs.d.ts +27 -0
  77. package/dist/host/procs.js +105 -0
  78. package/dist/host/run.d.ts +65 -0
  79. package/dist/host/run.js +299 -0
  80. package/dist/host/runtime.d.ts +8 -0
  81. package/dist/host/runtime.js +22 -0
  82. package/dist/host/s1-names.d.ts +23 -0
  83. package/dist/host/s1-names.js +22 -0
  84. package/dist/host/seat-credential.d.ts +27 -0
  85. package/dist/host/seat-credential.js +41 -0
  86. package/dist/host/seat-launch-main.d.ts +1 -0
  87. package/dist/host/seat-launch-main.js +59 -0
  88. package/dist/host/seat-launch.d.ts +116 -0
  89. package/dist/host/seat-launch.js +302 -0
  90. package/dist/host/seat.d.ts +48 -0
  91. package/dist/host/seat.js +149 -0
  92. package/dist/host/state.d.ts +33 -0
  93. package/dist/host/state.js +117 -0
  94. package/dist/host/status.d.ts +101 -0
  95. package/dist/host/status.js +307 -0
  96. package/dist/host/tmux.d.ts +56 -0
  97. package/dist/host/tmux.js +182 -0
  98. package/dist/host/transcript.d.ts +30 -0
  99. package/dist/host/transcript.js +130 -0
  100. package/dist/host/verbs.d.ts +52 -0
  101. package/dist/host/verbs.js +282 -0
  102. package/dist/index.d.ts +8 -6
  103. package/dist/index.js +209 -43
  104. package/dist/lib/agent.d.ts +13 -29
  105. package/dist/lib/agent.js +17 -29
  106. package/dist/lib/claude-bin.d.ts +6 -0
  107. package/dist/lib/claude-bin.js +26 -0
  108. package/dist/lib/claude-plugin.d.ts +11 -2
  109. package/dist/lib/claude-plugin.js +10 -1
  110. package/dist/lib/commit-report.d.ts +5 -4
  111. package/dist/lib/commit-report.js +6 -9
  112. package/dist/lib/config.d.ts +8 -1
  113. package/dist/lib/config.js +28 -0
  114. package/dist/lib/durable/api.d.ts +136 -0
  115. package/dist/lib/durable/api.js +220 -0
  116. package/dist/lib/durable/codes.d.ts +8 -0
  117. package/dist/lib/durable/codes.js +2 -0
  118. package/dist/lib/durable/gate.d.ts +48 -0
  119. package/dist/lib/durable/gate.js +122 -0
  120. package/dist/lib/durable/handover.d.ts +101 -0
  121. package/dist/lib/durable/handover.js +175 -0
  122. package/dist/lib/durable/launch.d.ts +199 -0
  123. package/dist/lib/durable/launch.js +461 -0
  124. package/dist/lib/durable/launching.d.ts +15 -0
  125. package/dist/lib/durable/launching.js +70 -0
  126. package/dist/lib/durable/live.d.ts +190 -0
  127. package/dist/lib/durable/live.js +633 -0
  128. package/dist/lib/durable/refusal.d.ts +57 -0
  129. package/dist/lib/durable/refusal.js +78 -0
  130. package/dist/lib/durable/view.d.ts +83 -0
  131. package/dist/lib/durable/view.js +244 -0
  132. package/dist/lib/experimental.d.ts +0 -9
  133. package/dist/lib/experimental.js +18 -17
  134. package/dist/lib/launcher-config.d.ts +13 -0
  135. package/dist/lib/launcher-config.js +32 -0
  136. package/dist/lib/runner/exec-append.d.ts +15 -0
  137. package/dist/lib/runner/exec-append.js +2 -0
  138. package/dist/lib/runner/executor-client.d.ts +2 -1
  139. package/dist/lib/runner/executor-state.d.ts +10 -0
  140. package/dist/lib/runner/executor-state.js +24 -0
  141. package/dist/lib/runner/github-base.d.ts +24 -0
  142. package/dist/lib/runner/github-base.js +27 -0
  143. package/dist/lib/runner/job.d.ts +9 -4
  144. package/dist/lib/runner/job.js +43 -15
  145. package/dist/lib/runner/oidc.d.ts +3 -2
  146. package/dist/lib/runner/oidc.js +7 -4
  147. package/dist/lib/runner/options.d.ts +6 -0
  148. package/dist/lib/runner/options.js +19 -1
  149. package/dist/lib/runner/phases.d.ts +7 -4
  150. package/dist/lib/runner/phases.js +8 -4
  151. package/dist/lib/runner/proxy.d.ts +5 -1
  152. package/dist/lib/runner/proxy.js +7 -1
  153. package/dist/lib/scan/cost-per-pr.d.ts +93 -0
  154. package/dist/lib/scan/cost-per-pr.js +101 -0
  155. package/dist/lib/scan/record.d.ts +27 -1
  156. package/dist/lib/scan/record.js +114 -16
  157. package/dist/lib/scan/report-email.d.ts +26 -6
  158. package/dist/lib/scan/report-email.js +32 -3
  159. package/dist/lib/scan/scan.d.ts +3 -0
  160. package/dist/lib/scan/scan.js +13 -1
  161. package/dist/lib/subscription-login.d.ts +15 -0
  162. package/dist/lib/subscription-login.js +28 -1
  163. package/dist/vendor/durable-contract/index.js +240 -18
  164. package/dist/vendor/shared/durable-contract/admission.d.ts +240 -0
  165. package/dist/vendor/shared/durable-contract/admission.js +533 -0
  166. package/dist/vendor/shared/durable-contract/arm.d.ts +51 -0
  167. package/dist/vendor/shared/durable-contract/arm.js +133 -0
  168. package/dist/vendor/shared/durable-contract/canonical.d.ts +29 -0
  169. package/dist/vendor/shared/durable-contract/canonical.js +98 -0
  170. package/dist/vendor/shared/durable-contract/cell-id.d.ts +22 -0
  171. package/dist/vendor/shared/durable-contract/cell-id.js +38 -0
  172. package/dist/vendor/shared/durable-contract/cli-output.d.ts +342 -0
  173. package/dist/vendor/shared/durable-contract/cli-output.js +201 -0
  174. package/dist/vendor/shared/durable-contract/cli-surface.d.ts +119 -0
  175. package/dist/vendor/shared/durable-contract/cli-surface.js +270 -0
  176. package/dist/vendor/shared/durable-contract/codes.d.ts +537 -0
  177. package/dist/vendor/shared/durable-contract/codes.js +475 -0
  178. package/dist/vendor/shared/durable-contract/constants.d.ts +153 -0
  179. package/dist/vendor/shared/durable-contract/constants.js +179 -0
  180. package/dist/vendor/shared/durable-contract/doors.d.ts +119 -0
  181. package/dist/vendor/shared/durable-contract/doors.js +249 -0
  182. package/dist/vendor/shared/durable-contract/envelope.d.ts +103 -0
  183. package/dist/vendor/shared/durable-contract/envelope.js +317 -0
  184. package/dist/vendor/shared/durable-contract/events.d.ts +147 -0
  185. package/dist/vendor/shared/durable-contract/events.js +630 -0
  186. package/dist/vendor/shared/durable-contract/executor-state.d.ts +41 -0
  187. package/dist/vendor/shared/durable-contract/executor-state.js +32 -0
  188. package/dist/vendor/shared/durable-contract/fixlines.d.ts +318 -0
  189. package/dist/vendor/shared/durable-contract/fixlines.js +483 -0
  190. package/dist/vendor/shared/durable-contract/freeze-record.d.ts +787 -0
  191. package/dist/vendor/shared/durable-contract/freeze-record.js +361 -0
  192. package/dist/vendor/shared/durable-contract/index.d.ts +36 -0
  193. package/dist/vendor/shared/durable-contract/index.js +37 -0
  194. package/dist/vendor/shared/durable-contract/journal-approvals.d.ts +29 -0
  195. package/dist/vendor/shared/durable-contract/journal-approvals.js +60 -0
  196. package/dist/vendor/shared/durable-contract/keys.d.ts +24 -0
  197. package/dist/vendor/shared/durable-contract/keys.js +66 -0
  198. package/dist/vendor/shared/durable-contract/lock.d.ts +128 -0
  199. package/dist/vendor/shared/durable-contract/lock.js +151 -0
  200. package/dist/vendor/shared/durable-contract/names.d.ts +25 -0
  201. package/dist/vendor/shared/durable-contract/names.js +78 -0
  202. package/dist/vendor/shared/durable-contract/owner-verbs.d.ts +175 -0
  203. package/dist/vendor/shared/durable-contract/owner-verbs.js +403 -0
  204. package/dist/vendor/shared/durable-contract/paths.d.ts +23 -0
  205. package/dist/vendor/shared/durable-contract/paths.js +35 -0
  206. package/dist/vendor/shared/durable-contract/pure-fn.d.ts +48 -0
  207. package/dist/vendor/shared/durable-contract/pure-fn.js +89 -0
  208. package/dist/vendor/shared/durable-contract/routes.d.ts +205 -0
  209. package/dist/vendor/shared/durable-contract/routes.js +315 -0
  210. package/dist/vendor/shared/durable-contract/schema.d.ts +126 -0
  211. package/dist/vendor/shared/durable-contract/schema.js +441 -0
  212. package/dist/vendor/shared/durable-contract/seal.d.ts +54 -0
  213. package/dist/vendor/shared/durable-contract/seal.js +99 -0
  214. package/dist/vendor/shared/durable-contract/secret-scope.d.ts +28 -0
  215. package/dist/vendor/shared/durable-contract/secret-scope.js +76 -0
  216. package/dist/vendor/shared/durable-contract/session-id.d.ts +21 -0
  217. package/dist/vendor/shared/durable-contract/session-id.js +58 -0
  218. package/dist/vendor/shared/durable-contract/spec.d.ts +162 -0
  219. package/dist/vendor/shared/durable-contract/spec.js +286 -0
  220. package/dist/vendor/shared/durable-contract/states.d.ts +96 -0
  221. package/dist/vendor/shared/durable-contract/states.js +218 -0
  222. package/dist/vendor/shared/durable-contract/telemetry.d.ts +94 -0
  223. package/dist/vendor/shared/durable-contract/telemetry.js +180 -0
  224. package/dist/vendor/shared/durable-contract/test-mode.d.ts +62 -0
  225. package/dist/vendor/shared/durable-contract/test-mode.js +140 -0
  226. package/dist/vendor/shared/durable-contract/turn.d.ts +57 -0
  227. package/dist/vendor/shared/durable-contract/turn.js +81 -0
  228. package/dist/vendor/shared/durable-contract/vectors.d.ts +191 -0
  229. package/dist/vendor/shared/durable-contract/vectors.js +208 -0
  230. package/package.json +3 -2
  231. package/plugins/integrity-manifest.json +35 -0
  232. package/plugins/mnemom/hooks/hooks.json +15 -1
  233. package/plugins/mnemom/hooks/register.tsx +60 -2
  234. package/plugins/mnemom/hooks/seat-hello.mjs +116 -0
  235. package/dist/commands/agent-implicit-notice.d.ts +0 -31
  236. package/dist/commands/agent-implicit-notice.js +0 -73
@@ -0,0 +1,205 @@
1
+ /**
2
+ * The executor and internal route table (wave-0 fix rulings 1.02 and 3a; BACKBONE A3; C04). One
3
+ * table for enrollment, the executor contract and the gateway internal routes, in the shape the
4
+ * datasets cite: a fixed route carries `path`; a route scoped to one id carries `pre` and `post`,
5
+ * and its path is `${pre}${id}${post}`. Not customer facing: the executor contract is called only
6
+ * by Mnemom's own daemon, and enrollment only through `mnemom agent host install` / `renew`.
7
+ *
8
+ * `auth` names the credential the route takes: the owner's JWT, the enrollment poll secret, the
9
+ * executor key from EXECUTOR_STATE_FILE, or the gateway-internal service credential. `owner` (ruling
10
+ * 33.22) is the owner's JWT, or the owner's personal org API key where the S1 owner-credential rule
11
+ * allows it.
12
+ *
13
+ * Owner session verbs (ruling 33.22; design-v3 section 3.5, BACKBONE A2): scoped on the
14
+ * conversation id under /v1/agent/sessions/; the `?agent_id=` query is not part of the match. Not
15
+ * frozen: move, inbox, triggers and profiles (not in A2), the pre-existing GET /{cid} and POST
16
+ * /{cid}/contract, and create and list (their methods are not spelled in the sources).
17
+ *
18
+ * Doors amendment (ruling 34.03): exec_ready_list (the pool discovery read) and exec_oidc_exchange
19
+ * (the CI runner's GitHub OIDC exchange for a one-lease org-scope key). S1 picks the literal paths;
20
+ * neither sits under /v1/executor/sessions/, so no session id can collide with them.
21
+ *
22
+ * Charter by hash (ruling 33.32, logged as 33.29 in the W1 build log; design-v3 section 3.5
23
+ * Internal): int_charter is the api route the gateway's durable seal calls on a cold charter LRU.
24
+ * The api serves it (A2: only the api holds the charter DEK); the gateway sends
25
+ * CHARTER_BY_HASH_BODY_KEYS with INTERNAL_KEY_HEADER and reads CHARTER_BY_HASH_RESPONSE.
26
+ */
27
+ export type RouteMethod = "GET" | "POST" | "DELETE";
28
+ /** github_oidc (ruling 34.03 (1)): a GitHub Actions workflow OIDC token, in the body, exchanged for a one-lease key. */
29
+ export type RouteAuth = "owner_jwt" | "owner" | "poll_secret" | "executor_key" | "internal" | "github_oidc";
30
+ export interface FixedRoute {
31
+ readonly method: RouteMethod;
32
+ readonly auth: RouteAuth;
33
+ readonly path: string;
34
+ }
35
+ export interface ScopedRoute {
36
+ readonly method: RouteMethod;
37
+ readonly auth: RouteAuth;
38
+ readonly pre: string;
39
+ readonly post: string;
40
+ }
41
+ export declare const ROUTES: Readonly<{
42
+ /** Start an enrollment (device code). */
43
+ exec_enroll: FixedRoute;
44
+ /** Poll an enrollment; errors are ENROLL_POLL_ERRORS. */
45
+ exec_enroll_poll: FixedRoute;
46
+ exec_list: FixedRoute;
47
+ exec_revoke: ScopedRoute;
48
+ exec_renew: ScopedRoute;
49
+ /** Polled every EXECUTOR_POLL_S. */
50
+ exec_sessions: FixedRoute;
51
+ exec_attest: ScopedRoute;
52
+ exec_claim: ScopedRoute;
53
+ exec_hello: ScopedRoute;
54
+ exec_release: ScopedRoute;
55
+ exec_append: ScopedRoute;
56
+ exec_seat_exit: ScopedRoute;
57
+ exec_fail: ScopedRoute;
58
+ /** One call per executor, batched over its sessions. */
59
+ exec_heartbeat: FixedRoute;
60
+ /** The org-scope executor's claimable pool sessions, oldest first; `?limit=` (READY_SESSIONS_LIMIT_MIN..MAX) is not part of the match. */
61
+ exec_ready_list: FixedRoute;
62
+ /** Body {oidc_token}; answers a one-lease org-scope executor key or an OIDC_EXCHANGE_REFUSALS refusal. */
63
+ exec_oidc_exchange: FixedRoute;
64
+ own_lock: ScopedRoute;
65
+ own_unlock: ScopedRoute;
66
+ own_pause: ScopedRoute;
67
+ own_resume: ScopedRoute;
68
+ own_complete: ScopedRoute;
69
+ own_kill: ScopedRoute;
70
+ own_amend: ScopedRoute;
71
+ own_fail: ScopedRoute;
72
+ own_events: ScopedRoute;
73
+ int_events: FixedRoute;
74
+ int_arm: FixedRoute;
75
+ int_sweep: FixedRoute;
76
+ /** Charter by hash (ruling 33.32): body CHARTER_BY_HASH_BODY_KEYS, answer CHARTER_BY_HASH_RESPONSE. */
77
+ int_charter: FixedRoute;
78
+ }>;
79
+ export type RouteName = keyof typeof ROUTES;
80
+ /**
81
+ * The concrete path of a route: a fixed route takes no id; a scoped route takes exactly one safe id
82
+ * (letters, digits, '-'; at most 64), so a path never gains a segment.
83
+ */
84
+ export declare function routePath(name: RouteName, id?: string): string;
85
+ /**
86
+ * The GET ROUTES.exec_sessions body (rulings 24.16, 33.28 (4), 33.28.2), in one of two forms:
87
+ * - `sessions`: `{sessions: [item, ...]}`, each item exactly the ten keys below. id is the
88
+ * agent_sessions.id uuid in lowercase, the id every scoped executor route takes; spec is the
89
+ * current charter version's launch_spec (null with no charter). No item carries a secret.
90
+ * - `grant_expired`: `{sessions: [], grant_expires_at, grant_expired: true}` when the executor's
91
+ * grant has lapsed. The daemon reports it in host status; it is not a malformed answer.
92
+ * Each value names its type: an S1 field type, `|null` when NULL is allowed, `session_state` a
93
+ * SESSION_STATES value, `state_reason` a reason valid for the item's state.
94
+ */
95
+ export declare const EXEC_SESSIONS_RESPONSE: Readonly<{
96
+ sessions: Readonly<{
97
+ item: Readonly<{
98
+ id: "uuid";
99
+ agent_id: "string";
100
+ state: "session_state";
101
+ state_reason: "state_reason|null";
102
+ lease_epoch: "integer";
103
+ lease_expires_at: "timestamp|null";
104
+ held_here: "boolean";
105
+ cc_session_id: "string|null";
106
+ cwd: "string|null";
107
+ spec: "object|null";
108
+ }>;
109
+ }>;
110
+ grant_expired: Readonly<{
111
+ sessions: "empty";
112
+ grant_expires_at: "timestamp";
113
+ grant_expired: true;
114
+ }>;
115
+ }>;
116
+ /** One poll item: exactly the ten keys of EXEC_SESSIONS_RESPONSE.sessions.item, each of its type. */
117
+ export declare function execSessionsItemOk(v: unknown): boolean;
118
+ /** True for either form of EXEC_SESSIONS_RESPONSE, closed at the top level and per item (ruling 33.28.2). */
119
+ export declare function execSessionsResponseOk(v: unknown): boolean;
120
+ /**
121
+ * The owner session read (GET /v1/agent/sessions/{cid}, durable_session_view): the one field S1
122
+ * freezes there. Open on purpose: every other key the view carries stays the api's, so this pins
123
+ * only that `cc_session_id` is present, a string or null, the value `sessions adopt` matches a live
124
+ * Claude Code process on. Not a closed schema of the view.
125
+ */
126
+ export declare const OWNER_SESSION_VIEW_FIELDS: Readonly<{
127
+ cc_session_id: "string|null";
128
+ }>;
129
+ /** True for an owner session view object carrying every OWNER_SESSION_VIEW_FIELDS key, of its type; other keys pass. */
130
+ export declare function ownerSessionViewOk(v: unknown): boolean;
131
+ /** The only header that carries the executor key on an executor route; never `Authorization` (33.28 (1)). */
132
+ export declare const EXECUTOR_KEY_HEADER = "X-Mnemom-Api-Key";
133
+ /** POST exec_heartbeat body: exactly these keys, both required (33.28.1); entries hold HEARTBEAT_ENTRY_FIELDS. */
134
+ export declare const HEARTBEAT_REQUEST_KEYS: readonly ["nonce", "entries"];
135
+ /**
136
+ * A heartbeat body (33.28.1): exactly {nonce, entries}, a non-empty nonce, and each entry exactly
137
+ * HEARTBEAT_ENTRY_FIELDS (no per-entry nonce, no `session` alias).
138
+ */
139
+ export declare function heartbeatRequestOk(v: unknown): boolean;
140
+ /** POST exec_claim body keys: nonce required, tokens and new_key optional (33.28 (3)). */
141
+ export declare const CLAIM_BODY_KEYS: readonly ["nonce", "tokens", "new_key"];
142
+ /** A claim body: only CLAIM_BODY_KEYS, a non-empty nonce, an object tokens and a boolean new_key when present. */
143
+ export declare function claimBodyOk(v: unknown): boolean;
144
+ /**
145
+ * The needs_key retry rule (33.28 (3)): after a 409 durable_claim_needs_key the daemon sends the same
146
+ * claim once more with new_key: true, and never sends new_key: true otherwise. Returns the body of
147
+ * the one retry, or null when no retry is due (another answer, or the retry was already sent).
148
+ */
149
+ export declare function claimNeedsKeyRetry(body: {
150
+ readonly nonce: string;
151
+ readonly tokens?: Record<string, unknown>;
152
+ }, answerCode: string | null, retried: boolean): {
153
+ nonce: string;
154
+ tokens?: Record<string, unknown>;
155
+ new_key: true;
156
+ } | null;
157
+ /** The header that carries the gateway-internal service credential on an `internal` route (ruling 33.32). */
158
+ export declare const INTERNAL_KEY_HEADER = "X-Internal-Key";
159
+ /** POST int_charter body: exactly these keys, both required (ruling 33.32). */
160
+ export declare const CHARTER_BY_HASH_BODY_KEYS: readonly ["session_id", "charter_hash"];
161
+ /**
162
+ * A charter-by-hash body: exactly {session_id, charter_hash}, session_id the agent_sessions.id uuid
163
+ * in lowercase and charter_hash the charter version's 64-hex hash (charterHash). A body that fails
164
+ * this names no charter, so the api answers it as it answers an unknown one: 404
165
+ * durable_charter_not_found.
166
+ */
167
+ export declare function charterByHashBodyOk(v: unknown): boolean;
168
+ /**
169
+ * The 200 answer of POST int_charter (ruling 33.32): the charter version whose hash is the body's
170
+ * charter_hash, for the body's session, with its contract decrypted by the api. Exactly these keys:
171
+ * - conversation_id: the session's own conversation id (non-empty);
172
+ * - version: the charter version (integer, at least 1);
173
+ * - sealed_at: the charter's original sealed_at instant, ISO 8601 in UTC as the api sends it:
174
+ * `YYYY-MM-DDTHH:MM:SS[.ffffff]` then `Z` or `+00:00` (CHARTER_SEALED_AT_RE);
175
+ * - contract: the decrypted envelope plaintext, exactly the four Contract keys (envelope.ts), each
176
+ * list an array of strings (never null: the gateway refuses a non-array requirements).
177
+ * An unknown session, or a hash that is not one of that session's charter versions, answers 404
178
+ * durable_charter_not_found, and the gateway refuses admission (durable_admission_unavailable).
179
+ */
180
+ export declare const CHARTER_BY_HASH_RESPONSE: Readonly<{
181
+ conversation_id: "string";
182
+ version: "integer>=1";
183
+ sealed_at: "utc_timestamp";
184
+ contract: Readonly<{
185
+ statement: "string";
186
+ requirements: "string[]";
187
+ allowed_paths: "string[]";
188
+ forbidden_paths: "string[]";
189
+ }>;
190
+ }>;
191
+ /** The charter answer's sealed_at: ISO 8601 with a `T`, up to six fraction digits, and `Z` or `+00:00`. */
192
+ export declare const CHARTER_SEALED_AT_RE: RegExp;
193
+ /** True for a CHARTER_BY_HASH_RESPONSE answer, closed at the top level and in the contract. */
194
+ export declare function charterByHashResponseOk(v: unknown): boolean;
195
+ /**
196
+ * POST int_events (GATEWAY_EVENTS_ROUTE) body: exactly these keys, all required (ruling 33.32 (2);
197
+ * G2 settle.ts appendGatewayEvent). refs may be null, which the api reads as `{}`.
198
+ */
199
+ export declare const GATEWAY_EVENTS_BODY_KEYS: readonly ["session_id", "kind", "epoch", "idempotency_key", "refs"];
200
+ /**
201
+ * A gateway events body: exactly GATEWAY_EVENTS_BODY_KEYS, session_id a lowercase uuid, kind one of
202
+ * GATEWAY_WRITABLE_KINDS, epoch a non-negative integer, a non-empty idempotency_key, and refs null
203
+ * or an object that passes the kind's S1 refs schema (null read as `{}`).
204
+ */
205
+ export declare function gatewayEventsBodyOk(v: unknown): boolean;
@@ -0,0 +1,315 @@
1
+ // GENERATED from shared/durable-contract/src/routes.ts by cli/scripts/sync-durable-contract.mjs; do not edit.
2
+ /**
3
+ * The executor and internal route table (wave-0 fix rulings 1.02 and 3a; BACKBONE A3; C04). One
4
+ * table for enrollment, the executor contract and the gateway internal routes, in the shape the
5
+ * datasets cite: a fixed route carries `path`; a route scoped to one id carries `pre` and `post`,
6
+ * and its path is `${pre}${id}${post}`. Not customer facing: the executor contract is called only
7
+ * by Mnemom's own daemon, and enrollment only through `mnemom agent host install` / `renew`.
8
+ *
9
+ * `auth` names the credential the route takes: the owner's JWT, the enrollment poll secret, the
10
+ * executor key from EXECUTOR_STATE_FILE, or the gateway-internal service credential. `owner` (ruling
11
+ * 33.22) is the owner's JWT, or the owner's personal org API key where the S1 owner-credential rule
12
+ * allows it.
13
+ *
14
+ * Owner session verbs (ruling 33.22; design-v3 section 3.5, BACKBONE A2): scoped on the
15
+ * conversation id under /v1/agent/sessions/; the `?agent_id=` query is not part of the match. Not
16
+ * frozen: move, inbox, triggers and profiles (not in A2), the pre-existing GET /{cid} and POST
17
+ * /{cid}/contract, and create and list (their methods are not spelled in the sources).
18
+ *
19
+ * Doors amendment (ruling 34.03): exec_ready_list (the pool discovery read) and exec_oidc_exchange
20
+ * (the CI runner's GitHub OIDC exchange for a one-lease org-scope key). S1 picks the literal paths;
21
+ * neither sits under /v1/executor/sessions/, so no session id can collide with them.
22
+ *
23
+ * Charter by hash (ruling 33.32, logged as 33.29 in the W1 build log; design-v3 section 3.5
24
+ * Internal): int_charter is the api route the gateway's durable seal calls on a cold charter LRU.
25
+ * The api serves it (A2: only the api holds the charter DEK); the gateway sends
26
+ * CHARTER_BY_HASH_BODY_KEYS with INTERNAL_KEY_HEADER and reads CHARTER_BY_HASH_RESPONSE.
27
+ */
28
+ import { ARM_ROUTE } from "./arm.js";
29
+ import { fieldOk } from "./admission.js";
30
+ import { HEX64_RE, UUID_RE } from "./canonical.js";
31
+ import { HEARTBEAT_ENTRY_FIELDS } from "./codes.js";
32
+ import { parseContract } from "./envelope.js";
33
+ import { GATEWAY_EVENTS_ROUTE, GATEWAY_WRITABLE_KINDS, validateRefs } from "./events.js";
34
+ import { ALL_STATE_REASONS, isSessionState, isValidStateReason } from "./states.js";
35
+ const fixed = (method, auth, path) => Object.freeze({ method, auth, path });
36
+ const scoped = (method, auth, pre, post) => Object.freeze({ method, auth, pre, post });
37
+ const EXECUTORS = "/v1/executors";
38
+ const EXEC_SESSIONS = "/v1/executor/sessions";
39
+ const EXEC_SESSION_PRE = `${EXEC_SESSIONS}/`;
40
+ const OWNER_SESSION_PRE = "/v1/agent/sessions/";
41
+ export const ROUTES = Object.freeze({
42
+ // ---- enrollment and grant (owner side) ----
43
+ /** Start an enrollment (device code). */
44
+ exec_enroll: fixed("POST", "owner_jwt", `${EXECUTORS}/enroll`),
45
+ /** Poll an enrollment; errors are ENROLL_POLL_ERRORS. */
46
+ exec_enroll_poll: fixed("POST", "poll_secret", `${EXECUTORS}/enroll/poll`),
47
+ exec_list: fixed("GET", "owner_jwt", EXECUTORS),
48
+ exec_revoke: scoped("DELETE", "owner_jwt", `${EXECUTORS}/`, ""),
49
+ exec_renew: scoped("POST", "owner_jwt", `${EXECUTORS}/`, "/renew"),
50
+ // ---- executor contract (daemon side) ----
51
+ /** Polled every EXECUTOR_POLL_S. */
52
+ exec_sessions: fixed("GET", "executor_key", EXEC_SESSIONS),
53
+ exec_attest: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/attest"),
54
+ exec_claim: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/claim"),
55
+ exec_hello: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/hello"),
56
+ exec_release: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/release"),
57
+ exec_append: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/events"),
58
+ exec_seat_exit: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/seat-exit"),
59
+ exec_fail: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/fail"),
60
+ /** One call per executor, batched over its sessions. */
61
+ exec_heartbeat: fixed("POST", "executor_key", "/v1/executor/heartbeat"),
62
+ // ---- Doors amendment (ruling 34.03) ----
63
+ /** The org-scope executor's claimable pool sessions, oldest first; `?limit=` (READY_SESSIONS_LIMIT_MIN..MAX) is not part of the match. */
64
+ exec_ready_list: fixed("GET", "executor_key", "/v1/executor/ready"),
65
+ /** Body {oidc_token}; answers a one-lease org-scope executor key or an OIDC_EXCHANGE_REFUSALS refusal. */
66
+ exec_oidc_exchange: fixed("POST", "github_oidc", "/v1/executor/oidc/exchange"),
67
+ // ---- owner session verbs (ruling 33.22) ----
68
+ own_lock: scoped("POST", "owner", OWNER_SESSION_PRE, "/lock"),
69
+ own_unlock: scoped("POST", "owner", OWNER_SESSION_PRE, "/unlock"),
70
+ own_pause: scoped("POST", "owner", OWNER_SESSION_PRE, "/pause"),
71
+ own_resume: scoped("POST", "owner", OWNER_SESSION_PRE, "/resume"),
72
+ own_complete: scoped("POST", "owner", OWNER_SESSION_PRE, "/complete"),
73
+ own_kill: scoped("POST", "owner", OWNER_SESSION_PRE, "/kill"),
74
+ own_amend: scoped("POST", "owner", OWNER_SESSION_PRE, "/amend"),
75
+ own_fail: scoped("POST", "owner", OWNER_SESSION_PRE, "/fail"),
76
+ own_events: scoped("GET", "owner", OWNER_SESSION_PRE, "/events"),
77
+ // ---- gateway internal ----
78
+ int_events: fixed("POST", "internal", GATEWAY_EVENTS_ROUTE),
79
+ int_arm: fixed("GET", "internal", ARM_ROUTE),
80
+ int_sweep: fixed("POST", "internal", "/internal/durable/sweep"),
81
+ /** Charter by hash (ruling 33.32): body CHARTER_BY_HASH_BODY_KEYS, answer CHARTER_BY_HASH_RESPONSE. */
82
+ int_charter: fixed("POST", "internal", "/internal/durable/charter"),
83
+ });
84
+ const ROUTE_ID_RE = /^[A-Za-z0-9-]{1,64}$/;
85
+ /**
86
+ * The concrete path of a route: a fixed route takes no id; a scoped route takes exactly one safe id
87
+ * (letters, digits, '-'; at most 64), so a path never gains a segment.
88
+ */
89
+ export function routePath(name, id) {
90
+ const r = ROUTES[name];
91
+ if ("path" in r) {
92
+ if (id !== undefined)
93
+ throw new RangeError(`route ${name} takes no id`);
94
+ return r.path;
95
+ }
96
+ if (id === undefined || !ROUTE_ID_RE.test(id))
97
+ throw new RangeError(`route ${name} needs a safe id`);
98
+ return `${r.pre}${id}${r.post}`;
99
+ }
100
+ /**
101
+ * The GET ROUTES.exec_sessions body (rulings 24.16, 33.28 (4), 33.28.2), in one of two forms:
102
+ * - `sessions`: `{sessions: [item, ...]}`, each item exactly the ten keys below. id is the
103
+ * agent_sessions.id uuid in lowercase, the id every scoped executor route takes; spec is the
104
+ * current charter version's launch_spec (null with no charter). No item carries a secret.
105
+ * - `grant_expired`: `{sessions: [], grant_expires_at, grant_expired: true}` when the executor's
106
+ * grant has lapsed. The daemon reports it in host status; it is not a malformed answer.
107
+ * Each value names its type: an S1 field type, `|null` when NULL is allowed, `session_state` a
108
+ * SESSION_STATES value, `state_reason` a reason valid for the item's state.
109
+ */
110
+ export const EXEC_SESSIONS_RESPONSE = Object.freeze({
111
+ sessions: Object.freeze({
112
+ item: Object.freeze({
113
+ id: "uuid",
114
+ agent_id: "string",
115
+ state: "session_state",
116
+ state_reason: "state_reason|null",
117
+ lease_epoch: "integer",
118
+ lease_expires_at: "timestamp|null",
119
+ held_here: "boolean",
120
+ cc_session_id: "string|null",
121
+ cwd: "string|null",
122
+ spec: "object|null",
123
+ }),
124
+ }),
125
+ grant_expired: Object.freeze({
126
+ sessions: "empty",
127
+ grant_expires_at: "timestamp",
128
+ grant_expired: true,
129
+ }),
130
+ });
131
+ const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
132
+ const keysExactly = (o, keys) => Object.keys(o).length === keys.length && keys.every((k) => k in o);
133
+ /** One poll item: exactly the ten keys of EXEC_SESSIONS_RESPONSE.sessions.item, each of its type. */
134
+ export function execSessionsItemOk(v) {
135
+ if (!isObj(v) || !keysExactly(v, Object.keys(EXEC_SESSIONS_RESPONSE.sessions.item)))
136
+ return false;
137
+ if (typeof v.id !== "string" || !UUID_RE.test(v.id))
138
+ return false;
139
+ if (!isSessionState(v.state))
140
+ return false;
141
+ if (!fieldOk({ type: "enum", nullable: true, values: ALL_STATE_REASONS }, v.state_reason) ||
142
+ !isValidStateReason(v.state, v.state_reason))
143
+ return false;
144
+ return (fieldOk({ type: "string", nullable: false }, v.agent_id) &&
145
+ fieldOk({ type: "integer", nullable: false }, v.lease_epoch) &&
146
+ fieldOk({ type: "timestamp", nullable: true }, v.lease_expires_at) &&
147
+ fieldOk({ type: "boolean", nullable: false }, v.held_here) &&
148
+ fieldOk({ type: "string", nullable: true }, v.cc_session_id) &&
149
+ fieldOk({ type: "string", nullable: true }, v.cwd) &&
150
+ fieldOk({ type: "object", nullable: true }, v.spec));
151
+ }
152
+ /** True for either form of EXEC_SESSIONS_RESPONSE, closed at the top level and per item (ruling 33.28.2). */
153
+ export function execSessionsResponseOk(v) {
154
+ if (!isObj(v) || !Array.isArray(v.sessions))
155
+ return false;
156
+ if ("grant_expired" in v)
157
+ return (keysExactly(v, Object.keys(EXEC_SESSIONS_RESPONSE.grant_expired)) &&
158
+ v.grant_expired === true &&
159
+ v.sessions.length === 0 &&
160
+ fieldOk({ type: "timestamp", nullable: false }, v.grant_expires_at));
161
+ return keysExactly(v, ["sessions"]) && v.sessions.every(execSessionsItemOk);
162
+ }
163
+ // ---- owner session view (mnemom-test #1085, lead ruling 2026-10-28, option A) -------------------
164
+ /**
165
+ * The owner session read (GET /v1/agent/sessions/{cid}, durable_session_view): the one field S1
166
+ * freezes there. Open on purpose: every other key the view carries stays the api's, so this pins
167
+ * only that `cc_session_id` is present, a string or null, the value `sessions adopt` matches a live
168
+ * Claude Code process on. Not a closed schema of the view.
169
+ */
170
+ export const OWNER_SESSION_VIEW_FIELDS = Object.freeze({
171
+ cc_session_id: "string|null",
172
+ });
173
+ /** True for an owner session view object carrying every OWNER_SESSION_VIEW_FIELDS key, of its type; other keys pass. */
174
+ export function ownerSessionViewOk(v) {
175
+ return (isObj(v) && "cc_session_id" in v && fieldOk({ type: "string", nullable: true }, v.cc_session_id));
176
+ }
177
+ // ---- executor wire (ruling 33.28, 33.28.1) ----------------------------------------------------
178
+ /** The only header that carries the executor key on an executor route; never `Authorization` (33.28 (1)). */
179
+ export const EXECUTOR_KEY_HEADER = "X-Mnemom-Api-Key";
180
+ /** POST exec_heartbeat body: exactly these keys, both required (33.28.1); entries hold HEARTBEAT_ENTRY_FIELDS. */
181
+ export const HEARTBEAT_REQUEST_KEYS = ["nonce", "entries"];
182
+ /**
183
+ * A heartbeat body (33.28.1): exactly {nonce, entries}, a non-empty nonce, and each entry exactly
184
+ * HEARTBEAT_ENTRY_FIELDS (no per-entry nonce, no `session` alias).
185
+ */
186
+ export function heartbeatRequestOk(v) {
187
+ if (!isObj(v) || !keysExactly(v, HEARTBEAT_REQUEST_KEYS))
188
+ return false;
189
+ if (typeof v.nonce !== "string" || v.nonce.length === 0 || !Array.isArray(v.entries))
190
+ return false;
191
+ return v.entries.every((e) => isObj(e) &&
192
+ keysExactly(e, HEARTBEAT_ENTRY_FIELDS) &&
193
+ fieldOk({ type: "uuid", nullable: false }, e.session_id) &&
194
+ fieldOk({ type: "integer", nullable: false }, e.epoch) &&
195
+ fieldOk({ type: "uuid", nullable: false }, e.spawn_id));
196
+ }
197
+ /** POST exec_claim body keys: nonce required, tokens and new_key optional (33.28 (3)). */
198
+ export const CLAIM_BODY_KEYS = ["nonce", "tokens", "new_key"];
199
+ /** A claim body: only CLAIM_BODY_KEYS, a non-empty nonce, an object tokens and a boolean new_key when present. */
200
+ export function claimBodyOk(v) {
201
+ if (!isObj(v))
202
+ return false;
203
+ if (Object.keys(v).some((k) => !CLAIM_BODY_KEYS.includes(k)))
204
+ return false;
205
+ if (typeof v.nonce !== "string" || v.nonce.length === 0)
206
+ return false;
207
+ if ("tokens" in v && !isObj(v.tokens))
208
+ return false;
209
+ return !("new_key" in v) || typeof v.new_key === "boolean";
210
+ }
211
+ /**
212
+ * The needs_key retry rule (33.28 (3)): after a 409 durable_claim_needs_key the daemon sends the same
213
+ * claim once more with new_key: true, and never sends new_key: true otherwise. Returns the body of
214
+ * the one retry, or null when no retry is due (another answer, or the retry was already sent).
215
+ */
216
+ export function claimNeedsKeyRetry(body, answerCode, retried) {
217
+ if (answerCode !== "durable_claim_needs_key" || retried)
218
+ return null;
219
+ return { ...body, new_key: true };
220
+ }
221
+ // ---- charter by hash (ruling 33.32) -----------------------------------------------------------
222
+ /** The header that carries the gateway-internal service credential on an `internal` route (ruling 33.32). */
223
+ export const INTERNAL_KEY_HEADER = "X-Internal-Key";
224
+ /** POST int_charter body: exactly these keys, both required (ruling 33.32). */
225
+ export const CHARTER_BY_HASH_BODY_KEYS = ["session_id", "charter_hash"];
226
+ /**
227
+ * A charter-by-hash body: exactly {session_id, charter_hash}, session_id the agent_sessions.id uuid
228
+ * in lowercase and charter_hash the charter version's 64-hex hash (charterHash). A body that fails
229
+ * this names no charter, so the api answers it as it answers an unknown one: 404
230
+ * durable_charter_not_found.
231
+ */
232
+ export function charterByHashBodyOk(v) {
233
+ return (isObj(v) &&
234
+ keysExactly(v, CHARTER_BY_HASH_BODY_KEYS) &&
235
+ typeof v.session_id === "string" &&
236
+ UUID_RE.test(v.session_id) &&
237
+ typeof v.charter_hash === "string" &&
238
+ HEX64_RE.test(v.charter_hash));
239
+ }
240
+ /**
241
+ * The 200 answer of POST int_charter (ruling 33.32): the charter version whose hash is the body's
242
+ * charter_hash, for the body's session, with its contract decrypted by the api. Exactly these keys:
243
+ * - conversation_id: the session's own conversation id (non-empty);
244
+ * - version: the charter version (integer, at least 1);
245
+ * - sealed_at: the charter's original sealed_at instant, ISO 8601 in UTC as the api sends it:
246
+ * `YYYY-MM-DDTHH:MM:SS[.ffffff]` then `Z` or `+00:00` (CHARTER_SEALED_AT_RE);
247
+ * - contract: the decrypted envelope plaintext, exactly the four Contract keys (envelope.ts), each
248
+ * list an array of strings (never null: the gateway refuses a non-array requirements).
249
+ * An unknown session, or a hash that is not one of that session's charter versions, answers 404
250
+ * durable_charter_not_found, and the gateway refuses admission (durable_admission_unavailable).
251
+ */
252
+ export const CHARTER_BY_HASH_RESPONSE = Object.freeze({
253
+ conversation_id: "string",
254
+ version: "integer>=1",
255
+ sealed_at: "utc_timestamp",
256
+ contract: Object.freeze({
257
+ statement: "string",
258
+ requirements: "string[]",
259
+ allowed_paths: "string[]",
260
+ forbidden_paths: "string[]",
261
+ }),
262
+ });
263
+ /** The charter answer's sealed_at: ISO 8601 with a `T`, up to six fraction digits, and `Z` or `+00:00`. */
264
+ export const CHARTER_SEALED_AT_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|\+00:00)$/;
265
+ /** True for a CHARTER_BY_HASH_RESPONSE answer, closed at the top level and in the contract. */
266
+ export function charterByHashResponseOk(v) {
267
+ if (!isObj(v) || !keysExactly(v, Object.keys(CHARTER_BY_HASH_RESPONSE)))
268
+ return false;
269
+ if (!fieldOk({ type: "string", nullable: false }, v.conversation_id))
270
+ return false;
271
+ if (!Number.isSafeInteger(v.version) || v.version < 1)
272
+ return false;
273
+ if (typeof v.sealed_at !== "string" || !CHARTER_SEALED_AT_RE.test(v.sealed_at))
274
+ return false;
275
+ if (!Number.isFinite(Date.parse(v.sealed_at)))
276
+ return false;
277
+ const c = v.contract;
278
+ return (isObj(c) &&
279
+ keysExactly(c, Object.keys(CHARTER_BY_HASH_RESPONSE.contract)) &&
280
+ Array.isArray(c.requirements) &&
281
+ Array.isArray(c.allowed_paths) &&
282
+ Array.isArray(c.forbidden_paths) &&
283
+ parseContract(c) !== null);
284
+ }
285
+ // ---- gateway events (ruling 33.32 (2)) --------------------------------------------------------
286
+ /**
287
+ * POST int_events (GATEWAY_EVENTS_ROUTE) body: exactly these keys, all required (ruling 33.32 (2);
288
+ * G2 settle.ts appendGatewayEvent). refs may be null, which the api reads as `{}`.
289
+ */
290
+ export const GATEWAY_EVENTS_BODY_KEYS = [
291
+ "session_id",
292
+ "kind",
293
+ "epoch",
294
+ "idempotency_key",
295
+ "refs",
296
+ ];
297
+ /**
298
+ * A gateway events body: exactly GATEWAY_EVENTS_BODY_KEYS, session_id a lowercase uuid, kind one of
299
+ * GATEWAY_WRITABLE_KINDS, epoch a non-negative integer, a non-empty idempotency_key, and refs null
300
+ * or an object that passes the kind's S1 refs schema (null read as `{}`).
301
+ */
302
+ export function gatewayEventsBodyOk(v) {
303
+ if (!isObj(v) || !keysExactly(v, GATEWAY_EVENTS_BODY_KEYS))
304
+ return false;
305
+ if (typeof v.session_id !== "string" || !UUID_RE.test(v.session_id))
306
+ return false;
307
+ const kind = v.kind;
308
+ if (!GATEWAY_WRITABLE_KINDS.includes(kind))
309
+ return false;
310
+ if (!fieldOk({ type: "integer", nullable: false }, v.epoch))
311
+ return false;
312
+ if (typeof v.idempotency_key !== "string" || v.idempotency_key.length === 0)
313
+ return false;
314
+ return validateRefs(kind, v.refs ?? {});
315
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * The M1 database catalogue S1 freezes for A1 and for the C17 catalog oracle (C17-R23..R27;
3
+ * BACKBONE A1; design §3.3). A1 implements exactly this; a needed difference is an S1 amendment.
4
+ *
5
+ * Column decisions beyond design §3.3 (whose lists end in "…"):
6
+ * - agent_sessions.cwd: the registered worktree root (the idle builder source reads it);
7
+ * - agent_sessions.spawn_id / hello_at: the current epoch's registered spawn and its accepted
8
+ * hello (heartbeat revival and durable_seat_exit test them in one row read);
9
+ * - durable_lease_keys.agent_id / org_id: attribution at the source (C14-R14);
10
+ * - agent_executors.label: the machine label `show` and `session_homed_elsewhere` print;
11
+ * - agent_session_events.principal_ref (C10-R0 (d)) and durable_event_kinds.writer (C10-R0 (e));
12
+ * - durable_approvals.subject / requested_by: the owner_mutation subject kind and requester.
13
+ *
14
+ * Doors amendment columns (rulings 34.03, 34.06, 34.10), each appended to its table:
15
+ * - agent_sessions.wait_ref (JSONB, NULL unless wait_reason is a PARKED_ONLY_WAIT_REASONS value);
16
+ * - agent_executors.scope (TEXT NOT NULL DEFAULT 'user', EXECUTOR_SCOPES);
17
+ * - durable_org_policy.dispatch_agent_id (nullable FK agents, same org).
18
+ *
19
+ * Amendment 54 (signed off by Wassim 2026-10-29; ruling B5a as amended, 1a + 2a), appended to its table:
20
+ * - agent_sessions.new_session (BOOLEAN NOT NULL DEFAULT false): true only on a row that
21
+ * create-and-lock inserted (`--new` under durable=always on a durable-only agent, one
22
+ * transaction with the lock); a plain lock never sets it. The executor learns it only from the
23
+ * claim success answer (`new_session: true`, while no seat hello was ever accepted for the
24
+ * session); EXEC_SESSIONS_RESPONSE item keys are unchanged.
25
+ */
26
+ export declare const M1_TABLES: readonly ["agent_sessions", "durable_lease_keys", "agent_session_charters", "agent_session_events", "durable_event_kinds", "agent_session_chains", "agent_executors", "agent_durable_profiles", "durable_cell_state", "durable_org_policy", "durable_user_policy", "durable_approvals", "durable_approval_attestations"];
27
+ export type M1Table = (typeof M1_TABLES)[number];
28
+ export declare const M1_COLUMNS: {
29
+ readonly [T in M1Table]: readonly string[];
30
+ };
31
+ /** Columns no anon/authenticated column-level grant may reach (C17-R27, C17-M44). */
32
+ export declare const CIPHERTEXT_COLUMNS: readonly [readonly ["agent_session_charters", "contract_ciphertext"], readonly ["agent_session_charters", "wrapped_data_key"]];
33
+ /** Columns that must NOT exist (dropped seams, cut items). */
34
+ export declare const FORBIDDEN_COLUMNS: readonly [readonly ["durable_cell_state", "test_barriers_enabled"]];
35
+ export interface Arbiter {
36
+ name: string;
37
+ table: M1Table;
38
+ columns: readonly string[];
39
+ nullsNotDistinct: boolean;
40
+ /** WHERE predicate as pg_get_indexdef prints it, or null for a full unique index. */
41
+ where: string | null;
42
+ }
43
+ /** Every unique index an M1 ON CONFLICT relies on (C17-R24). */
44
+ export declare const M1_ARBITERS: readonly Arbiter[];
45
+ /** The indexdef pg_get_indexdef prints for an arbiter (public schema, btree). */
46
+ export declare function arbiterIndexDef(a: Arbiter): string;
47
+ export declare const M1_FUNCTIONS: readonly ["resolve_durable_session_key", "agent_durable_only", "durable_schema_version", "durable_attest_begin", "durable_claim_lease", "durable_heartbeat", "durable_seat_hello", "durable_seat_exit", "durable_fail_session", "durable_release_lease", "durable_append_event", "durable_fence_check", "durable_guard_add", "durable_revoke_executor", "durable_sweep", "durable_kill_agent_sessions", "durable_agent_killed"];
48
+ /** Functions keeping EXECUTE for PUBLIC/anon/authenticated: none in M1. */
49
+ export declare const EXECUTE_EXCEPTIONS: readonly string[];
50
+ export declare const M1_TRIGGERS: readonly [{
51
+ readonly name: "durable_agent_killed";
52
+ readonly table: "agents";
53
+ readonly fn: "durable_agent_killed";
54
+ }];
55
+ /** Not in M1: must be absent from the catalog. */
56
+ export declare const ABSENT_FUNCTIONS_M1: readonly ["durable_assert_attach", "durable_set_org_policy", "durable_test_barrier"];
57
+ /** Down migrations refuse while any of these has rows. */
58
+ export declare const LEDGER_TABLES: readonly ["agent_sessions", "agent_session_events", "agent_session_charters", "durable_approvals", "durable_approval_attestations", "agent_executors"];
59
+ export declare const MIGRATION_LOCK_TIMEOUT = "3s";
60
+ /** The durable_cell_state seed: exactly one row per canonical cell id. */
61
+ export declare function cellSeedRows(): {
62
+ cell_id: string;
63
+ cell_generation: number;
64
+ unattended_enabled: false;
65
+ lease_seconds: number;
66
+ resume_reset_seconds: number;
67
+ }[];
68
+ /**
69
+ * durable_approvals.subject (kind owner_mutation) and its target rule (ruling 32.12). An enrollment
70
+ * approval is a row with subject 'enrollment' and a NULL session_id, scoped by org_id and
71
+ * requested_by; a grant_renewal names its executor inside the subject. Every other subject names a
72
+ * session, question included (ruling 34.06.1.1: the question wait's approval, appended). A1
73
+ * mirrors both CHECKs. The journal's approval.requested refs enum does not gain
74
+ * 'enrollment' (events.ts): executor.enrolled is journaled into no session (C10-R0(f)).
75
+ */
76
+ export declare const DURABLE_APPROVAL_SUBJECTS: readonly ["unlock", "unpause_contained", "grant_renewal", "budget_raise", "enrollment", "question"];
77
+ export type DurableApprovalSubject = (typeof DURABLE_APPROVAL_SUBJECTS)[number];
78
+ /** Subjects whose row may carry a NULL session_id. */
79
+ export declare const DURABLE_APPROVAL_SESSIONLESS_SUBJECTS: readonly ["grant_renewal", "enrollment"];
80
+ export declare const DURABLE_APPROVALS_SUBJECT_CHECK: string;
81
+ export declare const DURABLE_APPROVALS_TARGET_CHECK: string;
82
+ /** The target CHECK as a predicate. */
83
+ export declare function approvalTargetOk(subject: DurableApprovalSubject, sessionId: string | null): boolean;
84
+ /** The enrollment subject's risk_line_version: decimal text, never a number (ruling 32.30). */
85
+ export declare const RISK_LINE_VERSION_TEXT_RE: RegExp;
86
+ /**
87
+ * durable_approvals.subject_hash: lowercase hex sha256 of canonicalJson(subject) (rulings 32.12,
88
+ * 32.30). Subjects: {subject: 'enrollment', org_id, owner_user_id, runtime, risk_line_version},
89
+ * {subject: 'grant_renewal', executor_id}, {subject, session_id} for the session subjects. Rejects
90
+ * with RangeError on an unknown subject, an extra or missing key, or a value of the wrong form.
91
+ * An enrollment subject may also carry scope: 'org' and nothing else (ruling 34.03.1), so an org
92
+ * enrollment commits the approver to a runner that can claim any pool session in the org; without
93
+ * it the subject, and its hash, are M1's byte for byte.
94
+ */
95
+ export declare function approvalSubjectHash(subject: Readonly<Record<string, unknown>>): Promise<string>;
96
+ /**
97
+ * agent_sessions_ready_ck (ruling 34.02 (1), (2)): a ready row is locked, has no reason, no home and
98
+ * no holder. agent_sessions_state_reason_ck gains the arm `WHEN 'ready' THEN state_reason IS NULL`
99
+ * (STATE_REASONS.ready), and the state CHECK lists SESSION_STATES.
100
+ */
101
+ export declare const AGENT_SESSIONS_READY_CHECK: string;
102
+ /**
103
+ * agent_sessions_wait_parked_ck (ruling 34.06 (1), (2)): ci, question and budget are valid only with
104
+ * state 'parked', and wait_ref is set exactly when wait_reason is one of them. The wait_reason
105
+ * CHECK lists WAIT_REASONS.
106
+ */
107
+ export declare const AGENT_SESSIONS_WAIT_PARKED_CHECK: string;
108
+ /** agent_executors.scope CHECK (ruling 34.03 (1)). */
109
+ export declare const AGENT_EXECUTORS_SCOPE_CHECK = "scope IN ('user','org')";
110
+ /** The ready-row CHECK as a predicate over the columns it reads. */
111
+ export declare function readyRowOk(row: {
112
+ readonly state: string;
113
+ readonly durable_mode: string;
114
+ readonly state_reason: string | null;
115
+ readonly home_executor_id: string | null;
116
+ readonly lease_executor_id: string | null;
117
+ readonly lease_expires_at: string | null;
118
+ readonly spawn_id: string | null;
119
+ readonly hello_at: string | null;
120
+ }): boolean;
121
+ /** The wait-parked CHECK as a predicate. */
122
+ export declare function waitParkedRowOk(row: {
123
+ readonly state: string;
124
+ readonly wait_reason: string | null;
125
+ readonly wait_ref: unknown;
126
+ }): boolean;