@nylorun/runtime 0.38.0-beta → 0.39.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.
- package/CHANGELOG.md +70 -0
- package/dist/api/components.d.ts +1726 -80
- package/dist/api/http/routes/agents.js +3 -3
- package/dist/core/turn-manifest.d.ts +23 -0
- package/dist/core/turn-manifest.js +36 -1
- package/dist/gates/policy.d.ts +13 -3
- package/dist/gates/policy.js +17 -4
- package/dist/harness/executors.d.ts +4 -2
- package/dist/harness/executors.js +26 -4
- package/dist/harness-api/record.js +4 -2
- package/dist/harness-api/server.js +37 -13
- package/dist/harness-api/start.js +43 -5
- package/dist/keys/keys.d.ts +6 -3
- package/dist/keys/keys.js +1 -1
- package/dist/management-openapi.json +6 -4
- package/dist/mcp/pool.d.ts +5 -0
- package/dist/mcp/pool.js +1 -1
- package/dist/mcp/snapshot.d.ts +3 -1
- package/dist/mcp/snapshot.js +5 -3
- package/dist/openapi.json +1636 -563
- package/dist/store/postgres/store.js +14 -0
- package/dist/store/types.d.ts +9 -0
- package/dist/tenant/agent-versions.d.ts +13 -5
- package/dist/tenant/agent-versions.js +30 -6
- package/dist/tenant/agents.d.ts +14 -2
- package/dist/tenant/agents.js +51 -13
- package/dist/tenant/commands.js +16 -8
- package/dist/tenant/context.d.ts +30 -1
- package/dist/tenant/session-model.d.ts +35 -6
- package/dist/tenant/session-model.js +77 -9
- package/dist/tenant/sessions.js +13 -3
- package/dist/vault/service.d.ts +44 -3
- package/dist/vault/service.js +68 -27
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/vault/service.d.ts
CHANGED
|
@@ -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
|
|
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 {};
|
package/dist/vault/service.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
472
|
-
...(binding
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
/**
|
|
857
|
-
|
|
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
|
-
...
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
1154
|
-
|
|
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.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.39.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.
|
|
3
|
+
export const RUNTIME_VERSION = "0.39.0-beta";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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.
|
|
69
|
-
"@nylorun/harness": "0.
|
|
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",
|