@nylorun/runtime 0.25.0-beta → 0.27.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 +169 -0
- package/README.md +26 -16
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.js +1 -1
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +12 -18
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +4 -4
- package/dist/api/components.d.ts +326 -45
- package/dist/api/components.js +5 -1
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.d.ts +2 -2
- package/dist/api/http/app.js +2 -2
- package/dist/api/http/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/define.d.ts +1 -1
- package/dist/api/http/respond.js +7 -3
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/artifacts.js +19 -7
- package/dist/api/http/routes/coverage.js +1 -1
- package/dist/api/http/routes/environment-manifests.js +1 -1
- package/dist/api/http/routes/environments.js +6 -6
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +1 -1
- package/dist/api/http/routes/reads.d.ts +35 -0
- package/dist/api/http/routes/reads.js +50 -22
- package/dist/api/http/routes/sessions.js +12 -11
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +69 -16
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +6 -2
- package/dist/artifacts/disk.d.ts +0 -4
- package/dist/artifacts/disk.js +77 -73
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +70 -25
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +4 -3
- package/dist/gates/egress.d.ts +9 -9
- package/dist/gates/egress.js +19 -19
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +53 -17
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +28 -5
- package/dist/gates/policy.js +60 -17
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +0 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +4 -9
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +102 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +82 -73
- package/dist/harness-api/start.d.ts +20 -2
- package/dist/harness-api/start.js +57 -18
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +2 -45
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +0 -2
- package/dist/host/http.js +0 -27
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +1 -3
- package/dist/keys/contract.js +1 -3
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.js +6 -1
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +1961 -994
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +13 -6
- package/dist/mcp/preview.js +37 -20
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +866 -1218
- package/dist/reads/cursor.js +3 -1
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/environment.d.ts +2 -0
- package/dist/record/environment.js +2 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +3 -2
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +5 -8
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +19 -11
- package/dist/sandbox/pods/lifecycle.js +45 -23
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +4 -1
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +8 -3
- package/dist/sandbox/resolve.js +12 -2
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +34 -11
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/migrate.js +1 -1
- package/dist/store/postgres/reads.js +10 -2
- package/dist/store/postgres/schema.d.ts +19 -0
- package/dist/store/postgres/schema.js +7 -0
- package/dist/store/postgres/store.js +55 -26
- package/dist/store/types.d.ts +17 -7
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +21 -31
- package/dist/tenant/commands.js +9 -16
- package/dist/tenant/context.d.ts +14 -17
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +9 -36
- package/dist/tenant/environment-manifests.js +9 -1
- package/dist/tenant/environments.d.ts +0 -2
- package/dist/tenant/environments.js +54 -30
- package/dist/tenant/ephemeral.d.ts +1 -1
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +12 -11
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +24 -1
- package/dist/tenant/identity-file.js +35 -7
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +31 -23
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +90 -0
- package/dist/tenant/limits.js +175 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +17 -19
- package/dist/tenant/outbound.js +18 -21
- package/dist/tenant/run-grants.js +3 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.js +30 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.js +32 -29
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +4 -3
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +39 -48
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/types.d.ts +11 -16
- package/dist/tenant/worker.d.ts +2 -10
- package/dist/tenant/worker.js +5 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +9 -6
- package/dist/traces/map.js +9 -11
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +383 -0
- package/dist/tuning.js +658 -0
- package/dist/vault/headers.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
|
@@ -5,13 +5,37 @@ import { turnTrace } from "../../../traces/read.js";
|
|
|
5
5
|
import { readAccess, readStoreOf } from "../../../reads/access.js";
|
|
6
6
|
import { requireApplication, requirePrincipal } from "../../../tenant/auth.js";
|
|
7
7
|
import { fail } from "../../../tenant/http.js";
|
|
8
|
+
import { readRuntimeLimits } from "../../../tenant/runtime-limits.js";
|
|
8
9
|
import { environmentGrantsOf } from "../../../tenant/environments.js";
|
|
9
10
|
import { tenantRoute } from "../define.js";
|
|
10
11
|
import { jsonResponse } from "../respond.js";
|
|
12
|
+
/** A page's `limit`, held to the Tenant's Runtime limit `pageItems` (`pageSize`). */
|
|
13
|
+
const limitParam = (fallback) => z.coerce
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.min(1)
|
|
17
|
+
.optional()
|
|
18
|
+
.meta({
|
|
19
|
+
description: `A page holds at most the Tenant's Runtime limit \`pageItems\` (default 200); a larger limit gets that many${fallback === undefined ? "" : `. Default ${fallback}, or that limit when it is lower`}`,
|
|
20
|
+
});
|
|
11
21
|
export const pageQuery = z.object({
|
|
12
|
-
limit:
|
|
22
|
+
limit: limitParam(),
|
|
13
23
|
cursor: z.string().optional(),
|
|
14
24
|
});
|
|
25
|
+
/** The size of a page of a session's calls when the query names none (within `pageItems`). */
|
|
26
|
+
const CALLS_PAGE_DEFAULT = 50;
|
|
27
|
+
/** The size of a page of the model call export when the query names none (within `exportPageItems`). */
|
|
28
|
+
const EXPORT_PAGE_DEFAULT = 200;
|
|
29
|
+
/**
|
|
30
|
+
* The size of a page: `asked`, or, when the query names none, `fallback`, each within the Tenant's
|
|
31
|
+
* Runtime limit `name` (read for each request): a page asked larger holds that many, and its
|
|
32
|
+
* cursor reads on.
|
|
33
|
+
*/
|
|
34
|
+
export async function pageSize(ctx, asked, options = {}) {
|
|
35
|
+
const name = options.name ?? "pageItems";
|
|
36
|
+
const limit = (await ctx.store.tx((t) => readRuntimeLimits(t)))[name];
|
|
37
|
+
return Math.min(asked ?? options.fallback ?? limit, limit);
|
|
38
|
+
}
|
|
15
39
|
export const sessionPageQuery = pageQuery
|
|
16
40
|
.extend({
|
|
17
41
|
agentId: z.string().optional(),
|
|
@@ -30,13 +54,13 @@ export async function sessionPage(ctx, scope, query) {
|
|
|
30
54
|
return fail(400, "Pagination requires limit");
|
|
31
55
|
if (filters.ownerUserId !== undefined)
|
|
32
56
|
requireApplication(scope);
|
|
33
|
-
return readStoreOf(ctx).sessions(filters, { limit, cursor }, readAccess(scope));
|
|
57
|
+
return readStoreOf(ctx).sessions(filters, { limit: await pageSize(ctx, limit), cursor }, readAccess(scope));
|
|
34
58
|
}
|
|
35
59
|
export async function environmentPage(ctx, scope, query, labels) {
|
|
36
60
|
const { limit, cursor } = parseQuery(pageQuery.extend({ label: z.string().optional() }).strict(), query);
|
|
37
61
|
if (limit === undefined)
|
|
38
62
|
return fail(400, "Pagination requires limit");
|
|
39
|
-
return readStoreOf(ctx).environments(labels, { limit, cursor }, readAccess(scope), environmentGrantsOf(scope));
|
|
63
|
+
return readStoreOf(ctx).environments(labels, { limit: await pageSize(ctx, limit), cursor }, readAccess(scope), environmentGrantsOf(scope));
|
|
40
64
|
}
|
|
41
65
|
export function readRoutes(api) {
|
|
42
66
|
const sessionId = z.object({ sessionId: z.string() });
|
|
@@ -57,15 +81,14 @@ export function readRoutes(api) {
|
|
|
57
81
|
requirePrincipal(c.get("scope"));
|
|
58
82
|
return jsonResponse(200, await readStoreOf(c.env.tenant).manifest(c.req.param("sessionId"), readAccess(c.get("scope"))));
|
|
59
83
|
});
|
|
84
|
+
const callsQuery = turnQuery
|
|
85
|
+
.extend({
|
|
86
|
+
limit: limitParam(CALLS_PAGE_DEFAULT),
|
|
87
|
+
cursor: z.string().optional(),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
60
90
|
for (const kind of ["usage", "calls/model"]) {
|
|
61
|
-
const query = kind === "usage"
|
|
62
|
-
? turnQuery
|
|
63
|
-
: turnQuery
|
|
64
|
-
.extend({
|
|
65
|
-
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
66
|
-
cursor: z.string().optional(),
|
|
67
|
-
})
|
|
68
|
-
.strict();
|
|
91
|
+
const query = kind === "usage" ? turnQuery : callsQuery;
|
|
69
92
|
// Spend is the application's to show: no subject scope reaches it (`tenant:settings`,
|
|
70
93
|
// which used to, is retired in protocol 8).
|
|
71
94
|
tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
|
|
@@ -93,18 +116,14 @@ export function readRoutes(api) {
|
|
|
93
116
|
const q = parseQuery(turnQuery, c.req.query());
|
|
94
117
|
return jsonResponse(200, await store.usage(id, q.turnId, access));
|
|
95
118
|
}
|
|
96
|
-
const q = parseQuery(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
cursor: z.string().optional(),
|
|
100
|
-
})
|
|
101
|
-
.strict(), c.req.query());
|
|
102
|
-
return jsonResponse(200, await store.modelCalls(id, q.turnId, q, access));
|
|
119
|
+
const q = parseQuery(callsQuery, c.req.query());
|
|
120
|
+
const limit = await pageSize(c.env.tenant, q.limit, { fallback: CALLS_PAGE_DEFAULT });
|
|
121
|
+
return jsonResponse(200, await store.modelCalls(id, q.turnId, { ...q, limit }, access));
|
|
103
122
|
});
|
|
104
123
|
}
|
|
105
124
|
const toolCallsQuery = turnQuery
|
|
106
125
|
.extend({
|
|
107
|
-
limit:
|
|
126
|
+
limit: limitParam(CALLS_PAGE_DEFAULT),
|
|
108
127
|
cursor: z.string().optional(),
|
|
109
128
|
})
|
|
110
129
|
.strict();
|
|
@@ -125,7 +144,8 @@ export function readRoutes(api) {
|
|
|
125
144
|
}, async (c) => {
|
|
126
145
|
requirePrincipal(c.get("scope"));
|
|
127
146
|
const q = parseQuery(toolCallsQuery, c.req.query());
|
|
128
|
-
|
|
147
|
+
const limit = await pageSize(c.env.tenant, q.limit, { fallback: CALLS_PAGE_DEFAULT });
|
|
148
|
+
return jsonResponse(200, await readStoreOf(c.env.tenant).toolCalls(c.req.param("sessionId"), q.turnId, { ...q, limit }, readAccess(c.get("scope"))));
|
|
129
149
|
});
|
|
130
150
|
// A turn's spans (R7.5): the same mapping the trace projector posts, from the turn's log rows,
|
|
131
151
|
// with the installation's content opt-ins. Spans carry cost: the application's, as /calls are.
|
|
@@ -153,7 +173,14 @@ export function readRoutes(api) {
|
|
|
153
173
|
const exportQuery = z
|
|
154
174
|
.object({
|
|
155
175
|
after: z.string().optional(),
|
|
156
|
-
limit: z.coerce
|
|
176
|
+
limit: z.coerce
|
|
177
|
+
.number()
|
|
178
|
+
.int()
|
|
179
|
+
.min(1)
|
|
180
|
+
.optional()
|
|
181
|
+
.meta({
|
|
182
|
+
description: `A page holds at most the Tenant's Runtime limit \`exportPageItems\` (default 1000); a larger limit gets that many. Default ${EXPORT_PAGE_DEFAULT}, or that limit when it is lower`,
|
|
183
|
+
}),
|
|
157
184
|
})
|
|
158
185
|
.strict();
|
|
159
186
|
// The whole Tenant's ledger is the Management API's (protocol 8): a management key, as itself.
|
|
@@ -171,6 +198,7 @@ export function readRoutes(api) {
|
|
|
171
198
|
},
|
|
172
199
|
}, async (c) => {
|
|
173
200
|
const q = parseQuery(exportQuery, c.req.query());
|
|
174
|
-
|
|
201
|
+
const limit = await pageSize(c.env.tenant, q.limit, { fallback: EXPORT_PAGE_DEFAULT, name: "exportPageItems" });
|
|
202
|
+
return jsonResponse(200, await readStoreOf(c.env.tenant).exportModel(q.after, limit));
|
|
175
203
|
});
|
|
176
204
|
}
|
|
@@ -13,7 +13,7 @@ import { readHistory, requestCursor, streamHolderOf, streamSessionEvents } from
|
|
|
13
13
|
import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
|
|
14
14
|
import { readJson } from "../body.js";
|
|
15
15
|
import { TraceHeaders, tenantRoute } from "../define.js";
|
|
16
|
-
import { pageQuery, sessionPage, sessionPageQuery, parseQuery } from "./reads.js";
|
|
16
|
+
import { pageQuery, pageSize, sessionPage, sessionPageQuery, parseQuery } from "./reads.js";
|
|
17
17
|
import { jsonResponse } from "../respond.js";
|
|
18
18
|
const OWN_SESSIONS = {
|
|
19
19
|
credentials: ["application", "subject", "token"],
|
|
@@ -57,14 +57,11 @@ export function sessionRoutes(api) {
|
|
|
57
57
|
responses: {
|
|
58
58
|
200: json(AcceptedResponse, "The command, accepted"),
|
|
59
59
|
409: { description: "A turn is running, or the session does not wait on this" },
|
|
60
|
-
422: {
|
|
61
|
-
description: "A message names a file the Tenant's model does not read (`attachment_unsupported`; `details.parts` lists each with its reason). No turn starts.",
|
|
62
|
-
},
|
|
63
60
|
},
|
|
64
61
|
}, async (c) => {
|
|
65
62
|
// A `traceparent` joins the turn to the caller's trace (R7.4); a malformed one is ignored.
|
|
66
63
|
const trace = traceContextOf(c.req.raw.headers);
|
|
67
|
-
return jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c
|
|
64
|
+
return jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c)), c.get("scope"), trace ? { trace } : {}));
|
|
68
65
|
});
|
|
69
66
|
tenantRoute(api, {
|
|
70
67
|
credentials: ["application", "subject", "token"],
|
|
@@ -98,9 +95,12 @@ export function sessionRoutes(api) {
|
|
|
98
95
|
}),
|
|
99
96
|
body: body(PutAgentRequest),
|
|
100
97
|
},
|
|
101
|
-
responses: {
|
|
98
|
+
responses: {
|
|
99
|
+
200: json(PutAgentResponse, "The definition, stored (or checked, with dryRun)"),
|
|
100
|
+
413: { description: "A skill's files hold more than the Tenant's skill limit together (`limit_exceeded`)" },
|
|
101
|
+
},
|
|
102
102
|
}, async (c) => {
|
|
103
|
-
return jsonResponse(200, await putDefinition(c.env.tenant, c.req.param("agentId"), PutAgentRequestSchema.parse(await readJson(c
|
|
103
|
+
return jsonResponse(200, await putDefinition(c.env.tenant, c.req.param("agentId"), PutAgentRequestSchema.parse(await readJson(c)), { dryRun: c.req.query("dryRun") === "true" }));
|
|
104
104
|
});
|
|
105
105
|
tenantRoute(api, OWN_SESSIONS, {
|
|
106
106
|
method: "get",
|
|
@@ -121,7 +121,7 @@ export function sessionRoutes(api) {
|
|
|
121
121
|
path: "/v1/sessions/{sessionId}",
|
|
122
122
|
tags: ["Sessions"],
|
|
123
123
|
summary: "Open a session",
|
|
124
|
-
description: "Creates the session, or
|
|
124
|
+
description: "Creates the session, or answers the one already opened with the same agent, owner, info and Environment. Its Environment (its sandbox and vault) is chosen now and fixed for its life.",
|
|
125
125
|
request: { params: sessionId, body: body(PutSessionRequest) },
|
|
126
126
|
responses: {
|
|
127
127
|
200: json(SessionView, "The session"),
|
|
@@ -130,7 +130,7 @@ export function sessionRoutes(api) {
|
|
|
130
130
|
}, async (c) => {
|
|
131
131
|
const scope = c.get("scope");
|
|
132
132
|
const ctx = c.env.tenant;
|
|
133
|
-
const request = PutSessionRequestSchema.parse(await readJson(c
|
|
133
|
+
const request = PutSessionRequestSchema.parse(await readJson(c));
|
|
134
134
|
// Agent code may trust `info`: only an app server sets it.
|
|
135
135
|
if (scope.kind === "token" && request.info !== undefined)
|
|
136
136
|
fail(403, "A token caller cannot set session info", { code: "scope_required" });
|
|
@@ -172,7 +172,8 @@ export function sessionRoutes(api) {
|
|
|
172
172
|
const ctx = c.env.tenant;
|
|
173
173
|
const id = c.req.param("sessionId");
|
|
174
174
|
const cursor = await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
|
|
175
|
-
const { limit } = parseQuery(pageQuery.pick({ limit: true }), { limit: c.req.query("limit") });
|
|
175
|
+
const { limit: asked } = parseQuery(pageQuery.pick({ limit: true }), { limit: c.req.query("limit") });
|
|
176
|
+
const limit = asked === undefined ? undefined : await pageSize(ctx, asked);
|
|
176
177
|
return jsonResponse(200, await readHistory(ctx, id, cursor, c.req.query("agent"), limit));
|
|
177
178
|
});
|
|
178
179
|
tenantRoute(api, OWN_SESSIONS, {
|
|
@@ -222,7 +223,7 @@ export function sessionRoutes(api) {
|
|
|
222
223
|
await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
|
|
223
224
|
requireApplication(scope);
|
|
224
225
|
try {
|
|
225
|
-
return jsonResponse(200, await handleSessionSandboxTool(sandboxRouteDeps(ctx), id, c.req.param("tool"), await readJson(c
|
|
226
|
+
return jsonResponse(200, await handleSessionSandboxTool(sandboxRouteDeps(ctx), id, c.req.param("tool"), await readJson(c), c.req.raw.signal));
|
|
226
227
|
}
|
|
227
228
|
catch (error) {
|
|
228
229
|
if (error instanceof SandboxRouteError)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Tenant's own settings (`/v1/tenant/**`): its status, reset, a first configuration, the
|
|
3
|
-
* model it calls, what its model calls cost and may cost (budgets),
|
|
4
|
-
* sessions get.
|
|
3
|
+
* model it calls, what its model calls cost and may cost (budgets), the sandboxes its
|
|
4
|
+
* sessions get, and its Runtime limits.
|
|
5
5
|
*
|
|
6
6
|
* A reset that deletes sessions moves the Tenant to a new basin generation, so a session
|
|
7
7
|
* created again starts in an empty basin; the old basin is deleted after a grace period.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ModelUsageQuerySchema, PutModelBudgetsRequestSchema, PutToolPolicyRequestSchema, PutModelInputsRequestSchema, PutHostModelRequestSchema, PutTenantEnvironmentsRequestSchema, ResetTenantRequestSchema, SeedTenantConfigRequestSchema, SelectHostModelRequestSchema, } from "@nylorun/core/contracts";
|
|
3
|
-
import { HostModelCatalog, HostModelView, ListProvidersResponse, ModelBudgets, ModelInputsView, ModelUsageTotals, PutModelBudgetsRequest, PutToolPolicyRequest, PutModelInputsRequest, ToolPolicy, PutHostModelRequest, PutTenantEnvironmentsRequest, ResetTenantRequest, ResetTenantResponse, SeedTenantConfigRequest, SeedTenantConfigResponse, SelectHostModelRequest, TenantSandboxView, TenantStatus, } from "../../components.js";
|
|
2
|
+
import { ModelUsageQuerySchema, PutModelBudgetsRequestSchema, PutToolPolicyRequestSchema, PutModelInputsRequestSchema, PutHostModelRequestSchema, PutTenantEnvironmentsRequestSchema, PutTenantLimitsRequestSchema, RUNTIME_LIMIT_CEILINGS, RUNTIME_LIMIT_DEFAULTS, ResetTenantRequestSchema, runtimeLimitIssues, runtimeLimitsOf, SeedTenantConfigRequestSchema, SelectHostModelRequestSchema, } from "@nylorun/core/contracts";
|
|
3
|
+
import { HostModelCatalog, HostModelView, ListProvidersResponse, ModelBudgets, ModelInputsView, ModelUsageTotals, PutModelBudgetsRequest, PutToolPolicyRequest, PutModelInputsRequest, ToolPolicy, PutHostModelRequest, PutTenantEnvironmentsRequest, PutTenantLimitsRequest, ResetTenantRequest, ResetTenantResponse, SeedTenantConfigRequest, SeedTenantConfigResponse, SelectHostModelRequest, TenantLimitsView, TenantSandboxView, TenantStatus, } from "../../components.js";
|
|
4
|
+
import { readRuntimeLimits, writeLimitsConfig } from "../../../tenant/runtime-limits.js";
|
|
4
5
|
import { periodStart } from "../../../gates/meter.js";
|
|
5
|
-
import { readToolPolicy, TOOL_POLICY_SETTING } from "../../../gates/policy.js";
|
|
6
|
+
import { readToolPolicy, TOOL_POLICY_SETTING, ToolPolicyUnreadable } from "../../../gates/policy.js";
|
|
6
7
|
import { addableInputs, capabilitiesOf, inputsError } from "../../../model/capabilities.js";
|
|
7
8
|
import { catalogModel, hostModelCatalog } from "../../../model/catalog.js";
|
|
8
9
|
import { sandboxConfigErrors } from "../../../sandbox/resolve.js";
|
|
@@ -18,8 +19,14 @@ import { readJson } from "../body.js";
|
|
|
18
19
|
import { pathSegments, tenantRoute } from "../define.js";
|
|
19
20
|
import { jsonResponse } from "../respond.js";
|
|
20
21
|
import { rearmPodTimers } from "../../../tenant/environments.js";
|
|
22
|
+
import { hostTuning } from "../../../tuning.js";
|
|
23
|
+
import { budgetIssues, toolPolicyIssues } from "../../../tenant/limits.js";
|
|
21
24
|
/** Every Management API route (`/v1/tenant/*`): a management key, acting as itself (protocol 8). */
|
|
22
25
|
const MANAGEMENT = { credentials: ["management"], scopes: "never" };
|
|
26
|
+
/** Each Runtime limit's default and ceiling, for the limits routes' descriptions. */
|
|
27
|
+
const LIMITS_TABLE = `Defaults and ceilings: ${Object.entries(RUNTIME_LIMIT_CEILINGS)
|
|
28
|
+
.map(([name, ceiling]) => `\`${name}\` ${RUNTIME_LIMIT_DEFAULTS[name]} ${ceiling.unit}, at most ${ceiling.value} (${ceiling.why})`)
|
|
29
|
+
.join("; ")}.`;
|
|
23
30
|
const json = (schema, description) => ({
|
|
24
31
|
description,
|
|
25
32
|
content: { "application/json": { schema } },
|
|
@@ -94,7 +101,7 @@ export function tenantRoutes(api) {
|
|
|
94
101
|
vault,
|
|
95
102
|
sandbox: ctx.sandbox,
|
|
96
103
|
closing: ctx.closing || ctx.closed,
|
|
97
|
-
modelConfigured:
|
|
104
|
+
modelConfigured: ctx.scriptedModel ||
|
|
98
105
|
(await ctx.store.tx((t) => usesFixtureModel(t))) ||
|
|
99
106
|
(await vault.getHostModel()).configured,
|
|
100
107
|
...(ctx.stuckInvocations ? { stuckInvocations: ctx.stuckInvocations } : {}),
|
|
@@ -112,8 +119,8 @@ export function tenantRoutes(api) {
|
|
|
112
119
|
responses: { 200: json(ResetTenantResponse, "Reset") },
|
|
113
120
|
}, async (c) => {
|
|
114
121
|
const ctx = c.env.tenant;
|
|
115
|
-
const request = ResetTenantRequestSchema.parse(await readJson(c
|
|
116
|
-
await drain(ctx, request.activeWork,
|
|
122
|
+
const request = ResetTenantRequestSchema.parse(await readJson(c));
|
|
123
|
+
await drain(ctx, request.activeWork, hostTuning().tenant.drainTimeoutMs);
|
|
117
124
|
// A sessions reset moves the Tenant to a new basin generation, so a session created
|
|
118
125
|
// again with the same id, during or after the reset, starts in an empty basin at 0.
|
|
119
126
|
const sessionsReset = request.scope !== "environments";
|
|
@@ -143,7 +150,7 @@ export function tenantRoutes(api) {
|
|
|
143
150
|
responses: { 200: json(SeedTenantConfigResponse, "What was set, and what was kept") },
|
|
144
151
|
}, async (c) => {
|
|
145
152
|
const ctx = c.env.tenant;
|
|
146
|
-
const request = SeedTenantConfigRequestSchema.parse(await readJson(c
|
|
153
|
+
const request = SeedTenantConfigRequestSchema.parse(await readJson(c));
|
|
147
154
|
return jsonResponse(200, await seedTenantConfig({ store: ctx.store, vault: ctx.vault, keys: ctx.keys }, request));
|
|
148
155
|
});
|
|
149
156
|
tenantRoute(api, MANAGEMENT, {
|
|
@@ -162,11 +169,11 @@ export function tenantRoutes(api) {
|
|
|
162
169
|
path: "/v1/tenant/models/inputs",
|
|
163
170
|
tags: ["Tenant"],
|
|
164
171
|
summary: "Set what a catalog model reads",
|
|
165
|
-
description: "Replaces the inputs the operator added to one catalog model (Tenant setting `model.inputs`): only those in its `addableInputs`, which its provider API carries. Its catalog's own inputs always stay; an empty list removes the additions.
|
|
172
|
+
description: "Replaces the inputs the operator added to one catalog model (Tenant setting `model.inputs`): only those in its `addableInputs`, which its provider API carries. Its catalog's own inputs always stay; an empty list removes the additions. The agent's harness learns them in `turn.start.model` and decides how each file reaches the model. A custom endpoint sets `settings.inputs` on `PUT /v1/tenant/model` instead.",
|
|
166
173
|
request: { body: body(PutModelInputsRequest) },
|
|
167
174
|
responses: { 200: json(ModelInputsView, "What the model reads now") },
|
|
168
175
|
}, async (c) => {
|
|
169
|
-
const { provider, model, inputs } = PutModelInputsRequestSchema.parse(await readJson(c
|
|
176
|
+
const { provider, model, inputs } = PutModelInputsRequestSchema.parse(await readJson(c));
|
|
170
177
|
if (provider === "custom")
|
|
171
178
|
fail(400, "A custom endpoint's inputs are its settings: set settings.inputs on PUT /v1/tenant/model.");
|
|
172
179
|
const found = catalogModel(provider, model);
|
|
@@ -183,6 +190,33 @@ export function tenantRoutes(api) {
|
|
|
183
190
|
inputs: capabilitiesOf(found, added).inputs,
|
|
184
191
|
});
|
|
185
192
|
});
|
|
193
|
+
tenantRoute(api, MANAGEMENT, {
|
|
194
|
+
method: "get",
|
|
195
|
+
path: "/v1/tenant/limits",
|
|
196
|
+
tags: ["Tenant"],
|
|
197
|
+
summary: "Get the Tenant's Runtime limits",
|
|
198
|
+
description: `The sizes, counts and deadlines the Runtime holds what crosses it to: the Tenant's (Tenant setting \`limits.config\`), with the default for each it leaves out. Harnesses get the ones they must respect in \`turn.start.limits\` (record entry, model call, model file, tool answer, custom harness state, Harness API chunk, folder entries). ${LIMITS_TABLE}`,
|
|
199
|
+
responses: { 200: json(TenantLimitsView, "The limits in force") },
|
|
200
|
+
}, async (c) => jsonResponse(200, { limits: await c.env.tenant.store.tx((t) => readRuntimeLimits(t)) }));
|
|
201
|
+
tenantRoute(api, MANAGEMENT, {
|
|
202
|
+
method: "put",
|
|
203
|
+
path: "/v1/tenant/limits",
|
|
204
|
+
tags: ["Tenant"],
|
|
205
|
+
summary: "Set the Tenant's Runtime limits",
|
|
206
|
+
description: `Replaces the Tenant's Runtime limits: a limit left out takes its default. A limit above its ceiling, which a dependency sets, is \`400\` naming the ceiling, and nothing is written. Each is read where it is checked, so a new value holds for the next request, write, call or segment. ${LIMITS_TABLE}`,
|
|
207
|
+
request: { body: body(PutTenantLimitsRequest) },
|
|
208
|
+
responses: {
|
|
209
|
+
200: json(TenantLimitsView, "The limits now in force"),
|
|
210
|
+
400: { description: "A limit above its ceiling" },
|
|
211
|
+
},
|
|
212
|
+
}, async (c) => {
|
|
213
|
+
const { requestId: _requestId, ...config } = PutTenantLimitsRequestSchema.parse(await readJson(c));
|
|
214
|
+
const issues = runtimeLimitIssues(config.limits);
|
|
215
|
+
if (issues.length > 0)
|
|
216
|
+
fail(400, issues.join(" "));
|
|
217
|
+
await c.env.tenant.store.tx((t) => writeLimitsConfig(t, config));
|
|
218
|
+
return jsonResponse(200, { limits: runtimeLimitsOf(config.limits) });
|
|
219
|
+
});
|
|
186
220
|
tenantRoute(api, MANAGEMENT, {
|
|
187
221
|
method: "get",
|
|
188
222
|
path: "/v1/tenant/sandbox",
|
|
@@ -200,7 +234,7 @@ export function tenantRoutes(api) {
|
|
|
200
234
|
responses: { 200: json(TenantSandboxView, "The configuration now in force") },
|
|
201
235
|
}, async (c) => {
|
|
202
236
|
const ctx = c.env.tenant;
|
|
203
|
-
const { requestId: _requestId, ...config } = PutTenantEnvironmentsRequestSchema.parse(await readJson(c
|
|
237
|
+
const { requestId: _requestId, ...config } = PutTenantEnvironmentsRequestSchema.parse(await readJson(c));
|
|
204
238
|
const errors = sandboxConfigErrors(effectiveSandboxConfig(config));
|
|
205
239
|
if (errors.length > 0)
|
|
206
240
|
fail(400, errors.join(" "));
|
|
@@ -257,9 +291,12 @@ export function tenantRoutes(api) {
|
|
|
257
291
|
request: { body: body(PutModelBudgetsRequest) },
|
|
258
292
|
responses: { 200: json(ModelBudgets, "The budgets now in force") },
|
|
259
293
|
}, async (c) => {
|
|
260
|
-
const { budgets } = PutModelBudgetsRequestSchema.parse(await readJson(c
|
|
294
|
+
const { budgets } = PutModelBudgetsRequestSchema.parse(await readJson(c));
|
|
261
295
|
const updatedAt = new Date().toISOString();
|
|
262
296
|
const rows = await c.env.tenant.store.tx(async (t) => {
|
|
297
|
+
const issues = budgetIssues(budgets, await readRuntimeLimits(t));
|
|
298
|
+
if (issues.length > 0)
|
|
299
|
+
fail(400, issues.join(" "));
|
|
263
300
|
await t.putModelBudgets(budgets.map((budget) => budgetRow(budget, updatedAt)));
|
|
264
301
|
return t.listModelBudgets();
|
|
265
302
|
});
|
|
@@ -271,7 +308,18 @@ export function tenantRoutes(api) {
|
|
|
271
308
|
tags: ["Tenant"],
|
|
272
309
|
summary: "Get the Tenant's tool policy",
|
|
273
310
|
responses: { 200: json(ToolPolicy, "The rules the Tool Gate applies to every MCP and HTTP tool call") },
|
|
274
|
-
}, async (c) =>
|
|
311
|
+
}, async (c) => {
|
|
312
|
+
try {
|
|
313
|
+
return jsonResponse(200, await c.env.tenant.store.tx((t) => readToolPolicy(t)));
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
// A stored rule this Runtime refuses (an MCP tool named `server__tool`, protocol 15):
|
|
317
|
+
// every call is denied until the policy is set again.
|
|
318
|
+
if (error instanceof ToolPolicyUnreadable)
|
|
319
|
+
return fail(409, error.message, { code: "request_rejected" });
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
275
323
|
tenantRoute(api, MANAGEMENT, {
|
|
276
324
|
method: "put",
|
|
277
325
|
path: "/v1/tenant/tool-policy",
|
|
@@ -281,9 +329,14 @@ export function tenantRoutes(api) {
|
|
|
281
329
|
request: { body: body(PutToolPolicyRequest) },
|
|
282
330
|
responses: { 200: json(ToolPolicy, "The policy now in force") },
|
|
283
331
|
}, async (c) => {
|
|
284
|
-
const { rules } = PutToolPolicyRequestSchema.parse(await readJson(c
|
|
332
|
+
const { rules } = PutToolPolicyRequestSchema.parse(await readJson(c));
|
|
285
333
|
const policy = { rules, updatedAt: new Date().toISOString() };
|
|
286
|
-
await c.env.tenant.store.tx((t) =>
|
|
334
|
+
await c.env.tenant.store.tx(async (t) => {
|
|
335
|
+
const issues = toolPolicyIssues(rules, await readRuntimeLimits(t));
|
|
336
|
+
if (issues.length > 0)
|
|
337
|
+
fail(400, issues.join(" "));
|
|
338
|
+
await t.putSetting(TOOL_POLICY_SETTING, JSON.stringify(policy));
|
|
339
|
+
});
|
|
287
340
|
return jsonResponse(200, policy);
|
|
288
341
|
});
|
|
289
342
|
tenantRoute(api, MANAGEMENT, {
|
|
@@ -309,7 +362,7 @@ export function tenantRoutes(api) {
|
|
|
309
362
|
responses: { 200: json(HostModelView, "The model now in force") },
|
|
310
363
|
}, async (c) => {
|
|
311
364
|
const ctx = c.env.tenant;
|
|
312
|
-
return jsonResponse(200, await ctx.keys.putHostModel(PutHostModelRequestSchema.parse(await readJson(c
|
|
365
|
+
return jsonResponse(200, await ctx.keys.putHostModel(PutHostModelRequestSchema.parse(await readJson(c))));
|
|
313
366
|
});
|
|
314
367
|
tenantRoute(api, MANAGEMENT, {
|
|
315
368
|
method: "put",
|
|
@@ -320,6 +373,6 @@ export function tenantRoutes(api) {
|
|
|
320
373
|
responses: { 200: json(HostModelView, "The model now in force") },
|
|
321
374
|
}, async (c) => {
|
|
322
375
|
const ctx = c.env.tenant;
|
|
323
|
-
return jsonResponse(200, await ctx.keys.selectHostModel(SelectHostModelRequestSchema.parse(await readJson(c
|
|
376
|
+
return jsonResponse(200, await ctx.keys.selectHostModel(SelectHostModelRequestSchema.parse(await readJson(c))));
|
|
324
377
|
});
|
|
325
378
|
}
|
|
@@ -11,13 +11,6 @@
|
|
|
11
11
|
import type { MessagePart } from "@nylorun/core/contracts";
|
|
12
12
|
import { type ArtifactAccess } from "../artifacts/service.js";
|
|
13
13
|
import type { TenantContext } from "../tenant/context.js";
|
|
14
|
-
/** The largest file a message may carry inline, decoded. */
|
|
15
|
-
export declare const INLINE_FILE_MAX_BYTES: number;
|
|
16
|
-
/**
|
|
17
|
-
* The request body of an endpoint that takes inline files: one file at the cap, base64-encoded
|
|
18
|
-
* (a third larger), and the rest of the request.
|
|
19
|
-
*/
|
|
20
|
-
export declare const INLINE_BODY_MAX_BYTES: number;
|
|
21
14
|
/** A file sent inline: its name, declared media type and base64 bytes. */
|
|
22
15
|
export interface InlineFile {
|
|
23
16
|
readonly name: string;
|
|
@@ -40,8 +33,9 @@ export declare function refuseUrl(): never;
|
|
|
40
33
|
/** A name for a file its client did not name: its place in the message and its type's extension. */
|
|
41
34
|
export declare function inlineFileName(mediaType: string, index: number): string;
|
|
42
35
|
/**
|
|
43
|
-
* Stores `file` as a new artifact of `sessionId`, within the
|
|
44
|
-
* artifact limits: `400` for bad base64 or a bad
|
|
36
|
+
* Stores `file` as a new artifact of `sessionId`, within the Tenant's Runtime limit
|
|
37
|
+
* `inlineFileBytes` (read for the file) and its artifact limits: `400` for bad base64 or a bad
|
|
38
|
+
* media type, `413 limit_exceeded` past a limit.
|
|
45
39
|
*/
|
|
46
40
|
export declare function storeInlineFile(ctx: TenantContext, sessionId: string, file: InlineFile, access: ArtifactAccess): Promise<{
|
|
47
41
|
artifactId: string;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import { MAX_MESSAGE_PARTS } from "@nylorun/core/contracts";
|
|
2
1
|
import { commandKey } from "../core/flow-host.js";
|
|
3
|
-
import { extensionFor, normalizeContentType } from "
|
|
2
|
+
import { extensionFor, normalizeContentType } from "@nylorun/core/contracts";
|
|
4
3
|
import { deleteArtifact, uploadArtifact } from "../artifacts/service.js";
|
|
5
4
|
import { fail } from "../tenant/http.js";
|
|
6
|
-
|
|
7
|
-
export const INLINE_FILE_MAX_BYTES = 20 * 1024 * 1024;
|
|
8
|
-
/**
|
|
9
|
-
* The request body of an endpoint that takes inline files: one file at the cap, base64-encoded
|
|
10
|
-
* (a third larger), and the rest of the request.
|
|
11
|
-
*/
|
|
12
|
-
export const INLINE_BODY_MAX_BYTES = 28 * 1024 * 1024;
|
|
5
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
13
6
|
const BASE64 = /^[A-Za-z0-9+/_-]*={0,2}$/;
|
|
14
7
|
/** The 400 for a file sent by URL. */
|
|
15
8
|
export function refuseUrl() {
|
|
@@ -23,26 +16,26 @@ export function inlineFileName(mediaType, index) {
|
|
|
23
16
|
return `file-${index + 1}${extension ? `.${extension}` : ""}`;
|
|
24
17
|
}
|
|
25
18
|
/**
|
|
26
|
-
* Stores `file` as a new artifact of `sessionId`, within the
|
|
27
|
-
* artifact limits: `400` for bad base64 or a bad
|
|
19
|
+
* Stores `file` as a new artifact of `sessionId`, within the Tenant's Runtime limit
|
|
20
|
+
* `inlineFileBytes` (read for the file) and its artifact limits: `400` for bad base64 or a bad
|
|
21
|
+
* media type, `413 limit_exceeded` past a limit.
|
|
28
22
|
*/
|
|
29
23
|
export async function storeInlineFile(ctx, sessionId, file, access) {
|
|
30
24
|
const data = file.data.replace(/\s+/gu, "");
|
|
31
25
|
if (!BASE64.test(data) || data.length % 4 === 1)
|
|
32
26
|
fail(400, `File "${file.name}" is not valid base64`, { code: "invalid_request" });
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
const { inlineFileBytes } = await ctx.store.tx((t) => readRuntimeLimits(t));
|
|
28
|
+
const tooLarge = () => fail(413, `A file sent inline may hold at most ${inlineFileBytes} bytes (the Runtime limit inlineFileBytes)`, {
|
|
29
|
+
code: "limit_exceeded",
|
|
30
|
+
details: { limitBytes: inlineFileBytes },
|
|
31
|
+
});
|
|
32
|
+
// Three bytes per four characters, before decoding a byte; padding may take up to two.
|
|
33
|
+
if (Math.floor((data.length * 3) / 4) > inlineFileBytes + 2)
|
|
34
|
+
tooLarge();
|
|
39
35
|
// Node's base64 decoder reads both alphabets.
|
|
40
36
|
const bytes = Buffer.from(data, "base64");
|
|
41
|
-
if (bytes.byteLength >
|
|
42
|
-
|
|
43
|
-
code: "limit_exceeded",
|
|
44
|
-
details: { limitBytes: INLINE_FILE_MAX_BYTES },
|
|
45
|
-
});
|
|
37
|
+
if (bytes.byteLength > inlineFileBytes)
|
|
38
|
+
tooLarge();
|
|
46
39
|
if (bytes.byteLength === 0)
|
|
47
40
|
fail(400, `File "${file.name}" is empty`, { code: "invalid_request" });
|
|
48
41
|
const contentType = normalizeContentType(file.mediaType) ??
|
|
@@ -73,8 +66,9 @@ export async function priorParts(ctx, sessionId, idempotencyKey) {
|
|
|
73
66
|
*/
|
|
74
67
|
export async function messageParts(ctx, sessionId, idempotencyKey, inbound, access) {
|
|
75
68
|
const parts = inbound.filter((part) => part.type !== "text" || part.text !== "");
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
const { messageParts: most } = await ctx.store.tx((t) => readRuntimeLimits(t));
|
|
70
|
+
if (parts.length > most)
|
|
71
|
+
fail(400, `A message may carry at most ${most} parts (the Runtime limit messageParts)`, { code: "invalid_request" });
|
|
78
72
|
const prior = parts.some((part) => part.type === "inline")
|
|
79
73
|
? (await priorParts(ctx, sessionId, idempotencyKey))?.filter((part) => part.type === "file")
|
|
80
74
|
: undefined;
|
package/dist/api/openapi.js
CHANGED
|
@@ -118,7 +118,7 @@ const RUNTIME_TAGS = [
|
|
|
118
118
|
{
|
|
119
119
|
name: "Definition files",
|
|
120
120
|
group: "Agents",
|
|
121
|
-
description: "The files a definition names by the SHA-256 of their bytes (each skill's folder): upload each once before putting the definition, which is refused while it names one the Runtime does not hold (`definition_files_missing`). `saveAgent` in `@nylorun/agents` does both.",
|
|
121
|
+
description: "The files a definition names by the SHA-256 of their bytes (each skill's folder): upload each once before putting the definition, which is refused while it names one the Runtime does not hold (`definition_files_missing`) or a skill whose files hold more than the Tenant's skill limit together (`limit_exceeded`; `/v1/tenant/skills`, default 2 GiB). No file and no count of files has a limit of its own. `saveAgent` in `@nylorun/agents` does both.",
|
|
122
122
|
operations: ["PUT /v1/files/{file}", "HEAD /v1/files/{file}"],
|
|
123
123
|
},
|
|
124
124
|
{
|
|
@@ -285,12 +285,16 @@ const MANAGEMENT_TAGS = [
|
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
name: "Settings",
|
|
288
|
-
description: "The Tenant's sandbox backend, Environment and
|
|
288
|
+
description: "The Tenant's sandbox backend, Environment, artifact and skill settings, and its Runtime limits. `admin.settings`.",
|
|
289
289
|
operations: [
|
|
290
290
|
"GET /v1/tenant/sandbox",
|
|
291
291
|
"PUT /v1/tenant/sandbox",
|
|
292
292
|
"GET /v1/tenant/artifacts",
|
|
293
293
|
"PUT /v1/tenant/artifacts",
|
|
294
|
+
"GET /v1/tenant/skills",
|
|
295
|
+
"PUT /v1/tenant/skills",
|
|
296
|
+
"GET /v1/tenant/limits",
|
|
297
|
+
"PUT /v1/tenant/limits",
|
|
294
298
|
],
|
|
295
299
|
},
|
|
296
300
|
];
|
package/dist/artifacts/disk.d.ts
CHANGED
|
@@ -2,10 +2,6 @@ import { type EnvironmentRestore, type EnvironmentSnapshot, type EnvironmentVers
|
|
|
2
2
|
import type { Tx } from "../store/types.js";
|
|
3
3
|
import type { TenantContext } from "../tenant/context.js";
|
|
4
4
|
import { assertReaches } from "../tenant/environments.js";
|
|
5
|
-
/** The most entries one version holds (R6 Q11). */
|
|
6
|
-
export declare const DISK_MAX_ENTRIES = 200000;
|
|
7
|
-
/** The most bytes one version's files hold. */
|
|
8
|
-
export declare const DISK_MAX_BYTES: number;
|
|
9
5
|
/** The name of an Environment's disk artifact. */
|
|
10
6
|
export declare const DISK_ARTIFACT_NAME = "disk";
|
|
11
7
|
/**
|