@nylorun/runtime 0.14.0-beta → 0.15.0-beta

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 (198) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +75 -33
  3. package/dist/adapters/execution/restate.js +2 -2
  4. package/dist/adapters/replication/pgoutput.d.ts +5 -0
  5. package/dist/adapters/replication/pgoutput.js +4 -3
  6. package/dist/admin-openapi.json +184 -685
  7. package/dist/api/components.d.ts +308 -68
  8. package/dist/api/components.js +4 -5
  9. package/dist/api/gate/keys.d.ts +14 -0
  10. package/dist/api/gate/keys.js +59 -0
  11. package/dist/api/gate/routes.d.ts +34 -0
  12. package/dist/api/gate/routes.js +235 -0
  13. package/dist/api/http/define.js +0 -8
  14. package/dist/api/http/routes/access.js +25 -18
  15. package/dist/api/http/routes/tenant.d.ts +2 -1
  16. package/dist/api/http/routes/tenant.js +77 -5
  17. package/dist/api/http/routes/vaults.js +2 -2
  18. package/dist/api/openapi.js +4 -5
  19. package/dist/gates/contract.d.ts +51 -0
  20. package/dist/gates/contract.js +37 -0
  21. package/dist/gates/handler.d.ts +21 -0
  22. package/dist/gates/handler.js +55 -0
  23. package/dist/gates/http-client.d.ts +12 -0
  24. package/dist/gates/http-client.js +160 -0
  25. package/dist/gates/in-process.d.ts +32 -0
  26. package/dist/gates/in-process.js +42 -0
  27. package/dist/gates/inflight.d.ts +39 -0
  28. package/dist/gates/inflight.js +70 -0
  29. package/dist/gates/mcp-handler.d.ts +35 -0
  30. package/dist/gates/mcp-handler.js +142 -0
  31. package/dist/gates/meter.d.ts +24 -0
  32. package/dist/gates/meter.js +165 -0
  33. package/dist/gates/model-gate.d.ts +45 -0
  34. package/dist/gates/model-gate.js +1 -0
  35. package/dist/gates/tenant-vaults.d.ts +64 -0
  36. package/dist/gates/tenant-vaults.js +94 -0
  37. package/dist/gates/tool-calls.d.ts +51 -0
  38. package/dist/gates/tool-calls.js +64 -0
  39. package/dist/gates/tool-client.d.ts +12 -0
  40. package/dist/gates/tool-client.js +201 -0
  41. package/dist/gates/tool-contract.d.ts +116 -0
  42. package/dist/gates/tool-contract.js +64 -0
  43. package/dist/gates/tool-gate.d.ts +45 -0
  44. package/dist/gates/tool-gate.js +10 -0
  45. package/dist/host/admin-api.d.ts +7 -6
  46. package/dist/host/admin-api.js +9 -113
  47. package/dist/host/app.d.ts +1 -1
  48. package/dist/host/app.js +20 -24
  49. package/dist/host/config-for.d.ts +1 -1
  50. package/dist/host/config-for.js +2 -2
  51. package/dist/host/create-host.d.ts +5 -5
  52. package/dist/host/create-host.js +5 -15
  53. package/dist/host/execution.d.ts +20 -18
  54. package/dist/host/execution.js +2 -2
  55. package/dist/host/gates.d.ts +58 -0
  56. package/dist/host/gates.js +127 -0
  57. package/dist/host/http.d.ts +6 -1
  58. package/dist/host/http.js +17 -0
  59. package/dist/host/index.d.ts +1 -1
  60. package/dist/host/main.js +151 -71
  61. package/dist/host/stack-config.d.ts +71 -19
  62. package/dist/host/stack-config.js +192 -18
  63. package/dist/infra/database.d.ts +2 -2
  64. package/dist/infra/execution.d.ts +5 -5
  65. package/dist/infra/execution.js +8 -6
  66. package/dist/infra/index.d.ts +1 -1
  67. package/dist/infra/workers.d.ts +17 -17
  68. package/dist/infra/workers.js +9 -9
  69. package/dist/keys/client.d.ts +9 -0
  70. package/dist/keys/client.js +70 -0
  71. package/dist/keys/contract.d.ts +24 -0
  72. package/dist/keys/contract.js +12 -0
  73. package/dist/keys/keys.d.ts +41 -0
  74. package/dist/keys/keys.js +50 -0
  75. package/dist/mcp/connect.d.ts +43 -3
  76. package/dist/mcp/connect.js +12 -5
  77. package/dist/mcp/pool.d.ts +21 -3
  78. package/dist/mcp/pool.js +15 -2
  79. package/dist/openapi.json +887 -694
  80. package/dist/record/append.d.ts +51 -0
  81. package/dist/record/append.js +20 -0
  82. package/dist/{store/event.js → record/envelope.js} +1 -1
  83. package/dist/record/index.d.ts +11 -0
  84. package/dist/record/index.js +11 -0
  85. package/dist/store/postgres/connect.d.ts +15 -12
  86. package/dist/store/postgres/connect.js +24 -12
  87. package/dist/store/postgres/db.d.ts +16 -0
  88. package/dist/store/postgres/db.js +28 -0
  89. package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
  90. package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
  91. package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
  92. package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
  93. package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
  94. package/dist/store/postgres/migrate.d.ts +68 -0
  95. package/dist/store/postgres/migrate.js +183 -0
  96. package/dist/store/postgres/record-writer.d.ts +4 -0
  97. package/dist/store/postgres/record-writer.js +34 -0
  98. package/dist/store/postgres/record.d.ts +2 -8
  99. package/dist/store/postgres/record.js +50 -54
  100. package/dist/store/postgres/schema.d.ts +3096 -0
  101. package/dist/store/postgres/schema.js +363 -0
  102. package/dist/store/postgres/store.d.ts +2 -4
  103. package/dist/store/postgres/store.js +481 -567
  104. package/dist/store/postgres/tenant.d.ts +56 -0
  105. package/dist/store/postgres/tenant.js +154 -0
  106. package/dist/store/types.d.ts +43 -87
  107. package/dist/store/types.js +0 -1
  108. package/dist/streams/control.d.ts +1 -1
  109. package/dist/streams/control.js +6 -2
  110. package/dist/streams/relay/types.d.ts +2 -1
  111. package/dist/streams/relay/types.js +2 -1
  112. package/dist/streams/types.d.ts +5 -0
  113. package/dist/tenant/advance.d.ts +7 -2
  114. package/dist/tenant/advance.js +95 -16
  115. package/dist/tenant/cause.d.ts +25 -0
  116. package/dist/tenant/cause.js +45 -0
  117. package/dist/tenant/commands.js +4 -2
  118. package/dist/tenant/context.d.ts +26 -3
  119. package/dist/tenant/delivery-token.js +17 -18
  120. package/dist/tenant/delivery.d.ts +1 -1
  121. package/dist/tenant/delivery.js +18 -13
  122. package/dist/tenant/effects.d.ts +15 -1
  123. package/dist/tenant/effects.js +93 -17
  124. package/dist/tenant/ephemeral.d.ts +16 -7
  125. package/dist/tenant/ephemeral.js +45 -111
  126. package/dist/tenant/history.d.ts +64 -0
  127. package/dist/tenant/history.js +120 -0
  128. package/dist/tenant/model-setting.d.ts +2 -2
  129. package/dist/tenant/module.d.ts +14 -28
  130. package/dist/tenant/module.js +131 -271
  131. package/dist/tenant/paths.d.ts +11 -16
  132. package/dist/tenant/paths.js +24 -68
  133. package/dist/tenant/pool.d.ts +1 -1
  134. package/dist/tenant/pool.js +1 -1
  135. package/dist/tenant/principals.d.ts +23 -11
  136. package/dist/tenant/principals.js +61 -49
  137. package/dist/tenant/runtime.d.ts +19 -1
  138. package/dist/tenant/runtime.js +40 -26
  139. package/dist/tenant/scheduler.d.ts +4 -2
  140. package/dist/tenant/scheduler.js +4 -4
  141. package/dist/tenant/session-streams.d.ts +2 -0
  142. package/dist/tenant/session-streams.js +8 -3
  143. package/dist/tenant/status.d.ts +3 -0
  144. package/dist/tenant/status.js +1 -1
  145. package/dist/tenant/store-pg.d.ts +9 -6
  146. package/dist/tenant/store-pg.js +60 -125
  147. package/dist/tenant/streams.d.ts +9 -25
  148. package/dist/tenant/streams.js +7 -22
  149. package/dist/tenant/tokens.js +16 -14
  150. package/dist/tenant/types.d.ts +41 -92
  151. package/dist/tenant/types.js +4 -15
  152. package/dist/tenant/worker.js +4 -4
  153. package/dist/vault/host-model.d.ts +31 -0
  154. package/dist/vault/host-model.js +57 -0
  155. package/dist/vault/service.d.ts +26 -15
  156. package/dist/vault/service.js +70 -109
  157. package/dist/version.d.ts +1 -1
  158. package/dist/version.js +1 -1
  159. package/package.json +9 -5
  160. package/dist/store/memory.d.ts +0 -69
  161. package/dist/store/memory.js +0 -810
  162. package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
  163. package/dist/store/postgres/migrations/001_initial.js +0 -196
  164. package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
  165. package/dist/store/postgres/migrations/002_session_owner.js +0 -13
  166. package/dist/store/postgres/migrations/003_subject_tokens.d.ts +0 -8
  167. package/dist/store/postgres/migrations/003_subject_tokens.js +0 -42
  168. package/dist/store/postgres/migrations/004_publishable_keys.d.ts +0 -6
  169. package/dist/store/postgres/migrations/004_publishable_keys.js +0 -18
  170. package/dist/store/postgres/migrations/005_action_endpoints.d.ts +0 -6
  171. package/dist/store/postgres/migrations/005_action_endpoints.js +0 -32
  172. package/dist/store/postgres/migrations/006_drop_executors.d.ts +0 -11
  173. package/dist/store/postgres/migrations/006_drop_executors.js +0 -32
  174. package/dist/store/postgres/migrations/007_event_time.d.ts +0 -7
  175. package/dist/store/postgres/migrations/007_event_time.js +0 -14
  176. package/dist/store/postgres/migrations/008_durable_streams.d.ts +0 -13
  177. package/dist/store/postgres/migrations/008_durable_streams.js +0 -35
  178. package/dist/store/postgres/migrations/index.d.ts +0 -53
  179. package/dist/store/postgres/migrations/index.js +0 -96
  180. package/dist/store/postgres/migrations/shared/index.d.ts +0 -31
  181. package/dist/store/postgres/migrations/shared/index.js +0 -92
  182. package/dist/store/postgres/names.d.ts +0 -11
  183. package/dist/store/postgres/names.js +0 -42
  184. package/dist/store/postgres/tenants.d.ts +0 -90
  185. package/dist/store/postgres/tenants.js +0 -228
  186. package/dist/streams/relay/memory.d.ts +0 -69
  187. package/dist/streams/relay/memory.js +0 -176
  188. package/dist/tenant/envelope.d.ts +0 -7
  189. package/dist/tenant/envelope.js +0 -22
  190. package/dist/tenant/quarantine-error.d.ts +0 -6
  191. package/dist/tenant/quarantine-error.js +0 -9
  192. package/dist/tenant/quarantine.d.ts +0 -27
  193. package/dist/tenant/quarantine.js +0 -72
  194. package/dist/tenant/store-memory.d.ts +0 -16
  195. package/dist/tenant/store-memory.js +0 -111
  196. /package/dist/{store → record}/cursor.d.ts +0 -0
  197. /package/dist/{store → record}/cursor.js +0 -0
  198. /package/dist/{store/event.d.ts → record/envelope.d.ts} +0 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * The tables of a Tenant database (session-store.md §2–§3): the source drizzle-kit generates
3
+ * the migrations from (`drizzle/`, `CONTRIBUTING.md` "Adding a migration") and the query
4
+ * builder's view of them. Column names are the camelCase keys in snake_case (`casing:
5
+ * "snake_case"`, here, in `db.ts` and in `drizzle.config.ts`).
6
+ *
7
+ * - `nylorun` holds the Tenant's state: the one `tenant` row, the document tables, Action
8
+ * endpoints, principals, vaults and credentials, signing keys, publishable keys, subject
9
+ * epochs and usage, settings, the model usage ledger, the model budgets and the Tool Gate's
10
+ * crossings.
11
+ * - `nylorun_streams` holds the record (Durable Streams §6): `session_events`,
12
+ * `session_log_heads` and the relay's `relay_slots`. The relay's publication is custom SQL
13
+ * (`drizzle/0002_stream_relay.sql`).
14
+ *
15
+ * What the columns keep:
16
+ *
17
+ * - **Bodies are `json`, not `jsonb`** (`jsonText`), written as the text `JSON.stringify`
18
+ * wrote, so every string round-trips exactly, key order included. `jsonb` rejects the
19
+ * escapes `\u0000` (`22P05`) and unpaired surrogates (`\ud800`), which tool output can
20
+ * contain; `json` stores the text verbatim and checks only its syntax.
21
+ * - **Indexed fields of a body are stored generated columns** read through
22
+ * `nylorun.doc(body)` (custom SQL, `drizzle/0000_foundation.sql`), which casts to `jsonb`
23
+ * after replacing those two escapes with `\ufffd`: the body is untouched, and only an indexed
24
+ * field that itself contained one (never an id, status or timestamp) would see U+FFFD.
25
+ * Queries never apply `->`/`->>` to a `json` body (Postgres de-escapes the whole text and
26
+ * fails on the same escapes); they read the generated columns.
27
+ * - **Ids and timestamps compared as text are `COLLATE "C"`** (`textC`), so they order by code
28
+ * point whatever the database's collation.
29
+ * - **Timestamps are ISO text**, except the session lease `owner_expires_at` and the record's
30
+ * `committed_at` (`timestamptz`).
31
+ * - **Sealed secrets are `bytea` columns** (`bytes`), never inside a JSON body.
32
+ * - Sessions carry the store-managed ownership columns (`owner`, `epoch`,
33
+ * `owner_expires_at`), which are never in `body`.
34
+ *
35
+ * Columns are listed in the order the tables have them, so a database created by these
36
+ * migrations and one created by the hand-written migrations they replaced dump the same.
37
+ */
38
+ import { sql } from "drizzle-orm";
39
+ import { bigint, boolean, check, customType, doublePrecision, foreignKey, index, integer, pgSchema, primaryKey, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
40
+ export const TENANT_SCHEMA = "nylorun";
41
+ export const STREAMS_SCHEMA = "nylorun_streams";
42
+ /** The publication the stream relay subscribes to (`drizzle/0002_stream_relay.sql`). */
43
+ export const STREAMS_PUBLICATION = "nylorun_stream_relay";
44
+ export const nylorun = pgSchema(TENANT_SCHEMA);
45
+ export const nylorunStreams = pgSchema(STREAMS_SCHEMA);
46
+ // ---------------------------------------------------------------------------
47
+ // Column types
48
+ /** JSON text of `value`. Throws when `value` is not JSON (`undefined`, a function). */
49
+ export function toJson(value) {
50
+ const json = JSON.stringify(value);
51
+ if (json === undefined)
52
+ throw new Error("Document body must be JSON");
53
+ return json;
54
+ }
55
+ /**
56
+ * A `json` column written as the text `JSON.stringify` makes (`$1::text::json`), so the
57
+ * database keeps exactly that text. postgres.js parses `json` results, so a read gives the
58
+ * parsed value as is.
59
+ */
60
+ export const jsonText = customType({
61
+ dataType: () => "json",
62
+ toDriver: (value) => sql `${toJson(value)}::text::json`,
63
+ });
64
+ /** `bytea` to and from `Uint8Array` (postgres.js reads a `Buffer`; the row gets a copy). */
65
+ export const bytes = customType({
66
+ dataType: () => "bytea",
67
+ toDriver: (value) => Buffer.from(value.buffer, value.byteOffset, value.byteLength),
68
+ fromDriver: (value) => new Uint8Array(value),
69
+ });
70
+ /** `text COLLATE "C"`: ids and ISO timestamps that order by code point. */
71
+ export const textC = customType({
72
+ dataType: () => 'text COLLATE "C"',
73
+ });
74
+ /** A generated column reading `path` of the body through `nylorun.doc(body)`. */
75
+ const field = (path) => text().generatedAlwaysAs(sql.raw(`nylorun.doc(body)${path}`));
76
+ /** A table of JSON documents keyed by `id`. */
77
+ const documents = (name) => nylorun.table(name, { id: textC().primaryKey(), body: jsonText().notNull() });
78
+ // ---------------------------------------------------------------------------
79
+ // nylorun: the Tenant's state
80
+ /** The Tenant envelope: one row (`singleton`). */
81
+ export const tenant = nylorun.table("tenant", {
82
+ id: textC().primaryKey(),
83
+ name: text().notNull(),
84
+ createdAt: text().notNull(),
85
+ updatedAt: text().notNull(),
86
+ /** The number of migrations applied when the row was last migrated (`migrate.ts`). */
87
+ schemaVersion: integer().notNull(),
88
+ singleton: boolean().notNull().default(true).unique("tenant_singleton_key"),
89
+ /** The basin generation session streams go to (Durable Streams §8.1). */
90
+ basinGeneration: integer().notNull().default(0),
91
+ /** Earlier generations whose basins are still to be deleted. */
92
+ retiredGenerations: integer().array().notNull().default(sql `'{}'`),
93
+ }, () => [check("tenant_singleton_check", sql `singleton`)]);
94
+ export const definitions = documents("definitions");
95
+ export const sessions = nylorun.table("sessions", {
96
+ id: textC().primaryKey(),
97
+ body: jsonText().notNull(),
98
+ status: field("->>'status'"),
99
+ agentId: field("->>'agentId'"),
100
+ /** The lease holder; store-managed, never in `body`. */
101
+ owner: text(),
102
+ epoch: bigint({ mode: "number" }).notNull().default(0),
103
+ ownerExpiresAt: timestamp({ withTimezone: true, mode: "date" }),
104
+ /** The session owner (`ownerUserId`), not the lease column `owner`. */
105
+ ownerUserId: field("->>'ownerUserId'"),
106
+ }, (t) => [
107
+ index("sessions_status").on(t.status, t.ownerExpiresAt),
108
+ index("sessions_agent").on(t.agentId),
109
+ index("sessions_owner_user").on(t.ownerUserId),
110
+ ]);
111
+ export const commands = documents("commands");
112
+ export const effects = nylorun.table("effects", {
113
+ id: textC().primaryKey(),
114
+ body: jsonText().notNull(),
115
+ sessionId: field("->'request'->>'sessionId'"),
116
+ turnId: field("->'request'->>'turnId'"),
117
+ kind: field("->'request'->>'kind'"),
118
+ status: field("->>'status'"),
119
+ }, (t) => [
120
+ index("effects_session").on(t.sessionId, t.turnId, t.status),
121
+ index("effects_status").on(t.status, t.kind),
122
+ ]);
123
+ export const actions = nylorun.table("actions", {
124
+ id: textC().primaryKey(),
125
+ body: jsonText().notNull(),
126
+ sessionId: field("->>'sessionId'"),
127
+ turnId: field("->>'turnId'"),
128
+ agentId: field("->>'agentId'"),
129
+ status: field("->>'status'"),
130
+ kind: field("->>'kind'"),
131
+ /** When the Action being delivered is lost (ISO text). */
132
+ deadlineAt: field("->>'deadlineAt'"),
133
+ }, (t) => [
134
+ index("actions_session").on(t.sessionId, t.turnId, t.status),
135
+ index("actions_agent").on(t.agentId, t.status),
136
+ index("actions_status").on(t.status, t.kind),
137
+ index("actions_deadline").on(t.status, t.deadlineAt),
138
+ ]);
139
+ export const sandboxes = documents("sandboxes");
140
+ export const links = nylorun.table("links", {
141
+ id: textC().primaryKey(),
142
+ body: jsonText().notNull(),
143
+ workflowSessionId: field("->>'workflowSessionId'"),
144
+ }, (t) => [index("links_workflow").on(t.workflowSessionId)]);
145
+ /** An application principal: who holds a key, by its SHA-256. */
146
+ export const principals = nylorun.table("principals", {
147
+ id: textC().primaryKey(),
148
+ role: text().$type().notNull(),
149
+ tokenHash: text().notNull().unique("principals_token_hash_key"),
150
+ idempotencyKey: text(),
151
+ createdAt: text().notNull(),
152
+ });
153
+ export const vaults = nylorun.table("vaults", {
154
+ id: textC().primaryKey(),
155
+ name: text().notNull(),
156
+ ownerUserId: text().notNull(),
157
+ /** JSON text, or null. */
158
+ metadataJson: text(),
159
+ createdAt: textC().notNull(),
160
+ scope: text({ enum: ["user", "host"] }).notNull().default("user"),
161
+ }, (t) => [
162
+ index("vaults_owner").on(t.ownerUserId, t.createdAt, t.id),
163
+ uniqueIndex("vaults_one_host").on(t.scope).where(sql `scope = 'host'`),
164
+ check("vaults_scope_check", sql `scope IN ('user', 'host')`),
165
+ ]);
166
+ /** A credential of a vault: its binding in the clear, its secret sealed. */
167
+ export const vaultCredentials = nylorun.table("vault_credentials", {
168
+ id: textC().primaryKey(),
169
+ vaultId: text().notNull(),
170
+ name: text().notNull(),
171
+ type: text({ enum: ["bearer", "oauth", "model"] }).notNull(),
172
+ /** JSON text of the non-secret binding (url, or provider and model). */
173
+ bindingJson: text().notNull(),
174
+ expiresAt: text(),
175
+ createdAt: textC().notNull(),
176
+ rotatedAt: text(),
177
+ kekId: text().notNull(),
178
+ nonce: bytes().notNull(),
179
+ ciphertext: bytes().notNull(),
180
+ wrappedDek: bytes().notNull(),
181
+ }, (t) => [
182
+ foreignKey({
183
+ name: "vault_credentials_vault_id_fkey",
184
+ columns: [t.vaultId],
185
+ foreignColumns: [vaults.id],
186
+ }).onDelete("cascade"),
187
+ index("vault_credentials_vault").on(t.vaultId, t.createdAt, t.id),
188
+ ]);
189
+ export const vaultAudit = nylorun.table("vault_audit", {
190
+ ord: bigint({ mode: "number" }).primaryKey().generatedAlwaysAsIdentity(),
191
+ id: text().notNull().unique("vault_audit_id_key"),
192
+ at: text().notNull(),
193
+ actor: text().notNull(),
194
+ action: text().notNull(),
195
+ vaultId: text(),
196
+ credentialId: text(),
197
+ sessionId: text(),
198
+ target: text(),
199
+ outcome: text().notNull(),
200
+ }, (t) => [index("vault_audit_vault").on(t.vaultId, t.ord)]);
201
+ export const vaultIdempotency = nylorun.table("vault_idempotency", {
202
+ id: text().primaryKey(),
203
+ bodyHash: text().notNull(),
204
+ /** JSON text of the original response. */
205
+ response: text().notNull(),
206
+ });
207
+ export const tenantSettings = nylorun.table("tenant_settings", {
208
+ key: text().primaryKey(),
209
+ value: text().notNull(),
210
+ });
211
+ /** The Tenant's signing keys (subject tokens): the private half sealed, the public JWK not. */
212
+ export const signingKeys = nylorun.table("signing_keys", {
213
+ id: textC().primaryKey(),
214
+ state: text({ enum: ["standby", "current", "previous", "revoked"] }).notNull(),
215
+ alg: text({ enum: ["ES256"] }).notNull(),
216
+ /** JSON text of the public JWK. */
217
+ publicJwk: text().notNull(),
218
+ kekId: text().notNull(),
219
+ nonce: bytes().notNull(),
220
+ ciphertext: bytes().notNull(),
221
+ wrappedDek: bytes().notNull(),
222
+ createdAt: text().notNull(),
223
+ activatedAt: text(),
224
+ retiredAt: text(),
225
+ revokedAt: text(),
226
+ }, (t) => [
227
+ check("signing_keys_state_check", sql `state IN ('standby', 'current', 'previous', 'revoked')`),
228
+ check("signing_keys_alg_check", sql `alg = 'ES256'`),
229
+ uniqueIndex("signing_keys_one_standby").on(t.state).where(sql `state = 'standby'`),
230
+ uniqueIndex("signing_keys_one_current").on(t.state).where(sql `state = 'current'`),
231
+ uniqueIndex("signing_keys_one_previous").on(t.state).where(sql `state = 'previous'`),
232
+ ]);
233
+ /** Each subject's revocation epoch. */
234
+ export const subjectEpochs = nylorun.table("subject_epochs", {
235
+ subject: textC().primaryKey(),
236
+ epoch: bigint({ mode: "number" }).notNull(),
237
+ revokedAt: text().notNull(),
238
+ });
239
+ /** Each subject's turn bucket: tokens left and when they were last refilled. */
240
+ export const subjectUsage = nylorun.table("subject_usage", {
241
+ subject: textC().primaryKey(),
242
+ turnTokens: doublePrecision().notNull(),
243
+ refilledAt: text().notNull(),
244
+ });
245
+ /** Publishable keys (Host feature `browser-access`): public by design. */
246
+ export const publishableKeys = nylorun.table("publishable_keys", {
247
+ id: textC().primaryKey(),
248
+ key: textC().notNull().unique("publishable_keys_key_key"),
249
+ name: text().notNull().unique("publishable_keys_name_key"),
250
+ /** JSON array of allowed origins. */
251
+ originsJson: text().notNull(),
252
+ createdAt: text().notNull(),
253
+ revokedAt: text(),
254
+ });
255
+ /**
256
+ * Action endpoints: where the Runtime delivers each agent's Actions over HTTP, and the health
257
+ * recent deliveries and the last ping report.
258
+ */
259
+ export const endpoints = nylorun.table("endpoints", {
260
+ agentId: textC().primaryKey(),
261
+ url: text().notNull(),
262
+ implementationVersion: text().notNull(),
263
+ manifestHash: text(),
264
+ timeoutMs: integer().notNull(),
265
+ maxConcurrent: integer().notNull(),
266
+ principalId: text(),
267
+ lastDeliveryAt: text(),
268
+ lastSuccessAt: text(),
269
+ lastErrorCode: text(),
270
+ lastErrorMessage: text(),
271
+ consecutiveFailures: integer().notNull().default(0),
272
+ servedImplementationVersion: text(),
273
+ servedManifestHash: text(),
274
+ createdAt: text().notNull(),
275
+ updatedAt: text().notNull(),
276
+ });
277
+ /**
278
+ * The model usage ledger (P1.3): one row per model call a gate served, written after the call.
279
+ * A second row for the same effect (a call the gate ran again after a gateway restart) is
280
+ * flagged `duplicate`; both were billed. Budgets read their spend from it. Token counts are 0
281
+ * when the provider sent none.
282
+ */
283
+ export const modelUsage = nylorun.table("model_usage", {
284
+ id: textC().primaryKey(),
285
+ /** The model effect's id; unique per Tenant, since it carries the turn id. */
286
+ effectKey: textC().notNull(),
287
+ sessionId: textC().notNull(),
288
+ turnId: textC().notNull(),
289
+ agentId: textC().notNull(),
290
+ provider: text(),
291
+ model: text(),
292
+ inputTokens: integer().notNull(),
293
+ outputTokens: integer().notNull(),
294
+ totalTokens: integer().notNull(),
295
+ cachedTokens: integer().notNull(),
296
+ cacheWriteTokens: integer().notNull(),
297
+ reasoningTokens: integer().notNull(),
298
+ costUsd: doublePrecision().notNull(),
299
+ /** True when an earlier row has the same `effectKey`: the provider billed the call twice. */
300
+ duplicate: boolean().notNull(),
301
+ createdAt: text().notNull(),
302
+ }, (t) => [
303
+ index("model_usage_effect").on(t.effectKey),
304
+ index("model_usage_agent").on(t.agentId, t.createdAt),
305
+ index("model_usage_turn").on(t.turnId),
306
+ index("model_usage_created").on(t.createdAt),
307
+ ]);
308
+ /**
309
+ * Model budgets (P1.3): hard caps the model gate checks before each call, at least one limit
310
+ * set. One row per scope: `turn` and `tenant` use `*`, `agent` the agent id. `period` is the
311
+ * UTC `day` or `month` the spend is counted over, or null for `turn`.
312
+ */
313
+ export const modelBudgets = nylorun.table("model_budgets", {
314
+ scope: text({ enum: ["tenant", "agent", "turn"] }).notNull(),
315
+ scopeId: textC().notNull(),
316
+ period: text({ enum: ["day", "month"] }),
317
+ limitUsd: doublePrecision(),
318
+ limitTokens: bigint({ mode: "number" }),
319
+ updatedAt: text().notNull(),
320
+ }, (t) => [primaryKey({ name: "model_budgets_pkey", columns: [t.scope, t.scopeId] })]);
321
+ /**
322
+ * The Tool Gate's keyed MCP calls (F4.1 G3): one row per call, written by the gates service
323
+ * before it calls the server and given the answer after. A re-send whose row has an answer
324
+ * gets it; one whose row has none, and that the gateway no longer runs, was lost with an
325
+ * earlier gateway and answers `uncertain`, so a call is never run twice. Rows are deleted a
326
+ * day after they settle.
327
+ */
328
+ export const toolCrossings = nylorun.table("tool_crossings", {
329
+ /** The call's effect id (`Idempotency-Key`). */
330
+ key: textC().primaryKey(),
331
+ /** SHA-256 of the canonical request: a re-send with another request is refused. */
332
+ hash: text().notNull(),
333
+ startedAt: textC().notNull(),
334
+ settledAt: textC(),
335
+ /** The gate's answer (`McpAnswer`), once the call ended; null while it runs or once lost. */
336
+ answer: jsonText(),
337
+ }, (t) => [index("tool_crossings_settled").on(t.settledAt)]);
338
+ // ---------------------------------------------------------------------------
339
+ // nylorun_streams: the record
340
+ /** Each session's log head: the next seq and the basin generation its events go to. */
341
+ export const sessionLogHeads = nylorunStreams.table("session_log_heads", {
342
+ sessionId: textC().primaryKey(),
343
+ generation: integer().notNull(),
344
+ head: bigint({ mode: "number" }).notNull().default(0),
345
+ });
346
+ /** The record of every session event, the `nylorun.event/2` envelope as written. */
347
+ export const sessionEvents = nylorunStreams.table("session_events", {
348
+ sessionId: textC().notNull(),
349
+ seq: bigint({ mode: "number" }).notNull(),
350
+ generation: integer().notNull(),
351
+ type: text().notNull(),
352
+ body: jsonText().notNull(),
353
+ committedAt: timestamp({ withTimezone: true, mode: "string" }).notNull().defaultNow(),
354
+ }, (t) => [primaryKey({ name: "session_events_pkey", columns: [t.sessionId, t.seq] })]);
355
+ /**
356
+ * One row per replication slot: whether the record still has to be reconciled with S2 after
357
+ * the slot was created (a crash during reconciliation leaves it pending). The stream relay's
358
+ * replication adapter reads and writes it (`adapters/replication/pgoutput.ts`).
359
+ */
360
+ export const relaySlots = nylorunStreams.table("relay_slots", {
361
+ slotName: text().primaryKey(),
362
+ reconcilePending: boolean().notNull(),
363
+ });
@@ -1,11 +1,9 @@
1
1
  import type { Sql } from "postgres";
2
2
  import type { SessionStore, SessionStoreOptions } from "../types.js";
3
3
  export interface PostgresSessionStoreOptions extends SessionStoreOptions {
4
- /** The shared pool. The store never ends it. */
4
+ /** The pool on the Tenant's database. The store never ends it. */
5
5
  sql: Sql;
6
- /** The Tenant schema. Defaults to `tenantSchemaName(tenantId)`. */
7
- schema?: string;
8
- /** The version `health()` expects. Defaults to `POSTGRES_SCHEMA_VERSION`. */
6
+ /** The schema version `health()` expects. Defaults to the shipped migrations' count. */
9
7
  schemaVersion?: number;
10
8
  }
11
9
  export declare function createPostgresSessionStore(options: PostgresSessionStoreOptions): SessionStore;