@nylorun/runtime 0.36.1-beta → 0.37.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 +49 -0
- package/README.md +10 -0
- package/dist/api/a2a/card.d.ts +10 -4
- package/dist/api/a2a/card.js +18 -6
- package/dist/api/a2a/endpoint.d.ts +5 -2
- package/dist/api/a2a/endpoint.js +31 -9
- package/dist/api/a2a/extensions.d.ts +41 -0
- package/dist/api/a2a/extensions.js +92 -0
- package/dist/api/a2a/list.d.ts +60 -0
- package/dist/api/a2a/list.js +169 -0
- package/dist/api/a2a/protocol.d.ts +105 -9
- package/dist/api/a2a/protocol.js +169 -18
- package/dist/api/a2a/push.d.ts +76 -0
- package/dist/api/a2a/push.js +305 -0
- package/dist/api/a2a/routes.d.ts +12 -6
- package/dist/api/a2a/routes.js +409 -112
- package/dist/api/a2a/tasks.d.ts +72 -4
- package/dist/api/a2a/tasks.js +305 -27
- package/dist/api/components.d.ts +532 -0
- package/dist/api/components.js +3 -1
- package/dist/api/http/app.js +2 -0
- package/dist/api/http/routes/artifacts.js +19 -0
- package/dist/api/http/routes/turns.d.ts +18 -0
- package/dist/api/http/routes/turns.js +67 -0
- package/dist/api/inbound-files.d.ts +12 -1
- package/dist/api/inbound-files.js +44 -0
- package/dist/api/openapi.js +7 -2
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/main.js +1 -0
- package/dist/host/stack-config.d.ts +16 -0
- package/dist/host/stack-config.js +30 -0
- package/dist/management-openapi.json +5 -2
- package/dist/openapi.json +1485 -738
- package/dist/reads/types.d.ts +19 -0
- package/dist/store/postgres/drizzle/0039_a2a_push.sql +32 -0
- package/dist/store/postgres/drizzle/0040_turn_index.sql +23 -0
- package/dist/store/postgres/drizzle/0041_turn_index_backfill.sql +88 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +21 -0
- package/dist/store/postgres/reads.js +41 -1
- package/dist/store/postgres/schema.d.ts +1296 -534
- package/dist/store/postgres/schema.js +92 -0
- package/dist/store/postgres/store.js +133 -3
- package/dist/store/postgres/turns.d.ts +62 -0
- package/dist/store/postgres/turns.js +153 -0
- package/dist/store/types.d.ts +73 -3
- package/dist/tenant/runtime.js +7 -5
- package/dist/tenant/session-model.d.ts +11 -1
- package/dist/tenant/session-model.js +34 -5
- package/dist/tenant/sessions.d.ts +2 -1
- package/dist/tenant/sessions.js +8 -2
- package/dist/tenant/sweep.d.ts +3 -0
- package/dist/tenant/sweep.js +39 -11
- package/dist/tenant/types.d.ts +23 -0
- package/dist/tuning.d.ts +9 -0
- package/dist/tuning.js +11 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TurnChangesPage } from "../../components.js";
|
|
3
|
+
import { taskIdOf } from "../../a2a/tasks.js";
|
|
4
|
+
import { readAccess, readStoreOf } from "../../../reads/access.js";
|
|
5
|
+
import { tenantRoute } from "../define.js";
|
|
6
|
+
import { jsonResponse } from "../respond.js";
|
|
7
|
+
import { parseQuery } from "./reads.js";
|
|
8
|
+
/** A page's size when the query names none, and the most one holds. */
|
|
9
|
+
export const TURNS_PAGE_DEFAULT = 100;
|
|
10
|
+
export const TURNS_PAGE_MAX = 500;
|
|
11
|
+
const turnsQuery = z
|
|
12
|
+
.object({
|
|
13
|
+
changedAfter: z.string().optional().meta({
|
|
14
|
+
description: "The `cursor` a previous page returned: read the changes after it. Absent: from the first turn.",
|
|
15
|
+
}),
|
|
16
|
+
sessionId: z.string().optional().meta({ description: "Only this session's turns." }),
|
|
17
|
+
limit: z.coerce
|
|
18
|
+
.number()
|
|
19
|
+
.int()
|
|
20
|
+
.min(1)
|
|
21
|
+
.optional()
|
|
22
|
+
.meta({
|
|
23
|
+
description: `At most this many turns, up to ${TURNS_PAGE_MAX}; a larger limit gets that many. Default ${TURNS_PAGE_DEFAULT}`,
|
|
24
|
+
}),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
/** A turn index row as the API shows it. */
|
|
28
|
+
export function turnSummary(row) {
|
|
29
|
+
const totals = row.totals;
|
|
30
|
+
return {
|
|
31
|
+
turnId: row.turnId,
|
|
32
|
+
sessionId: row.sessionId,
|
|
33
|
+
agentId: row.agentId,
|
|
34
|
+
status: row.status,
|
|
35
|
+
startedAt: row.startedAt,
|
|
36
|
+
endedAt: row.endedAt,
|
|
37
|
+
changedAt: row.changedAt,
|
|
38
|
+
totals: totals ? { steps: totals.steps, tokens: totals.tokens, costUsd: totals.costUsd } : null,
|
|
39
|
+
a2a: row.a2aContextId === null
|
|
40
|
+
? null
|
|
41
|
+
: { contextId: row.a2aContextId, taskId: taskIdOf(row.a2aContextId, row.turnId) },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function turnRoutes(api) {
|
|
45
|
+
tenantRoute(api, { credentials: ["application", "subject", "token"], scopes: ["sessions:own"] }, {
|
|
46
|
+
method: "get",
|
|
47
|
+
path: "/v1/turns",
|
|
48
|
+
tags: ["Sessions"],
|
|
49
|
+
summary: "Follow turns as they change",
|
|
50
|
+
description: "The turn index's change feed (Host feature `turns`): each turn's status, times, totals and A2A context and task, in the order the turns changed, a turn again each time it changes. Pass the page's `cursor` as `changedAfter` to read on; it is returned even when no turn changed. With `sessionId`, one session's turns, oldest first. An application key reads the whole Tenant; acting for a person, only theirs.",
|
|
51
|
+
request: { query: turnsQuery },
|
|
52
|
+
responses: {
|
|
53
|
+
200: {
|
|
54
|
+
description: "The turns that changed after the cursor",
|
|
55
|
+
content: { "application/json": { schema: TurnChangesPage } },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}, async (c) => {
|
|
59
|
+
const scope = c.get("scope");
|
|
60
|
+
const q = parseQuery(turnsQuery, c.req.query());
|
|
61
|
+
const page = await readStoreOf(c.env.tenant).turnChanges(q.sessionId === undefined ? {} : { sessionId: q.sessionId }, {
|
|
62
|
+
limit: Math.min(q.limit ?? TURNS_PAGE_DEFAULT, TURNS_PAGE_MAX),
|
|
63
|
+
...(q.changedAfter === undefined ? {} : { changedAfter: q.changedAfter }),
|
|
64
|
+
}, readAccess(scope));
|
|
65
|
+
return jsonResponse(200, { ...page, turns: page.turns.map(turnSummary) });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Files that arrive inside a message (multimodal messages, M7): the AG-UI and A2A endpoints take
|
|
3
3
|
* a file's bytes inline (base64), store them as an artifact of the message's session, and send
|
|
4
4
|
* the message as `parts` through the session's normal command path, so every check a message
|
|
5
|
-
* with file parts meets applies.
|
|
5
|
+
* with file parts meets applies. AG-UI refuses a file by URL; A2A's `url` parts are fetched
|
|
6
|
+
* (`fetchUrlFile`, through `guardedFetch` within `inlineFileBytes`) and then stored the same way.
|
|
6
7
|
*
|
|
7
8
|
* A retried message (the same idempotency key) reuses the files the first one stored rather than
|
|
8
9
|
* storing them again, so it replays the first turn instead of conflicting with it. A message the
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
import type { MessagePart } from "@nylorun/core/contracts";
|
|
12
13
|
import { type ArtifactAccess } from "../artifacts/service.js";
|
|
13
14
|
import type { TenantContext } from "../tenant/context.js";
|
|
15
|
+
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
14
16
|
/** A file sent inline: its name, declared media type and base64 bytes. */
|
|
15
17
|
export interface InlineFile {
|
|
16
18
|
readonly name: string;
|
|
@@ -30,6 +32,15 @@ export type InboundPart = {
|
|
|
30
32
|
};
|
|
31
33
|
/** The 400 for a file sent by URL. */
|
|
32
34
|
export declare function refuseUrl(): never;
|
|
35
|
+
/**
|
|
36
|
+
* Fetches a file sent by URL (A2A `url` parts) under `policy`, within the Tenant's Runtime limit
|
|
37
|
+
* `inlineFileBytes`, as an inline file: its media type is the part's, else the answer's. A URL
|
|
38
|
+
* the policy refuses, or that does not answer 2xx, is `400`; a file past the limit `413`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fetchUrlFile(ctx: TenantContext, url: string, named: {
|
|
41
|
+
mediaType?: string;
|
|
42
|
+
filename?: string;
|
|
43
|
+
}, policy: OutboundPolicy): Promise<InlineFile>;
|
|
33
44
|
/** A name for a file its client did not name: its place in the message and its type's extension. */
|
|
34
45
|
export declare function inlineFileName(mediaType: string, index: number): string;
|
|
35
46
|
/**
|
|
@@ -3,6 +3,7 @@ import { extensionFor, normalizeContentType } from "@nylorun/core/contracts";
|
|
|
3
3
|
import { deleteArtifact, uploadArtifact } from "../artifacts/service.js";
|
|
4
4
|
import { fail } from "../tenant/http.js";
|
|
5
5
|
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
6
|
+
import { guardedFetch, OutboundFailed, OutboundRefused } from "../tenant/outbound.js";
|
|
6
7
|
const BASE64 = /^[A-Za-z0-9+/_-]*={0,2}$/;
|
|
7
8
|
/** The 400 for a file sent by URL. */
|
|
8
9
|
export function refuseUrl() {
|
|
@@ -10,6 +11,49 @@ export function refuseUrl() {
|
|
|
10
11
|
code: "invalid_request",
|
|
11
12
|
});
|
|
12
13
|
}
|
|
14
|
+
/** How long fetching one file `url` part may take. */
|
|
15
|
+
const URL_FETCH_TIMEOUT_MS = 60_000;
|
|
16
|
+
/**
|
|
17
|
+
* Fetches a file sent by URL (A2A `url` parts) under `policy`, within the Tenant's Runtime limit
|
|
18
|
+
* `inlineFileBytes`, as an inline file: its media type is the part's, else the answer's. A URL
|
|
19
|
+
* the policy refuses, or that does not answer 2xx, is `400`; a file past the limit `413`.
|
|
20
|
+
*/
|
|
21
|
+
export async function fetchUrlFile(ctx, url, named, policy) {
|
|
22
|
+
const { inlineFileBytes } = await ctx.store.tx((t) => readRuntimeLimits(t));
|
|
23
|
+
let response;
|
|
24
|
+
try {
|
|
25
|
+
response = await guardedFetch(policy, { maxResponseBytes: inlineFileBytes, timeoutMs: URL_FETCH_TIMEOUT_MS })(url);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof OutboundFailed && error.code === "TOO_LARGE")
|
|
29
|
+
return fail(413, `A file sent by URL may hold at most ${inlineFileBytes} bytes (the Runtime limit inlineFileBytes)`, {
|
|
30
|
+
code: "limit_exceeded",
|
|
31
|
+
details: { limitBytes: inlineFileBytes },
|
|
32
|
+
});
|
|
33
|
+
const reason = error instanceof OutboundRefused || error instanceof OutboundFailed ? error.message : "it did not answer";
|
|
34
|
+
return fail(400, `The file at ${new URL(url).host} could not be fetched: ${reason}`, { code: "invalid_request" });
|
|
35
|
+
}
|
|
36
|
+
if (!response.ok)
|
|
37
|
+
return fail(400, `The file at ${new URL(url).host} could not be fetched: it answered ${response.status}`, {
|
|
38
|
+
code: "invalid_request",
|
|
39
|
+
});
|
|
40
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
41
|
+
const answered = response.headers.get("content-type")?.split(";")[0]?.trim();
|
|
42
|
+
const segment = new URL(url).pathname.split("/").filter(Boolean).at(-1) ?? "";
|
|
43
|
+
let last = segment;
|
|
44
|
+
try {
|
|
45
|
+
last = decodeURIComponent(segment);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Not percent-encoding: the segment as it is.
|
|
49
|
+
}
|
|
50
|
+
const mediaType = named.mediaType ?? (answered || "application/octet-stream");
|
|
51
|
+
return {
|
|
52
|
+
name: named.filename ?? (last !== "" ? last : inlineFileName(mediaType, 0)),
|
|
53
|
+
mediaType,
|
|
54
|
+
data: bytes.toString("base64"),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
13
57
|
/** A name for a file its client did not name: its place in the message and its type's extension. */
|
|
14
58
|
export function inlineFileName(mediaType, index) {
|
|
15
59
|
const extension = extensionFor(mediaType);
|
package/dist/api/openapi.js
CHANGED
|
@@ -202,6 +202,7 @@ const RUNTIME_TAGS = [
|
|
|
202
202
|
"GET /v1/sessions/{sessionId}/calls/tool",
|
|
203
203
|
"GET /v1/sessions/{sessionId}/turns/{turnId}/trace",
|
|
204
204
|
"POST /v1/sessions/{sessionId}/sandbox/{tool}",
|
|
205
|
+
"GET /v1/turns",
|
|
205
206
|
],
|
|
206
207
|
},
|
|
207
208
|
{
|
|
@@ -218,8 +219,12 @@ const RUNTIME_TAGS = [
|
|
|
218
219
|
{
|
|
219
220
|
name: "A2A",
|
|
220
221
|
group: "Sessions",
|
|
221
|
-
description: "The same sessions through Agent2Agent: a context is one session per subject, Agent and `contextId`, and a task is one turn; every caller's session runs on the Agent's disk and vault, so an app that serves several audiences makes an Agent for each. The card is how an A2A client finds the Agent.",
|
|
222
|
-
operations: [
|
|
222
|
+
description: "The same sessions through Agent2Agent (A2A 1.0, JSON-RPC) under the Nylorun profile v2: a context is one session per subject, Agent and `contextId`, and a task is one turn; every caller's session runs on the Agent's disk and vault, so an app that serves several audiences makes an Agent for each. The card is how an A2A client finds the Agent; push notifications carry a task's updates to the caller, and the files a task writes are downloaded from `/v1/a2a/files/…` by the same subject.",
|
|
223
|
+
operations: [
|
|
224
|
+
"GET /v1/a2a/agents/{agentId}/card",
|
|
225
|
+
"POST /v1/a2a/agents/{agentId}",
|
|
226
|
+
"GET /v1/a2a/files/{artifactId}/versions/{version}",
|
|
227
|
+
],
|
|
223
228
|
},
|
|
224
229
|
{
|
|
225
230
|
name: "Artifacts",
|
|
@@ -16,4 +16,6 @@ export declare function configForFactory(options: {
|
|
|
16
16
|
issuers?: TenantConfig["issuers"];
|
|
17
17
|
/** The URL clients reach the Runtime API at (`NYLORUN_PUBLIC_URL`). */
|
|
18
18
|
publicUrl?: string;
|
|
19
|
+
/** The A2A server's settings (`StackConfig.a2a`). */
|
|
20
|
+
a2a?: TenantConfig["a2a"];
|
|
19
21
|
}): (id: string) => TenantConfig;
|
package/dist/host/config-for.js
CHANGED
|
@@ -17,6 +17,7 @@ export function configForFactory(options) {
|
|
|
17
17
|
...(options.delivery ? { delivery: options.delivery } : {}),
|
|
18
18
|
...(options.issuers ? { issuers: options.issuers } : {}),
|
|
19
19
|
...(options.publicUrl ? { publicUrl: options.publicUrl } : {}),
|
|
20
|
+
...(options.a2a ? { a2a: options.a2a } : {}),
|
|
20
21
|
logger: {
|
|
21
22
|
info: (message, fields) => options.logger.info(message, { tenantId: id, ...fields }),
|
|
22
23
|
warn: (message, fields) => options.logger.warn(message, { tenantId: id, ...fields }),
|
package/dist/host/main.js
CHANGED
|
@@ -293,6 +293,7 @@ export async function main() {
|
|
|
293
293
|
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
294
294
|
...(issuers ? { issuers } : {}),
|
|
295
295
|
...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
|
|
296
|
+
...(stack.a2a ? { a2a: stack.a2a } : {}),
|
|
296
297
|
});
|
|
297
298
|
// The process's Durable Session Execution: Restate when its endpoints are set, else the
|
|
298
299
|
// in-process memory execution. An invocation that arrives while the Tenant opens waits for it.
|
|
@@ -264,6 +264,17 @@ export interface StackConfig {
|
|
|
264
264
|
privateAddresses?: "allow" | "refuse";
|
|
265
265
|
allowHttp?: boolean;
|
|
266
266
|
};
|
|
267
|
+
/**
|
|
268
|
+
* The A2A server's push notifications (Runtime config `a2a`): `NYLORUN_A2A_PUSH_SECRET`
|
|
269
|
+
* (`a2a.push.secret`, `whsec_<base64>`) signs them, and `NYLORUN_A2A_PUSH_ALLOW_HOSTS`
|
|
270
|
+
* (`a2a.push.allowHosts`, comma-separated `host:port`s) names the private hosts they may reach.
|
|
271
|
+
*/
|
|
272
|
+
a2a?: {
|
|
273
|
+
push?: {
|
|
274
|
+
secret?: string;
|
|
275
|
+
allowHosts?: string[];
|
|
276
|
+
};
|
|
277
|
+
};
|
|
267
278
|
/** Present when the process runs core or loop: who its Tenant is on first start. */
|
|
268
279
|
tenant?: TenantSettings;
|
|
269
280
|
/**
|
|
@@ -348,6 +359,11 @@ export declare function normalizeAllowedHost(name: string, entry: string): strin
|
|
|
348
359
|
export declare function isLoopbackHost(host: string): boolean;
|
|
349
360
|
/** Parse the stack configuration; throws `StackConfigError` naming the variable. */
|
|
350
361
|
export declare function parseStackConfig(env: EnvSnapshot, argv: readonly string[]): StackConfig;
|
|
362
|
+
/**
|
|
363
|
+
* `host:port` entries, comma-separated; a host may be a name, an IPv4 or a bracketed IPv6 address,
|
|
364
|
+
* and the port `*` for any port (a test receiver on a port it picks).
|
|
365
|
+
*/
|
|
366
|
+
export declare function parseAllowHosts(value: string): string[];
|
|
351
367
|
/** Endpoint summary for logs: which endpoints are set, never their values. */
|
|
352
368
|
export declare function describeEndpoints(endpoints: StackEndpoints): string[];
|
|
353
369
|
export {};
|
|
@@ -328,6 +328,7 @@ export function parseStackConfig(env, argv) {
|
|
|
328
328
|
if (harnessListener && listen?.port === harnessListener.listen.port)
|
|
329
329
|
throw new StackConfigError("NYLORUN_HARNESS_LISTEN_PORT must differ from NYLORUN_LISTEN_PORT");
|
|
330
330
|
const delivery = parseDelivery(env);
|
|
331
|
+
const a2a = parseA2a(env);
|
|
331
332
|
const tenant = servesApi ? parseTenant(env) : undefined;
|
|
332
333
|
const workloadToken = parseWorkloadToken(env);
|
|
333
334
|
const modelHostIdentity = parseModelHostIdentity(env);
|
|
@@ -353,6 +354,7 @@ export function parseStackConfig(env, argv) {
|
|
|
353
354
|
...(harnessListener ? { harnessListener } : {}),
|
|
354
355
|
...(sandboxes ? { sandboxes } : {}),
|
|
355
356
|
...(delivery ? { delivery } : {}),
|
|
357
|
+
...(a2a ? { a2a } : {}),
|
|
356
358
|
...(tenant ? { tenant } : {}),
|
|
357
359
|
...(workloadToken ? { workloadToken } : {}),
|
|
358
360
|
...(modelHostIdentity ? { modelHostIdentity } : {}),
|
|
@@ -714,6 +716,34 @@ function parseDelivery(env) {
|
|
|
714
716
|
...(http ? { allowHttp: http === "allow" } : {}),
|
|
715
717
|
};
|
|
716
718
|
}
|
|
719
|
+
/** `StackConfig.a2a` from `NYLORUN_A2A_PUSH_*`, or `undefined` when none is set. */
|
|
720
|
+
function parseA2a(env) {
|
|
721
|
+
const secret = read(env, "NYLORUN_A2A_PUSH_SECRET");
|
|
722
|
+
if (secret !== undefined) {
|
|
723
|
+
const key = secret.startsWith("whsec_") ? Buffer.from(secret.slice(6), "base64") : undefined;
|
|
724
|
+
if (!key || key.length < 24 || !/^[A-Za-z0-9+/]+={0,2}$/.test(secret.slice(6)))
|
|
725
|
+
throw new StackConfigError("NYLORUN_A2A_PUSH_SECRET must be a Standard Webhooks secret: whsec_ and the base64 of at least 24 bytes");
|
|
726
|
+
}
|
|
727
|
+
const hosts = read(env, "NYLORUN_A2A_PUSH_ALLOW_HOSTS");
|
|
728
|
+
const allowHosts = hosts === undefined ? undefined : parseAllowHosts(hosts);
|
|
729
|
+
if (secret === undefined && allowHosts === undefined)
|
|
730
|
+
return undefined;
|
|
731
|
+
return { push: { ...(secret !== undefined ? { secret } : {}), ...(allowHosts ? { allowHosts } : {}) } };
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* `host:port` entries, comma-separated; a host may be a name, an IPv4 or a bracketed IPv6 address,
|
|
735
|
+
* and the port `*` for any port (a test receiver on a port it picks).
|
|
736
|
+
*/
|
|
737
|
+
export function parseAllowHosts(value) {
|
|
738
|
+
const entries = value
|
|
739
|
+
.split(",")
|
|
740
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
741
|
+
.filter((entry) => entry !== "");
|
|
742
|
+
for (const entry of entries)
|
|
743
|
+
if (!/^(\[[0-9a-f:.]+\]|[a-z0-9.-]+):(\d{1,5}|\*)$/.test(entry) || Number(entry.slice(entry.lastIndexOf(":") + 1)) > 65535)
|
|
744
|
+
throw new StackConfigError(`NYLORUN_A2A_PUSH_ALLOW_HOSTS entries must be host:port, not ${entry}`);
|
|
745
|
+
return entries;
|
|
746
|
+
}
|
|
717
747
|
/** Endpoint summary for logs: which endpoints are set, never their values. */
|
|
718
748
|
export function describeEndpoints(endpoints) {
|
|
719
749
|
return Object.entries(endpoints)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.2.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Nylorun Management API",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.37.0-beta",
|
|
6
6
|
"x-nylorun-protocol": {
|
|
7
7
|
"min": 16,
|
|
8
8
|
"max": 18,
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"subject-headers",
|
|
23
23
|
"ag-ui-endpoint",
|
|
24
24
|
"a2a-endpoint",
|
|
25
|
+
"a2a-profile-v2",
|
|
25
26
|
"sandbox-pods",
|
|
26
27
|
"trusted-issuers",
|
|
27
28
|
"session-reads",
|
|
@@ -37,7 +38,9 @@
|
|
|
37
38
|
"agent-folders",
|
|
38
39
|
"session-model",
|
|
39
40
|
"model-host-identity",
|
|
40
|
-
"credential-provider"
|
|
41
|
+
"credential-provider",
|
|
42
|
+
"turns",
|
|
43
|
+
"agent-model"
|
|
41
44
|
]
|
|
42
45
|
},
|
|
43
46
|
"description": "For operators: the Tenant's models, vaults, signing keys, settings and application keys, at `/v1/tenant/*` on the Tenant's URL, through `@nylorun/admin`. Every operation takes a management key, acting as itself: never for a subject and never from a browser. Management keys are issued only on the Tenant's machine (`nylorun tenant keys put <id> --management`) or from `NYLORUN_MANAGEMENT_KEY_FILE`."
|