@nylorun/runtime 0.38.0-beta → 0.40.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 (40) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/dist/api/a2a/endpoint.js +1 -1
  3. package/dist/api/a2a/protocol.d.ts +6 -0
  4. package/dist/api/a2a/protocol.js +13 -0
  5. package/dist/api/a2a/routes.js +1 -0
  6. package/dist/api/components.d.ts +1726 -80
  7. package/dist/api/http/routes/agents.js +3 -3
  8. package/dist/core/turn-manifest.d.ts +23 -0
  9. package/dist/core/turn-manifest.js +36 -1
  10. package/dist/gates/policy.d.ts +13 -3
  11. package/dist/gates/policy.js +17 -4
  12. package/dist/harness/executors.d.ts +4 -2
  13. package/dist/harness/executors.js +26 -4
  14. package/dist/harness-api/record.js +4 -2
  15. package/dist/harness-api/server.js +37 -13
  16. package/dist/harness-api/start.js +43 -5
  17. package/dist/keys/keys.d.ts +6 -3
  18. package/dist/keys/keys.js +1 -1
  19. package/dist/management-openapi.json +6 -4
  20. package/dist/mcp/pool.d.ts +5 -0
  21. package/dist/mcp/pool.js +1 -1
  22. package/dist/mcp/snapshot.d.ts +3 -1
  23. package/dist/mcp/snapshot.js +5 -3
  24. package/dist/openapi.json +1637 -564
  25. package/dist/store/postgres/store.js +14 -0
  26. package/dist/store/types.d.ts +9 -0
  27. package/dist/tenant/agent-versions.d.ts +13 -5
  28. package/dist/tenant/agent-versions.js +30 -6
  29. package/dist/tenant/agents.d.ts +14 -2
  30. package/dist/tenant/agents.js +51 -13
  31. package/dist/tenant/commands.js +16 -8
  32. package/dist/tenant/context.d.ts +30 -1
  33. package/dist/tenant/session-model.d.ts +35 -6
  34. package/dist/tenant/session-model.js +77 -9
  35. package/dist/tenant/sessions.js +13 -3
  36. package/dist/vault/service.d.ts +44 -3
  37. package/dist/vault/service.js +68 -27
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.js +1 -1
  40. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import type { CredentialCoverage, CredentialCoverageRequest, CredentialIdentity, EnvironmentSecretSlot, AgentSecretView, HostModelAuthType, HostModelProviderInfo, HostModelView, SessionModelChoice, SessionModelView, PutAgentSecretRequest, PutHostModelRequest, CustomModelSettings, ModelInput, SelectHostModelRequest } from "@nylorun/core/contracts";
1
+ import type { CredentialCoverage, CredentialCoverageRequest, CredentialIdentity, EnvironmentSecretInject, EnvironmentSecretSlot, AgentSecretView, HostModelAuthType, HostModelProviderInfo, HostModelView, SessionModelChoice, SessionModelView, PutAgentSecretRequest, PutHostModelRequest, CustomModelSettings, ModelInput, SelectHostModelRequest } from "@nylorun/core/contracts";
2
2
  import type { AgentManifest, WorkflowManifest } from "@nylorun/core/define";
3
3
  import type { SessionStore, Tx, VaultCredentialRow } from "../store/types.js";
4
4
  export declare const HOST_VAULT_ID = "host";
@@ -43,6 +43,35 @@ export interface SealedSecretCopies {
43
43
  /** Names left behind: no slot of the fork declares them, or their slot cannot take the value. */
44
44
  readonly dropped: readonly string[];
45
45
  }
46
+ /** A user credential's `bindingJson`: everything about it that is not secret. */
47
+ type CredentialBinding = {
48
+ /** A `bearer` or `headers` credential's server URL; absent for the environment kinds. */
49
+ url?: string;
50
+ /** An `environment_secret`'s variable, hosts and header (R2c); sealed into its AAD. */
51
+ secretName?: string;
52
+ allowedHosts?: string[];
53
+ inject?: EnvironmentSecretInject;
54
+ /** An `environment_variable`'s name and plain value (R2c): not a secret. */
55
+ variableName?: string;
56
+ variableValue?: string;
57
+ via?: string;
58
+ /**
59
+ * `slot`: `via` is the secret slot's, copied when it was written, not the write's own
60
+ * (`secret-routing`): a request follows the slot of the version the Agent runs (`routingOf`).
61
+ * Absent: `via` is the secret's own (or, written before the marker, the slot's then).
62
+ */
63
+ viaFrom?: "slot";
64
+ /** How the gateway at `via` prefixes tool names (`gateway-credentials`). */
65
+ toolPrefix?: string;
66
+ identity?: CredentialIdentity;
67
+ /** A `headers` credential's names, lower-cased. */
68
+ headerNames?: string[];
69
+ };
70
+ /** How a secret write was made (`putEnvironmentSecret`); `null` crosses the keys wire as none. */
71
+ export interface SecretWriteOptions {
72
+ /** The body's `via` is its secret slot's, not the write's own (`secret-routing`). */
73
+ readonly viaFromSlot?: boolean;
74
+ }
46
75
  export type AuthorizeResult = {
47
76
  status: "unauthenticated";
48
77
  url: string;
@@ -114,8 +143,8 @@ export declare class VaultService {
114
143
  * bound to one of its hosts or to its URL. Runs under the Environment row's lock, which also
115
144
  * orders it with session opens and deletes, and counts as the Environment's use.
116
145
  */
117
- putEnvironmentSecret(environmentId: string, name: string, body: PutAgentSecretRequest, actor: string): Promise<AgentSecretView>;
118
- /** The Environment's secrets: names, types and bindings, never values. */
146
+ putEnvironmentSecret(environmentId: string, name: string, body: PutAgentSecretRequest, actor: string, options?: SecretWriteOptions | null): Promise<AgentSecretView>;
147
+ /** The Environment's secrets: names, types, bindings and the routing in force, never values. */
119
148
  listEnvironmentSecrets(environmentId: string): Promise<AgentSecretView[]>;
120
149
  getEnvironmentSecret(environmentId: string, name: string): Promise<AgentSecretView>;
121
150
  deleteEnvironmentSecret(environmentId: string, name: string, actor: string): Promise<{
@@ -232,4 +261,16 @@ export declare function hostModelAad(provider: string, model: string): Buffer;
232
261
  * binding (the hosts and header of an `environment_secret`, the URL of a `bearer` or `headers`).
233
262
  */
234
263
  export declare function slotHolds(slot: EnvironmentSecretSlot, row: Pick<VaultCredentialRow, "type" | "bindingJson">): boolean;
264
+ /**
265
+ * Where a `bearer` or `headers` credential's requests go, and how the gateway there names tools,
266
+ * at the time of the request (Host feature `secret-routing`), under `slot`, the slot it fills of
267
+ * the version its Agent runs: `via` is the secret's own (written with it), else the slot's, else
268
+ * none (the request goes to the URL); a `via` copied from the slot (`viaFrom: "slot"`) follows the
269
+ * slot. `toolPrefix` is the slot's, else the secret's, and only with a `via`. The credential is
270
+ * chosen by the URL either way.
271
+ */
272
+ export declare function routingOf(binding: Pick<CredentialBinding, "via" | "viaFrom" | "toolPrefix">, slot?: EnvironmentSecretSlot): {
273
+ via?: string;
274
+ toolPrefix?: string;
275
+ };
235
276
  export {};
@@ -239,7 +239,7 @@ export class VaultService {
239
239
  * bound to one of its hosts or to its URL. Runs under the Environment row's lock, which also
240
240
  * orders it with session opens and deletes, and counts as the Environment's use.
241
241
  */
242
- async putEnvironmentSecret(environmentId, name, body, actor) {
242
+ async putEnvironmentSecret(environmentId, name, body, actor, options) {
243
243
  const invalid = environmentSecretNameError(body.type, name);
244
244
  if (invalid)
245
245
  throw new VaultError(400, invalid);
@@ -252,6 +252,8 @@ export class VaultService {
252
252
  // A workload credential holds no value: the bearer is the Runtime's workload token (#496).
253
253
  payload = body.type === "workload" ? {} : payloadOf(body);
254
254
  binding = bindingOf(url, payload, body);
255
+ if (options?.viaFromSlot === true && binding.via !== undefined && body.type !== "workload")
256
+ binding.viaFrom = "slot";
255
257
  }
256
258
  const kek = this.kek();
257
259
  return this.store.tx(async (t) => {
@@ -289,17 +291,18 @@ export class VaultService {
289
291
  await this.audit(t, { actor, action: "create", vaultId: vault.id, credentialId: id, outcome: "created" });
290
292
  }
291
293
  await t.updateEnvironment(environmentId, { lastUsedAt: now }, now);
292
- return secretViewOf(row);
294
+ return secretViewOf(row, slotOf(await appliedSlots(t, environmentId), row));
293
295
  });
294
296
  }
295
- /** The Environment's secrets: names, types and bindings, never values. */
297
+ /** The Environment's secrets: names, types, bindings and the routing in force, never values. */
296
298
  async listEnvironmentSecrets(environmentId) {
297
299
  return this.store.tx(async (t) => {
298
300
  const vault = await this.environmentVault(t, environmentId);
301
+ const slots = await appliedSlots(t, environmentId);
299
302
  return (await t.credentialsForVault(vault.id))
300
303
  .filter(isUserCredential)
301
304
  .sort((a, b) => a.name.localeCompare(b.name))
302
- .map(secretViewOf);
305
+ .map((row) => secretViewOf(row, slotOf(slots, row)));
303
306
  });
304
307
  }
305
308
  async getEnvironmentSecret(environmentId, name) {
@@ -308,7 +311,7 @@ export class VaultService {
308
311
  const row = await t.credentialByName(vault.id, name);
309
312
  if (!row || !isUserCredential(row))
310
313
  throw new VaultError(404, "Secret not found");
311
- return secretViewOf(row);
314
+ return secretViewOf(row, slotOf(await appliedSlots(t, environmentId), row));
312
315
  });
313
316
  }
314
317
  async deleteEnvironmentSecret(environmentId, name, actor) {
@@ -404,7 +407,8 @@ export class VaultService {
404
407
  });
405
408
  return { result: { status: "refused", url, credentialIds: [], reason: "vault_missing" } };
406
409
  }
407
- const live = await liveFilter(t, environmentId);
410
+ const slots = await appliedSlots(t, environmentId);
411
+ const live = liveFilterOf(slots);
408
412
  const matches = (await t.credentialsForVault(vault.id))
409
413
  .filter(isUserCredential)
410
414
  .filter(live)
@@ -426,7 +430,9 @@ export class VaultService {
426
430
  result: { status: "refused", url, credentialIds: matches.map((row) => row.id), reason: "ambiguous" },
427
431
  };
428
432
  }
429
- return { row: matches[0] };
433
+ const row = matches[0];
434
+ const slot = slotOf(slots, row);
435
+ return slot === undefined ? { row } : { row, slot };
430
436
  });
431
437
  if ("result" in decided)
432
438
  return decided.result;
@@ -468,8 +474,8 @@ export class VaultService {
468
474
  status: "authorized",
469
475
  url,
470
476
  headers,
471
- ...(binding.via === undefined ? {} : { via: binding.via }),
472
- ...(binding.toolPrefix === undefined ? {} : { toolPrefix: binding.toolPrefix }),
477
+ // The secret's via, else its slot's; the slot's tool prefix, else the secret's (`secret-routing`).
478
+ ...routingOf(binding, decided.slot),
473
479
  ...(row.type === "workload" ? { workload: true } : {}),
474
480
  ...(binding.identity === undefined ? {} : { identity: binding.identity }),
475
481
  };
@@ -486,10 +492,13 @@ export class VaultService {
486
492
  const credentials = [];
487
493
  {
488
494
  const vault = await this.environmentVault(t, environmentId);
489
- const live = await liveFilter(t, environmentId);
495
+ const slots = environmentId === undefined ? undefined : await appliedSlots(t, environmentId);
496
+ const live = liveFilterOf(slots);
490
497
  for (const row of await t.credentialsForVault(vault.id))
491
- if (isUserCredential(row) && live(row))
492
- credentials.push({ row, url: bindingUrl(row) });
498
+ if (isUserCredential(row) && live(row)) {
499
+ const slot = slotOf(slots, row);
500
+ credentials.push({ row, url: bindingUrl(row), ...(slot === undefined ? {} : { slot }) });
501
+ }
493
502
  }
494
503
  const entries = credentialNeeds(manifest).map((need) => {
495
504
  let url;
@@ -502,7 +511,7 @@ export class VaultService {
502
511
  return coverageEntry(need, need.url, undefined, environmentId, error.message);
503
512
  }
504
513
  const bound = credentials.find((item) => item.url === url);
505
- return coverageEntry(need, url, bound && coverageCredential(environmentId, bound.row), environmentId);
514
+ return coverageEntry(need, url, bound && coverageCredential(environmentId, bound.row, bound.slot), environmentId);
506
515
  });
507
516
  return {
508
517
  agentId: request.agentId,
@@ -791,9 +800,9 @@ function isUserCredential(row) {
791
800
  function bindingUrl(row) {
792
801
  return JSON.parse(row.bindingJson).url;
793
802
  }
794
- /** Where a credential is, for coverage: never its value. */
795
- function coverageCredential(environmentId, row) {
796
- const via = JSON.parse(row.bindingJson).via;
803
+ /** Where a credential is, for coverage, with the `via` in force (`routingOf`): never its value. */
804
+ function coverageCredential(environmentId, row, slot) {
805
+ const { via } = routingOf(credentialBinding(row), slot);
797
806
  return { agentId: environmentId, name: row.name, ...(via === undefined ? {} : { via }) };
798
807
  }
799
808
  /** One coverage entry, with the sentence that says what the session would do. */
@@ -853,9 +862,13 @@ function conflictOf(type, binding, others) {
853
862
  function credentialBinding(row) {
854
863
  return JSON.parse(row.bindingJson);
855
864
  }
856
- /** A secret as `GET .../secrets/{name}` shows it: never its value. */
857
- function secretViewOf(row) {
865
+ /**
866
+ * A secret as `GET .../secrets/{name}` shows it, with the routing in force under `slot`, the
867
+ * slot of the version its Agent runs that it fills (`routingOf`): never its value.
868
+ */
869
+ function secretViewOf(row, slot) {
858
870
  const binding = credentialBinding(row);
871
+ const routing = routingOf(binding, slot);
859
872
  return {
860
873
  name: row.name,
861
874
  type: row.type,
@@ -868,8 +881,7 @@ function secretViewOf(row) {
868
881
  ? {}
869
882
  : { url: binding.url },
870
883
  ...(binding.headerNames === undefined ? {} : { headerNames: binding.headerNames }),
871
- ...(binding.via === undefined ? {} : { via: binding.via }),
872
- ...(binding.toolPrefix === undefined ? {} : { toolPrefix: binding.toolPrefix }),
884
+ ...routing,
873
885
  ...(binding.identity === undefined ? {} : { identity: binding.identity }),
874
886
  createdAt: row.createdAt,
875
887
  ...(row.rotatedAt ? { rotatedAt: row.rotatedAt } : {}),
@@ -1074,11 +1086,15 @@ function rebind(name, slot, row, payload, binding) {
1074
1086
  return undefined;
1075
1087
  const sealed = payloadOf(auth);
1076
1088
  const routing = slot;
1077
- // The gateway's tool prefix is the vault's, not the slot's: it stays while the slot keeps `via`.
1089
+ // The secret's own via goes with it (`secret-routing`); else the fork's slot's, which its
1090
+ // sessions then follow. The gateway's tool prefix is the vault's; the slot's is read at use.
1091
+ const own = binding.via !== undefined && binding.viaFrom !== "slot" ? binding.via : undefined;
1092
+ const via = own ?? routing.via ?? null;
1093
+ const rebound = bindingOf(normalizeVaultUrl(routing.url), sealed, { ...routing, via, toolPrefix: binding.toolPrefix ?? null });
1078
1094
  return {
1079
1095
  type,
1080
1096
  payload: sealed,
1081
- binding: bindingOf(normalizeVaultUrl(routing.url), sealed, { ...routing, toolPrefix: binding.toolPrefix ?? null }),
1097
+ binding: own === undefined && rebound.via !== undefined ? { ...rebound, viaFrom: "slot" } : rebound,
1082
1098
  };
1083
1099
  }
1084
1100
  catch (error) {
@@ -1146,11 +1162,36 @@ export function slotHolds(slot, row) {
1146
1162
  * again. Without slots, every one.
1147
1163
  */
1148
1164
  async function liveFilter(t, environmentId) {
1149
- const slots = (await t.environment(environmentId))?.applied.secrets;
1165
+ return liveFilterOf(await appliedSlots(t, environmentId));
1166
+ }
1167
+ /** `liveFilter` under `slots`, the secret slots of the runtime an Agent applies. */
1168
+ function liveFilterOf(slots) {
1150
1169
  if (slots === undefined)
1151
1170
  return () => true;
1152
- return (row) => {
1153
- const slot = slots[row.name];
1154
- return slot !== undefined && slotHolds(slot, row);
1155
- };
1171
+ return (row) => slotOf(slots, row) !== undefined;
1172
+ }
1173
+ /** The secret slots of the runtime Agent `environmentId` applies; undefined when it declares none. */
1174
+ async function appliedSlots(t, environmentId) {
1175
+ return (await t.environment(environmentId))?.applied.secrets;
1176
+ }
1177
+ /** The slot of `slots` that the credential in `row` fills (`slotHolds`), if any. */
1178
+ function slotOf(slots, row) {
1179
+ const slot = slots?.[row.name];
1180
+ return slot !== undefined && slotHolds(slot, row) ? slot : undefined;
1181
+ }
1182
+ /**
1183
+ * Where a `bearer` or `headers` credential's requests go, and how the gateway there names tools,
1184
+ * at the time of the request (Host feature `secret-routing`), under `slot`, the slot it fills of
1185
+ * the version its Agent runs: `via` is the secret's own (written with it), else the slot's, else
1186
+ * none (the request goes to the URL); a `via` copied from the slot (`viaFrom: "slot"`) follows the
1187
+ * slot. `toolPrefix` is the slot's, else the secret's, and only with a `via`. The credential is
1188
+ * chosen by the URL either way.
1189
+ */
1190
+ export function routingOf(binding, slot) {
1191
+ const routed = slot?.type === "bearer" || slot?.type === "headers" ? slot : undefined;
1192
+ const via = binding.viaFrom === "slot" && routed !== undefined ? routed.via : (binding.via ?? routed?.via);
1193
+ if (via === undefined)
1194
+ return {};
1195
+ const toolPrefix = routed?.toolPrefix ?? binding.toolPrefix;
1196
+ return toolPrefix === undefined ? { via } : { via, toolPrefix };
1156
1197
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const RUNTIME_VERSION = "0.38.0-beta";
1
+ export declare const RUNTIME_VERSION = "0.40.0-beta";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Kept in step with package.json by runtime/server/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.38.0-beta";
3
+ export const RUNTIME_VERSION = "0.40.0-beta";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/runtime",
3
- "version": "0.38.0-beta",
3
+ "version": "0.40.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",
@@ -65,8 +65,8 @@
65
65
  "@hono/node-server": "^2.1.5",
66
66
  "@hono/zod-openapi": "^1.6.3",
67
67
  "@modelcontextprotocol/sdk": "^1.32.1",
68
- "@nylorun/core": "0.33.0-beta",
69
- "@nylorun/harness": "0.30.1-beta",
68
+ "@nylorun/core": "0.34.0-beta",
69
+ "@nylorun/harness": "0.31.0-beta",
70
70
  "@restatedev/restate-sdk": "1.17.3",
71
71
  "@restatedev/restate-sdk-clients": "1.17.3",
72
72
  "@s2-dev/streamstore": "0.28.0",