@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
package/dist/tuning.js ADDED
@@ -0,0 +1,658 @@
1
+ /**
2
+ * Host tuning (B15): the timings, retries, poll intervals and internal sizes of the Runtime's
3
+ * processes, one typed schema with a default each. An operator sets any of them in
4
+ * `NYLORUN_TUNING`, a JSON object of the groups below (`{"tenant":{"openTimeoutMs":60000}}`),
5
+ * read once when a process starts (`parseStackConfig`, then `useHostTuning` in `host/main.ts`).
6
+ * What is left out keeps its default, which is the value the Runtime used before it was
7
+ * configuration. An unknown key, or a value past its ceiling, stops the process naming it.
8
+ *
9
+ * Every service reads the parts it uses: the gateway (`gates`, `keys`, `egress`), core and loop
10
+ * (the rest), a harness container or a pod's engine (`harness`, `engine`). Core hands a pod
11
+ * sandbox's engine the `harness` and `engine` groups the operator set (`podTuningEnv`), so a pod
12
+ * runs with the timings of the Runtime that started it.
13
+ *
14
+ * Tenant-facing limits (sizes a client meets, request and turn deadlines) are not here: they are
15
+ * Runtime limits and Tenant settings. `engine` is our harness's own configuration: the options
16
+ * of `createHarness` in `@nylorun/harness`, whose defaults stay there.
17
+ */
18
+ import { z } from "zod";
19
+ import { TIMER_MAX_MS } from "@nylorun/core/contracts";
20
+ /**
21
+ * The longest delay Node's timers take (`setTimeout`, `setInterval`): 2^31 - 1 ms, a signed
22
+ * 32-bit integer. A longer one fires at once. Fixed by the platform, so a ceiling, not a setting.
23
+ */
24
+ export const MAX_TIMER_MS = TIMER_MAX_MS;
25
+ /** S3's smallest multipart part but the last (5 MiB), and its largest part (5 GiB). */
26
+ export const S3_MIN_PART_BYTES = 5 * 1024 * 1024;
27
+ export const S3_MAX_PART_BYTES = 5 * 1024 * 1024 * 1024;
28
+ /**
29
+ * How long a wake's dedupe key merges repeated wakes, at least (Durable Session Execution
30
+ * §12.3): a wake sent again within a day must not start a second advance. A floor of the
31
+ * design, so a setting may only raise it.
32
+ */
33
+ export const DEDUPE_RETENTION_MIN_MS = 24 * 60 * 60_000;
34
+ /** The longest `?wait=` the sandboxes service takes (`MaxWait` in its API): a dependency's ceiling. */
35
+ export const SANDBOXES_MAX_WAIT_MS = 30_000;
36
+ /**
37
+ * The most bind parameters one Postgres statement takes: the extended protocol's `Bind` message
38
+ * counts them in a 16-bit integer. A dependency's ceiling.
39
+ */
40
+ export const POSTGRES_MAX_PARAMETERS = 65_535;
41
+ /** The most columns one batched content row binds (`artifact_content`: id, version, hash, size). */
42
+ const CONTENT_ROW_PARAMETERS = 4;
43
+ /** The most records, and bytes, one S2 append takes and one S2 read returns: S2's ceilings. */
44
+ export const S2_BATCH_MAX_RECORDS = 1000;
45
+ export const S2_BATCH_MAX_BYTES = 1024 * 1024;
46
+ const MINUTE = 60_000;
47
+ const HOUR = 60 * MINUTE;
48
+ const KIB = 1024;
49
+ const MIB = 1024 * KIB;
50
+ /** A delay or timeout in milliseconds: a whole number, at most `MAX_TIMER_MS`. */
51
+ const ms = (value, min = 1) => z.number().int().min(min).max(MAX_TIMER_MS).default(value);
52
+ /** A count: a whole number of at least `min`. */
53
+ const count = (value, min = 1, max = Number.MAX_SAFE_INTEGER) => z.number().int().min(min).max(max).default(value);
54
+ /** A size in bytes. */
55
+ const bytes = (value, min = 1, max = Number.MAX_SAFE_INTEGER) => z.number().int().min(min).max(max).default(value);
56
+ /** Exponential backoff: the first wait, doubled after each failure up to the longest. */
57
+ const backoff = (minMs, maxMs) => z
58
+ .strictObject({ minMs: ms(minMs), maxMs: ms(maxMs) })
59
+ .refine((value) => value.minMs <= value.maxMs, { message: "minMs must be at most maxMs" })
60
+ .prefault({});
61
+ // Every field of a group has a default, so an empty group is all defaults.
62
+ const group = (shape) => z.strictObject(shape).prefault({});
63
+ /** A partial engine option group: each number validated, the rest left to `createHarness`. */
64
+ const enginePart = (shape) => z.strictObject(shape).partial().optional();
65
+ const positiveInt = () => z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
66
+ const positiveShare = () => z.number().gt(0).max(1);
67
+ export const HostTuningSchema = z
68
+ .strictObject({
69
+ /** The keys service and its client (F4.2), and the egress CA's certificates (R2c). */
70
+ keys: group({
71
+ /** The largest request body the keys service reads. */
72
+ bodyBytes: bytes(MIB),
73
+ /** How long core waits for the keys service to answer one call. */
74
+ timeoutMs: ms(30_000),
75
+ /** How far before now a leaf certificate is valid from, for clocks that differ. */
76
+ leafBackdateMs: ms(5 * MINUTE, 0),
77
+ /** How long a leaf egress-gate presents is valid. */
78
+ leafLifetimeMs: ms(HOUR),
79
+ /** How far before now the egress CA is valid from. */
80
+ caBackdateMs: ms(HOUR, 0),
81
+ /** How many days the egress CA is valid. */
82
+ caLifetimeDays: count(3650),
83
+ /**
84
+ * The longest life of a token the Runtime signs (a user's access token, a capability link): a
85
+ * link asked for longer gets this, and a key rotation waits this long for the old key's tokens.
86
+ */
87
+ tokenTtlMaxSeconds: count(15 * 60),
88
+ /** How long a run token lives, and how little of its life is left when the lease re-mints it. */
89
+ runTokenTtlSeconds: count(15 * 60),
90
+ runTokenRenewSeconds: count(5 * 60),
91
+ /** How long a host or an egress token lives; a pod's engine renews them before. */
92
+ hostTokenTtlSeconds: count(15 * 60),
93
+ /** How far a token's `exp` may be past its `iat` beyond its lifetime, for clocks that differ. */
94
+ tokenLifetimeSlackSeconds: count(60, 0),
95
+ /** The clock skew accepted when a token's times are checked (the Runtime's and an issuer's). */
96
+ clockToleranceSeconds: count(30, 0),
97
+ }),
98
+ /** Core's Harness API listener and server (F6.2). */
99
+ harnessApi: group({
100
+ /** The largest `host/join` or `host/renew` body. */
101
+ joinBodyBytes: bytes(8 * KIB),
102
+ /** The largest setup report a pod's engine posts. */
103
+ reportBodyBytes: bytes(64 * KIB),
104
+ /** How long the listener waits for connections to close before it ends them. */
105
+ closeGraceMs: ms(1_000, 0),
106
+ /** How long a run offered to the harnesses waits for one to take it. */
107
+ offerWaitMs: ms(10_000),
108
+ /** How long a pod sandbox's segment waits for its engine to take it. */
109
+ podOfferWaitMs: ms(MINUTE),
110
+ /** How long the Tenant's own harness has to connect to its loopback listener. */
111
+ loopbackConnectTimeoutMs: ms(10_000),
112
+ /** How the Tenant's own harness reconnects to its loopback listener. */
113
+ loopbackBackoff: backoff(50, 500),
114
+ /**
115
+ * Segment rollover (Model Calls §10): a turn ends its segment at a step boundary after this
116
+ * many steps or this long, and continues in a new one, well inside the advance deadline.
117
+ */
118
+ rolloverSteps: count(50),
119
+ rolloverMs: ms(20 * MINUTE),
120
+ }),
121
+ /** The Runtime API's long-lived responses. */
122
+ api: group({
123
+ /** How often an AG-UI event stream sends a heartbeat comment. */
124
+ agUiHeartbeatMs: ms(15_000),
125
+ /** How often a session event stream sends a keepalive comment. */
126
+ eventStreamKeepaliveMs: ms(15_000),
127
+ }),
128
+ /** The Host process. */
129
+ host: group({
130
+ /** How long shutdown waits for requests in progress before it closes their connections. */
131
+ shutdownGraceMs: ms(10_000, 0),
132
+ /** The bound on one `/ready` pass. Compose probes it every 2 s with a 5 s timeout. */
133
+ readinessTimeoutMs: ms(2_000),
134
+ /** How long `nylorun-operate status` waits for the running Host's `/ready`. */
135
+ operateReadyTimeoutMs: ms(5_000),
136
+ /**
137
+ * The project credential store (`ProjectCredentialStore`): how often a writer checks its lock,
138
+ * and how many times before it gives up.
139
+ */
140
+ credentialLockPollMs: ms(25),
141
+ credentialLockAttempts: count(100),
142
+ }),
143
+ /** The Object store (`NYLORUN_OBJECT_STORE_*`). */
144
+ objectStore: group({
145
+ /** The size of each part of a multipart upload but the last: S3 takes 5 MiB to 5 GiB. */
146
+ partSizeBytes: bytes(8 * MIB, S3_MIN_PART_BYTES, S3_MAX_PART_BYTES),
147
+ /** Tries of a request answered `5xx` or `429`, or unreachable, the first included. */
148
+ attempts: count(3),
149
+ /** The wait after the first failed try, doubled after each. */
150
+ retryBaseMs: ms(100),
151
+ /** How core retries creating the bucket while the store starts. */
152
+ bucketRetry: backoff(1_000, 5_000),
153
+ bucketAttempts: count(10),
154
+ }),
155
+ /** The Tenant: opening it, its advances, its sweep and its record. */
156
+ tenant: group({
157
+ /** The bound on opening the Tenant; a slower open fails with `open-timeout`. */
158
+ openTimeoutMs: ms(30_000),
159
+ /** Retries of an open that failed outside the Tenant. */
160
+ openRetry: backoff(1_000, 30_000),
161
+ /** The ownership lease of an advance (§10.6); renewed every third while it runs. */
162
+ ownerLeaseMs: ms(30_000),
163
+ /** The shortest `busy` retry, so a lease about to lapse is not polled in a tight loop. */
164
+ busyRetryMinMs: ms(25),
165
+ /** How soon an advance whose segment no harness took is tried again. */
166
+ unavailableRetryMs: ms(1_000),
167
+ /** How long draining waits for running turns before a reset or a close goes on. */
168
+ drainTimeoutMs: ms(30_000),
169
+ /** How long status waits for the execution to list stuck invocations. */
170
+ statusTimeoutMs: ms(5_000),
171
+ /** The grace added to the longest token lifetime before a previous signing key may go. */
172
+ signingKeyRotationGraceMs: ms(MINUTE, 0),
173
+ /**
174
+ * The `Retry-After` of a request refused while its Environment is busy, or while the
175
+ * sandboxes service is not ready.
176
+ */
177
+ retryAfterSeconds: count(5),
178
+ /** How long a pending Environment seed holds the disk before another process may take it over. */
179
+ seedLeaseMs: ms(10 * MINUTE),
180
+ }),
181
+ /** The Tenant's sweep (`tenant/sweep.ts`): outbox, orphaned sessions, stale pods. */
182
+ sweep: group({
183
+ /** The delay between sweep passes of one Tenant. */
184
+ intervalMs: ms(5_000),
185
+ /** Rows one sweep query reads at a time. */
186
+ batchSize: count(100),
187
+ /** How long a control signal is kept after it was written. */
188
+ signalRetentionMs: ms(HOUR),
189
+ /** How old an outbox row is before the sweep delivers it. */
190
+ outboxGraceMs: ms(30_000, 0),
191
+ /** How often the orphan scan and the stale pod check run. */
192
+ orphanScanMs: ms(MINUTE),
193
+ /** How long one pass may spend on the outbox, and on orphaned sessions. */
194
+ drainBudgetMs: ms(2_000),
195
+ /** Tenant sweeps the Host arms at once when it starts. */
196
+ armConcurrency: count(16),
197
+ /** Delivery attempts of one outbox row before it is parked. */
198
+ outboxAttempts: count(10),
199
+ /** The wait after an outbox row's failed delivery, doubled after each. */
200
+ outboxRetry: backoff(5_000, 10 * MINUTE),
201
+ /** How long a pod row may go unchanged before the sweep checks it. */
202
+ stalePodMs: ms(MINUTE),
203
+ }),
204
+ /** Durable Session Execution: Restate, or the in-process execution. */
205
+ execution: group({
206
+ /**
207
+ * How long one advance may run before its signal aborts; below Restate's inactivity and abort
208
+ * timeouts (`restate.inactivityMs`), so an advance always ends before Restate gives up on it.
209
+ */
210
+ advanceDeadlineMs: ms(50 * MINUTE),
211
+ /** How long an aborted advance has to settle before it is abandoned. */
212
+ advanceGraceMs: ms(30_000, 0),
213
+ /** How long stopping waits for running handlers after aborting them. */
214
+ stopGraceMs: ms(30_000, 0),
215
+ /** How long a wake's dedupe key merges repeated wakes. At least 24 hours (§12.3). */
216
+ dedupeRetentionMs: ms(DEDUPE_RETENTION_MIN_MS, DEDUPE_RETENTION_MIN_MS),
217
+ /** The in-process execution: the first retry of a handler that throws, doubled after each. */
218
+ memoryRetryMs: ms(50),
219
+ /** The in-process execution: attempts before a throwing handler is reported. */
220
+ memoryAttempts: count(5),
221
+ }),
222
+ /** Restate (`NYLORUN_RESTATE_*`). */
223
+ restate: group({
224
+ /** How long one send through the ingress keeps retrying. Shorter than `sweep.outboxGraceMs`. */
225
+ sendRetryMs: ms(10_000),
226
+ sendBackoff: backoff(100, 2_000),
227
+ /** How long the ingress may take to accept one send. */
228
+ sendTimeoutMs: ms(10_000),
229
+ /** How long an invocation may run without journal progress before Restate suspends it. */
230
+ inactivityMs: ms(HOUR),
231
+ /** How long after the inactivity timeout Restate aborts the attempt. */
232
+ abortMs: ms(HOUR),
233
+ /** Retries of a handler that throws; after `handlerAttempts` the invocation is paused. */
234
+ handlerRetry: backoff(100, 30_000),
235
+ handlerAttempts: count(70),
236
+ /** How long `start` keeps registering the Worker while Restate comes up. */
237
+ registrationDeadlineMs: ms(MINUTE),
238
+ registrationBackoff: backoff(100, 2_000),
239
+ /** How long one registration request may take. */
240
+ registrationRequestTimeoutMs: ms(30_000),
241
+ /** How long one admin query may take. */
242
+ queryTimeoutMs: ms(10_000),
243
+ /** How long stopping lets Restate read answers before it drops the connections. */
244
+ closeGraceMs: ms(2_000, 0),
245
+ /** A sweep chain is stale past this many sweep intervals, and never sooner than `staleSweepMinMs`. */
246
+ staleSweepIntervals: count(3),
247
+ staleSweepMinMs: ms(MINUTE),
248
+ /** Stuck invocations one status lists by default, and at most. */
249
+ stuckListDefault: count(100),
250
+ stuckListMax: count(1_000),
251
+ }),
252
+ /** Postgres (`NYLORUN_DATABASE_URL`). */
253
+ postgres: group({
254
+ /** The Tenant pool's size. */
255
+ poolSize: count(10),
256
+ /** The Tenant pool's statement timeout; 0 turns it off. */
257
+ statementTimeoutMs: ms(MINUTE, 0),
258
+ /** The Tenant pool's idle-in-transaction timeout; 0 turns it off. */
259
+ idleInTransactionTimeoutMs: ms(MINUTE, 0),
260
+ /** Seconds closing a pool waits for its queries before it ends their connections. */
261
+ closeTimeoutSeconds: count(5, 0),
262
+ /** Seconds an idle pooled connection stays open. */
263
+ idleTimeoutSeconds: count(30),
264
+ /** Seconds a connection may take to open. */
265
+ connectTimeoutSeconds: count(10),
266
+ /** The pool of the replication service (the stream relay, the trace projector). */
267
+ replicationPoolSize: count(2),
268
+ /** The read pool (`nylorun-reads`): its size and its timeouts. */
269
+ readPoolSize: count(4),
270
+ readConnectTimeoutSeconds: count(2),
271
+ readStatementTimeoutMs: ms(2_000),
272
+ readCloseTimeoutSeconds: count(2, 0),
273
+ /** The control bus: how often it reads back signals it may have missed. */
274
+ controlPollMs: ms(5_000),
275
+ /** How far back a read-back looks; longer than the poll. */
276
+ controlWindowSeconds: count(120),
277
+ /** How the control bus listens again after its connection ends. */
278
+ controlRetry: backoff(100, 5_000),
279
+ /** How soon the stream relay and the trace projector try again to take their replication slot. */
280
+ replicationRetryMs: ms(5_000),
281
+ /**
282
+ * Rows one statement writes or looks up when a folder version or a definition names many
283
+ * files: Postgres binds at most 65,535 parameters a statement, four a row.
284
+ */
285
+ contentBatchRows: count(1_000, 1, Math.floor(POSTGRES_MAX_PARAMETERS / CONTENT_ROW_PARAMETERS)),
286
+ }),
287
+ /** Durable Streams (S2) and the stream relay. */
288
+ streams: group({
289
+ /** Reads, basin checks and deletions retried by the Tenant's streams. */
290
+ retry: backoff(100, 2_000),
291
+ /** The longest wait between two basin repairs. */
292
+ basinRetryMaxMs: ms(5_000),
293
+ /** A repair within this long after the last one waits for the next failure. */
294
+ basinRepairCooldownMs: ms(1_000, 0),
295
+ /** A repair that keeps failing is logged on its first failure and on every this many after. */
296
+ basinLogEveryFailures: count(10),
297
+ /** How often every session stream is checked against its session. */
298
+ feedCheckMs: ms(30_000),
299
+ /** How long a retired basin stays for readers that have not moved yet. */
300
+ retireGraceMs: ms(MINUTE, 0),
301
+ /** Attempts to delete a retired basin before it waits for the Tenant's next open. */
302
+ retireAttempts: count(5),
303
+ /** How long a streams probe waits. */
304
+ probeTimeoutMs: ms(2_000),
305
+ /** The stream relay's retries of a failed append. */
306
+ relayRetry: backoff(50, 2_000),
307
+ /** The most records and bytes one relay append holds: S2 takes 1000 records and 1 MiB a batch. */
308
+ relayBatchRecords: count(500, 1, S2_BATCH_MAX_RECORDS),
309
+ relayBatchBytes: bytes(768 * KIB, 1, S2_BATCH_MAX_BYTES),
310
+ /** Sessions the relay reconciles at once, and the session heads it reads a page at a time. */
311
+ relayReconcileConcurrency: count(8),
312
+ relayHeadsPage: count(500),
313
+ /** S2: retries of a read. */
314
+ s2Retry: backoff(100, 2_000),
315
+ /** S2: attempts of an append that loses a `transaction_conflict` race, and its first wait. */
316
+ s2ConflictAttempts: count(6),
317
+ s2ConflictRetryMs: ms(25),
318
+ /** S2: the first wait while a basin's deletion finishes, and how long it may take. */
319
+ s2BasinRetryMs: ms(500),
320
+ s2BasinDeletionWaitMs: ms(2 * MINUTE),
321
+ }),
322
+ /** The Model Gate, the Tool Gate and their clients in the loop. */
323
+ gates: group({
324
+ /** How long the loop's connection to the gate may stay silent; above the provider timeout. */
325
+ clientTimeoutMs: ms(630_000),
326
+ /** How long a cancel, or a short Tool Gate request, may take. */
327
+ shortTimeoutMs: ms(2_000),
328
+ /** The TCP keepalive of the loop's connections to the gate. */
329
+ keepAliveMs: ms(30_000),
330
+ /** The gate listener's request timeout; above the client's. */
331
+ requestTimeoutMs: ms(660_000),
332
+ /** How long the gate's database probe may take. */
333
+ probeTimeoutMs: ms(2_000),
334
+ /** How long the gate's MCP connections may sit unused. */
335
+ mcpIdleMs: ms(15 * MINUTE),
336
+ /** How much of an MCP connection's unexpected error message the loop is told. */
337
+ errorMessageChars: count(2_000),
338
+ /** How often idle MCP connections are swept: the idle time, within these bounds. */
339
+ mcpSweepMinMs: ms(1_000),
340
+ mcpSweepMaxMs: ms(MINUTE),
341
+ /** How long a settled tool call crossing is kept, and how often old ones are pruned. */
342
+ toolCrossingTtlMs: ms(24 * HOUR),
343
+ toolCrossingPruneMs: ms(HOUR),
344
+ /** How long the gate drains calls in progress when it stops, checking this often. */
345
+ drainMs: ms(10_000, 0),
346
+ drainPollMs: ms(50),
347
+ /** How long a settled model call's outcome is kept for a retry to join, and how many. */
348
+ inflightTtlMs: ms(30 * MINUTE),
349
+ inflightMax: count(10_000),
350
+ }),
351
+ /** egress-gate (F7.2): sandbox pods' CONNECT proxy. */
352
+ egress: group({
353
+ /** Tunnels one sandbox may hold open at once. */
354
+ maxTunnelsPerSandbox: count(64),
355
+ /** A tunnel with no traffic either way for this long is closed. */
356
+ idleMs: ms(5 * MINUTE),
357
+ /** How long a sandbox's epoch and allowlist are reused, and how many are kept. */
358
+ cacheMs: ms(5_000, 0),
359
+ cacheEntries: count(4_096),
360
+ /** How long the gate waits for the upstream connection. */
361
+ connectTimeoutMs: ms(10_000),
362
+ /** The proxy listener's header and request timeouts. */
363
+ headersTimeoutMs: ms(10_000),
364
+ requestTimeoutMs: ms(15_000),
365
+ /** The header timeout of a tunnel whose TLS the gate terminates for a bound secret. */
366
+ credentialsHeadersTimeoutMs: ms(MINUTE),
367
+ /** The upstream sockets one such tunnel opens at once. */
368
+ credentialsMaxSockets: count(8),
369
+ }),
370
+ /** Sandbox pods through the sandboxes service (F7.2). */
371
+ pods: group({
372
+ /** How long a pod may take to be ready before its start fails. */
373
+ readyDeadlineMs: ms(5 * MINUTE),
374
+ /** How often a pod that is changing is checked. */
375
+ pollMs: ms(2_000),
376
+ /** How soon a pod whose service did not answer is checked again. */
377
+ unreachableRetryMs: ms(5_000),
378
+ /** How long the sandboxes service's readiness answer is reused. */
379
+ readyCacheMs: ms(5_000, 0),
380
+ /** How soon a reconcile that could not decide runs again. */
381
+ reconcileRetryMs: ms(5_000),
382
+ /** How often a disk copy or a seed checks whether a pod's engine has connected. */
383
+ enginePollMs: ms(250),
384
+ /** The least memory a sandbox gets, in MiB: a manifest or a Tenant limit asking less rounds up. */
385
+ memoryFloorMiB: count(128),
386
+ /** The sandboxes service: one request's timeout, and its `/ready` probe's. */
387
+ requestTimeoutMs: ms(20_000),
388
+ readyProbeTimeoutMs: ms(3_000),
389
+ /** How long a `?wait=` request waits when its caller names no time; the service takes 30 s at most. */
390
+ waitMs: z.number().int().min(1).max(SANDBOXES_MAX_WAIT_MS).default(SANDBOXES_MAX_WAIT_MS),
391
+ /** Added to a `?wait=` request's timeout for the answer to arrive. */
392
+ waitSlackMs: ms(10_000, 0),
393
+ }),
394
+ /**
395
+ * A harness process (`--service harness`), a pod's engine, and the Tenant's own harness: the
396
+ * harness host's timings.
397
+ */
398
+ harness: group({
399
+ /** How often the WebSocket pings, and how long it may stay silent before it is closed. */
400
+ pingMs: ms(30_000),
401
+ silenceMs: ms(90_000),
402
+ /** How the harness reconnects to core's Harness API. */
403
+ reconnect: backoff(500, 10_000),
404
+ /** How long the WebSocket handshake may take. */
405
+ handshakeTimeoutMs: ms(10_000),
406
+ /** How long a run has to give its lease back when the connection closes. */
407
+ reconnectStopMs: ms(5_000, 0),
408
+ /** How long stopping the process waits for runs: a harness container, a pod's engine. */
409
+ stopMs: ms(10_000, 0),
410
+ podStopMs: ms(7_000, 0),
411
+ /** The run tokens a harness keeps, the oldest dropped first. */
412
+ runTokensMax: count(10_000),
413
+ /** How often idle MCP connections are swept, and how long one may sit unused. */
414
+ mcpSweepMs: ms(MINUTE),
415
+ mcpIdleMs: ms(15 * MINUTE),
416
+ /**
417
+ * The most `tools/list` pages one MCP server's listing reads (discovery, and a preview), so a
418
+ * server whose cursor never ends cannot hold a turn; the tools on later pages are left out.
419
+ */
420
+ mcpListPages: count(100),
421
+ /** How much of an MCP connection's error message its discovery diagnostic keeps. */
422
+ mcpDiagnosticChars: count(500),
423
+ /**
424
+ * The Runtime's own commands in a sandbox (making folders, mounting skills, the tools'
425
+ * helpers): how long one may take, and how much of one a failure quotes.
426
+ */
427
+ helperCommandTimeoutMs: ms(MINUTE),
428
+ helperCommandExcerptChars: count(120),
429
+ /**
430
+ * The shortest credential value scrubbed from what a tool, a command or a model returns
431
+ * (every process that scrubs reads it): a shorter one would blank out ordinary text that
432
+ * happens to contain it.
433
+ */
434
+ scrubMinChars: count(8),
435
+ /** How much of a command an `environment.exec` event keeps. */
436
+ execCommandChars: count(500),
437
+ /** The local sandbox backend: the most bytes kept of each of a command's output streams. */
438
+ commandStreamBytes: bytes(4 * MIB),
439
+ /** A pod's engine: how often it renews its host and egress tokens, and how early. */
440
+ podRenewMs: ms(5 * MINUTE),
441
+ podRenewEarlyMs: ms(MINUTE, 0),
442
+ /** A pod's engine: how long `host/join` and `host/renew`, and other host calls, may take. */
443
+ podJoinTimeoutMs: ms(15_000),
444
+ podRequestTimeoutMs: ms(5 * MINUTE),
445
+ /**
446
+ * A pod's engine, before it joins: how long it waits for its NetworkPolicy, each probe of an
447
+ * address the policy blocks, the pause after a probe that connected, and the failed probes
448
+ * in a row that prove an address blocked.
449
+ */
450
+ networkPolicyTimeoutMs: ms(2 * MINUTE),
451
+ networkProbeMs: ms(1_000),
452
+ networkPauseMs: ms(250, 0),
453
+ networkConfirmations: count(3),
454
+ /** Environment setup: how often a failed setup is checked for a retry, and after a seed. */
455
+ setupRetryPollMs: ms(5_000),
456
+ setupAfterSeedPollMs: ms(1_000),
457
+ /** How many characters of a failed setup command's output its report keeps (the end). */
458
+ setupReportTailChars: count(4 * KIB),
459
+ }),
460
+ /** Traces (R7.2): the OTLP exporter. */
461
+ traces: group({
462
+ /** How long one OTLP request may take. */
463
+ timeoutMs: ms(10_000),
464
+ /** Tries after the first, and the wait before the first retry, doubled for each after. */
465
+ retries: count(3, 0),
466
+ backoffMs: ms(500),
467
+ /** The trace projector: spans per post, how long a span waits for its batch to fill, the spans
468
+ * that may wait while a post is out (the rest are dropped), the turns whose trace it keeps,
469
+ * and how soon it reads again when Postgres did not answer. */
470
+ batchSpans: count(512),
471
+ batchDelayMs: ms(2_000),
472
+ queuedSpansMax: count(8_192),
473
+ turnCacheSize: count(1_024),
474
+ retryMs: ms(1_000),
475
+ }),
476
+ /**
477
+ * Our engine (`createHarness` in `@nylorun/harness`): each left out is the engine's default.
478
+ * A harness host passes these to the engine it runs.
479
+ */
480
+ engine: z
481
+ .strictObject({
482
+ /** The transcript cache's budget, in MiB. */
483
+ transcriptCacheMb: positiveInt().optional(),
484
+ /** The most bytes one `read_artifact` page holds. */
485
+ artifactPageBytes: positiveInt().optional(),
486
+ /** The most bytes of one skill file read into the model's context. */
487
+ skillReadBytes: positiveInt().optional(),
488
+ /** How soon a failed lease is asked for again. */
489
+ leaseRetryMs: z.number().int().positive().max(MAX_TIMER_MS).optional(),
490
+ /** The waits before each resend of an effect core has not answered; the last repeats. */
491
+ resendDelaysMs: z.array(z.number().int().positive().max(MAX_TIMER_MS)).min(1).optional(),
492
+ /** How much of a failed agent-as-tool's last output its parent sees. */
493
+ partialOutputChars: positiveInt().optional(),
494
+ /** How much of a value a flow's failure message quotes. */
495
+ flowValueChars: positiveInt().optional(),
496
+ compaction: enginePart({
497
+ keepTokens: positiveInt(),
498
+ keepShare: positiveShare(),
499
+ reserveTokens: positiveInt(),
500
+ maxReserveTokens: positiveInt(),
501
+ maxReserveShare: positiveShare(),
502
+ clipChars: positiveInt(),
503
+ summaryOverheadTokens: positiveInt(),
504
+ imageTokens: positiveInt(),
505
+ fileTokensMax: positiveInt(),
506
+ minChunkTokens: positiveInt(),
507
+ minSummaryTokens: positiveInt(),
508
+ summaryShare: positiveShare(),
509
+ charsPerToken: positiveInt(),
510
+ }),
511
+ sandboxTools: enginePart({
512
+ outputChars: positiveInt(),
513
+ defaultTimeoutS: positiveInt(),
514
+ maxTimeoutS: positiveInt(),
515
+ readLines: positiveInt(),
516
+ maxReadLines: positiveInt(),
517
+ lineChars: positiveInt(),
518
+ grepResults: positiveInt(),
519
+ maxGrepResults: positiveInt(),
520
+ globResults: positiveInt(),
521
+ globScan: positiveInt(),
522
+ }),
523
+ mcpTools: enginePart({
524
+ deferShare: positiveShare(),
525
+ charsPerToken: positiveInt(),
526
+ fallbackContextWindow: positiveInt(),
527
+ serverInstructionsMaxChars: positiveInt(),
528
+ toolSearchDefaultLimit: positiveInt(),
529
+ toolSearchMaxLimit: positiveInt(),
530
+ }),
531
+ toolResults: enginePart({
532
+ resultInlineBytes: positiveInt(),
533
+ previewHeadBytes: positiveInt(),
534
+ previewTailBytes: positiveInt(),
535
+ stepBudgetBytes: positiveInt(),
536
+ stubBytes: positiveInt(),
537
+ smallBytes: positiveInt(),
538
+ minPreviewBytes: positiveInt(),
539
+ entryEnvelopeBytes: positiveInt(),
540
+ }),
541
+ outputs: enginePart({
542
+ dir: z.string().startsWith("/"),
543
+ maxBytes: positiveInt(),
544
+ }),
545
+ })
546
+ .prefault({}),
547
+ })
548
+ .superRefine((tuning, issues) => {
549
+ // Settings that only work against another.
550
+ const rule = (ok, path, message) => {
551
+ if (!ok)
552
+ issues.addIssue({ code: "custom", path, message });
553
+ };
554
+ rule(tuning.gates.requestTimeoutMs > tuning.gates.clientTimeoutMs, ["gates", "requestTimeoutMs"], "must be above gates.clientTimeoutMs: the gate must not end a call its client still waits for");
555
+ rule(tuning.gates.mcpSweepMinMs <= tuning.gates.mcpSweepMaxMs, ["gates", "mcpSweepMinMs"], "must be at most gates.mcpSweepMaxMs");
556
+ rule(tuning.postgres.controlWindowSeconds * 1000 > tuning.postgres.controlPollMs, ["postgres", "controlWindowSeconds"], "must be longer than postgres.controlPollMs, so a read-back catches a missed notification");
557
+ rule(tuning.execution.advanceDeadlineMs < tuning.restate.inactivityMs, ["execution", "advanceDeadlineMs"], "must be below restate.inactivityMs, so an advance ends before Restate suspends it");
558
+ for (const key of ["runTokenTtlSeconds", "hostTokenTtlSeconds"])
559
+ rule(tuning.keys[key] <= tuning.keys.tokenTtlMaxSeconds, ["keys", key], "must be at most keys.tokenTtlMaxSeconds, which a key rotation waits for");
560
+ rule(tuning.keys.runTokenRenewSeconds < tuning.keys.runTokenTtlSeconds, ["keys", "runTokenRenewSeconds"], "must be below keys.runTokenTtlSeconds");
561
+ rule(tuning.keys.hostTokenTtlSeconds * 1000 > tuning.harness.podRenewMs, ["keys", "hostTokenTtlSeconds"], "must be longer than harness.podRenewMs, so a pod's engine renews its tokens before they end");
562
+ rule(tuning.restate.stuckListDefault <= tuning.restate.stuckListMax, ["restate", "stuckListDefault"], "must be at most restate.stuckListMax");
563
+ });
564
+ function deepFreeze(value) {
565
+ if (value && typeof value === "object") {
566
+ for (const item of Object.values(value))
567
+ deepFreeze(item);
568
+ Object.freeze(value);
569
+ }
570
+ return value;
571
+ }
572
+ /** The tuning with every default: the Runtime's values when nothing is set. */
573
+ export const DEFAULT_HOST_TUNING = deepFreeze(HostTuningSchema.parse({}));
574
+ export class HostTuningError extends Error {
575
+ constructor(message) {
576
+ super(message);
577
+ this.name = "HostTuningError";
578
+ }
579
+ }
580
+ /** `input` over the defaults; throws `HostTuningError` naming each refused key. */
581
+ export function hostTuningOf(input, source = "NYLORUN_TUNING") {
582
+ const parsed = HostTuningSchema.safeParse(input ?? {});
583
+ if (!parsed.success)
584
+ throw new HostTuningError(`${source}: ${parsed.error.issues
585
+ .map((issue) => `${issue.path.length ? issue.path.join(".") : "(root)"}: ${issue.message}`)
586
+ .join("; ")}`);
587
+ return deepFreeze(parsed.data);
588
+ }
589
+ /** The tuning `NYLORUN_TUNING` names (JSON), and the overrides as set; the defaults when unset. */
590
+ export function parseHostTuning(raw) {
591
+ if (raw === undefined || raw.trim() === "")
592
+ return { tuning: DEFAULT_HOST_TUNING };
593
+ let value;
594
+ try {
595
+ value = JSON.parse(raw);
596
+ }
597
+ catch (error) {
598
+ throw new HostTuningError(`NYLORUN_TUNING must be a JSON object: ${error instanceof Error ? error.message : String(error)}`);
599
+ }
600
+ if (!value || typeof value !== "object" || Array.isArray(value))
601
+ throw new HostTuningError("NYLORUN_TUNING must be a JSON object of tuning groups, e.g. {\"tenant\":{\"openTimeoutMs\":60000}}");
602
+ const set = value;
603
+ const tuning = hostTuningOf(set);
604
+ const pod = podTuningOf(set);
605
+ if (pod !== undefined && Buffer.byteLength(pod) > POD_ENV_VALUE_MAX_BYTES)
606
+ throw new HostTuningError(`NYLORUN_TUNING's ${POD_GROUPS.join(" and ")} groups take ${Buffer.byteLength(pod)} bytes as JSON; a sandbox pod's engine gets them in one environment variable, of at most ${POD_ENV_VALUE_MAX_BYTES} bytes (the sandboxes service's limit)`);
607
+ return { tuning, overrides: set };
608
+ }
609
+ /** The groups a pod's engine reads. */
610
+ const POD_GROUPS = ["harness", "engine"];
611
+ /**
612
+ * The longest environment variable value the sandboxes service puts in a pod's spec (its `env`
613
+ * validation): a dependency's ceiling, not a setting.
614
+ */
615
+ const POD_ENV_VALUE_MAX_BYTES = 4096;
616
+ /** The pod groups of `set` as JSON, or undefined when it sets neither. */
617
+ function podTuningOf(set) {
618
+ const picked = Object.fromEntries(POD_GROUPS.filter((name) => set[name] !== undefined).map((name) => [name, set[name]]));
619
+ return Object.keys(picked).length === 0 ? undefined : JSON.stringify(picked);
620
+ }
621
+ let current = DEFAULT_HOST_TUNING;
622
+ let overrides;
623
+ /**
624
+ * The process's tuning: what `useHostTuning` set when the process started, or the defaults. Read
625
+ * where a value is used, never kept at module load, so what the process was started with applies.
626
+ */
627
+ export function hostTuning() {
628
+ return current;
629
+ }
630
+ /**
631
+ * Sets the process's tuning, once at start (`host/main.ts`); tests set and reset it. `set` is the
632
+ * overrides as the operator wrote them, which a pod's engine is handed (`podTuningEnv`).
633
+ */
634
+ export function useHostTuning(tuning, set) {
635
+ current = tuning;
636
+ overrides = set;
637
+ }
638
+ /**
639
+ * `NYLORUN_TUNING` for a pod sandbox's engine: the `harness` and `engine` groups the operator
640
+ * set, or nothing when they set neither.
641
+ */
642
+ export function podTuningEnv() {
643
+ const pod = overrides && podTuningOf(overrides);
644
+ return pod === undefined ? {} : { NYLORUN_TUNING: pod };
645
+ }
646
+ /** Every setting's dotted path (`tenant.openTimeoutMs`), for the docs check. */
647
+ export function hostTuningPaths() {
648
+ const paths = [];
649
+ const walk = (value, prefix) => {
650
+ if (value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0 && !("minMs" in value))
651
+ for (const [key, item] of Object.entries(value))
652
+ walk(item, prefix ? `${prefix}.${key}` : key);
653
+ else
654
+ paths.push(prefix);
655
+ };
656
+ walk(DEFAULT_HOST_TUNING, "");
657
+ return paths;
658
+ }