@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,31 @@
1
+ /**
2
+ * The Tenant's host model credential in plaintext: reading it to call the provider, and
3
+ * writing back a credential the provider adapter refreshed (OAuth). Only the Model Gate holds
4
+ * one (`gates/`; `scripts/check-boundaries.mjs` enforces it), so the loop process never reads a
5
+ * model credential. `VaultService` seals the credential but never reads it back.
6
+ *
7
+ * Decryption runs after the read commits; the key is resolved before the write's transaction.
8
+ */
9
+ import type { SessionStore } from "../store/types.js";
10
+ import { type HostModelSecret } from "./service.js";
11
+ export interface HostModelVaultOptions {
12
+ readonly store: SessionStore;
13
+ /** The Tenant's key-encryption key; may throw when it is missing. */
14
+ readonly kek: () => Buffer;
15
+ }
16
+ export declare class HostModelVault {
17
+ private readonly store;
18
+ private readonly kek;
19
+ constructor(options: HostModelVaultOptions);
20
+ /** Reads the active host model secret. Decrypts after the read commits. */
21
+ readHostModel(): Promise<HostModelSecret | undefined>;
22
+ /** Replaces the active host model's secret, keeping its binding. */
23
+ updateHostCredential(credential: {
24
+ type: "api_key" | "oauth";
25
+ key?: string;
26
+ env?: Record<string, string>;
27
+ refresh?: string;
28
+ access?: string;
29
+ expires?: number;
30
+ }): Promise<void>;
31
+ }
@@ -0,0 +1,57 @@
1
+ import { encryptSecret } from "./crypto.js";
2
+ import { VaultError } from "./error.js";
3
+ import { activeHostCredentialRow, HOST_VAULT_ID, hostModelAad, modelBinding, readHostPayload, } from "./service.js";
4
+ export class HostModelVault {
5
+ store;
6
+ kek;
7
+ constructor(options) {
8
+ this.store = options.store;
9
+ this.kek = options.kek;
10
+ }
11
+ /** Reads the active host model secret. Decrypts after the read commits. */
12
+ async readHostModel() {
13
+ const row = await this.store.tx((t) => activeHostCredentialRow(t));
14
+ if (!row)
15
+ return undefined;
16
+ const binding = modelBinding(row);
17
+ const payload = readHostPayload(this.kek(), row, binding);
18
+ const credential = binding.authType === "oauth"
19
+ ? { type: "oauth", ...payload.oauth }
20
+ : {
21
+ type: "api_key",
22
+ key: payload.apiKey,
23
+ ...(payload.env ? { env: payload.env } : {}),
24
+ };
25
+ return {
26
+ provider: binding.provider,
27
+ model: binding.model,
28
+ ...(binding.baseUrl ? { baseUrl: binding.baseUrl } : {}),
29
+ ...(binding.settings ? { settings: binding.settings } : {}),
30
+ authType: binding.authType,
31
+ credential,
32
+ };
33
+ }
34
+ /** Replaces the active host model's secret, keeping its binding. */
35
+ async updateHostCredential(credential) {
36
+ const kek = this.kek();
37
+ await this.store.tx(async (t) => {
38
+ const row = await activeHostCredentialRow(t);
39
+ if (!row)
40
+ throw new VaultError(404, "Model provider is not configured");
41
+ const binding = modelBinding(row);
42
+ const payload = credential.type === "oauth"
43
+ ? { oauth: credential }
44
+ : {
45
+ apiKey: credential.key,
46
+ ...(credential.env ? { env: credential.env } : {}),
47
+ };
48
+ const sealed = encryptSecret(kek, hostModelAad(binding.provider, binding.model), Buffer.from(JSON.stringify(payload), "utf8"));
49
+ await t.updateCredential(HOST_VAULT_ID, row.id, {
50
+ type: "model",
51
+ bindingJson: JSON.stringify({ ...binding, authType: credential.type }),
52
+ ...sealed,
53
+ rotatedAt: new Date().toISOString(),
54
+ });
55
+ });
56
+ }
57
+ }
@@ -1,5 +1,6 @@
1
1
  import type { CreateCredentialRequest, CreateVaultRequest, CredentialInfo, CredentialSelection, HostModelProviderInfo, HostModelView, PutHostModelRequest, CustomModelSettings, RotateCredentialRequest, SelectHostModelRequest, VaultInfo } from "@nylorun/core/contracts";
2
- import type { SessionStore, Tx } from "../store/types.js";
2
+ import type { SessionStore, Tx, VaultCredentialRow } from "../store/types.js";
3
+ export declare const HOST_VAULT_ID = "host";
3
4
  export type HostModelSecret = {
4
5
  provider: string;
5
6
  model: string;
@@ -87,16 +88,6 @@ export declare class VaultService {
87
88
  }>;
88
89
  putHostModel(body: PutHostModelRequest): Promise<HostModelView>;
89
90
  selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>;
90
- /** Reads the active host model secret. Decrypts after the read commits. */
91
- readHostModel(): Promise<HostModelSecret | undefined>;
92
- updateHostCredential(credential: {
93
- type: "api_key" | "oauth";
94
- key?: string;
95
- env?: Record<string, string>;
96
- refresh?: string;
97
- access?: string;
98
- expires?: number;
99
- }): Promise<void>;
100
91
  /** Calls the token endpoint outside any transaction, then stores the result. */
101
92
  /**
102
93
  * Refreshes an OAuth grant once for every concurrent use in this process. A rotating refresh
@@ -117,12 +108,32 @@ export declare class VaultService {
117
108
  private validateHostModel;
118
109
  private insertHostCredential;
119
110
  private hostModelView;
120
- private hostModelRows;
121
- private hostCredentialRowFor;
122
- private activeHostCredentialRow;
123
- private activeProviderId;
124
111
  private setActiveProvider;
125
112
  private deleteHostProviderRows;
126
113
  private credentialRow;
127
114
  private audit;
128
115
  }
116
+ /** The host vault's model credential rows that parse. */
117
+ export declare function hostModelRows(t: Tx): Promise<VaultCredentialRow[]>;
118
+ /** The host model credential of `provider`. */
119
+ export declare function hostCredentialRowFor(t: Tx, provider: string): Promise<VaultCredentialRow | undefined>;
120
+ /** The provider the host model selection names, or the only (or legacy) one configured. */
121
+ export declare function activeProviderId(t: Tx): Promise<string | undefined>;
122
+ /** The active host model credential row (ciphertext), in `t`. */
123
+ export declare function activeHostCredentialRow(t: Tx): Promise<VaultCredentialRow | undefined>;
124
+ export declare function readHostPayload(kek: Buffer, row: VaultCredentialRow, binding: ModelBinding): HostSecretPayload;
125
+ type ModelBinding = {
126
+ provider: string;
127
+ model: string;
128
+ baseUrl?: string;
129
+ settings?: CustomModelSettings;
130
+ authType: "api_key" | "oauth";
131
+ };
132
+ type HostSecretPayload = {
133
+ apiKey?: string;
134
+ env?: Record<string, string>;
135
+ oauth?: HostModelSecret["credential"];
136
+ };
137
+ export declare function modelBinding(row: VaultCredentialRow): ModelBinding;
138
+ export declare function hostModelAad(provider: string, model: string): Buffer;
139
+ export {};
@@ -34,10 +34,12 @@
34
34
  * listHostProviders(): Promise<{ providers: HostModelProviderInfo[] }>
35
35
  * putHostModel(body: PutHostModelRequest): Promise<HostModelView>
36
36
  * selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>
37
- * readHostModel(): Promise<HostModelSecret | undefined>
38
- * updateHostCredential(credential): Promise<void>
39
37
  * ```
40
38
  *
39
+ * Reading the host model's secret and writing back a refreshed one are `HostModelVault`'s
40
+ * (`vault/host-model.ts`), which only the Model Gate uses: this service seals the host model's
41
+ * credential but never reads it back.
42
+ *
41
43
  * ## Transactions and I/O
42
44
  *
43
45
  * - No network I/O runs inside a transaction. The KEK getter may touch the
@@ -65,7 +67,7 @@ import { hostModelCatalog } from "../model/catalog.js";
65
67
  import { normalizeVaultUrl } from "./url.js";
66
68
  const REFRESH_SKEW_MS = 60_000;
67
69
  const REFRESH_TIMEOUT_MS = 30_000;
68
- const HOST_VAULT_ID = "host";
70
+ export const HOST_VAULT_ID = "host";
69
71
  const HOST_MODEL_ID = "host-model";
70
72
  export class VaultService {
71
73
  store;
@@ -375,9 +377,9 @@ export class VaultService {
375
377
  }
376
378
  async listHostProviders() {
377
379
  return this.store.tx(async (t) => {
378
- const active = await this.activeProviderId(t);
380
+ const active = await activeProviderId(t);
379
381
  const catalog = new Map(hostModelCatalog().providers.map((provider) => [provider.id, provider.name]));
380
- const providers = (await this.hostModelRows(t)).map((row) => {
382
+ const providers = (await hostModelRows(t)).map((row) => {
381
383
  const binding = modelBinding(row);
382
384
  return {
383
385
  id: binding.provider,
@@ -434,7 +436,7 @@ export class VaultService {
434
436
  const kek = this.kek();
435
437
  return this.store.tx((t) => this.replay(t, `host-model-select:${body.idempotencyKey}`, body, async () => {
436
438
  this.validateHostModel(body);
437
- const row = await this.hostCredentialRowFor(t, body.provider);
439
+ const row = await hostCredentialRowFor(t, body.provider);
438
440
  if (!row)
439
441
  throw new VaultError(404, "Model provider is not configured");
440
442
  const binding = modelBinding(row);
@@ -462,51 +464,6 @@ export class VaultService {
462
464
  return this.hostModelView(t);
463
465
  }));
464
466
  }
465
- /** Reads the active host model secret. Decrypts after the read commits. */
466
- async readHostModel() {
467
- const row = await this.store.tx((t) => this.activeHostCredentialRow(t));
468
- if (!row)
469
- return undefined;
470
- const binding = modelBinding(row);
471
- const payload = readHostPayload(this.kek(), row, binding);
472
- const credential = binding.authType === "oauth"
473
- ? { type: "oauth", ...payload.oauth }
474
- : {
475
- type: "api_key",
476
- key: payload.apiKey,
477
- ...(payload.env ? { env: payload.env } : {}),
478
- };
479
- return {
480
- provider: binding.provider,
481
- model: binding.model,
482
- ...(binding.baseUrl ? { baseUrl: binding.baseUrl } : {}),
483
- ...(binding.settings ? { settings: binding.settings } : {}),
484
- authType: binding.authType,
485
- credential,
486
- };
487
- }
488
- async updateHostCredential(credential) {
489
- const kek = this.kek();
490
- await this.store.tx(async (t) => {
491
- const row = await this.activeHostCredentialRow(t);
492
- if (!row)
493
- throw new VaultError(404, "Model provider is not configured");
494
- const binding = modelBinding(row);
495
- const payload = credential.type === "oauth"
496
- ? { oauth: credential }
497
- : {
498
- apiKey: credential.key,
499
- ...(credential.env ? { env: credential.env } : {}),
500
- };
501
- const sealed = encryptSecret(kek, hostModelAad(binding.provider, binding.model), Buffer.from(JSON.stringify(payload), "utf8"));
502
- await t.updateCredential(HOST_VAULT_ID, row.id, {
503
- type: "model",
504
- bindingJson: JSON.stringify({ ...binding, authType: credential.type }),
505
- ...sealed,
506
- rotatedAt: new Date().toISOString(),
507
- });
508
- });
509
- }
510
467
  // --- internals -----------------------------------------------------------------
511
468
  /** Calls the token endpoint outside any transaction, then stores the result. */
512
469
  /**
@@ -725,7 +682,7 @@ export class VaultService {
725
682
  });
726
683
  }
727
684
  async hostModelView(t) {
728
- const row = await this.activeHostCredentialRow(t);
685
+ const row = await activeHostCredentialRow(t);
729
686
  if (!row)
730
687
  return { configured: false };
731
688
  const binding = modelBinding(row);
@@ -738,65 +695,12 @@ export class VaultService {
738
695
  ...(binding.settings ? { settings: binding.settings } : {}),
739
696
  };
740
697
  }
741
- async hostModelRows(t) {
742
- return (await t.credentialsForVault(HOST_VAULT_ID, { type: "model" })).filter((row) => {
743
- try {
744
- modelBinding(row);
745
- return true;
746
- }
747
- catch {
748
- return false;
749
- }
750
- });
751
- }
752
- async hostCredentialRowFor(t, provider) {
753
- const preferred = hostModelCredentialId(provider);
754
- const rows = await this.hostModelRows(t);
755
- return (rows.find((row) => row.id === preferred) ??
756
- rows.find((row) => modelBinding(row).provider === provider));
757
- }
758
- async activeHostCredentialRow(t) {
759
- const active = await this.activeProviderId(t);
760
- if (!active)
761
- return undefined;
762
- return this.hostCredentialRowFor(t, active);
763
- }
764
- async activeProviderId(t) {
765
- const vault = await t.getVault(HOST_VAULT_ID);
766
- if (vault?.metadataJson) {
767
- let activeProvider;
768
- try {
769
- activeProvider = JSON.parse(vault.metadataJson).activeProvider;
770
- }
771
- catch {
772
- /* Fall through to the only configured provider. */
773
- }
774
- if (typeof activeProvider === "string" && activeProvider) {
775
- try {
776
- if (await this.hostCredentialRowFor(t, activeProvider))
777
- return activeProvider;
778
- }
779
- catch {
780
- /* An invalid provider id falls through, as before. */
781
- }
782
- }
783
- }
784
- const rows = await this.hostModelRows(t);
785
- if (rows.length === 0)
786
- return undefined;
787
- if (rows.length === 1)
788
- return modelBinding(rows[0]).provider;
789
- const legacy = rows.find((row) => row.id === HOST_MODEL_ID);
790
- if (legacy)
791
- return modelBinding(legacy).provider;
792
- return modelBinding(rows[0]).provider;
793
- }
794
698
  async setActiveProvider(t, provider) {
795
699
  await this.ensureHostVault(t);
796
700
  await t.updateVaultMetadata(HOST_VAULT_ID, JSON.stringify({ activeProvider: provider }));
797
701
  }
798
702
  async deleteHostProviderRows(t, provider) {
799
- const ids = (await this.hostModelRows(t))
703
+ const ids = (await hostModelRows(t))
800
704
  .filter((row) => modelBinding(row).provider === provider)
801
705
  .map((row) => row.id);
802
706
  ids.push(hostModelCredentialId(provider));
@@ -862,7 +766,64 @@ function readPayload(kek, row) {
862
766
  plaintext.fill(0);
863
767
  }
864
768
  }
865
- function readHostPayload(kek, row, binding) {
769
+ /** The host vault's model credential rows that parse. */
770
+ export async function hostModelRows(t) {
771
+ return (await t.credentialsForVault(HOST_VAULT_ID, { type: "model" })).filter((row) => {
772
+ try {
773
+ modelBinding(row);
774
+ return true;
775
+ }
776
+ catch {
777
+ return false;
778
+ }
779
+ });
780
+ }
781
+ /** The host model credential of `provider`. */
782
+ export async function hostCredentialRowFor(t, provider) {
783
+ const preferred = hostModelCredentialId(provider);
784
+ const rows = await hostModelRows(t);
785
+ return (rows.find((row) => row.id === preferred) ??
786
+ rows.find((row) => modelBinding(row).provider === provider));
787
+ }
788
+ /** The provider the host model selection names, or the only (or legacy) one configured. */
789
+ export async function activeProviderId(t) {
790
+ const vault = await t.getVault(HOST_VAULT_ID);
791
+ if (vault?.metadataJson) {
792
+ let activeProvider;
793
+ try {
794
+ activeProvider = JSON.parse(vault.metadataJson).activeProvider;
795
+ }
796
+ catch {
797
+ /* Fall through to the only configured provider. */
798
+ }
799
+ if (typeof activeProvider === "string" && activeProvider) {
800
+ try {
801
+ if (await hostCredentialRowFor(t, activeProvider))
802
+ return activeProvider;
803
+ }
804
+ catch {
805
+ /* An invalid provider id falls through, as before. */
806
+ }
807
+ }
808
+ }
809
+ const rows = await hostModelRows(t);
810
+ if (rows.length === 0)
811
+ return undefined;
812
+ if (rows.length === 1)
813
+ return modelBinding(rows[0]).provider;
814
+ const legacy = rows.find((row) => row.id === HOST_MODEL_ID);
815
+ if (legacy)
816
+ return modelBinding(legacy).provider;
817
+ return modelBinding(rows[0]).provider;
818
+ }
819
+ /** The active host model credential row (ciphertext), in `t`. */
820
+ export async function activeHostCredentialRow(t) {
821
+ const active = await activeProviderId(t);
822
+ if (!active)
823
+ return undefined;
824
+ return hostCredentialRowFor(t, active);
825
+ }
826
+ export function readHostPayload(kek, row, binding) {
866
827
  const plaintext = decryptSecret(kek, hostModelAad(binding.provider, binding.model), Buffer.from(row.nonce), Buffer.from(row.ciphertext), Buffer.from(row.wrappedDek), row.kekId);
867
828
  try {
868
829
  return JSON.parse(plaintext.toString("utf8"));
@@ -933,7 +894,7 @@ function requireTimestamp(value) {
933
894
  throw new VaultError(400, "Credential expiry is invalid");
934
895
  return new Date(Date.parse(value)).toISOString();
935
896
  }
936
- function modelBinding(row) {
897
+ export function modelBinding(row) {
937
898
  const binding = JSON.parse(row.bindingJson);
938
899
  if (!binding.provider || !binding.model || !binding.authType)
939
900
  throw new VaultError(500, "Host model credential is unreadable");
@@ -944,7 +905,7 @@ function hostModelCredentialId(provider) {
944
905
  throw new VaultError(400, "Invalid provider id");
945
906
  return `${HOST_MODEL_ID}:${provider}`;
946
907
  }
947
- function hostModelAad(provider, model) {
908
+ export function hostModelAad(provider, model) {
948
909
  return Buffer.from(canonical({
949
910
  scope: "host",
950
911
  vaultId: HOST_VAULT_ID,
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const RUNTIME_VERSION = "0.14.0-beta";
1
+ export declare const RUNTIME_VERSION = "0.15.0-beta";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Kept in step with package.json by runtime/scripts/check-package.mjs, which runs in npm run check.
2
2
  // release:prepare also rewrites this when it bumps @nylorun/runtime.
3
- export const RUNTIME_VERSION = "0.14.0-beta";
3
+ export const RUNTIME_VERSION = "0.15.0-beta";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/runtime",
3
- "version": "0.14.0-beta",
3
+ "version": "0.15.0-beta",
4
4
  "description": "Nylorun Runtime: the agent execution Host on Postgres, Restate and S2, and model providers.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -45,7 +45,7 @@
45
45
  "provenance": true
46
46
  },
47
47
  "scripts": {
48
- "build": "npm run clean && node ../scripts/check-typescript-version.mjs && tsc -p tsconfig.json && node scripts/build-openapi.mjs",
48
+ "build": "npm run clean && node ../scripts/check-typescript-version.mjs && tsc -p tsconfig.json && node scripts/copy-migrations.mjs && node scripts/build-openapi.mjs",
49
49
  "check": "npm run build && npm test && node scripts/check-package.mjs",
50
50
  "typecheck": "node ../scripts/check-typescript-version.mjs && tsc -p tsconfig.json --noEmit",
51
51
  "test": "vitest run",
@@ -54,7 +54,9 @@
54
54
  "test:stack:down": "node test/stack/up.mjs down",
55
55
  "prepack": "npm run build",
56
56
  "clean": "node -e \"import('node:fs').then(({rmSync})=>rmSync('dist',{recursive:true,force:true}))\"",
57
- "start": "node dist/host/main.js"
57
+ "start": "node dist/host/main.js",
58
+ "db:generate": "drizzle-kit generate",
59
+ "db:studio": "drizzle-kit studio"
58
60
  },
59
61
  "dependencies": {
60
62
  "@ag-ui/core": "~1.0.0",
@@ -63,10 +65,11 @@
63
65
  "@hono/node-server": "^2.1.1",
64
66
  "@hono/zod-openapi": "^1.6.3",
65
67
  "@modelcontextprotocol/sdk": "^1.30.0",
66
- "@nylorun/core": "0.9.0-beta",
67
- "@nylorun/harness": "0.21.0-beta",
68
+ "@nylorun/core": "0.10.0-beta",
69
+ "@nylorun/harness": "0.21.1-beta",
68
70
  "@restatedev/restate-sdk": "1.16.9",
69
71
  "@s2-dev/streamstore": "0.27.0",
72
+ "drizzle-orm": "^0.45.3",
70
73
  "hono": "^4.13.12",
71
74
  "jose": "^6.2.12",
72
75
  "just-bash": "3.4.2",
@@ -82,6 +85,7 @@
82
85
  "@types/node": "^26.5.0",
83
86
  "@types/pg": "^8.23.1",
84
87
  "@typescript/native": "npm:typescript@^7.0.2",
88
+ "drizzle-kit": "^0.31.11",
85
89
  "typescript": "npm:@typescript/typescript6@^6.0.2",
86
90
  "vitest": "^5.0.1"
87
91
  }
@@ -1,69 +0,0 @@
1
- import { MemoryRecord } from "../streams/relay/memory.js";
2
- import { type CommitListener, type DocTable, type EndpointRow, type BasinGenerations, type PrincipalRow, type PublishableKeyRow, type SessionOwnership, type SessionStore, type SessionStoreOptions, type SigningKeyRow, type StoreHealth, type SubjectUsageRow, type Tx, type VaultAuditRow, type VaultCredentialRow, type VaultIdempotencyRow, type VaultRow } from "./types.js";
3
- /** Schema version the fake reports; it has no migrations. */
4
- export declare const MEMORY_SCHEMA_VERSION = 1;
5
- type SessionMeta = SessionOwnership;
6
- interface State {
7
- /** JSON text per id, like a `body` column. */
8
- docs: Record<DocTable, Map<string, string>>;
9
- sessionMeta: Map<string, SessionMeta>;
10
- /** sessionId → its log head: the next seq and the basin generation it was started in. */
11
- heads: Map<string, {
12
- head: number;
13
- generation: number;
14
- }>;
15
- basin: BasinGenerations;
16
- endpoints: Map<string, EndpointRow>;
17
- principals: Map<string, PrincipalRow>;
18
- vaults: Map<string, VaultRow>;
19
- credentials: Map<string, VaultCredentialRow>;
20
- audit: VaultAuditRow[];
21
- idempotency: Map<string, VaultIdempotencyRow>;
22
- settings: Map<string, string>;
23
- signingKeys: Map<string, SigningKeyRow>;
24
- subjectEpochs: Map<string, number>;
25
- subjectUsage: Map<string, SubjectUsageRow>;
26
- publishableKeys: Map<string, PublishableKeyRow>;
27
- }
28
- /**
29
- * The data behind in-memory Session Stores: what a database is to its connections. Several
30
- * `MemorySessionStore`s on one `MemoryStoreData` see each other's commits and serialize their
31
- * transactions together, as several processes on one Tenant schema would; it outlives them,
32
- * so a test can close a Tenant and open it again.
33
- */
34
- export declare class MemoryStoreData {
35
- /** @internal */ state: State;
36
- /** @internal */ queue: Promise<void>;
37
- /** The record of committed events, read back by the stream relay. */
38
- readonly record: MemoryRecord;
39
- }
40
- /**
41
- * In-memory `SessionStore` for unit tests. Not a supported profile.
42
- *
43
- * Transactions are serialized (one at a time), each works on a copy of the
44
- * state and replaces it on commit, so a throw rolls back everything: documents,
45
- * sequences, record rows and `afterCommit` callbacks. Commit listeners see
46
- * only this store's commits, as with a Postgres connection.
47
- */
48
- export declare class MemorySessionStore implements SessionStore {
49
- /** Shared with other stores on the same data; a fresh, private one by default. */
50
- private readonly data;
51
- readonly tenantId: string;
52
- private readonly active;
53
- private readonly listeners;
54
- private readonly now;
55
- private readonly onError;
56
- private closed;
57
- constructor(options: SessionStoreOptions,
58
- /** Shared with other stores on the same data; a fresh, private one by default. */
59
- data?: MemoryStoreData);
60
- tx<T>(fn: (t: Tx) => Promise<T>): Promise<T>;
61
- onCommit(listener: CommitListener): () => void;
62
- record(): MemoryRecord;
63
- health(): Promise<StoreHealth>;
64
- close(): Promise<void>;
65
- private acquire;
66
- }
67
- /** The time column a signing key's new state stamps. */
68
- export declare function signingKeyStamp(state: SigningKeyRow["state"], at: string): Partial<SigningKeyRow>;
69
- export {};