@nylorun/runtime 0.26.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.
Files changed (321) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +18 -14
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.js +1 -1
  12. package/dist/api/a2a/endpoint.js +3 -4
  13. package/dist/api/a2a/protocol.js +4 -0
  14. package/dist/api/a2a/routes.d.ts +2 -6
  15. package/dist/api/a2a/routes.js +12 -18
  16. package/dist/api/ag-ui/endpoint.js +2 -3
  17. package/dist/api/ag-ui/routes.js +4 -4
  18. package/dist/api/components.d.ts +326 -45
  19. package/dist/api/components.js +5 -1
  20. package/dist/api/gate/keys.js +7 -4
  21. package/dist/api/gate/routes.d.ts +6 -2
  22. package/dist/api/gate/routes.js +15 -11
  23. package/dist/api/http/body.d.ts +15 -3
  24. package/dist/api/http/body.js +15 -7
  25. package/dist/api/http/respond.js +6 -2
  26. package/dist/api/http/routes/access.js +5 -4
  27. package/dist/api/http/routes/artifacts.js +19 -7
  28. package/dist/api/http/routes/coverage.js +1 -1
  29. package/dist/api/http/routes/environment-manifests.js +1 -1
  30. package/dist/api/http/routes/environments.js +6 -6
  31. package/dist/api/http/routes/files.d.ts +2 -1
  32. package/dist/api/http/routes/files.js +31 -5
  33. package/dist/api/http/routes/mcp.js +1 -1
  34. package/dist/api/http/routes/reads.d.ts +35 -0
  35. package/dist/api/http/routes/reads.js +50 -22
  36. package/dist/api/http/routes/sessions.js +11 -10
  37. package/dist/api/http/routes/tenant.d.ts +2 -2
  38. package/dist/api/http/routes/tenant.js +69 -16
  39. package/dist/api/inbound-files.d.ts +3 -9
  40. package/dist/api/inbound-files.js +18 -24
  41. package/dist/api/openapi.js +6 -2
  42. package/dist/artifacts/disk.d.ts +0 -4
  43. package/dist/artifacts/disk.js +77 -73
  44. package/dist/artifacts/files.d.ts +13 -4
  45. package/dist/artifacts/files.js +27 -9
  46. package/dist/artifacts/folder-upload.d.ts +75 -0
  47. package/dist/artifacts/folder-upload.js +263 -0
  48. package/dist/artifacts/folders.d.ts +14 -8
  49. package/dist/artifacts/folders.js +19 -11
  50. package/dist/artifacts/links.js +7 -6
  51. package/dist/artifacts/parts.d.ts +9 -14
  52. package/dist/artifacts/parts.js +11 -9
  53. package/dist/artifacts/pipe.d.ts +15 -0
  54. package/dist/artifacts/pipe.js +71 -0
  55. package/dist/artifacts/quota.d.ts +36 -0
  56. package/dist/artifacts/quota.js +44 -0
  57. package/dist/artifacts/service.d.ts +25 -1
  58. package/dist/artifacts/service.js +70 -25
  59. package/dist/blob/fs.js +2 -1
  60. package/dist/blob/s3.d.ts +6 -2
  61. package/dist/blob/s3.js +9 -7
  62. package/dist/blob/types.d.ts +1 -1
  63. package/dist/blob/types.js +1 -1
  64. package/dist/core/limits.d.ts +11 -35
  65. package/dist/core/limits.js +5 -73
  66. package/dist/core/sandbox-routes.js +0 -1
  67. package/dist/core/turn-manifest.d.ts +1 -2
  68. package/dist/core/turn-manifest.js +4 -28
  69. package/dist/execution/memory.d.ts +8 -5
  70. package/dist/execution/memory.js +8 -6
  71. package/dist/execution/types.d.ts +1 -6
  72. package/dist/execution/types.js +0 -5
  73. package/dist/gates/contract.d.ts +3 -2
  74. package/dist/gates/contract.js +1 -1
  75. package/dist/gates/egress-credentials.js +4 -3
  76. package/dist/gates/egress.d.ts +9 -9
  77. package/dist/gates/egress.js +14 -16
  78. package/dist/gates/fixture-model.js +13 -2
  79. package/dist/gates/handler.d.ts +1 -3
  80. package/dist/gates/handler.js +4 -3
  81. package/dist/gates/http-client.d.ts +4 -3
  82. package/dist/gates/http-client.js +5 -7
  83. package/dist/gates/http-tool.d.ts +24 -4
  84. package/dist/gates/http-tool.js +40 -12
  85. package/dist/gates/in-process.d.ts +2 -1
  86. package/dist/gates/in-process.js +17 -9
  87. package/dist/gates/inflight.d.ts +2 -2
  88. package/dist/gates/inflight.js +3 -2
  89. package/dist/gates/journal.js +5 -7
  90. package/dist/gates/mcp-handler.d.ts +4 -3
  91. package/dist/gates/mcp-handler.js +15 -5
  92. package/dist/gates/model-gate.d.ts +11 -2
  93. package/dist/gates/policy.d.ts +28 -5
  94. package/dist/gates/policy.js +60 -17
  95. package/dist/gates/provider-gate.d.ts +22 -0
  96. package/dist/gates/provider-gate.js +30 -0
  97. package/dist/gates/tool-calls.d.ts +1 -3
  98. package/dist/gates/tool-calls.js +2 -3
  99. package/dist/gates/tool-client.d.ts +1 -1
  100. package/dist/gates/tool-client.js +5 -6
  101. package/dist/gates/tool-contract.d.ts +8 -5
  102. package/dist/gates/tool-contract.js +1 -1
  103. package/dist/gates/tool-gate.d.ts +18 -7
  104. package/dist/gates/tool-gate.js +49 -7
  105. package/dist/harness/calls.d.ts +27 -25
  106. package/dist/harness/calls.js +40 -46
  107. package/dist/harness/executors.d.ts +10 -3
  108. package/dist/harness/executors.js +53 -23
  109. package/dist/harness/main.js +2 -2
  110. package/dist/harness/pod.d.ts +0 -2
  111. package/dist/harness/pod.js +7 -6
  112. package/dist/harness/run-tokens.d.ts +4 -1
  113. package/dist/harness/run-tokens.js +6 -2
  114. package/dist/harness/service.d.ts +17 -14
  115. package/dist/harness/service.js +21 -35
  116. package/dist/harness/setup.d.ts +0 -2
  117. package/dist/harness/setup.js +4 -9
  118. package/dist/harness/ws-client.d.ts +2 -2
  119. package/dist/harness/ws-client.js +7 -5
  120. package/dist/harness/ws-port.d.ts +3 -4
  121. package/dist/harness/ws-port.js +3 -4
  122. package/dist/harness-api/artifacts.d.ts +20 -0
  123. package/dist/harness-api/artifacts.js +478 -0
  124. package/dist/harness-api/in-process.d.ts +2 -2
  125. package/dist/harness-api/in-process.js +18 -16
  126. package/dist/harness-api/record.d.ts +10 -9
  127. package/dist/harness-api/record.js +102 -157
  128. package/dist/harness-api/renew.d.ts +1 -1
  129. package/dist/harness-api/renew.js +2 -2
  130. package/dist/harness-api/server.d.ts +1 -16
  131. package/dist/harness-api/server.js +82 -73
  132. package/dist/harness-api/start.d.ts +20 -2
  133. package/dist/harness-api/start.js +57 -18
  134. package/dist/harness-api/tool-journal.d.ts +15 -10
  135. package/dist/harness-api/tool-journal.js +26 -24
  136. package/dist/harness-api/workspace.d.ts +0 -4
  137. package/dist/harness-api/workspace.js +2 -45
  138. package/dist/harness-api/ws-server.js +10 -10
  139. package/dist/host/create-host.js +2 -3
  140. package/dist/host/execution.js +2 -3
  141. package/dist/host/gates.d.ts +2 -9
  142. package/dist/host/gates.js +15 -12
  143. package/dist/host/http.d.ts +0 -2
  144. package/dist/host/http.js +0 -27
  145. package/dist/host/main.js +10 -3
  146. package/dist/host/operate.js +12 -3
  147. package/dist/host/stack-config.d.ts +11 -0
  148. package/dist/host/stack-config.js +21 -1
  149. package/dist/index.d.ts +0 -1
  150. package/dist/index.js +0 -1
  151. package/dist/infra/index.js +2 -1
  152. package/dist/infra/readiness.d.ts +0 -2
  153. package/dist/infra/readiness.js +4 -3
  154. package/dist/keys/client.d.ts +1 -0
  155. package/dist/keys/client.js +2 -3
  156. package/dist/keys/contract.d.ts +0 -2
  157. package/dist/keys/contract.js +0 -2
  158. package/dist/keys/egress-ca.d.ts +0 -2
  159. package/dist/keys/egress-ca.js +2 -3
  160. package/dist/keys/keys.js +6 -1
  161. package/dist/keys/x509.d.ts +8 -4
  162. package/dist/keys/x509.js +14 -5
  163. package/dist/management-openapi.json +1961 -994
  164. package/dist/mcp/connect.d.ts +35 -26
  165. package/dist/mcp/connect.js +112 -141
  166. package/dist/mcp/pool.d.ts +27 -22
  167. package/dist/mcp/pool.js +34 -48
  168. package/dist/mcp/preview.d.ts +12 -5
  169. package/dist/mcp/preview.js +36 -19
  170. package/dist/mcp/snapshot.d.ts +35 -86
  171. package/dist/mcp/snapshot.js +93 -135
  172. package/dist/model/auth-store.js +5 -3
  173. package/dist/model/capabilities.d.ts +1 -1
  174. package/dist/model/catalog.d.ts +9 -36
  175. package/dist/model/catalog.js +6 -33
  176. package/dist/model/descriptor.d.ts +51 -0
  177. package/dist/model/descriptor.js +83 -0
  178. package/dist/model/models.d.ts +0 -3
  179. package/dist/model/models.js +3 -5
  180. package/dist/model/native-blocks.js +1 -1
  181. package/dist/model/pi-model.d.ts +16 -5
  182. package/dist/model/pi-model.js +86 -105
  183. package/dist/openapi.json +838 -1218
  184. package/dist/reads/cursor.js +3 -1
  185. package/dist/record/append.d.ts +34 -2
  186. package/dist/record/append.js +50 -1
  187. package/dist/record/environment.d.ts +2 -0
  188. package/dist/record/environment.js +2 -0
  189. package/dist/record/index.d.ts +1 -1
  190. package/dist/record/index.js +1 -1
  191. package/dist/redact.d.ts +1 -1
  192. package/dist/redact.js +3 -2
  193. package/dist/sandbox/egress-token.d.ts +3 -3
  194. package/dist/sandbox/egress-token.js +3 -3
  195. package/dist/sandbox/join.js +3 -2
  196. package/dist/sandbox/manager.d.ts +41 -41
  197. package/dist/sandbox/manager.js +112 -44
  198. package/dist/sandbox/placement.d.ts +1 -1
  199. package/dist/sandbox/placement.js +5 -8
  200. package/dist/sandbox/pods/client.d.ts +1 -10
  201. package/dist/sandbox/pods/client.js +8 -4
  202. package/dist/sandbox/pods/lifecycle.d.ts +19 -11
  203. package/dist/sandbox/pods/lifecycle.js +45 -23
  204. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  205. package/dist/sandbox/pods/network-gate.js +7 -7
  206. package/dist/sandbox/pods/reconcile.js +4 -1
  207. package/dist/sandbox/pods/spec.js +8 -5
  208. package/dist/sandbox/policy.d.ts +7 -1
  209. package/dist/sandbox/policy.js +10 -3
  210. package/dist/sandbox/resolve.d.ts +8 -3
  211. package/dist/sandbox/resolve.js +12 -2
  212. package/dist/sandbox/setup.d.ts +3 -1
  213. package/dist/sandbox/setup.js +34 -11
  214. package/dist/sandbox/share.d.ts +5 -14
  215. package/dist/sandbox/share.js +5 -34
  216. package/dist/sandbox/skills.d.ts +7 -13
  217. package/dist/sandbox/skills.js +16 -31
  218. package/dist/sandbox/tenant-config.d.ts +2 -2
  219. package/dist/sandbox/tenant-config.js +4 -3
  220. package/dist/sandbox/tools.d.ts +7 -1
  221. package/dist/sandbox/tools.js +26 -26
  222. package/dist/sandbox/types.d.ts +10 -3
  223. package/dist/sandbox/types.js +9 -0
  224. package/dist/store/postgres/connect.d.ts +6 -5
  225. package/dist/store/postgres/connect.js +14 -11
  226. package/dist/store/postgres/control.js +13 -13
  227. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  228. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  229. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  230. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  231. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  232. package/dist/store/postgres/reads.js +10 -2
  233. package/dist/store/postgres/schema.d.ts +19 -0
  234. package/dist/store/postgres/schema.js +7 -0
  235. package/dist/store/postgres/store.js +55 -26
  236. package/dist/store/types.d.ts +17 -7
  237. package/dist/streams/relay/core.d.ts +3 -3
  238. package/dist/streams/relay/core.js +9 -10
  239. package/dist/tenant/advance.js +21 -31
  240. package/dist/tenant/commands.js +9 -16
  241. package/dist/tenant/context.d.ts +14 -17
  242. package/dist/tenant/definition-files.d.ts +25 -4
  243. package/dist/tenant/definition-files.js +85 -47
  244. package/dist/tenant/effects.js +9 -36
  245. package/dist/tenant/environment-manifests.js +9 -1
  246. package/dist/tenant/environments.d.ts +0 -2
  247. package/dist/tenant/environments.js +49 -28
  248. package/dist/tenant/ephemeral.js +2 -1
  249. package/dist/tenant/history.d.ts +1 -1
  250. package/dist/tenant/history.js +1 -1
  251. package/dist/tenant/host-token.d.ts +3 -3
  252. package/dist/tenant/host-token.js +11 -10
  253. package/dist/tenant/http.d.ts +5 -8
  254. package/dist/tenant/http.js +9 -0
  255. package/dist/tenant/identity-file.d.ts +23 -0
  256. package/dist/tenant/identity-file.js +34 -6
  257. package/dist/tenant/issuers.d.ts +5 -5
  258. package/dist/tenant/issuers.js +31 -23
  259. package/dist/tenant/jwt.d.ts +8 -2
  260. package/dist/tenant/jwt.js +10 -2
  261. package/dist/tenant/limits.d.ts +90 -0
  262. package/dist/tenant/limits.js +175 -0
  263. package/dist/tenant/module.d.ts +4 -1
  264. package/dist/tenant/module.js +9 -8
  265. package/dist/tenant/outbound.d.ts +11 -14
  266. package/dist/tenant/outbound.js +10 -15
  267. package/dist/tenant/run-grants.js +3 -2
  268. package/dist/tenant/run-token.d.ts +0 -4
  269. package/dist/tenant/run-token.js +6 -11
  270. package/dist/tenant/runtime-limits.d.ts +24 -0
  271. package/dist/tenant/runtime-limits.js +27 -0
  272. package/dist/tenant/runtime.js +30 -43
  273. package/dist/tenant/session-streams.js +7 -6
  274. package/dist/tenant/session.d.ts +1 -1
  275. package/dist/tenant/session.js +1 -1
  276. package/dist/tenant/sessions.js +32 -29
  277. package/dist/tenant/signing-keys.d.ts +1 -1
  278. package/dist/tenant/signing-keys.js +5 -6
  279. package/dist/tenant/status.js +4 -3
  280. package/dist/tenant/streams.js +18 -20
  281. package/dist/tenant/sweep.d.ts +1 -21
  282. package/dist/tenant/sweep.js +39 -48
  283. package/dist/tenant/transcript.d.ts +70 -23
  284. package/dist/tenant/transcript.js +28 -22
  285. package/dist/tenant/types.d.ts +9 -14
  286. package/dist/tenant/worker.d.ts +2 -10
  287. package/dist/tenant/worker.js +5 -14
  288. package/dist/traces/ids.js +5 -1
  289. package/dist/traces/map.d.ts +9 -6
  290. package/dist/traces/map.js +9 -11
  291. package/dist/traces/otlp.d.ts +3 -3
  292. package/dist/traces/otlp.js +5 -3
  293. package/dist/traces/projector.d.ts +9 -4
  294. package/dist/traces/projector.js +13 -7
  295. package/dist/traces/read.js +6 -1
  296. package/dist/tuning.d.ts +383 -0
  297. package/dist/tuning.js +658 -0
  298. package/dist/vault/headers.js +4 -2
  299. package/dist/version.d.ts +1 -1
  300. package/dist/version.js +1 -1
  301. package/package.json +3 -3
  302. package/dist/artifacts/export.d.ts +0 -8
  303. package/dist/artifacts/export.js +0 -204
  304. package/dist/artifacts/media-types.d.ts +0 -30
  305. package/dist/artifacts/media-types.js +0 -137
  306. package/dist/artifacts/workspace.d.ts +0 -60
  307. package/dist/artifacts/workspace.js +0 -45
  308. package/dist/media.d.ts +0 -10
  309. package/dist/media.js +0 -58
  310. package/dist/sandbox/runtime-tools.d.ts +0 -9
  311. package/dist/sandbox/runtime-tools.js +0 -16
  312. package/dist/sandbox/session-sandbox.d.ts +0 -16
  313. package/dist/sandbox/session-sandbox.js +0 -77
  314. package/dist/tenant/artifact-tool.d.ts +0 -13
  315. package/dist/tenant/artifact-tool.js +0 -243
  316. package/dist/tenant/skill-tool.d.ts +0 -20
  317. package/dist/tenant/skill-tool.js +0 -67
  318. package/dist/tenant/tool-results.d.ts +0 -57
  319. package/dist/tenant/tool-results.js +0 -298
  320. package/dist/tenant/tool-search.d.ts +0 -39
  321. 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: z.coerce.number().int().min(1).max(200).optional(),
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(turnQuery
97
- .extend({
98
- limit: z.coerce.number().int().min(1).max(200).default(50),
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: z.coerce.number().int().min(1).max(200).default(50),
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
- return jsonResponse(200, await readStoreOf(c.env.tenant).toolCalls(c.req.param("sessionId"), q.turnId, q, readAccess(c.get("scope"))));
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.number().int().min(1).max(1000).default(200),
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
- return jsonResponse(200, await readStoreOf(c.env.tenant).exportModel(q.after, q.limit));
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.req.raw)), c.get("scope"), trace ? { trace } : {}));
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: { 200: json(PutAgentResponse, "The definition, stored (or checked, with dryRun)") },
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.req.raw)), { dryRun: c.req.query("dryRun") === "true" }));
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",
@@ -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.req.raw));
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.req.raw), c.req.raw.signal));
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), and the sandboxes its
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: !ctx.useVaultModel ||
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.req.raw));
116
- await drain(ctx, request.activeWork, 30_000);
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.req.raw));
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. A message naming a file the Tenant's model does not read is refused with `422 attachment_unsupported`. A custom endpoint sets `settings.inputs` on `PUT /v1/tenant/model` instead.",
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.req.raw));
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.req.raw));
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.req.raw));
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) => jsonResponse(200, await c.env.tenant.store.tx((t) => readToolPolicy(t))));
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.req.raw));
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) => t.putSetting(TOOL_POLICY_SETTING, JSON.stringify(policy)));
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.req.raw))));
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.req.raw))));
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 inline cap and the Tenant's
44
- * artifact limits: `400` for bad base64 or a bad media type, `413 limit_exceeded` past a limit.
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 "../artifacts/media-types.js";
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
- /** The largest file a message may carry inline, decoded. */
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 inline cap and the Tenant's
27
- * artifact limits: `400` for bad base64 or a bad media type, `413 limit_exceeded` past a limit.
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
- // Three bytes per four characters, before decoding a byte.
34
- if (Math.floor((data.length * 3) / 4) > INLINE_FILE_MAX_BYTES + 2)
35
- fail(413, `A file sent inline may hold at most ${INLINE_FILE_MAX_BYTES} bytes`, {
36
- code: "limit_exceeded",
37
- details: { limitBytes: INLINE_FILE_MAX_BYTES },
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 > INLINE_FILE_MAX_BYTES)
42
- fail(413, `A file sent inline may hold at most ${INLINE_FILE_MAX_BYTES} bytes`, {
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
- if (parts.length > MAX_MESSAGE_PARTS)
77
- fail(400, `A message may carry at most ${MAX_MESSAGE_PARTS} parts`, { code: "invalid_request" });
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;
@@ -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 artifact settings. `admin.settings`.",
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
  ];
@@ -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
  /**