@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.
Files changed (326) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +26 -16
  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/app.d.ts +2 -2
  24. package/dist/api/http/app.js +2 -2
  25. package/dist/api/http/body.d.ts +15 -3
  26. package/dist/api/http/body.js +15 -7
  27. package/dist/api/http/define.d.ts +1 -1
  28. package/dist/api/http/respond.js +7 -3
  29. package/dist/api/http/routes/access.js +5 -4
  30. package/dist/api/http/routes/artifacts.js +19 -7
  31. package/dist/api/http/routes/coverage.js +1 -1
  32. package/dist/api/http/routes/environment-manifests.js +1 -1
  33. package/dist/api/http/routes/environments.js +6 -6
  34. package/dist/api/http/routes/files.d.ts +2 -1
  35. package/dist/api/http/routes/files.js +31 -5
  36. package/dist/api/http/routes/mcp.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +35 -0
  38. package/dist/api/http/routes/reads.js +50 -22
  39. package/dist/api/http/routes/sessions.js +12 -11
  40. package/dist/api/http/routes/tenant.d.ts +2 -2
  41. package/dist/api/http/routes/tenant.js +69 -16
  42. package/dist/api/inbound-files.d.ts +3 -9
  43. package/dist/api/inbound-files.js +18 -24
  44. package/dist/api/openapi.js +6 -2
  45. package/dist/artifacts/disk.d.ts +0 -4
  46. package/dist/artifacts/disk.js +77 -73
  47. package/dist/artifacts/files.d.ts +13 -4
  48. package/dist/artifacts/files.js +27 -9
  49. package/dist/artifacts/folder-upload.d.ts +75 -0
  50. package/dist/artifacts/folder-upload.js +263 -0
  51. package/dist/artifacts/folders.d.ts +14 -8
  52. package/dist/artifacts/folders.js +19 -11
  53. package/dist/artifacts/links.js +7 -6
  54. package/dist/artifacts/parts.d.ts +9 -14
  55. package/dist/artifacts/parts.js +11 -9
  56. package/dist/artifacts/pipe.d.ts +15 -0
  57. package/dist/artifacts/pipe.js +71 -0
  58. package/dist/artifacts/quota.d.ts +36 -0
  59. package/dist/artifacts/quota.js +44 -0
  60. package/dist/artifacts/service.d.ts +25 -1
  61. package/dist/artifacts/service.js +70 -25
  62. package/dist/blob/fs.js +2 -1
  63. package/dist/blob/s3.d.ts +6 -2
  64. package/dist/blob/s3.js +9 -7
  65. package/dist/blob/types.d.ts +1 -1
  66. package/dist/blob/types.js +1 -1
  67. package/dist/core/limits.d.ts +11 -35
  68. package/dist/core/limits.js +5 -73
  69. package/dist/core/sandbox-routes.js +0 -1
  70. package/dist/core/turn-manifest.d.ts +1 -2
  71. package/dist/core/turn-manifest.js +4 -28
  72. package/dist/execution/memory.d.ts +8 -5
  73. package/dist/execution/memory.js +8 -6
  74. package/dist/execution/types.d.ts +1 -6
  75. package/dist/execution/types.js +0 -5
  76. package/dist/gates/contract.d.ts +3 -2
  77. package/dist/gates/contract.js +1 -1
  78. package/dist/gates/egress-credentials.js +4 -3
  79. package/dist/gates/egress.d.ts +9 -9
  80. package/dist/gates/egress.js +19 -19
  81. package/dist/gates/fixture-model.js +13 -2
  82. package/dist/gates/handler.d.ts +1 -3
  83. package/dist/gates/handler.js +4 -3
  84. package/dist/gates/http-client.d.ts +4 -3
  85. package/dist/gates/http-client.js +5 -7
  86. package/dist/gates/http-tool.d.ts +24 -4
  87. package/dist/gates/http-tool.js +53 -17
  88. package/dist/gates/in-process.d.ts +2 -1
  89. package/dist/gates/in-process.js +17 -9
  90. package/dist/gates/inflight.d.ts +2 -2
  91. package/dist/gates/inflight.js +3 -2
  92. package/dist/gates/journal.js +5 -7
  93. package/dist/gates/mcp-handler.d.ts +4 -3
  94. package/dist/gates/mcp-handler.js +15 -5
  95. package/dist/gates/model-gate.d.ts +11 -2
  96. package/dist/gates/policy.d.ts +28 -5
  97. package/dist/gates/policy.js +60 -17
  98. package/dist/gates/provider-gate.d.ts +22 -0
  99. package/dist/gates/provider-gate.js +30 -0
  100. package/dist/gates/tool-calls.d.ts +1 -3
  101. package/dist/gates/tool-calls.js +2 -3
  102. package/dist/gates/tool-client.d.ts +1 -1
  103. package/dist/gates/tool-client.js +5 -6
  104. package/dist/gates/tool-contract.d.ts +8 -5
  105. package/dist/gates/tool-contract.js +1 -1
  106. package/dist/gates/tool-gate.d.ts +18 -7
  107. package/dist/gates/tool-gate.js +49 -7
  108. package/dist/harness/calls.d.ts +27 -25
  109. package/dist/harness/calls.js +40 -46
  110. package/dist/harness/executors.d.ts +10 -3
  111. package/dist/harness/executors.js +53 -23
  112. package/dist/harness/main.js +2 -2
  113. package/dist/harness/pod.d.ts +0 -2
  114. package/dist/harness/pod.js +7 -6
  115. package/dist/harness/run-tokens.d.ts +4 -1
  116. package/dist/harness/run-tokens.js +6 -2
  117. package/dist/harness/service.d.ts +17 -14
  118. package/dist/harness/service.js +21 -35
  119. package/dist/harness/setup.d.ts +0 -2
  120. package/dist/harness/setup.js +4 -9
  121. package/dist/harness/ws-client.d.ts +2 -2
  122. package/dist/harness/ws-client.js +7 -5
  123. package/dist/harness/ws-port.d.ts +3 -4
  124. package/dist/harness/ws-port.js +3 -4
  125. package/dist/harness-api/artifacts.d.ts +20 -0
  126. package/dist/harness-api/artifacts.js +478 -0
  127. package/dist/harness-api/in-process.d.ts +2 -2
  128. package/dist/harness-api/in-process.js +18 -16
  129. package/dist/harness-api/record.d.ts +10 -9
  130. package/dist/harness-api/record.js +102 -157
  131. package/dist/harness-api/renew.d.ts +1 -1
  132. package/dist/harness-api/renew.js +2 -2
  133. package/dist/harness-api/server.d.ts +1 -16
  134. package/dist/harness-api/server.js +82 -73
  135. package/dist/harness-api/start.d.ts +20 -2
  136. package/dist/harness-api/start.js +57 -18
  137. package/dist/harness-api/tool-journal.d.ts +15 -10
  138. package/dist/harness-api/tool-journal.js +26 -24
  139. package/dist/harness-api/workspace.d.ts +0 -4
  140. package/dist/harness-api/workspace.js +2 -45
  141. package/dist/harness-api/ws-server.js +10 -10
  142. package/dist/host/create-host.js +2 -3
  143. package/dist/host/execution.js +2 -3
  144. package/dist/host/gates.d.ts +2 -9
  145. package/dist/host/gates.js +15 -12
  146. package/dist/host/http.d.ts +0 -2
  147. package/dist/host/http.js +0 -27
  148. package/dist/host/main.js +10 -3
  149. package/dist/host/operate.js +12 -3
  150. package/dist/host/stack-config.d.ts +11 -0
  151. package/dist/host/stack-config.js +21 -1
  152. package/dist/index.d.ts +0 -1
  153. package/dist/index.js +0 -1
  154. package/dist/infra/index.js +2 -1
  155. package/dist/infra/readiness.d.ts +0 -2
  156. package/dist/infra/readiness.js +4 -3
  157. package/dist/keys/client.d.ts +1 -0
  158. package/dist/keys/client.js +2 -3
  159. package/dist/keys/contract.d.ts +1 -3
  160. package/dist/keys/contract.js +1 -3
  161. package/dist/keys/egress-ca.d.ts +0 -2
  162. package/dist/keys/egress-ca.js +2 -3
  163. package/dist/keys/keys.js +6 -1
  164. package/dist/keys/x509.d.ts +8 -4
  165. package/dist/keys/x509.js +14 -5
  166. package/dist/management-openapi.json +1961 -994
  167. package/dist/mcp/connect.d.ts +35 -26
  168. package/dist/mcp/connect.js +112 -141
  169. package/dist/mcp/pool.d.ts +27 -22
  170. package/dist/mcp/pool.js +34 -48
  171. package/dist/mcp/preview.d.ts +13 -6
  172. package/dist/mcp/preview.js +37 -20
  173. package/dist/mcp/snapshot.d.ts +35 -86
  174. package/dist/mcp/snapshot.js +93 -135
  175. package/dist/model/auth-store.js +5 -3
  176. package/dist/model/capabilities.d.ts +1 -1
  177. package/dist/model/catalog.d.ts +9 -36
  178. package/dist/model/catalog.js +6 -33
  179. package/dist/model/descriptor.d.ts +51 -0
  180. package/dist/model/descriptor.js +83 -0
  181. package/dist/model/models.d.ts +0 -3
  182. package/dist/model/models.js +3 -5
  183. package/dist/model/native-blocks.js +1 -1
  184. package/dist/model/pi-model.d.ts +16 -5
  185. package/dist/model/pi-model.js +86 -105
  186. package/dist/openapi.json +866 -1218
  187. package/dist/reads/cursor.js +3 -1
  188. package/dist/record/append.d.ts +34 -2
  189. package/dist/record/append.js +50 -1
  190. package/dist/record/environment.d.ts +2 -0
  191. package/dist/record/environment.js +2 -0
  192. package/dist/record/index.d.ts +1 -1
  193. package/dist/record/index.js +1 -1
  194. package/dist/redact.d.ts +1 -1
  195. package/dist/redact.js +3 -2
  196. package/dist/sandbox/egress-token.d.ts +3 -3
  197. package/dist/sandbox/egress-token.js +3 -3
  198. package/dist/sandbox/join.js +3 -2
  199. package/dist/sandbox/manager.d.ts +41 -41
  200. package/dist/sandbox/manager.js +112 -44
  201. package/dist/sandbox/placement.d.ts +1 -1
  202. package/dist/sandbox/placement.js +5 -8
  203. package/dist/sandbox/pods/client.d.ts +1 -10
  204. package/dist/sandbox/pods/client.js +8 -4
  205. package/dist/sandbox/pods/lifecycle.d.ts +19 -11
  206. package/dist/sandbox/pods/lifecycle.js +45 -23
  207. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  208. package/dist/sandbox/pods/network-gate.js +7 -7
  209. package/dist/sandbox/pods/reconcile.js +4 -1
  210. package/dist/sandbox/pods/spec.js +8 -5
  211. package/dist/sandbox/policy.d.ts +7 -1
  212. package/dist/sandbox/policy.js +10 -3
  213. package/dist/sandbox/resolve.d.ts +8 -3
  214. package/dist/sandbox/resolve.js +12 -2
  215. package/dist/sandbox/setup.d.ts +3 -1
  216. package/dist/sandbox/setup.js +34 -11
  217. package/dist/sandbox/share.d.ts +5 -14
  218. package/dist/sandbox/share.js +5 -34
  219. package/dist/sandbox/skills.d.ts +7 -13
  220. package/dist/sandbox/skills.js +16 -31
  221. package/dist/sandbox/tenant-config.d.ts +2 -2
  222. package/dist/sandbox/tenant-config.js +4 -3
  223. package/dist/sandbox/tools.d.ts +7 -1
  224. package/dist/sandbox/tools.js +26 -26
  225. package/dist/sandbox/types.d.ts +10 -3
  226. package/dist/sandbox/types.js +9 -0
  227. package/dist/store/postgres/connect.d.ts +6 -5
  228. package/dist/store/postgres/connect.js +14 -11
  229. package/dist/store/postgres/control.js +13 -13
  230. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  231. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  232. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  233. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  234. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  235. package/dist/store/postgres/migrate.js +1 -1
  236. package/dist/store/postgres/reads.js +10 -2
  237. package/dist/store/postgres/schema.d.ts +19 -0
  238. package/dist/store/postgres/schema.js +7 -0
  239. package/dist/store/postgres/store.js +55 -26
  240. package/dist/store/types.d.ts +17 -7
  241. package/dist/streams/relay/core.d.ts +3 -3
  242. package/dist/streams/relay/core.js +9 -10
  243. package/dist/tenant/advance.js +21 -31
  244. package/dist/tenant/commands.js +9 -16
  245. package/dist/tenant/context.d.ts +14 -17
  246. package/dist/tenant/definition-files.d.ts +25 -4
  247. package/dist/tenant/definition-files.js +85 -47
  248. package/dist/tenant/effects.js +9 -36
  249. package/dist/tenant/environment-manifests.js +9 -1
  250. package/dist/tenant/environments.d.ts +0 -2
  251. package/dist/tenant/environments.js +54 -30
  252. package/dist/tenant/ephemeral.d.ts +1 -1
  253. package/dist/tenant/ephemeral.js +2 -1
  254. package/dist/tenant/history.d.ts +1 -1
  255. package/dist/tenant/history.js +1 -1
  256. package/dist/tenant/host-token.d.ts +3 -3
  257. package/dist/tenant/host-token.js +12 -11
  258. package/dist/tenant/http.d.ts +5 -8
  259. package/dist/tenant/http.js +9 -0
  260. package/dist/tenant/identity-file.d.ts +24 -1
  261. package/dist/tenant/identity-file.js +35 -7
  262. package/dist/tenant/issuers.d.ts +5 -5
  263. package/dist/tenant/issuers.js +31 -23
  264. package/dist/tenant/jwt.d.ts +8 -2
  265. package/dist/tenant/jwt.js +10 -2
  266. package/dist/tenant/limits.d.ts +90 -0
  267. package/dist/tenant/limits.js +175 -0
  268. package/dist/tenant/module.d.ts +4 -1
  269. package/dist/tenant/module.js +9 -8
  270. package/dist/tenant/outbound.d.ts +17 -19
  271. package/dist/tenant/outbound.js +18 -21
  272. package/dist/tenant/run-grants.js +3 -2
  273. package/dist/tenant/run-token.d.ts +0 -4
  274. package/dist/tenant/run-token.js +6 -11
  275. package/dist/tenant/runtime-limits.d.ts +24 -0
  276. package/dist/tenant/runtime-limits.js +27 -0
  277. package/dist/tenant/runtime.js +30 -43
  278. package/dist/tenant/session-streams.js +7 -6
  279. package/dist/tenant/session.d.ts +1 -1
  280. package/dist/tenant/session.js +1 -1
  281. package/dist/tenant/sessions.js +32 -29
  282. package/dist/tenant/signing-keys.d.ts +1 -1
  283. package/dist/tenant/signing-keys.js +5 -6
  284. package/dist/tenant/status.js +4 -3
  285. package/dist/tenant/streams.js +18 -20
  286. package/dist/tenant/sweep.d.ts +1 -21
  287. package/dist/tenant/sweep.js +39 -48
  288. package/dist/tenant/transcript.d.ts +70 -23
  289. package/dist/tenant/transcript.js +28 -22
  290. package/dist/tenant/types.d.ts +11 -16
  291. package/dist/tenant/worker.d.ts +2 -10
  292. package/dist/tenant/worker.js +5 -14
  293. package/dist/traces/ids.js +5 -1
  294. package/dist/traces/map.d.ts +9 -6
  295. package/dist/traces/map.js +9 -11
  296. package/dist/traces/otlp.d.ts +3 -3
  297. package/dist/traces/otlp.js +5 -3
  298. package/dist/traces/projector.d.ts +9 -4
  299. package/dist/traces/projector.js +13 -7
  300. package/dist/traces/read.js +6 -1
  301. package/dist/tuning.d.ts +383 -0
  302. package/dist/tuning.js +658 -0
  303. package/dist/vault/headers.js +4 -2
  304. package/dist/version.d.ts +1 -1
  305. package/dist/version.js +1 -1
  306. package/package.json +3 -3
  307. package/dist/artifacts/export.d.ts +0 -8
  308. package/dist/artifacts/export.js +0 -204
  309. package/dist/artifacts/media-types.d.ts +0 -30
  310. package/dist/artifacts/media-types.js +0 -137
  311. package/dist/artifacts/workspace.d.ts +0 -60
  312. package/dist/artifacts/workspace.js +0 -45
  313. package/dist/media.d.ts +0 -10
  314. package/dist/media.js +0 -58
  315. package/dist/sandbox/runtime-tools.d.ts +0 -9
  316. package/dist/sandbox/runtime-tools.js +0 -16
  317. package/dist/sandbox/session-sandbox.d.ts +0 -16
  318. package/dist/sandbox/session-sandbox.js +0 -77
  319. package/dist/tenant/artifact-tool.d.ts +0 -13
  320. package/dist/tenant/artifact-tool.js +0 -243
  321. package/dist/tenant/skill-tool.d.ts +0 -20
  322. package/dist/tenant/skill-tool.js +0 -67
  323. package/dist/tenant/tool-results.d.ts +0 -57
  324. package/dist/tenant/tool-results.js +0 -298
  325. package/dist/tenant/tool-search.d.ts +0 -39
  326. package/dist/tenant/tool-search.js +0 -103
@@ -0,0 +1,175 @@
1
+ /**
2
+ * What artifacts, skills and disk versions may hold: the Tenant's limits (`artifacts.config`,
3
+ * `skills.config` here, and the Runtime limits `diskVersion*` of `limits.config`), and an
4
+ * Environment's, which its manifest sets (`limits`). A limit the manifest leaves out is the
5
+ * Tenant's as it is when the limit is checked, and none is ever above the Tenant's: a manifest
6
+ * asking for more is refused when the Environment is made, and a Tenant that lowers a limit
7
+ * later lowers it for every Environment.
8
+ *
9
+ * Also the shape a manifest and an Environment's labels must fit, which the Tenant's Runtime
10
+ * limits set (`manifestShapeIssues`, `labelIssues`), checked when they are put rather than by
11
+ * their schemas (B15).
12
+ */
13
+ import { SKILL_TOTAL_BYTES_DEFAULT, TenantSkillsConfigSchema, } from "@nylorun/core/contracts";
14
+ import { parseSandboxDuration } from "@nylorun/core/define";
15
+ import { readArtifactLimits } from "../artifacts/config.js";
16
+ import { readRuntimeLimits } from "./runtime-limits.js";
17
+ export const SKILLS_CONFIG_SETTING = "skills.config";
18
+ /** The skill limits in force: the Tenant's, with the default for what it leaves unset. */
19
+ export function effectiveSkillLimits(config) {
20
+ return { totalBytes: config.limits?.totalBytes ?? SKILL_TOTAL_BYTES_DEFAULT };
21
+ }
22
+ export async function readSkillsConfig(t) {
23
+ const raw = await t.getSetting(SKILLS_CONFIG_SETTING);
24
+ if (raw === undefined)
25
+ return {};
26
+ const parsed = TenantSkillsConfigSchema.safeParse(JSON.parse(raw));
27
+ return parsed.success ? parsed.data : {};
28
+ }
29
+ export async function readSkillLimits(t) {
30
+ return effectiveSkillLimits(await readSkillsConfig(t));
31
+ }
32
+ export async function writeSkillsConfig(t, config) {
33
+ await t.putSetting(SKILLS_CONFIG_SETTING, JSON.stringify(TenantSkillsConfigSchema.parse(config)));
34
+ }
35
+ /** The Tenant's artifact, skill and disk version limits. */
36
+ export async function readTenantLimits(t) {
37
+ const runtime = await readRuntimeLimits(t);
38
+ return {
39
+ artifacts: await readArtifactLimits(t),
40
+ skills: await readSkillLimits(t),
41
+ disk: { versionBytes: runtime.diskVersionBytes, versionEntries: runtime.diskVersionEntries },
42
+ };
43
+ }
44
+ /** The limits in force for an Environment whose manifest sets `set`: each within the Tenant's. */
45
+ export function limitsInForce(tenant, set) {
46
+ const within = (asked, ceiling) => Math.min(asked ?? ceiling, ceiling);
47
+ return {
48
+ artifacts: {
49
+ fileBytes: within(set?.artifacts?.fileBytes, tenant.artifacts.fileBytes),
50
+ totalBytes: within(set?.artifacts?.totalBytes, tenant.artifacts.totalBytes),
51
+ },
52
+ skills: { totalBytes: within(set?.skills?.totalBytes, tenant.skills.totalBytes) },
53
+ disk: {
54
+ versionBytes: within(set?.disk?.versionBytes, tenant.disk.versionBytes),
55
+ versionEntries: within(set?.disk?.versionEntries, tenant.disk.versionEntries),
56
+ },
57
+ };
58
+ }
59
+ /** Why a manifest's `limits` cannot be made: each limit above the Tenant's. */
60
+ export function limitIssues(set, tenant) {
61
+ const issues = [];
62
+ const check = (path, asked, ceiling, unit = "bytes") => {
63
+ if (asked !== undefined && asked > ceiling)
64
+ issues.push(`limits.${path} asks for ${asked} ${unit}; this Tenant allows at most ${ceiling}.`);
65
+ };
66
+ check("artifacts.fileBytes", set?.artifacts?.fileBytes, tenant.artifacts.fileBytes);
67
+ check("artifacts.totalBytes", set?.artifacts?.totalBytes, tenant.artifacts.totalBytes);
68
+ check("skills.totalBytes", set?.skills?.totalBytes, tenant.skills.totalBytes);
69
+ check("disk.versionBytes", set?.disk?.versionBytes, tenant.disk.versionBytes);
70
+ check("disk.versionEntries", set?.disk?.versionEntries, tenant.disk.versionEntries, "entries");
71
+ return issues;
72
+ }
73
+ /**
74
+ * Why `labels` cannot be set: more than the Tenant's Runtime limit `count` names
75
+ * (`environmentLabels` or `artifactLabels`), or a key or a value longer than `labelKeyChars` or
76
+ * `labelValueChars`.
77
+ */
78
+ export function labelIssues(labels, limits, count = "environmentLabels") {
79
+ if (labels === undefined)
80
+ return [];
81
+ const issues = [];
82
+ const keys = Object.keys(labels);
83
+ if (keys.length > limits[count])
84
+ issues.push(`labels has ${keys.length}; the Runtime limit ${count} allows at most ${limits[count]} (PUT /v1/tenant/limits).`);
85
+ for (const key of keys) {
86
+ if (key.length > limits.labelKeyChars)
87
+ issues.push(`Label key ${JSON.stringify(key.slice(0, limits.labelKeyChars))}… has ${key.length} characters; the Runtime limit labelKeyChars allows at most ${limits.labelKeyChars} (PUT /v1/tenant/limits).`);
88
+ const value = labels[key];
89
+ if (value.length > limits.labelValueChars)
90
+ issues.push(`Label ${JSON.stringify(key)}'s value has ${value.length} characters; the Runtime limit labelValueChars allows at most ${limits.labelValueChars} (PUT /v1/tenant/limits).`);
91
+ }
92
+ return issues;
93
+ }
94
+ /** `"{what} has {n} characters; the Runtime limit {name} allows at most {limit} …"`, when over. */
95
+ function longer(what, text, name, limits) {
96
+ return text !== undefined && text.length > limits[name]
97
+ ? [`${what} has ${text.length} characters; the Runtime limit ${name} allows at most ${limits[name]} (PUT /v1/tenant/limits).`]
98
+ : [];
99
+ }
100
+ /** `"{what} has {n}; the Runtime limit {name} allows at most {limit} …"`, when over. */
101
+ function more(what, count, name, limits) {
102
+ return count > limits[name]
103
+ ? [`${what} has ${count}; the Runtime limit ${name} allows at most ${limits[name]} (PUT /v1/tenant/limits).`]
104
+ : [];
105
+ }
106
+ /**
107
+ * Why a secret write does not fit the Tenant's Runtime limits: an `environment_secret`'s or an
108
+ * `environment_variable`'s value past `secretValueChars`, or more hosts than `secretHosts`.
109
+ */
110
+ export function secretRequestIssues(request, limits) {
111
+ const valued = request.type === "environment_secret" || request.type === "environment_variable";
112
+ return [
113
+ ...(valued && typeof request.value === "string" ? longer("value", request.value, "secretValueChars", limits) : []),
114
+ ...more("allowedHosts", request.allowedHosts?.length ?? 0, "secretHosts", limits),
115
+ ];
116
+ }
117
+ /**
118
+ * Why a tool policy does not fit the Tenant's Runtime limits: more rules than `toolRules`, a
119
+ * selector or a `when` path past `toolRuleFieldChars`, a `reason` or a `matches` past
120
+ * `toolRuleTextChars`.
121
+ */
122
+ export function toolPolicyIssues(rules, limits) {
123
+ const issues = more("rules", rules.length, "toolRules", limits);
124
+ rules.forEach((rule, index) => {
125
+ const at = `rules[${index}]`;
126
+ issues.push(...longer(`${at}.tool`, rule.tool, "toolRuleFieldChars", limits), ...longer(`${at}.server`, rule.server, "toolRuleFieldChars", limits), ...longer(`${at}.agent`, rule.agent, "toolRuleFieldChars", limits), ...longer(`${at}.reason`, rule.reason, "toolRuleTextChars", limits));
127
+ for (const [path, condition] of Object.entries(rule.when ?? {}))
128
+ issues.push(...longer(`${at}.when path ${JSON.stringify(path.slice(0, limits.toolRuleFieldChars))}`, path, "toolRuleFieldChars", limits), ...longer(`${at}.when[${JSON.stringify(path)}].matches`, condition.matches, "toolRuleTextChars", limits));
129
+ });
130
+ return issues;
131
+ }
132
+ /** Why a set of model budgets does not fit: more than the Tenant's Runtime limit `modelBudgets`. */
133
+ export function budgetIssues(budgets, limits) {
134
+ return more("budgets", budgets.length, "modelBudgets", limits);
135
+ }
136
+ /**
137
+ * Why an agent's `requires` does not fit: more secrets than an Environment's manifest may declare
138
+ * slots for (the Runtime limit `environmentSecretSlots`).
139
+ */
140
+ export function requiresIssues(requires, limits) {
141
+ return more("requires.secrets", requires?.secrets?.length ?? 0, "environmentSecretSlots", limits);
142
+ }
143
+ /**
144
+ * Why a manifest (inline, or saved) does not fit the Tenant's Runtime limits: its description,
145
+ * its setup commands, their lengths and timeouts, its input file entries and the files of each,
146
+ * and its secret slots, their descriptions and hosts.
147
+ */
148
+ export function manifestShapeIssues(manifest, limits) {
149
+ const issues = [];
150
+ const over = (what, count, name) => {
151
+ if (count > limits[name])
152
+ issues.push(`${what} has ${count}; the Runtime limit ${name} allows at most ${limits[name]} (PUT /v1/tenant/limits).`);
153
+ };
154
+ over("setup", manifest.setup?.length ?? 0, "environmentSetupCommands");
155
+ over("files", manifest.files?.length ?? 0, "environmentFiles");
156
+ over("secrets", Object.keys(manifest.secrets ?? {}).length, "environmentSecretSlots");
157
+ issues.push(...longer("description", manifest.description, "manifestDescriptionChars", limits));
158
+ for (const [name, slot] of Object.entries(manifest.secrets ?? {})) {
159
+ issues.push(...longer(`secrets.${name}.description`, slot.description, "secretDescriptionChars", limits));
160
+ if ("allowedHosts" in slot && Array.isArray(slot.allowedHosts))
161
+ issues.push(...more(`secrets.${name}.allowedHosts`, slot.allowedHosts.length, "secretHosts", limits));
162
+ }
163
+ manifest.setup?.forEach((command, index) => {
164
+ if (command.command.length > limits.setupCommandChars)
165
+ issues.push(`setup[${index}].command has ${command.command.length} characters; the Runtime limit setupCommandChars allows at most ${limits.setupCommandChars} (PUT /v1/tenant/limits).`);
166
+ const ms = command.timeout === undefined ? undefined : parseSandboxDuration(command.timeout);
167
+ if (ms !== undefined && ms > limits.setupCommandMs)
168
+ issues.push(`setup[${index}].timeout asks for ${ms} ms; the Runtime limit setupCommandMs allows at most ${limits.setupCommandMs} (PUT /v1/tenant/limits).`);
169
+ });
170
+ manifest.files?.forEach((file, index) => {
171
+ if ("files" in file)
172
+ over(`files[${index}].files`, Object.keys(file.files).length, "environmentFilesPerEntry");
173
+ });
174
+ return issues;
175
+ }
@@ -3,7 +3,10 @@ export interface CreateTenantModuleOptions {
3
3
  /** Opens the Host's Tenant (`store-pg.ts`). */
4
4
  open: TenantOpener;
5
5
  logger: Logger;
6
- /** Bound on opening the Tenant; a slower open fails with `open-timeout`. */
6
+ /**
7
+ * Bound on opening the Tenant; a slower open fails with `open-timeout`. Default the Host
8
+ * tuning's `tenant.openTimeoutMs` (30 s).
9
+ */
7
10
  openTimeoutMs?: number;
8
11
  /**
9
12
  * Runs once the Tenant is open, at start or on a later retry: the Host starts what needs the
@@ -1,10 +1,7 @@
1
1
  import { openError, TenantOpenError } from "./cause.js";
2
2
  import { TimeoutError, withTimeout } from "./pool.js";
3
+ import { hostTuning } from "../tuning.js";
3
4
  import { TenantUnavailableError, } from "./types.js";
4
- const OPEN_TIMEOUT_MS = 30_000;
5
- /** Retries of an open that failed outside the Tenant: from 1 s, doubling, at most 30 s apart. */
6
- const RETRY_FIRST_MS = 1_000;
7
- const RETRY_MAX_MS = 30_000;
8
5
  /**
9
6
  * Deep Tenant module (§8): the Host's one Tenant over an injected opener (tenancy.md §5).
10
7
  *
@@ -15,11 +12,15 @@ const RETRY_MAX_MS = 30_000;
15
12
  * with backoff, and by the next request, which gets 503 meanwhile.
16
13
  */
17
14
  export function createTenantModule(options) {
18
- const { logger, openTimeoutMs = OPEN_TIMEOUT_MS } = options;
15
+ const tuning = hostTuning().tenant;
16
+ const { logger, openTimeoutMs = tuning.openTimeoutMs } = options;
17
+ // Retries of an open that failed outside the Tenant: from `openRetry.minMs`, doubling, at most
18
+ // `openRetry.maxMs` apart.
19
+ const { minMs: firstRetryMs, maxMs: maxRetryMs } = tuning.openRetry;
19
20
  let state = { kind: "idle" };
20
21
  let opening;
21
22
  let retry;
22
- let nextDelayMs = RETRY_FIRST_MS;
23
+ let nextDelayMs = firstRetryMs;
23
24
  const current = () => state;
24
25
  async function openOnce() {
25
26
  let handle;
@@ -64,7 +65,7 @@ export function createTenantModule(options) {
64
65
  return;
65
66
  }
66
67
  state = { kind: "open", handle };
67
- nextDelayMs = RETRY_FIRST_MS;
68
+ nextDelayMs = firstRetryMs;
68
69
  logger.info("tenant opened", { tenantId: handle.envelope.id });
69
70
  try {
70
71
  await options.onOpen?.(handle);
@@ -92,7 +93,7 @@ export function createTenantModule(options) {
92
93
  if (retry)
93
94
  return;
94
95
  const delayMs = nextDelayMs;
95
- nextDelayMs = Math.min(delayMs * 2, RETRY_MAX_MS);
96
+ nextDelayMs = Math.min(delayMs * 2, maxRetryMs);
96
97
  retry = setTimeout(() => {
97
98
  retry = undefined;
98
99
  void load();
@@ -30,21 +30,21 @@ export type OutboundResult = {
30
30
  code: string;
31
31
  message: string;
32
32
  };
33
- /** The largest answer read from an endpoint. */
34
- export declare const MAX_RESPONSE_BYTES: number;
35
33
  /** True when `address` is private, loopback, link-local or unspecified (IPv4-mapped included). */
36
34
  export declare function isPrivateAddress(address: string): boolean;
37
35
  /** Why the Host refuses `url`, or `undefined` when it may be called. */
38
36
  export declare function refusal(url: URL, policy: OutboundPolicy): string | undefined;
39
37
  /**
40
- * POSTs (or, with `method`, PUTs or PATCHes) the JSON `body` to `url` under `policy`. Never
38
+ * Sends one request to `url` under `policy`: with `method` (default `POST`) and the JSON `body`,
39
+ * or with no body (and no `content-type`) when `body` is undefined, as for a `GET`. Never
41
40
  * throws; `signal` aborts the request.
42
41
  */
43
- export declare function post(target: string, body: string, headers: Record<string, string>, options: {
42
+ export declare function send(target: string, body: string | undefined, headers: Record<string, string>, options: {
44
43
  policy: OutboundPolicy;
45
44
  signal: AbortSignal;
46
- maxResponseBytes?: number;
47
- method?: "POST" | "PUT" | "PATCH";
45
+ /** The largest answer read: the Tenant's Runtime limit `toolAnswerBytes` for an HTTP tool. */
46
+ maxResponseBytes: number;
47
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
48
48
  }): Promise<OutboundResult>;
49
49
  /**
50
50
  * A refusal of `guardedFetch`: the Host's policy forbids the URL or every address it resolves
@@ -59,34 +59,32 @@ export declare class OutboundRefused extends Error {
59
59
  export declare class OutboundFailed extends Error {
60
60
  readonly code: string;
61
61
  /**
62
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
62
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
63
63
  * may have acted on it. False for a failure to resolve or connect.
64
64
  */
65
65
  readonly sent: boolean;
66
66
  constructor(message: string, code: string,
67
67
  /**
68
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
68
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
69
69
  * may have acted on it. False for a failure to resolve or connect.
70
70
  */
71
71
  sent?: boolean);
72
72
  }
73
- /** The largest answer `guardedFetch` reads. */
74
- export declare const MAX_FETCH_RESPONSE_BYTES: number;
75
- /** How long a `guardedFetch` request may take without a signal of its own. */
76
- export declare const FETCH_TIMEOUT_MS = 30000;
77
73
  /**
78
74
  * A `fetch` under the Host's address policy (F9 C2): the literal-IP and DNS checks of `post`
79
75
  * on the address actually connected to, `localhost` rewritten for the local stack, no
80
- * redirects (`redirect: "error"`; a 3xx answer rejects), a bounded answer and a 30 s timeout
81
- * unless the caller passes a signal. A refused URL or address rejects with `OutboundRefused`,
82
- * any other failure with `OutboundFailed`.
76
+ * redirects (`redirect: "error"`; a 3xx answer rejects), an answer of at most `maxResponseBytes`
77
+ * and a `timeoutMs` timeout unless the caller passes a signal, both the caller's (a Tenant's
78
+ * Runtime limits). A refused URL or address rejects with `OutboundRefused`, any other failure
79
+ * with `OutboundFailed`.
83
80
  *
84
81
  * `stream`: the Response resolves once the headers arrive and its body streams, unbounded and
85
82
  * with no timeout but the caller's signal: remote MCP servers, whose answers and event streams
86
83
  * stay open (`mcp/connect.ts`).
87
84
  */
88
- export declare function guardedFetch(policy: OutboundPolicy, options?: {
89
- maxResponseBytes?: number;
90
- timeoutMs?: number;
91
- stream?: boolean;
85
+ export declare function guardedFetch(policy: OutboundPolicy, options: {
86
+ maxResponseBytes: number;
87
+ timeoutMs: number;
88
+ } | {
89
+ stream: true;
92
90
  }): typeof fetch;
@@ -13,8 +13,6 @@ import { request as httpRequest } from "node:http";
13
13
  import { request as httpsRequest } from "node:https";
14
14
  import { BlockList, isIP } from "node:net";
15
15
  import { Readable } from "node:stream";
16
- /** The largest answer read from an endpoint. */
17
- export const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
18
16
  const PRIVATE = new BlockList();
19
17
  for (const [network, prefix] of [
20
18
  ["0.0.0.0", 8],
@@ -76,12 +74,13 @@ function guardedLookup(policy) {
76
74
  });
77
75
  }
78
76
  /**
79
- * POSTs (or, with `method`, PUTs or PATCHes) the JSON `body` to `url` under `policy`. Never
77
+ * Sends one request to `url` under `policy`: with `method` (default `POST`) and the JSON `body`,
78
+ * or with no body (and no `content-type`) when `body` is undefined, as for a `GET`. Never
80
79
  * throws; `signal` aborts the request.
81
80
  */
82
- export async function post(target, body, headers, options) {
81
+ export async function send(target, body, headers, options) {
83
82
  const { policy, signal } = options;
84
- const limit = options.maxResponseBytes ?? MAX_RESPONSE_BYTES;
83
+ const limit = options.maxResponseBytes;
85
84
  const url = new URL(target);
86
85
  const refused = refusal(url, policy);
87
86
  if (refused)
@@ -108,8 +107,9 @@ export async function post(target, body, headers, options) {
108
107
  headers: {
109
108
  ...headers,
110
109
  host: originalHost,
111
- "content-type": "application/json",
112
- "content-length": String(Buffer.byteLength(body)),
110
+ ...(body === undefined
111
+ ? {}
112
+ : { "content-type": "application/json", "content-length": String(Buffer.byteLength(body)) }),
113
113
  },
114
114
  lookup: lookup,
115
115
  signal,
@@ -156,7 +156,7 @@ export class OutboundFailed extends Error {
156
156
  sent;
157
157
  constructor(message, code,
158
158
  /**
159
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
159
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
160
160
  * may have acted on it. False for a failure to resolve or connect.
161
161
  */
162
162
  sent = false) {
@@ -166,25 +166,22 @@ export class OutboundFailed extends Error {
166
166
  this.name = "OutboundFailed";
167
167
  }
168
168
  }
169
- /** The largest answer `guardedFetch` reads. */
170
- export const MAX_FETCH_RESPONSE_BYTES = 1024 * 1024;
171
- /** How long a `guardedFetch` request may take without a signal of its own. */
172
- export const FETCH_TIMEOUT_MS = 30_000;
173
169
  /**
174
170
  * A `fetch` under the Host's address policy (F9 C2): the literal-IP and DNS checks of `post`
175
171
  * on the address actually connected to, `localhost` rewritten for the local stack, no
176
- * redirects (`redirect: "error"`; a 3xx answer rejects), a bounded answer and a 30 s timeout
177
- * unless the caller passes a signal. A refused URL or address rejects with `OutboundRefused`,
178
- * any other failure with `OutboundFailed`.
172
+ * redirects (`redirect: "error"`; a 3xx answer rejects), an answer of at most `maxResponseBytes`
173
+ * and a `timeoutMs` timeout unless the caller passes a signal, both the caller's (a Tenant's
174
+ * Runtime limits). A refused URL or address rejects with `OutboundRefused`, any other failure
175
+ * with `OutboundFailed`.
179
176
  *
180
177
  * `stream`: the Response resolves once the headers arrive and its body streams, unbounded and
181
178
  * with no timeout but the caller's signal: remote MCP servers, whose answers and event streams
182
179
  * stay open (`mcp/connect.ts`).
183
180
  */
184
- export function guardedFetch(policy, options = {}) {
185
- const limit = options.maxResponseBytes ?? MAX_FETCH_RESPONSE_BYTES;
186
- const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
187
- const stream = options.stream === true;
181
+ export function guardedFetch(policy, options) {
182
+ const stream = "stream" in options;
183
+ const limit = "stream" in options ? Infinity : options.maxResponseBytes;
184
+ const timeoutMs = "stream" in options ? undefined : options.timeoutMs;
188
185
  const lookup = guardedLookup(policy);
189
186
  const guarded = async (input, init = {}) => {
190
187
  const request = input instanceof Request ? input : undefined;
@@ -215,7 +212,7 @@ export function guardedFetch(policy, options = {}) {
215
212
  }
216
213
  const originalHost = url.host;
217
214
  dockerHost(url, policy);
218
- const signal = init.signal ?? request?.signal ?? (stream ? undefined : AbortSignal.timeout(timeoutMs));
215
+ const signal = init.signal ?? request?.signal ?? (timeoutMs === undefined ? undefined : AbortSignal.timeout(timeoutMs));
219
216
  const outgoing = { host: originalHost };
220
217
  for (const [name, value] of headers)
221
218
  outgoing[name] = value;
@@ -223,7 +220,7 @@ export function guardedFetch(policy, options = {}) {
223
220
  outgoing["content-length"] = String(body.byteLength);
224
221
  return await new Promise((resolve, reject) => {
225
222
  let settled = false;
226
- // Set when the request is fully sent, as `post()` does: a failure after it may follow
223
+ // Set when the request is fully sent, as `send()` does: a failure after it may follow
227
224
  // the server acting on the request.
228
225
  let delivered = false;
229
226
  const settle = (fn) => {
@@ -1,4 +1,5 @@
1
- import { mintRunToken, RUN_TOKEN_RENEW_SECONDS } from "./run-token.js";
1
+ import { mintRunToken } from "./run-token.js";
2
+ import { hostTuning } from "../tuning.js";
2
3
  export function createRunGrants() {
3
4
  const grants = new Map();
4
5
  return {
@@ -50,7 +51,7 @@ export async function renewRunGrant(ctx, lease, run, active = () => true) {
50
51
  const held = ctx.runGrants.get(lease.sessionId);
51
52
  if (held && held.claims.epoch !== lease.epoch)
52
53
  return;
53
- if (held && held.claims.expiresAt - Date.now() > RUN_TOKEN_RENEW_SECONDS * 1000)
54
+ if (held && held.claims.expiresAt - Date.now() > hostTuning().keys.runTokenRenewSeconds * 1000)
54
55
  return;
55
56
  const grant = await mintRunToken(ctx, lease, held ? { agentId: held.claims.agentId, activeTurnId: held.claims.turnId } : run);
56
57
  if (active())
@@ -4,10 +4,6 @@ import type { SessionStore } from "../store/types.js";
4
4
  import type { Lease } from "./context.js";
5
5
  export declare const RUN_TOKEN_TYP = "nylorun-run+jwt";
6
6
  export declare const RUN_TOKEN_AUD = "nylorun-gates";
7
- /** How long a run token lives. */
8
- export declare const RUN_TOKEN_TTL_SECONDS: number;
9
- /** The lease heartbeat re-mints a token with less than this left. */
10
- export declare const RUN_TOKEN_RENEW_SECONDS: number;
11
7
  /** What a verified run token says: one lease of one session's turn. */
12
8
  export interface RunClaims {
13
9
  readonly tenantId: string;
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Signed by the keys service with the Tenant's current signing key, like capability links,
8
8
  * and told apart from them by `typ` and `aud`. Internal: not in `openapi.json`. A token lives
9
- * `RUN_TOKEN_TTL_SECONDS`, no longer than `TOKEN_TTL_MAX_SECONDS`, so a key rotation never
9
+ * `keys.runTokenTtlSeconds` (Host tuning), no longer than `keys.tokenTtlMaxSeconds`, so a key rotation never
10
10
  * revokes a live one (`signing-keys.ts`).
11
11
  *
12
12
  * ```text
@@ -18,15 +18,10 @@
18
18
  import { randomUUID } from "node:crypto";
19
19
  import { errors, importJWK, jwtVerify } from "jose";
20
20
  import { tenantTokenIssuer } from "@nylorun/core/contracts";
21
- import { CLOCK_TOLERANCE_SECONDS, tokenKeyId } from "./jwt.js";
21
+ import { clockToleranceSeconds, tokenKeyId } from "./jwt.js";
22
+ import { hostTuning } from "../tuning.js";
22
23
  export const RUN_TOKEN_TYP = "nylorun-run+jwt";
23
24
  export const RUN_TOKEN_AUD = "nylorun-gates";
24
- /** How long a run token lives. */
25
- export const RUN_TOKEN_TTL_SECONDS = 15 * 60;
26
- /** The lease heartbeat re-mints a token with less than this left. */
27
- export const RUN_TOKEN_RENEW_SECONDS = 5 * 60;
28
- /** Allowed spread between `iat` and `exp` beyond the lifetime. */
29
- const LIFETIME_SLACK_SECONDS = 60;
30
25
  /**
31
26
  * Mints the run token of `lease` over `session`'s active turn. Throws when the session has
32
27
  * no active turn: there is no run to name.
@@ -37,7 +32,7 @@ export async function mintRunToken(signer, lease, session) {
37
32
  throw new Error(`Session ${lease.sessionId} has no active turn to mint a run token for`);
38
33
  const tenantId = signer.config.tenantId;
39
34
  const iat = Math.floor(Date.now() / 1000);
40
- const exp = iat + RUN_TOKEN_TTL_SECONDS;
35
+ const exp = iat + hostTuning().keys.runTokenTtlSeconds;
41
36
  // Signed by the keys service (F4.2): this process never holds the private key.
42
37
  const { token } = await signer.keys.sign({
43
38
  typ: RUN_TOKEN_TYP,
@@ -92,7 +87,7 @@ export async function verifyRunToken(store, tenantId, raw, cache = new Map()) {
92
87
  issuer: tenantTokenIssuer(tenantId),
93
88
  audience: RUN_TOKEN_AUD,
94
89
  typ: RUN_TOKEN_TYP,
95
- clockTolerance: CLOCK_TOLERANCE_SECONDS,
90
+ clockTolerance: clockToleranceSeconds(),
96
91
  requiredClaims: ["sub", "iat", "exp", "jti"],
97
92
  });
98
93
  payload = verified.payload;
@@ -112,7 +107,7 @@ export async function verifyRunToken(store, tenantId, raw, cache = new Map()) {
112
107
  typeof epc !== "number" ||
113
108
  !Number.isSafeInteger(epc) ||
114
109
  epc < 1 ||
115
- exp - iat > RUN_TOKEN_TTL_SECONDS + LIFETIME_SLACK_SECONDS)
110
+ exp - iat > hostTuning().keys.runTokenTtlSeconds + hostTuning().keys.tokenLifetimeSlackSeconds)
116
111
  return refused("claims");
117
112
  return {
118
113
  ok: true,
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The Tenant's Runtime limits (harness boundary S0), stored as Tenant setting `limits.config`
3
+ * (`GET`/`PUT /v1/tenant/limits`): what one record entry, model call, model file, tool answer,
4
+ * custom harness state and Harness API chunk may hold. Unset limits take their defaults
5
+ * (`RUNTIME_LIMIT_DEFAULTS`); none may pass its ceiling (`runtimeLimitIssues`), which the route
6
+ * checks before it writes.
7
+ *
8
+ * Read from Postgres where a limit is checked, never kept: the Record seam, the store's event
9
+ * write, the gates and the segment's `turn.start` each read it in their own transaction.
10
+ */
11
+ import { type RuntimeLimits, type TenantLimitsConfig } from "@nylorun/core/contracts";
12
+ export declare const LIMITS_CONFIG_SETTING = "limits.config";
13
+ interface SettingsReader {
14
+ getSetting(key: string): Promise<string | undefined>;
15
+ }
16
+ interface SettingsWriter extends SettingsReader {
17
+ putSetting(key: string, value: string): Promise<void>;
18
+ }
19
+ /** The Tenant's stored limits; a stored value that does not parse counts as none set. */
20
+ export declare function readLimitsConfig(t: SettingsReader): Promise<TenantLimitsConfig>;
21
+ /** The Runtime limits in force: the Tenant's, with the default for each it leaves out. */
22
+ export declare function readRuntimeLimits(t: SettingsReader): Promise<RuntimeLimits>;
23
+ export declare function writeLimitsConfig(t: SettingsWriter, config: TenantLimitsConfig): Promise<void>;
24
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The Tenant's Runtime limits (harness boundary S0), stored as Tenant setting `limits.config`
3
+ * (`GET`/`PUT /v1/tenant/limits`): what one record entry, model call, model file, tool answer,
4
+ * custom harness state and Harness API chunk may hold. Unset limits take their defaults
5
+ * (`RUNTIME_LIMIT_DEFAULTS`); none may pass its ceiling (`runtimeLimitIssues`), which the route
6
+ * checks before it writes.
7
+ *
8
+ * Read from Postgres where a limit is checked, never kept: the Record seam, the store's event
9
+ * write, the gates and the segment's `turn.start` each read it in their own transaction.
10
+ */
11
+ import { TenantLimitsConfigSchema, runtimeLimitsOf, } from "@nylorun/core/contracts";
12
+ export const LIMITS_CONFIG_SETTING = "limits.config";
13
+ /** The Tenant's stored limits; a stored value that does not parse counts as none set. */
14
+ export async function readLimitsConfig(t) {
15
+ const raw = await t.getSetting(LIMITS_CONFIG_SETTING);
16
+ if (raw === undefined)
17
+ return {};
18
+ const parsed = TenantLimitsConfigSchema.safeParse(JSON.parse(raw));
19
+ return parsed.success ? parsed.data : {};
20
+ }
21
+ /** The Runtime limits in force: the Tenant's, with the default for each it leaves out. */
22
+ export async function readRuntimeLimits(t) {
23
+ return runtimeLimitsOf((await readLimitsConfig(t)).limits);
24
+ }
25
+ export async function writeLimitsConfig(t, config) {
26
+ await t.putSetting(LIMITS_CONFIG_SETTING, JSON.stringify(TenantLimitsConfigSchema.parse(config)));
27
+ }