@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
@@ -14,10 +14,6 @@ type Exists = (id: string) => boolean | Promise<boolean>;
14
14
  export interface WorkspacePort {
15
15
  /** Runs one sandbox tool in the workspace of `session`. */
16
16
  run(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, toolName: SandboxToolName, input: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
17
- /** Lists the files under `dir` of the workspace of `session` (the turn-end export, F8.2). */
18
- listFiles(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, dir: string, maxEntries: number, signal: AbortSignal): ReturnType<SandboxManager["listFiles"]>;
19
- /** Reads a file of the workspace of `session` as bytes, at most `maxBytes` (`save_artifact`). */
20
- readBytes(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, path: string, maxBytes: number, signal: AbortSignal): ReturnType<SandboxManager["readBytes"]>;
21
17
  /** One operation on the disk of the workspace of `session` (R2d step 3). */
22
18
  disk(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, op: DiskOp, signal: AbortSignal): ReturnType<SandboxManager["disk"]>;
23
19
  report(): Promise<WorkspaceReport>;
@@ -13,8 +13,8 @@
13
13
  * removes theirs (`workspace.remove`); it also keeps the `sandboxes` table in step with the
14
14
  * list.
15
15
  * - **Pod sandboxes** (F7.2, `withPodWorkspaces`): a session attached to a pod sandbox has its
16
- * workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls,
17
- * listings and reads go there whatever serves the other workspaces; with the pod stopped or
16
+ * workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls
17
+ * and disk operations go there whatever serves the other workspaces; with the pod stopped or
18
18
  * starting they are `409 environment_unavailable`.
19
19
  */
20
20
  import { HarnessApiError } from "@nylorun/core/harness-api";
@@ -26,8 +26,6 @@ import { NoWorkspaceHarness } from "./server.js";
26
26
  export function localWorkspace(manager) {
27
27
  return {
28
28
  run: (session, sandbox, toolName, input, signal) => manager.run(session, sandbox, toolName, input, signal),
29
- listFiles: (session, sandbox, dir, maxEntries, signal) => manager.listFiles(session, sandbox, dir, maxEntries, signal),
30
- readBytes: (session, sandbox, path, maxBytes, signal) => manager.readBytes(session, sandbox, path, maxBytes, signal),
31
29
  disk: (session, sandbox, op, signal) => manager.disk(session, sandbox, op, signal),
32
30
  report: () => manager.report(),
33
31
  sweep: (options) => manager.sweep(options),
@@ -119,45 +117,6 @@ export function remoteWorkspace(options) {
119
117
  throw error;
120
118
  }
121
119
  },
122
- async listFiles(session, sandbox, dir, maxEntries, signal) {
123
- try {
124
- const answer = (await options.server.workspace("workspace.read", { session: sessionOf(session), spec: sandbox ?? {}, list: { dir, maxEntries } }, signal, target));
125
- if (answer.kind === "listed")
126
- return {
127
- kind: "listed",
128
- path: String(answer.path),
129
- listing: answer.listing,
130
- };
131
- if (answer.kind === "missing")
132
- return { kind: "missing", path: String(answer.path) };
133
- return { kind: "failed", code: String(answer.code), message: String(answer.message) };
134
- }
135
- catch (error) {
136
- // No harness holds the workspaces: there is nothing to export.
137
- if (error instanceof NoWorkspaceHarness)
138
- return { kind: "missing", path: dir };
139
- throw error;
140
- }
141
- },
142
- async readBytes(session, sandbox, path, maxBytes, signal) {
143
- try {
144
- const answer = (await options.server.workspace("workspace.read", {
145
- session: sessionOf(session),
146
- spec: sandbox ?? {},
147
- bytes: { path, maxBytes },
148
- }, signal, target));
149
- if (answer.kind === "read")
150
- return { kind: "read", path: String(answer.path), bytes: new Uint8Array(Buffer.from(String(answer.base64), "base64")) };
151
- if (answer.kind === "missing")
152
- return { kind: "missing", path: String(answer.path) };
153
- return { kind: "failed", code: String(answer.code), message: String(answer.message) };
154
- }
155
- catch (error) {
156
- if (error instanceof NoWorkspaceHarness)
157
- return { kind: "failed", code: "sandbox.unavailable", message: error.message };
158
- throw error;
159
- }
160
- },
161
120
  async disk(session, sandbox, op, signal) {
162
121
  try {
163
122
  const answer = (await options.server.workspace("list" in op || "read" in op ? "workspace.read" : "workspace.write", { session: sessionOf(session), spec: sandbox ?? {}, disk: toWireOp(op) }, signal, target));
@@ -215,8 +174,6 @@ export function withPodWorkspaces(base, options) {
215
174
  return {
216
175
  ...base,
217
176
  run: async (session, sandbox, toolName, input, signal) => (await routed(session)).run(session, sandbox, toolName, input, signal),
218
- listFiles: async (session, sandbox, dir, maxEntries, signal) => (await routed(session)).listFiles(session, sandbox, dir, maxEntries, signal),
219
- readBytes: async (session, sandbox, path, maxBytes, signal) => (await routed(session)).readBytes(session, sandbox, path, maxBytes, signal),
220
177
  disk: async (session, sandbox, op, signal) => (await routed(session)).disk(session, sandbox, op, signal),
221
178
  };
222
179
  }
@@ -6,8 +6,8 @@
6
6
  * An upgrade is accepted only on `/nylorun/harness/v1`, with a `Host` in the allowlist (421),
7
7
  * `Authorization: Bearer <token>` (compared in constant time; 401) and `Nylorun-Harness-Api: 1`
8
8
  * (426), while the Tenant is open (503). Frames are at most 64 MiB and every one is validated
9
- * (`createChannel({ validate: true })`). Core pings every 30 s and drops a harness silent for
10
- * 90 s; the connection then counts as lost (`connection.lost`).
9
+ * (`createChannel({ validate: true })`). Core pings every `harness.pingMs` (Host tuning, 30 s) and
10
+ * drops a harness silent for `harness.silenceMs` (90 s); the connection then counts as lost (`connection.lost`).
11
11
  *
12
12
  * Sandbox pods (F7.2, D42) reach this listener too, on the Docker host's published port, and hold
13
13
  * no harness token. A pod's engine exchanges its join token for a host token with `POST
@@ -30,15 +30,12 @@ import { tokenType } from "../tenant/jwt.js";
30
30
  import { HOST_TOKEN_TYP } from "../tenant/host-token.js";
31
31
  import { HostAuthError } from "../sandbox/join.js";
32
32
  import { parseSetupReport } from "../sandbox/setup.js";
33
+ import { hostTuning } from "../tuning.js";
33
34
  export { HOST_JOIN_PATH, HOST_RENEW_PATH };
34
35
  /** Where a pod's engine reads its setup and reports it (R2d step 2, `sandbox/setup.ts`). */
35
36
  export const HOST_SETUP_PATH = `${HARNESS_API_PATH}/host/setup`;
36
37
  /** Where it reads its input files: `/{index}`, and a folder's file `/{index}/{path}`. */
37
38
  export const HOST_FILES_PATH = `${HARNESS_API_PATH}/host/files`;
38
- /** A join's body is small. */
39
- const MAX_JOIN_BODY = 8 * 1024;
40
- /** A setup report carries the end of a command's output. */
41
- const MAX_REPORT_BODY = 64 * 1024;
42
39
  function refuse(socket, status, text, headers = {}) {
43
40
  const lines = [
44
41
  `HTTP/1.1 ${status} ${text}`,
@@ -85,7 +82,9 @@ export async function startHarnessListener(options) {
85
82
  });
86
83
  response.end(text);
87
84
  };
88
- const readBody = (request, max = MAX_JOIN_BODY) => new Promise((resolve, reject) => {
85
+ // A join's body is small (`harnessApi.joinBodyBytes`); a setup report carries the end of a
86
+ // command's output (`harnessApi.reportBodyBytes`).
87
+ const readBody = (request, max = hostTuning().harnessApi.joinBodyBytes) => new Promise((resolve, reject) => {
89
88
  let size = 0;
90
89
  const chunks = [];
91
90
  request.on("data", (chunk) => {
@@ -139,7 +138,7 @@ export async function startHarnessListener(options) {
139
138
  return reply(response, 200, await authority.setup(token));
140
139
  if (request.method !== "POST")
141
140
  return reply(response, 405, undefined, { allow: "GET, POST" });
142
- const report = parseSetupReport(JSON.parse(await readBody(request, MAX_REPORT_BODY)));
141
+ const report = parseSetupReport(JSON.parse(await readBody(request, hostTuning().harnessApi.reportBodyBytes)));
143
142
  if (!report)
144
143
  throw new HostAuthError(400, "The report is malformed");
145
144
  await authority.reportSetup(token, report);
@@ -148,7 +147,8 @@ export async function startHarnessListener(options) {
148
147
  if (request.method !== "GET")
149
148
  return reply(response, 405, undefined, { allow: "GET" });
150
149
  const [index, ...rest] = path.slice(HOST_FILES_PATH.length + 1).split("/");
151
- if (!/^\d{1,2}$/.test(index ?? ""))
150
+ // Any entry of the recipe: `setupFile` answers none for an index past its last.
151
+ if (!/^\d+$/.test(index ?? "") || !Number.isSafeInteger(Number(index)))
152
152
  return reply(response, 404);
153
153
  const file = await authority.setupFile(token, Number(index), rest.length === 0 ? undefined : rest.map((part) => decodeURIComponent(part)).join("/"));
154
154
  if (!file)
@@ -281,7 +281,7 @@ export async function startHarnessListener(options) {
281
281
  setTimeout(() => {
282
282
  for (const client of wss.clients)
283
283
  client.terminate();
284
- }, 1_000).unref();
284
+ }, hostTuning().harnessApi.closeGraceMs).unref();
285
285
  await closed;
286
286
  wss.close();
287
287
  },
@@ -1,6 +1,7 @@
1
1
  import { createServer, } from "node:http";
2
2
  import { getRequestListener, RequestError } from "@hono/node-server";
3
3
  import { createHostApp } from "./app.js";
4
+ import { hostTuning } from "../tuning.js";
4
5
  import { isLoopbackHost } from "./stack-config.js";
5
6
  import { bindListener as bind, EXIT_NON_LOOPBACK, headerValue, HostListenError, isAllowedRequestHost, pathnameIsLogged, redactRoutePath, rejectedResponse, sendRejected, } from "./http.js";
6
7
  export function createHost(options) {
@@ -132,7 +133,7 @@ export function createHost(options) {
132
133
  const graceful = await Promise.race([
133
134
  stopped.then(() => true),
134
135
  new Promise((resolve) => {
135
- grace = setTimeout(() => resolve(false), SHUTDOWN_GRACE_MS);
136
+ grace = setTimeout(() => resolve(false), hostTuning().host.shutdownGraceMs);
136
137
  grace.unref();
137
138
  }),
138
139
  ]);
@@ -172,8 +173,6 @@ const SENT = {
172
173
  function alreadySent(response) {
173
174
  return response.headers.has("x-hono-already-sent") ? SENT : response;
174
175
  }
175
- /** How long shutdown waits for requests in progress before closing their connections. */
176
- const SHUTDOWN_GRACE_MS = 10_000;
177
176
  /**
178
177
  * A response that has started and not ended is a stream: JSON answers are written and ended
179
178
  * at once. (`getHeader` cannot tell, since streams pass their headers to `writeHead`.)
@@ -1,7 +1,6 @@
1
1
  import { startWorker, stopWorker } from "../infra/workers.js";
2
2
  import { TenantWorkers, } from "../tenant/worker.js";
3
- /** How many sweep arms `armAll` sends at once. */
4
- const ARM_CONCURRENCY = 16;
3
+ import { hostTuning } from "../tuning.js";
5
4
  export function createHostExecution(options) {
6
5
  const { execution, services, logger } = options;
7
6
  const workers = new TenantWorkers({
@@ -58,7 +57,7 @@ export function createHostExecution(options) {
58
57
  }
59
58
  }
60
59
  };
61
- await Promise.all(Array.from({ length: Math.min(ARM_CONCURRENCY, queue.length) }, next));
60
+ await Promise.all(Array.from({ length: Math.min(hostTuning().sweep.armConcurrency, queue.length) }, next));
62
61
  if (failures.length > 0)
63
62
  throw new AggregateError(failures, `Arming ${failures.length} Tenant sweep(s) failed`);
64
63
  },
@@ -15,14 +15,11 @@ import { type InflightCallsOptions } from "../gates/inflight.js";
15
15
  import type { openMcpServer } from "../mcp/connect.js";
16
16
  import type { OutboundPolicy } from "../tenant/outbound.js";
17
17
  import { type BlobStore } from "../blob/index.js";
18
- import type { ModelCallSettings } from "../gates/model-gate.js";
19
18
  import { type TenantVaults } from "../gates/tenant-vaults.js";
20
19
  import type { PostgresClient } from "../store/postgres/connect.js";
21
20
  import type { Logger } from "../tenant/types.js";
22
21
  import type { EgressConfig, GatesConfig } from "./stack-config.js";
23
22
  import { type EgressGate, type StartEgressGateOptions } from "../gates/egress.js";
24
- /** Above the gate's 600 s provider request timeout and the loop's 630 s client timeout. */
25
- export declare const GATES_REQUEST_TIMEOUT_MS = 660000;
26
23
  export interface StartGatesOptions {
27
24
  readonly gates: GatesConfig;
28
25
  readonly logger: Logger;
@@ -32,11 +29,7 @@ export interface StartGatesOptions {
32
29
  readonly database?: PostgresClient;
33
30
  /** Replaces the Postgres-backed Tenant vault (tests). */
34
31
  readonly vaults?: TenantVaults;
35
- /** Retries and timeouts of model calls (tests). */
36
- readonly settings?: ModelCallSettings;
37
- /** Largest model call body (tests). */
38
- readonly maxBodyBytes?: number;
39
- /** How long `close` lets running calls finish. Default 10 s. */
32
+ /** How long `close` lets running calls finish. Default `gates.drainMs` (Host tuning, 10 s). */
40
33
  readonly drainMs?: number;
41
34
  /** How long keyed outcomes are kept, and how many (tests). */
42
35
  readonly inflight?: InflightCallsOptions;
@@ -45,7 +38,7 @@ export interface StartGatesOptions {
45
38
  * Default: no limits.
46
39
  */
47
40
  readonly delivery?: OutboundPolicy;
48
- /** How long an MCP connection may sit unused. Default `GATE_MCP_IDLE_MS`. */
41
+ /** How long an MCP connection may sit unused. Default `gates.mcpIdleMs` (Host tuning, 15 min). */
49
42
  readonly mcpIdleMs?: number;
50
43
  /** Tests replace how a remote MCP server is opened. */
51
44
  readonly openMcp?: typeof openMcpServer;
@@ -15,7 +15,7 @@ import { getRequestListener } from "@hono/node-server";
15
15
  import { createGatesApp } from "../api/gate/routes.js";
16
16
  import { createModelCallHandler } from "../gates/handler.js";
17
17
  import { createInflightCalls } from "../gates/inflight.js";
18
- import { createMcpHandler, GATE_MCP_IDLE_MS } from "../gates/mcp-handler.js";
18
+ import { createMcpHandler } from "../gates/mcp-handler.js";
19
19
  import { createToolCalls } from "../gates/tool-calls.js";
20
20
  import { gateHttpTools } from "../gates/http-tool.js";
21
21
  import { existsSync } from "node:fs";
@@ -24,13 +24,13 @@ import { staleRun, verifyRunToken } from "../tenant/run-token.js";
24
24
  import { createFsBlobStore } from "../blob/index.js";
25
25
  import { createTenantVaults } from "../gates/tenant-vaults.js";
26
26
  import { journalCheck } from "../gates/journal.js";
27
+ import { readRuntimeLimits } from "../tenant/runtime-limits.js";
27
28
  import { probeDatabase } from "../infra/database.js";
28
29
  import { bindListener, headerValue, isAllowedRequestHost, sendRejected } from "./http.js";
29
30
  import { startEgressGate, storeEgressRecord, storeEgressSandboxes, } from "../gates/egress.js";
30
31
  import { verifyEgressToken } from "../sandbox/egress-token.js";
31
32
  import { leafKeyPair } from "../keys/x509.js";
32
- /** Above the gate's 600 s provider request timeout and the loop's 630 s client timeout. */
33
- export const GATES_REQUEST_TIMEOUT_MS = 660_000;
33
+ import { hostTuning } from "../tuning.js";
34
34
  export async function startGates(options) {
35
35
  const { gates, logger, database } = options;
36
36
  let vaults = options.vaults;
@@ -47,8 +47,9 @@ export async function startGates(options) {
47
47
  (options.hostRoot !== undefined
48
48
  ? createFsBlobStore({ root: tenantPaths(options.hostRoot).blobs })
49
49
  : undefined);
50
+ const tuning = hostTuning().gates;
50
51
  const inflight = createInflightCalls(options.inflight);
51
- const mcpIdleMs = options.mcpIdleMs ?? GATE_MCP_IDLE_MS;
52
+ const mcpIdleMs = options.mcpIdleMs ?? tuning.mcpIdleMs;
52
53
  const mcp = createMcpHandler({
53
54
  vaults,
54
55
  logger,
@@ -61,9 +62,9 @@ export async function startGates(options) {
61
62
  logger,
62
63
  ...(options.inflight ? { inflight: options.inflight } : {}),
63
64
  });
64
- const sweep = setInterval(() => void mcp.sweep(), Math.max(1_000, Math.min(mcpIdleMs, 60_000)));
65
+ const sweep = setInterval(() => void mcp.sweep(), Math.max(tuning.mcpSweepMinMs, Math.min(mcpIdleMs, tuning.mcpSweepMaxMs)));
65
66
  sweep.unref();
66
- const prune = setInterval(() => void toolCalls.prune(), 60 * 60_000);
67
+ const prune = setInterval(() => void toolCalls.prune(), tuning.toolCrossingPruneMs);
67
68
  prune.unref();
68
69
  // Run tokens verify against the Tenant's public keys and session rows (F5): the same
69
70
  // database the ledger and `tool_crossings` use.
@@ -81,7 +82,6 @@ export async function startGates(options) {
81
82
  modelGate: createModelCallHandler({
82
83
  vaults,
83
84
  logger,
84
- ...(options.settings ? { settings: options.settings } : {}),
85
85
  ...(blobs ? { blobs } : {}),
86
86
  }),
87
87
  ready: async () => {
@@ -91,14 +91,15 @@ export async function startGates(options) {
91
91
  if (!database)
92
92
  return true;
93
93
  try {
94
- await probeDatabase(database, AbortSignal.timeout(2000));
94
+ await probeDatabase(database, AbortSignal.timeout(tuning.probeTimeoutMs));
95
95
  return true;
96
96
  }
97
97
  catch {
98
98
  return false;
99
99
  }
100
100
  },
101
- ...(options.maxBodyBytes !== undefined ? { maxBodyBytes: options.maxBodyBytes } : {}),
101
+ // The Tenant's Runtime limits, read from its database for each call.
102
+ limits: async () => (await vaults.open()).store.tx((t) => readRuntimeLimits(t)),
102
103
  mcp,
103
104
  http: gateHttpTools(vaults, options.delivery ?? {}, logger),
104
105
  toolCalls,
@@ -122,7 +123,9 @@ export async function startGates(options) {
122
123
  res.once("close", () => inFlight.delete(res));
123
124
  void listener(req, res);
124
125
  });
125
- server.requestTimeout = GATES_REQUEST_TIMEOUT_MS;
126
+ // Above the gate's 600 s provider request timeout and the loop's client timeout
127
+ // (`gates.clientTimeoutMs`): the tuning refuses one at or below the other.
128
+ server.requestTimeout = tuning.requestTimeoutMs;
126
129
  await bindListener(server, gates.listen.port, gates.listen.host, (error) => logger.error("listener_error", { error: error.message }));
127
130
  const port = server.address().port;
128
131
  allowedHosts = [
@@ -137,9 +140,9 @@ export async function startGates(options) {
137
140
  closing ??= (async () => {
138
141
  const closed = new Promise((resolve) => server.close(() => resolve()));
139
142
  server.closeIdleConnections();
140
- const deadline = Date.now() + (options.drainMs ?? 10_000);
143
+ const deadline = Date.now() + (options.drainMs ?? tuning.drainMs);
141
144
  while (inFlight.size > 0 && Date.now() < deadline)
142
- await new Promise((resolve) => setTimeout(resolve, 50));
145
+ await new Promise((resolve) => setTimeout(resolve, tuning.drainPollMs));
143
146
  // Closing a running call's connection aborts an unkeyed one; keyed calls stop here.
144
147
  server.closeAllConnections();
145
148
  inflight.close();
@@ -52,5 +52,3 @@ export declare function redactRoutePath(pathname: string): string;
52
52
  export declare function adminKeyMatches(presented: string | undefined, adminKey: string): boolean;
53
53
  /** A request header's first value. */
54
54
  export declare function headerValue(incoming: IncomingMessage, name: string): string | undefined;
55
- /** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
56
- export declare function readJsonBody(request: Request, limit?: number): Promise<unknown>;
package/dist/host/http.js CHANGED
@@ -164,30 +164,3 @@ export function headerValue(incoming, name) {
164
164
  return raw[0];
165
165
  return raw;
166
166
  }
167
- /** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
168
- export async function readJsonBody(request, limit = 1024 * 1024) {
169
- if (request.body === null)
170
- return undefined;
171
- const chunks = [];
172
- let size = 0;
173
- const reader = request.body.getReader();
174
- for (;;) {
175
- const { done, value } = await reader.read();
176
- if (done)
177
- break;
178
- size += value.byteLength;
179
- if (size > limit) {
180
- await reader.cancel();
181
- throw Object.assign(new Error("Request too large"), { status: 413 });
182
- }
183
- chunks.push(value);
184
- }
185
- if (size === 0)
186
- return undefined;
187
- try {
188
- return JSON.parse(Buffer.concat(chunks).toString("utf8"));
189
- }
190
- catch {
191
- throw Object.assign(new Error("Invalid JSON"), { status: 400 });
192
- }
193
- }
package/dist/host/main.js CHANGED
@@ -44,6 +44,7 @@ import { createPostgresSessionStore } from "../store/postgres/store.js";
44
44
  import { createTraceProjector, TRACE_SLOT } from "../traces/projector.js";
45
45
  import { createOtlpPoster } from "../traces/otlp.js";
46
46
  import { traceKey } from "../traces/key.js";
47
+ import { readRuntimeLimits } from "../tenant/runtime-limits.js";
47
48
  import { configForFactory } from "./config-for.js";
48
49
  import { createHost } from "./create-host.js";
49
50
  import { createHostExecution } from "./execution.js";
@@ -63,6 +64,7 @@ import { runHarness } from "../harness/main.js";
63
64
  import { startHarnessListener } from "../harness-api/ws-server.js";
64
65
  import { httpSandboxesClient } from "../sandbox/pods/client.js";
65
66
  import { retry } from "../retry.js";
67
+ import { hostTuning, useHostTuning } from "../tuning.js";
66
68
  const nodeRequire = createRequire(import.meta.url);
67
69
  function coreVersion() {
68
70
  try {
@@ -129,7 +131,8 @@ function objectStore(config) {
129
131
  */
130
132
  async function ensureBucket(store, bucket, logger) {
131
133
  try {
132
- await retry(() => store.ensureBucket(), { minMs: 1000, maxMs: 5000, attempts: 10 });
134
+ const { bucketRetry, bucketAttempts } = hostTuning().objectStore;
135
+ await retry(() => store.ensureBucket(), { ...bucketRetry, attempts: bucketAttempts });
133
136
  logger.info("object_store_ready", { bucket });
134
137
  }
135
138
  catch (error) {
@@ -191,7 +194,7 @@ async function runGates(stack) {
191
194
  }
192
195
  catch (error) {
193
196
  await Promise.all(servers.map((server) => server.close()));
194
- await database.end({ timeout: 5 });
197
+ await database.end({ timeout: hostTuning().postgres.closeTimeoutSeconds });
195
198
  if (error instanceof HostListenError) {
196
199
  logger.error("listen_failed", { message: error.message, exitCode: error.exitCode });
197
200
  process.exitCode = error.exitCode;
@@ -206,12 +209,15 @@ async function runGates(stack) {
206
209
  return;
207
210
  stopping = true;
208
211
  void Promise.all(servers.map((server) => server.close()))
209
- .then(() => database.end({ timeout: 5 }))
212
+ .then(() => database.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }))
210
213
  .finally(() => process.exit(0));
211
214
  });
212
215
  }
213
216
  export async function main() {
214
217
  const stack = parseStackConfig(process.env, process.argv.slice(2));
218
+ // Read once, before any service starts: each reads the parts it uses where it uses them.
219
+ if (stack.tuning)
220
+ useHostTuning(stack.tuning.values, stack.tuning.overrides);
215
221
  if (stack.services.has("gates") || stack.services.has("keys") || stack.services.has("egress"))
216
222
  return runGates(stack);
217
223
  if (stack.services.has("harness"))
@@ -332,6 +338,7 @@ export async function main() {
332
338
  log: (message, fields) => logger.warn(message, fields),
333
339
  }),
334
340
  key: () => traceKey(store),
341
+ contentBytes: async () => (await store.tx((t) => readRuntimeLimits(t))).traceContentBytes,
335
342
  turnMessage: createPostgresTurnMessages(database),
336
343
  content: new Set(stack.traces.content),
337
344
  log,
@@ -33,6 +33,7 @@ import { operatorKeys } from "../tenant/operator-keys.js";
33
33
  import { deleteHarnessToken, listHarnessTokens, putHarnessToken } from "../tenant/harness-tokens.js";
34
34
  import { RUNTIME_VERSION } from "../version.js";
35
35
  import { DEFAULT_CONTAINER_LISTEN_PORT } from "./stack-config.js";
36
+ import { parseHostTuning } from "../tuning.js";
36
37
  export const EXIT_REFUSED = 1;
37
38
  export const EXIT_TENANT = 2;
38
39
  export const EXIT_USAGE = 64;
@@ -51,6 +52,14 @@ class Exit extends Error {
51
52
  this.code = code;
52
53
  }
53
54
  }
55
+ /**
56
+ * The Host tuning this container runs with (`NYLORUN_TUNING`, as the Host reads it): how long
57
+ * the command waits for the Host's `/ready` (`host.operateReadyTimeoutMs`) and for its pool to
58
+ * close (`postgres.closeTimeoutSeconds`).
59
+ */
60
+ function tuningOf(io) {
61
+ return parseHostTuning(io.env.NYLORUN_TUNING).tuning;
62
+ }
54
63
  /** Runs one command; resolves to the exit code. */
55
64
  export async function runOperate(argv, io) {
56
65
  try {
@@ -70,7 +79,7 @@ export async function runOperate(argv, io) {
70
79
  return await command(operatorKeys(store), io, store);
71
80
  }
72
81
  finally {
73
- await sql.end({ timeout: 5 });
82
+ await sql.end({ timeout: tuningOf(io).postgres.closeTimeoutSeconds });
74
83
  }
75
84
  }
76
85
  catch (error) {
@@ -242,13 +251,13 @@ async function tenantStatus(io) {
242
251
  return unavailable(envelope ?? null, `The database cannot be read: ${error instanceof Error ? error.message : String(error)}`);
243
252
  }
244
253
  finally {
245
- await sql.end({ timeout: 5 });
254
+ await sql.end({ timeout: tuningOf(io).postgres.closeTimeoutSeconds });
246
255
  }
247
256
  const port = io.env.NYLORUN_LISTEN_PORT ?? String(DEFAULT_CONTAINER_LISTEN_PORT);
248
257
  const ready = `http://127.0.0.1:${port}/ready`;
249
258
  let answer;
250
259
  try {
251
- const response = await (io.fetch ?? fetch)(ready, { signal: AbortSignal.timeout(5000) });
260
+ const response = await (io.fetch ?? fetch)(ready, { signal: AbortSignal.timeout(tuningOf(io).host.operateReadyTimeoutMs) });
252
261
  answer = (await response.json());
253
262
  }
254
263
  catch {
@@ -41,10 +41,13 @@
41
41
  * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
42
42
  * their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
43
43
  * (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
44
+ * So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
45
+ * retries and internal sizes.
44
46
  */
45
47
  import { type TraceResource } from "../traces/otlp.js";
46
48
  import { type TraceContentKind } from "../traces/map.js";
47
49
  import { type BlockedAddress } from "../sandbox/pods/network-gate.js";
50
+ import { type HostTuning } from "../tuning.js";
48
51
  /** A Runtime service this release has. */
49
52
  export type RuntimeService = "core" | "loop" | "gates" | "keys" | "egress" | "harness";
50
53
  export type RuntimeServices = ReadonlySet<RuntimeService>;
@@ -249,6 +252,14 @@ export interface StackConfig {
249
252
  * present only with an endpoint (Q5): without one there is no replication slot and no cost.
250
253
  */
251
254
  traces?: TraceConfig;
255
+ /**
256
+ * Present when `NYLORUN_TUNING` is set: the Host tuning (`tuning.ts`) over its defaults,
257
+ * and the overrides as written, which core hands a pod sandbox's engine. Every service reads it.
258
+ */
259
+ tuning?: {
260
+ values: HostTuning;
261
+ overrides: Record<string, unknown>;
262
+ };
252
263
  }
253
264
  /** Traces (R7.2), from the standard `OTEL_*` variables and `NYLORUN_TRACE_CONTENT`. */
254
265
  export interface TraceConfig {
@@ -41,11 +41,14 @@
41
41
  * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
42
42
  * their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
43
43
  * (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
44
+ * So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
45
+ * retries and internal sizes.
44
46
  */
45
47
  import { DEFAULT_SERVICE_NAME } from "../traces/otlp.js";
46
48
  import { TRACE_CONTENT_KINDS } from "../traces/map.js";
47
49
  import { isTenantId } from "@nylorun/core/compatibility";
48
50
  import { blockedAddresses } from "../sandbox/pods/network-gate.js";
51
+ import { HostTuningError, parseHostTuning } from "../tuning.js";
49
52
  export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys", "egress", "harness"];
50
53
  /**
51
54
  * Services that may share a process (D12): they hold the same secrets and parse the same
@@ -254,8 +257,9 @@ function parseIdentityKeys(env) {
254
257
  /** Parse the stack configuration; throws `StackConfigError` naming the variable. */
255
258
  export function parseStackConfig(env, argv) {
256
259
  const { services, deprecatedRole } = parseServices(argv);
260
+ const tuning = parseTuning(env);
257
261
  if (services.has("harness"))
258
- return { services, harness: parseHarnessService(env), endpoints: {} };
262
+ return { services, harness: parseHarnessService(env), endpoints: {}, ...(tuning ? { tuning } : {}) };
259
263
  // The image sets NYLORUN_LISTEN_*: only the API's processes read them.
260
264
  const servesApi = services.has("core") || services.has("loop");
261
265
  const listen = servesApi ? parseListen(env) : undefined;
@@ -345,8 +349,24 @@ export function parseStackConfig(env, argv) {
345
349
  ...(tenant ? { tenant } : {}),
346
350
  ...(objectStore ? { objectStore } : {}),
347
351
  ...(traces ? { traces } : {}),
352
+ ...(tuning ? { tuning } : {}),
348
353
  };
349
354
  }
355
+ /** `StackConfig.tuning` from `NYLORUN_TUNING`, or undefined when it is unset. */
356
+ function parseTuning(env) {
357
+ const raw = read(env, "NYLORUN_TUNING");
358
+ if (raw === undefined)
359
+ return undefined;
360
+ try {
361
+ const { tuning, overrides } = parseHostTuning(raw);
362
+ return { values: tuning, overrides: overrides ?? {} };
363
+ }
364
+ catch (error) {
365
+ if (error instanceof HostTuningError)
366
+ throw new StackConfigError(error.message);
367
+ throw error;
368
+ }
369
+ }
350
370
  /**
351
371
  * `StackConfig.traces` (R7.2). The OTLP endpoint is the collector's base URL, to which
352
372
  * `/v1/traces` is added; `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is the full URL and wins. Headers
package/dist/index.d.ts CHANGED
@@ -5,7 +5,6 @@ export { createTrustedIssuers, type TrustedIssuers, type TrustedIssuersOptions,
5
5
  export type { TenantConfig, TenantHandle, TenantPaths, Logger, OpenTenantRuntime, } from "./tenant/types.js";
6
6
  export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
7
7
  export type { RuntimeAgent } from "./contracts.js";
8
- export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
9
8
  export type { RuntimeModelAdapter } from "./contracts.js";
10
9
  export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
11
10
  export type { HostProcessPathInputs } from "./host/environment.js";
package/dist/index.js CHANGED
@@ -3,5 +3,4 @@ export { startEphemeralRuntime, } from "./tenant/ephemeral.js";
3
3
  export { parseIdentityFile, IdentityFileError, } from "./tenant/identity-file.js";
4
4
  export { createTrustedIssuers, } from "./tenant/issuers.js";
5
5
  export { scriptedModel, gatewayModel } from "./core/provider.js";
6
- export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
7
6
  export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
@@ -2,6 +2,7 @@ import { createDatabase } from "./database.js";
2
2
  import { createExecution, executionKind } from "./execution.js";
3
3
  import { createReadiness, infraProbes } from "./readiness.js";
4
4
  import { createStreams, streamsKind } from "./streams.js";
5
+ import { hostTuning } from "../tuning.js";
5
6
  export { createDatabase, probeDatabase } from "./database.js";
6
7
  export { createExecution, probeExecution, WORKER_LISTEN } from "./execution.js";
7
8
  export { createReadiness, infraProbes } from "./readiness.js";
@@ -30,7 +31,7 @@ export function createInfra(config, options = {}) {
30
31
  ...(streams ? { streams } : {}),
31
32
  ...(readiness ? { readiness } : {}),
32
33
  async close() {
33
- await Promise.allSettled([database?.end({ timeout: 5 }), streams?.close()]);
34
+ await Promise.allSettled([database?.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }), streams?.close()]);
34
35
  },
35
36
  };
36
37
  }
@@ -21,8 +21,6 @@ export interface ReadinessReport {
21
21
  errors: Record<string, string>;
22
22
  }
23
23
  export type Readiness = () => Promise<ReadinessReport>;
24
- /** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
25
- export declare const READINESS_TIMEOUT_MS = 2000;
26
24
  export declare function createReadiness(probes: Readonly<Record<string, Probe>>, options?: {
27
25
  timeoutMs?: number;
28
26
  }): Readiness;
@@ -1,9 +1,10 @@
1
1
  import { probeDatabase } from "./database.js";
2
2
  import { probeExecution } from "./execution.js";
3
- /** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
4
- export const READINESS_TIMEOUT_MS = 2000;
3
+ import { hostTuning } from "../tuning.js";
5
4
  export function createReadiness(probes, options = {}) {
6
- const timeoutMs = options.timeoutMs ?? READINESS_TIMEOUT_MS;
5
+ // The bound on one pass: `host.readinessTimeoutMs` (Host tuning). Compose probes `/ready` every
6
+ // 2 s with a 5 s timeout.
7
+ const timeoutMs = options.timeoutMs ?? hostTuning().host.readinessTimeoutMs;
7
8
  return async () => {
8
9
  const entries = Object.entries(probes);
9
10
  const results = await Promise.all(entries.map(async ([name, probe]) => [name, await runProbe(probe, timeoutMs)]));
@@ -4,6 +4,7 @@ export interface HttpKeysOptions {
4
4
  readonly url: string;
5
5
  /** `NYLORUN_GATES_TOKEN`. */
6
6
  readonly token: string;
7
+ /** Default `keys.timeoutMs` (Host tuning): signing and vault writes are short. */
7
8
  readonly timeoutMs?: number;
8
9
  }
9
10
  export declare function httpKeys(options: HttpKeysOptions): Keys;
@@ -10,12 +10,11 @@ import { HttpError } from "../tenant/http.js";
10
10
  import { VaultError } from "../vault/error.js";
11
11
  import { KEYS_PATH } from "./contract.js";
12
12
  import { KEYS_OPERATIONS } from "./keys.js";
13
- /** Signing and vault writes are short; anything slower is a gateway in trouble. */
14
- const KEYS_TIMEOUT_MS = 30_000;
13
+ import { hostTuning } from "../tuning.js";
15
14
  export function httpKeys(options) {
16
15
  const where = new URL(options.url).origin;
17
16
  function call(operation, args) {
18
- const timeoutMs = options.timeoutMs ?? KEYS_TIMEOUT_MS;
17
+ const timeoutMs = options.timeoutMs ?? hostTuning().keys.timeoutMs;
19
18
  const url = new URL(`${KEYS_PATH}/${operation}`, options.url);
20
19
  const send = url.protocol === "https:" ? httpsRequest : httpRequest;
21
20
  const payload = Buffer.from(JSON.stringify({ args }), "utf8");