@nylorun/runtime 0.26.0-beta → 0.28.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 (379) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +18 -14
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.d.ts +4 -3
  12. package/dist/api/a2a/card.js +4 -4
  13. package/dist/api/a2a/endpoint.js +3 -4
  14. package/dist/api/a2a/protocol.js +4 -0
  15. package/dist/api/a2a/routes.d.ts +2 -6
  16. package/dist/api/a2a/routes.js +30 -25
  17. package/dist/api/ag-ui/endpoint.js +2 -3
  18. package/dist/api/ag-ui/routes.js +8 -11
  19. package/dist/api/components.d.ts +6789 -3405
  20. package/dist/api/components.js +43 -30
  21. package/dist/api/gate/keys.js +7 -4
  22. package/dist/api/gate/routes.d.ts +6 -2
  23. package/dist/api/gate/routes.js +15 -11
  24. package/dist/api/http/app.js +9 -4
  25. package/dist/api/http/body.d.ts +17 -3
  26. package/dist/api/http/body.js +35 -7
  27. package/dist/api/http/respond.js +6 -2
  28. package/dist/api/http/routes/access.js +5 -4
  29. package/dist/api/http/routes/agent-manifests.d.ts +10 -0
  30. package/dist/api/http/routes/agent-manifests.js +135 -0
  31. package/dist/api/http/routes/agents.d.ts +11 -0
  32. package/dist/api/http/routes/agents.js +418 -0
  33. package/dist/api/http/routes/artifacts.js +21 -9
  34. package/dist/api/http/routes/coverage.d.ts +4 -4
  35. package/dist/api/http/routes/coverage.js +13 -7
  36. package/dist/api/http/routes/files.d.ts +2 -1
  37. package/dist/api/http/routes/files.js +31 -5
  38. package/dist/api/http/routes/mcp.js +3 -3
  39. package/dist/api/http/routes/me.d.ts +1 -1
  40. package/dist/api/http/routes/me.js +1 -2
  41. package/dist/api/http/routes/memories.d.ts +9 -0
  42. package/dist/api/http/routes/memories.js +152 -0
  43. package/dist/api/http/routes/reads.d.ts +10 -111
  44. package/dist/api/http/routes/reads.js +51 -29
  45. package/dist/api/http/routes/sessions.js +26 -54
  46. package/dist/api/http/routes/tenant.d.ts +2 -2
  47. package/dist/api/http/routes/tenant.js +77 -24
  48. package/dist/api/http/routes/triggers.d.ts +10 -0
  49. package/dist/api/http/routes/triggers.js +180 -0
  50. package/dist/api/inbound-files.d.ts +3 -9
  51. package/dist/api/inbound-files.js +18 -24
  52. package/dist/api/openapi.js +73 -47
  53. package/dist/artifacts/disk.d.ts +10 -14
  54. package/dist/artifacts/disk.js +99 -95
  55. package/dist/artifacts/files.d.ts +13 -4
  56. package/dist/artifacts/files.js +27 -9
  57. package/dist/artifacts/folder-upload.d.ts +75 -0
  58. package/dist/artifacts/folder-upload.js +263 -0
  59. package/dist/artifacts/folders.d.ts +14 -8
  60. package/dist/artifacts/folders.js +19 -11
  61. package/dist/artifacts/links.js +7 -6
  62. package/dist/artifacts/parts.d.ts +9 -14
  63. package/dist/artifacts/parts.js +11 -9
  64. package/dist/artifacts/pipe.d.ts +15 -0
  65. package/dist/artifacts/pipe.js +71 -0
  66. package/dist/artifacts/quota.d.ts +36 -0
  67. package/dist/artifacts/quota.js +44 -0
  68. package/dist/artifacts/service.d.ts +25 -1
  69. package/dist/artifacts/service.js +72 -27
  70. package/dist/blob/fs.js +2 -1
  71. package/dist/blob/s3.d.ts +6 -2
  72. package/dist/blob/s3.js +9 -7
  73. package/dist/blob/types.d.ts +1 -1
  74. package/dist/blob/types.js +1 -1
  75. package/dist/core/flow-host.d.ts +0 -1
  76. package/dist/core/limits.d.ts +11 -35
  77. package/dist/core/limits.js +5 -73
  78. package/dist/core/sandbox-routes.js +0 -1
  79. package/dist/core/turn-manifest.d.ts +1 -2
  80. package/dist/core/turn-manifest.js +4 -28
  81. package/dist/execution/memory.d.ts +8 -5
  82. package/dist/execution/memory.js +8 -6
  83. package/dist/execution/types.d.ts +1 -6
  84. package/dist/execution/types.js +0 -5
  85. package/dist/gates/contract.d.ts +3 -2
  86. package/dist/gates/contract.js +1 -1
  87. package/dist/gates/egress-credentials.js +5 -4
  88. package/dist/gates/egress.d.ts +13 -13
  89. package/dist/gates/egress.js +19 -21
  90. package/dist/gates/fixture-model.js +13 -2
  91. package/dist/gates/handler.d.ts +1 -3
  92. package/dist/gates/handler.js +4 -3
  93. package/dist/gates/http-client.d.ts +4 -3
  94. package/dist/gates/http-client.js +5 -7
  95. package/dist/gates/http-tool.d.ts +24 -4
  96. package/dist/gates/http-tool.js +41 -13
  97. package/dist/gates/in-process.d.ts +2 -1
  98. package/dist/gates/in-process.js +17 -9
  99. package/dist/gates/inflight.d.ts +2 -2
  100. package/dist/gates/inflight.js +3 -2
  101. package/dist/gates/journal.js +5 -7
  102. package/dist/gates/mcp-handler.d.ts +4 -3
  103. package/dist/gates/mcp-handler.js +15 -5
  104. package/dist/gates/meter.js +6 -5
  105. package/dist/gates/model-gate.d.ts +11 -2
  106. package/dist/gates/policy.d.ts +34 -6
  107. package/dist/gates/policy.js +66 -18
  108. package/dist/gates/provider-gate.d.ts +22 -0
  109. package/dist/gates/provider-gate.js +30 -0
  110. package/dist/gates/tool-calls.d.ts +1 -3
  111. package/dist/gates/tool-calls.js +2 -3
  112. package/dist/gates/tool-client.d.ts +1 -1
  113. package/dist/gates/tool-client.js +5 -6
  114. package/dist/gates/tool-contract.d.ts +8 -5
  115. package/dist/gates/tool-contract.js +1 -1
  116. package/dist/gates/tool-gate.d.ts +18 -7
  117. package/dist/gates/tool-gate.js +49 -7
  118. package/dist/harness/calls.d.ts +27 -25
  119. package/dist/harness/calls.js +40 -46
  120. package/dist/harness/executors.d.ts +10 -3
  121. package/dist/harness/executors.js +53 -23
  122. package/dist/harness/main.js +2 -2
  123. package/dist/harness/pod.d.ts +2 -2
  124. package/dist/harness/pod.js +7 -6
  125. package/dist/harness/run-tokens.d.ts +4 -1
  126. package/dist/harness/run-tokens.js +6 -2
  127. package/dist/harness/service.d.ts +17 -14
  128. package/dist/harness/service.js +21 -35
  129. package/dist/harness/setup.d.ts +0 -2
  130. package/dist/harness/setup.js +21 -15
  131. package/dist/harness/ws-client.d.ts +2 -2
  132. package/dist/harness/ws-client.js +7 -5
  133. package/dist/harness/ws-port.d.ts +3 -4
  134. package/dist/harness/ws-port.js +3 -4
  135. package/dist/harness-api/artifacts.d.ts +20 -0
  136. package/dist/harness-api/artifacts.js +478 -0
  137. package/dist/harness-api/in-process.d.ts +2 -2
  138. package/dist/harness-api/in-process.js +18 -16
  139. package/dist/harness-api/record.d.ts +10 -9
  140. package/dist/harness-api/record.js +113 -157
  141. package/dist/harness-api/renew.d.ts +1 -1
  142. package/dist/harness-api/renew.js +2 -2
  143. package/dist/harness-api/server.d.ts +1 -16
  144. package/dist/harness-api/server.js +86 -73
  145. package/dist/harness-api/start.d.ts +27 -5
  146. package/dist/harness-api/start.js +102 -19
  147. package/dist/harness-api/tool-journal.d.ts +15 -10
  148. package/dist/harness-api/tool-journal.js +26 -24
  149. package/dist/harness-api/workspace.d.ts +0 -4
  150. package/dist/harness-api/workspace.js +4 -47
  151. package/dist/harness-api/ws-server.js +10 -10
  152. package/dist/host/create-host.js +2 -3
  153. package/dist/host/execution.js +2 -3
  154. package/dist/host/gates.d.ts +2 -9
  155. package/dist/host/gates.js +15 -12
  156. package/dist/host/http.d.ts +2 -4
  157. package/dist/host/http.js +5 -29
  158. package/dist/host/main.js +10 -3
  159. package/dist/host/operate.js +12 -3
  160. package/dist/host/stack-config.d.ts +11 -0
  161. package/dist/host/stack-config.js +21 -1
  162. package/dist/index.d.ts +0 -1
  163. package/dist/index.js +0 -1
  164. package/dist/infra/index.js +2 -1
  165. package/dist/infra/readiness.d.ts +0 -2
  166. package/dist/infra/readiness.js +4 -3
  167. package/dist/keys/client.d.ts +1 -0
  168. package/dist/keys/client.js +2 -3
  169. package/dist/keys/contract.d.ts +0 -2
  170. package/dist/keys/contract.js +0 -2
  171. package/dist/keys/egress-ca.d.ts +0 -2
  172. package/dist/keys/egress-ca.js +2 -3
  173. package/dist/keys/keys.d.ts +15 -4
  174. package/dist/keys/keys.js +22 -1
  175. package/dist/keys/webhooks.d.ts +25 -0
  176. package/dist/keys/webhooks.js +62 -0
  177. package/dist/keys/x509.d.ts +8 -4
  178. package/dist/keys/x509.js +14 -5
  179. package/dist/management-openapi.json +3043 -1954
  180. package/dist/mcp/connect.d.ts +35 -26
  181. package/dist/mcp/connect.js +112 -141
  182. package/dist/mcp/pool.d.ts +27 -22
  183. package/dist/mcp/pool.js +34 -48
  184. package/dist/mcp/preview.d.ts +12 -5
  185. package/dist/mcp/preview.js +43 -26
  186. package/dist/mcp/snapshot.d.ts +35 -86
  187. package/dist/mcp/snapshot.js +93 -135
  188. package/dist/memory/service.d.ts +97 -0
  189. package/dist/memory/service.js +335 -0
  190. package/dist/memory/tool.d.ts +36 -0
  191. package/dist/memory/tool.js +84 -0
  192. package/dist/model/auth-store.js +5 -3
  193. package/dist/model/capabilities.d.ts +1 -1
  194. package/dist/model/catalog.d.ts +9 -36
  195. package/dist/model/catalog.js +6 -33
  196. package/dist/model/descriptor.d.ts +51 -0
  197. package/dist/model/descriptor.js +83 -0
  198. package/dist/model/models.d.ts +0 -3
  199. package/dist/model/models.js +3 -5
  200. package/dist/model/native-blocks.js +1 -1
  201. package/dist/model/pi-model.d.ts +16 -5
  202. package/dist/model/pi-model.js +86 -105
  203. package/dist/openapi.json +13653 -9986
  204. package/dist/reads/cursor.js +3 -1
  205. package/dist/reads/types.d.ts +3 -3
  206. package/dist/record/agent.d.ts +27 -0
  207. package/dist/record/agent.js +46 -0
  208. package/dist/record/append.d.ts +34 -2
  209. package/dist/record/append.js +50 -1
  210. package/dist/record/index.d.ts +2 -2
  211. package/dist/record/index.js +2 -2
  212. package/dist/redact.d.ts +1 -1
  213. package/dist/redact.js +3 -2
  214. package/dist/sandbox/egress-token.d.ts +3 -3
  215. package/dist/sandbox/egress-token.js +3 -3
  216. package/dist/sandbox/join.js +8 -7
  217. package/dist/sandbox/manager.d.ts +41 -41
  218. package/dist/sandbox/manager.js +112 -44
  219. package/dist/sandbox/placement.d.ts +2 -2
  220. package/dist/sandbox/placement.js +7 -10
  221. package/dist/sandbox/pods/client.d.ts +1 -10
  222. package/dist/sandbox/pods/client.js +8 -4
  223. package/dist/sandbox/pods/lifecycle.d.ts +25 -17
  224. package/dist/sandbox/pods/lifecycle.js +51 -29
  225. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  226. package/dist/sandbox/pods/network-gate.js +7 -7
  227. package/dist/sandbox/pods/reconcile.js +14 -3
  228. package/dist/sandbox/pods/spec.js +8 -5
  229. package/dist/sandbox/policy.d.ts +7 -1
  230. package/dist/sandbox/policy.js +10 -3
  231. package/dist/sandbox/resolve.d.ts +11 -6
  232. package/dist/sandbox/resolve.js +19 -9
  233. package/dist/sandbox/setup.d.ts +6 -2
  234. package/dist/sandbox/setup.js +47 -14
  235. package/dist/sandbox/share.d.ts +5 -14
  236. package/dist/sandbox/share.js +5 -34
  237. package/dist/sandbox/skills.d.ts +7 -13
  238. package/dist/sandbox/skills.js +16 -31
  239. package/dist/sandbox/tenant-config.d.ts +2 -2
  240. package/dist/sandbox/tenant-config.js +4 -3
  241. package/dist/sandbox/tools.d.ts +7 -1
  242. package/dist/sandbox/tools.js +26 -26
  243. package/dist/sandbox/types.d.ts +10 -3
  244. package/dist/sandbox/types.js +9 -0
  245. package/dist/store/postgres/connect.d.ts +6 -5
  246. package/dist/store/postgres/connect.js +14 -11
  247. package/dist/store/postgres/control.js +13 -13
  248. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  249. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  250. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  251. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  252. package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
  253. package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
  254. package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
  255. package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
  256. package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
  257. package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
  258. package/dist/store/postgres/reads.js +17 -82
  259. package/dist/store/postgres/record-writer.d.ts +4 -4
  260. package/dist/store/postgres/record-writer.js +9 -9
  261. package/dist/store/postgres/schema.d.ts +1382 -151
  262. package/dist/store/postgres/schema.js +191 -37
  263. package/dist/store/postgres/store.js +384 -105
  264. package/dist/store/postgres/tenant.js +1 -2
  265. package/dist/store/types.d.ts +172 -57
  266. package/dist/store/types.js +0 -1
  267. package/dist/streams/relay/core.d.ts +3 -3
  268. package/dist/streams/relay/core.js +9 -10
  269. package/dist/tenant/advance.js +41 -39
  270. package/dist/tenant/agent-manifests.d.ts +72 -0
  271. package/dist/tenant/agent-manifests.js +359 -0
  272. package/dist/tenant/agent-versions.d.ts +126 -0
  273. package/dist/tenant/agent-versions.js +389 -0
  274. package/dist/tenant/agents.d.ts +230 -0
  275. package/dist/tenant/{environments.js → agents.js} +493 -328
  276. package/dist/tenant/auth.d.ts +1 -1
  277. package/dist/tenant/auth.js +3 -3
  278. package/dist/tenant/commands.d.ts +63 -2
  279. package/dist/tenant/commands.js +240 -90
  280. package/dist/tenant/context.d.ts +57 -33
  281. package/dist/tenant/context.js +6 -2
  282. package/dist/tenant/definition-files.d.ts +25 -4
  283. package/dist/tenant/definition-files.js +85 -47
  284. package/dist/tenant/effects.js +15 -51
  285. package/dist/tenant/ephemeral.js +2 -1
  286. package/dist/tenant/history.d.ts +1 -1
  287. package/dist/tenant/history.js +1 -1
  288. package/dist/tenant/host-token.d.ts +3 -3
  289. package/dist/tenant/host-token.js +11 -10
  290. package/dist/tenant/http.d.ts +5 -8
  291. package/dist/tenant/http.js +9 -0
  292. package/dist/tenant/identity-file.d.ts +29 -6
  293. package/dist/tenant/identity-file.js +45 -15
  294. package/dist/tenant/issuers.d.ts +5 -5
  295. package/dist/tenant/issuers.js +34 -26
  296. package/dist/tenant/jwt.d.ts +8 -2
  297. package/dist/tenant/jwt.js +10 -2
  298. package/dist/tenant/limits.d.ts +87 -0
  299. package/dist/tenant/limits.js +172 -0
  300. package/dist/tenant/memories.d.ts +38 -0
  301. package/dist/tenant/memories.js +119 -0
  302. package/dist/tenant/module.d.ts +4 -1
  303. package/dist/tenant/module.js +9 -8
  304. package/dist/tenant/outbound.d.ts +11 -14
  305. package/dist/tenant/outbound.js +10 -15
  306. package/dist/tenant/reset.js +2 -2
  307. package/dist/tenant/run-grants.d.ts +6 -0
  308. package/dist/tenant/run-grants.js +7 -2
  309. package/dist/tenant/run-token.d.ts +0 -4
  310. package/dist/tenant/run-token.js +6 -11
  311. package/dist/tenant/runtime-limits.d.ts +24 -0
  312. package/dist/tenant/runtime-limits.js +27 -0
  313. package/dist/tenant/runtime.d.ts +1 -1
  314. package/dist/tenant/runtime.js +38 -43
  315. package/dist/tenant/session-streams.js +7 -6
  316. package/dist/tenant/session.d.ts +1 -1
  317. package/dist/tenant/session.js +1 -1
  318. package/dist/tenant/sessions.d.ts +28 -45
  319. package/dist/tenant/sessions.js +89 -209
  320. package/dist/tenant/signing-keys.d.ts +1 -1
  321. package/dist/tenant/signing-keys.js +5 -6
  322. package/dist/tenant/status.js +10 -10
  323. package/dist/tenant/streams.js +18 -20
  324. package/dist/tenant/sweep.d.ts +1 -21
  325. package/dist/tenant/sweep.js +51 -51
  326. package/dist/tenant/transcript.d.ts +70 -23
  327. package/dist/tenant/transcript.js +28 -22
  328. package/dist/tenant/triggers.d.ts +143 -0
  329. package/dist/tenant/triggers.js +715 -0
  330. package/dist/tenant/turn-timing.d.ts +11 -1
  331. package/dist/tenant/turn-timing.js +3 -1
  332. package/dist/tenant/types.d.ts +9 -14
  333. package/dist/tenant/worker.d.ts +4 -10
  334. package/dist/tenant/worker.js +10 -14
  335. package/dist/traces/ids.js +5 -1
  336. package/dist/traces/map.d.ts +10 -7
  337. package/dist/traces/map.js +16 -12
  338. package/dist/traces/otlp.d.ts +3 -3
  339. package/dist/traces/otlp.js +5 -3
  340. package/dist/traces/projector.d.ts +9 -4
  341. package/dist/traces/projector.js +13 -7
  342. package/dist/traces/read.js +6 -1
  343. package/dist/tuning.d.ts +390 -0
  344. package/dist/tuning.js +677 -0
  345. package/dist/vault/headers.js +4 -2
  346. package/dist/vault/service.d.ts +13 -8
  347. package/dist/vault/service.js +61 -17
  348. package/dist/version.d.ts +1 -1
  349. package/dist/version.js +1 -1
  350. package/package.json +13 -12
  351. package/dist/api/http/routes/environment-manifests.d.ts +0 -9
  352. package/dist/api/http/routes/environment-manifests.js +0 -118
  353. package/dist/api/http/routes/environments.d.ts +0 -11
  354. package/dist/api/http/routes/environments.js +0 -332
  355. package/dist/artifacts/export.d.ts +0 -8
  356. package/dist/artifacts/export.js +0 -204
  357. package/dist/artifacts/media-types.d.ts +0 -30
  358. package/dist/artifacts/media-types.js +0 -137
  359. package/dist/artifacts/workspace.d.ts +0 -60
  360. package/dist/artifacts/workspace.js +0 -45
  361. package/dist/media.d.ts +0 -10
  362. package/dist/media.js +0 -58
  363. package/dist/record/environment.d.ts +0 -25
  364. package/dist/record/environment.js +0 -44
  365. package/dist/sandbox/runtime-tools.d.ts +0 -9
  366. package/dist/sandbox/runtime-tools.js +0 -16
  367. package/dist/sandbox/session-sandbox.d.ts +0 -16
  368. package/dist/sandbox/session-sandbox.js +0 -77
  369. package/dist/tenant/artifact-tool.d.ts +0 -13
  370. package/dist/tenant/artifact-tool.js +0 -243
  371. package/dist/tenant/environment-manifests.d.ts +0 -57
  372. package/dist/tenant/environment-manifests.js +0 -200
  373. package/dist/tenant/environments.d.ts +0 -174
  374. package/dist/tenant/skill-tool.d.ts +0 -20
  375. package/dist/tenant/skill-tool.js +0 -67
  376. package/dist/tenant/tool-results.d.ts +0 -57
  377. package/dist/tenant/tool-results.js +0 -298
  378. package/dist/tenant/tool-search.d.ts +0 -39
  379. package/dist/tenant/tool-search.js +0 -103
@@ -1,332 +0,0 @@
1
- import { z } from "zod";
2
- import { ENVIRONMENT_EVENT_TYPES, ForkEnvironmentRequestSchema, isEnvironmentId, PutEnvironmentRequestSchema, RestoreEnvironmentRequestSchema, } from "@nylorun/core/contracts";
3
- import { deleteEnvironmentVersion, environmentVersions, restoreEnvironment, snapshotEnvironment, } from "../../../artifacts/disk.js";
4
- import { DeleteEnvironmentResponse, DeletedResponse, EnvironmentPage, EnvironmentRestore, EnvironmentSnapshot, EnvironmentVersions, ForkEnvironmentRequest, ForkEnvironmentResponse, RestoreEnvironmentRequest, EnvironmentSecretView, EnvironmentView, ListEnvironmentEventsResponse, ListEnvironmentSecretsResponse, ListEnvironmentsResponse, PutEnvironmentRequest, PutEnvironmentSecretRequest, PutEnvironmentSecretValueRequest, } from "../../components.js";
5
- import { fail } from "../../../tenant/http.js";
6
- import { actorOf, assertReaches, deleteEnvironment, environmentEventsOf, forkEnvironment, getEnvironment, listEnvironments, putEnvironment, resetEnvironment, secretRequestFor, stopEnvironment, } from "../../../tenant/environments.js";
7
- import { readJson } from "../body.js";
8
- import { tenantRoute } from "../define.js";
9
- import { environmentPage, pageQuery } from "./reads.js";
10
- import { jsonResponse } from "../respond.js";
11
- /** Reading an Environment: any caller that may use sessions, or change Environments. */
12
- const READ = {
13
- credentials: ["application", "subject", "token"],
14
- scopes: ["sessions:own", "environments:write"],
15
- };
16
- /** Creating, changing or deleting one, and writing its secrets. */
17
- const WRITE = {
18
- credentials: ["application", "subject", "token"],
19
- scopes: ["environments:write"],
20
- };
21
- const json = (schema, description) => ({
22
- description,
23
- content: { "application/json": { schema } },
24
- });
25
- const environmentId = z.object({
26
- environmentId: z
27
- .string()
28
- .meta({ description: "The Environment id, percent-encoded: `users%2Falice%2Fweb` for `users/alice/web`" }),
29
- });
30
- const secretPath = environmentId.extend({
31
- name: z.string().meta({
32
- description: "The secret's name: the variable for `environment_secret` and `environment_variable`, a label for `bearer` and `headers`",
33
- }),
34
- });
35
- /** The path's Environment id, or a 400 naming the rule. */
36
- function idOf(value) {
37
- if (!isEnvironmentId(value))
38
- return fail(400, "An Environment id is up to 200 characters: /-separated segments of letters, digits, '.', '_' and '-'");
39
- return value;
40
- }
41
- /** `label=k=v`, repeated: every one must match. */
42
- function labelsOf(values) {
43
- const labels = {};
44
- for (const value of values ?? []) {
45
- const at = value.indexOf("=");
46
- if (at <= 0)
47
- fail(400, `label must be key=value, not ${value}`);
48
- labels[value.slice(0, at)] = value.slice(at + 1);
49
- }
50
- return labels;
51
- }
52
- export function environmentRoutes(api) {
53
- tenantRoute(api, READ, {
54
- method: "get",
55
- path: "/v1/environments",
56
- tags: ["Environments"],
57
- summary: "List Environments",
58
- description: "Every Environment with all the labels asked for. A token caller sees only the Environments its grants reach.",
59
- request: {
60
- query: pageQuery.extend({
61
- label: z
62
- .union([z.string(), z.array(z.string())])
63
- .optional()
64
- .meta({ description: "`key=value`; repeat it to require several labels" }),
65
- }),
66
- },
67
- responses: {
68
- 200: json(z.union([ListEnvironmentsResponse, EnvironmentPage]), "The Environments; limit opts into pagination"),
69
- },
70
- }, async (c) => jsonResponse(200, c.req.query("limit") !== undefined
71
- ? await environmentPage(c.env.tenant, c.get("scope"), c.req.query(), labelsOf(c.req.queries("label")))
72
- : await listEnvironments(c.env.tenant, labelsOf(c.req.queries("label")), c.get("scope"))));
73
- tenantRoute(api, WRITE, {
74
- method: "put",
75
- path: "/v1/environments/{environmentId}",
76
- tags: ["Environments"],
77
- summary: "Create, find or change an Environment",
78
- description: "Creates the Environment and its vault within the Tenant's limits, or answers the one with this id, so get-or-create is one call. It is made from one version of an environment manifest (`manifest: \"name\"` takes the latest, `\"name@n\"` version n) or from the inline fields, and never changes what it runs after that (protocol 14, D63): on an existing one, labels replace, `ownerUserId` sets or (with `null`) clears the owner guard, fields that match what it runs are accepted, and anything else is `409 environment_immutable`, which names the fork that makes a new Environment instead. Kind `pod` needs sandbox pods (`nylorun local sandbox enable`) and is created at once.",
79
- request: {
80
- params: environmentId,
81
- body: { required: true, content: { "application/json": { schema: PutEnvironmentRequest } } },
82
- },
83
- responses: {
84
- 200: json(EnvironmentView, "The Environment"),
85
- 409: {
86
- description: "An Environment with this id runs something else (`environment_immutable`), the Tenant holds as many Environments as it allows (`limit_exceeded`), kind pod without sandbox pods (`environment_unavailable`), or it is being deleted (`environment_busy`)",
87
- },
88
- },
89
- }, async (c) => {
90
- const id = idOf(c.req.param("environmentId"));
91
- const body = PutEnvironmentRequestSchema.parse(await readJson(c.req.raw));
92
- return jsonResponse(200, await putEnvironment(c.env.tenant, id, body, c.get("scope")));
93
- });
94
- tenantRoute(api, READ, {
95
- method: "get",
96
- path: "/v1/environments/{environmentId}",
97
- tags: ["Environments"],
98
- summary: "Get an Environment",
99
- description: "Its recipe, versions, labels, owner guard, state and the sessions that name it (acting for a person, only theirs).",
100
- request: { params: environmentId },
101
- responses: { 200: json(EnvironmentView, "The Environment") },
102
- }, async (c) => jsonResponse(200, await getEnvironment(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"))));
103
- tenantRoute(api, READ, {
104
- method: "get",
105
- path: "/v1/environments/{environmentId}/events",
106
- tags: ["Environments"],
107
- summary: "Read an Environment's events",
108
- description: "`environment.created`, `.attached`, `.detached`, `.deleted`, a pod's `.running`, `.suspended`, `.relaunched`, `.lost`, `.reset` and `.failed`, and `environment.egress` for every connection its sandbox opens through egress-gate, naming the turns that were running (D67), in order, numbered from 0, at most 1000 per call.",
109
- request: {
110
- params: environmentId,
111
- query: z.object({
112
- from: z.string().optional().meta({ description: "The first `seq` to return" }),
113
- types: z.string().optional().meta({ description: "Only these event types, comma-separated (`environment.egress`)" }),
114
- since: z.string().optional().meta({ description: "Only events recorded at or after this ISO time" }),
115
- until: z.string().optional().meta({ description: "Only events recorded before this ISO time" }),
116
- }),
117
- },
118
- responses: { 200: json(ListEnvironmentEventsResponse, "The events") },
119
- }, async (c) => {
120
- const from = c.req.query("from");
121
- if (from !== undefined && !/^\d+$/.test(from))
122
- fail(400, "from must be a seq");
123
- const types = c.req.query("types")?.split(",").map((type) => type.trim()).filter(Boolean);
124
- const unknown = types?.find((type) => !ENVIRONMENT_EVENT_TYPES.includes(type));
125
- if (unknown !== undefined)
126
- fail(400, `types: ${unknown} is not an Environment event type`);
127
- const time = (name) => {
128
- const value = c.req.query(name);
129
- if (value === undefined)
130
- return undefined;
131
- const at = Date.parse(value);
132
- if (!Number.isFinite(at))
133
- fail(400, `${name} must be an ISO time`);
134
- return new Date(at).toISOString();
135
- };
136
- const since = time("since");
137
- const until = time("until");
138
- return jsonResponse(200, await environmentEventsOf(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"), {
139
- ...(from === undefined ? {} : { fromSeq: Number(from) }),
140
- ...(types === undefined || types.length === 0 ? {} : { types }),
141
- ...(since === undefined ? {} : { since }),
142
- ...(until === undefined ? {} : { until }),
143
- }));
144
- });
145
- tenantRoute(api, WRITE, {
146
- method: "delete",
147
- path: "/v1/environments/{environmentId}",
148
- tags: ["Environments"],
149
- summary: "Delete an Environment",
150
- description: "Deletes the Environment, its vault with every secret, and its files. Sessions that name it keep its id; their next turn is refused (`environment_unavailable`) until an Environment with this id exists again.",
151
- request: { params: environmentId },
152
- responses: {
153
- 200: json(DeleteEnvironmentResponse, "Deleted, or there was none"),
154
- 409: { description: "A session that names it has a turn running (`environment_busy`)" },
155
- },
156
- }, async (c) => jsonResponse(200, await deleteEnvironment(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"))));
157
- for (const [action, summary, description, run] of [
158
- [
159
- "stop",
160
- "Stop a pod Environment",
161
- "Suspends the pod: its volume is kept, and the next turn of a session that names it starts it again. Refused during a turn (`environment_busy`).",
162
- stopEnvironment,
163
- ],
164
- [
165
- "reset",
166
- "Reset a pod Environment",
167
- "A new pod on a new, empty volume with the recipe applied; the old pod and volume are deleted. The way out of `environment_lost`. Refused during a turn (`environment_busy`).",
168
- resetEnvironment,
169
- ],
170
- ])
171
- tenantRoute(api, WRITE, {
172
- method: "post",
173
- path: `/v1/environments/{environmentId}/${action}`,
174
- tags: ["Environments"],
175
- summary,
176
- description,
177
- request: { params: environmentId },
178
- responses: {
179
- 200: json(EnvironmentView, "The Environment"),
180
- 400: { description: "The Environment is not of kind pod" },
181
- 409: {
182
- description: "A turn holds it (`environment_busy`), or no sandbox pods (`environment_unavailable`)",
183
- },
184
- },
185
- }, async (c) => jsonResponse(200, await run(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"))));
186
- tenantRoute(api, WRITE, {
187
- method: "post",
188
- path: "/v1/environments/{environmentId}/snapshot",
189
- tags: ["Environments"],
190
- summary: "Snapshot an Environment's disk",
191
- description: "Between turns, a new version of the Environment's disk (`/workspace`, and a pod's `/harness` but the engine's own files): files with their modes and modification times, links and empty directories, as a version of the folder artifact the Environment owns. Unchanged files are stored once; an unchanged disk makes no version (`unchanged`). A stopped pod is started first. Refused during a turn or another copy (`environment_busy`).",
192
- request: { params: environmentId },
193
- responses: {
194
- 201: json(EnvironmentSnapshot, "The version"),
195
- 400: { description: "The Environment is of kind none" },
196
- 409: { description: "A turn or another copy holds the disk (`environment_busy`), or the pod could not start (`environment_unavailable`)" },
197
- 413: { description: "Past the entries, bytes or Tenant total a version may hold (`limit_exceeded`)" },
198
- },
199
- }, async (c) => jsonResponse(201, await snapshotEnvironment(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"))));
200
- tenantRoute(api, WRITE, {
201
- method: "post",
202
- path: "/v1/environments/{environmentId}/restore",
203
- tags: ["Environments"],
204
- summary: "Restore a version of an Environment's disk",
205
- description: "Puts disk version `version` (or `latest`) back. A virtual Environment's workspace is rewritten at once (`done`); a pod gets a new volume seeded from the version before any turn (`pending`, with `disk.operation` in the view until it ends). The Environment keeps its id, vault and sessions. Refused during a turn or another copy (`environment_busy`).",
206
- request: {
207
- params: environmentId,
208
- body: { required: true, content: { "application/json": { schema: RestoreEnvironmentRequest } } },
209
- },
210
- responses: {
211
- 200: json(EnvironmentRestore, "Restored, or being restored"),
212
- 404: { description: "No such Environment, or no such version" },
213
- 409: { description: "A turn or another copy holds the disk (`environment_busy`)" },
214
- },
215
- }, async (c) => {
216
- const body = RestoreEnvironmentRequestSchema.parse(await readJson(c.req.raw));
217
- return jsonResponse(200, await restoreEnvironment(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"), body.version));
218
- });
219
- tenantRoute(api, WRITE, {
220
- method: "post",
221
- path: "/v1/environments/{environmentId}/fork",
222
- tags: ["Environments"],
223
- summary: "Fork an Environment",
224
- description: "How an Environment gets a new manifest version (Q15): a new Environment `id`, made from `manifest` (default the source's manifest at its latest version), its disk started from a version of this one's (`disk`: `latest`, the default, takes a snapshot first; a version number; or `empty`) and its secrets copied inside the Runtime (`secrets`: `copy`, the default, copies those the new manifest declares; `none`). On a fork onto other setup the new manifest's setup runs on the copied disk before its first turn. This Environment keeps what it runs, its disk and its sessions. `dryRun` answers the plan and makes nothing.",
225
- request: {
226
- params: environmentId,
227
- body: { required: true, content: { "application/json": { schema: ForkEnvironmentRequest } } },
228
- },
229
- responses: {
230
- 200: json(ForkEnvironmentResponse, "The plan (`dryRun`)"),
231
- 201: json(ForkEnvironmentResponse, "The new Environment, and the plan it ran"),
232
- 400: { description: "The source was made from inline fields and no manifest is named, or the manifest does not resolve" },
233
- 404: { description: "No such Environment, manifest version (`manifest_not_found`) or disk version" },
234
- 409: { description: "The id exists, or a turn or another copy holds the source's disk (`environment_busy`)" },
235
- },
236
- }, async (c) => {
237
- const body = ForkEnvironmentRequestSchema.parse(await readJson(c.req.raw));
238
- const answer = await forkEnvironment(c.env.tenant, idOf(c.req.param("environmentId")), body, c.get("scope"));
239
- return jsonResponse("dryRun" in answer ? 200 : 201, answer);
240
- });
241
- tenantRoute(api, READ, {
242
- method: "get",
243
- path: "/v1/environments/{environmentId}/versions",
244
- tags: ["Environments"],
245
- summary: "List an Environment's disk versions",
246
- description: "Newest first. Read a version's tree, files, diff and zip through `/v1/artifacts/{artifactId}/versions/{version}/…`.",
247
- request: { params: environmentId },
248
- responses: { 200: json(EnvironmentVersions, "The versions") },
249
- }, async (c) => jsonResponse(200, await environmentVersions(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"))));
250
- tenantRoute(api, WRITE, {
251
- method: "delete",
252
- path: "/v1/environments/{environmentId}/versions/{version}",
253
- tags: ["Environments"],
254
- summary: "Delete an Environment's disk version",
255
- description: "Deletes the version, and the stored files no other version names. Refused while a seed reads it.",
256
- request: { params: environmentId.extend({ version: z.string() }) },
257
- responses: {
258
- 200: json(z.object({ environmentId: z.string(), version: z.number(), deleted: z.boolean() }), "Deleted, or there was none"),
259
- 409: { description: "A new volume is being seeded from it (`environment_busy`)" },
260
- },
261
- }, async (c) => {
262
- const version = c.req.param("version") ?? "";
263
- if (!/^[1-9]\d{0,8}$/.test(version))
264
- fail(400, "version must be a version number");
265
- return jsonResponse(200, await deleteEnvironmentVersion(c.env.tenant, idOf(c.req.param("environmentId")), c.get("scope"), Number(version)));
266
- });
267
- tenantRoute(api, READ, {
268
- method: "get",
269
- path: "/v1/environments/{environmentId}/secrets",
270
- tags: ["Environments"],
271
- summary: "List an Environment's secrets",
272
- description: "Each secret's name, type and binding (its URL, or its hosts and header). Never a value.",
273
- request: { params: environmentId },
274
- responses: { 200: json(ListEnvironmentSecretsResponse, "The secrets") },
275
- }, async (c) => {
276
- const id = idOf(c.req.param("environmentId"));
277
- assertReaches(c.get("scope"), id);
278
- return jsonResponse(200, { secrets: await c.env.tenant.vault.listEnvironmentSecrets(id) });
279
- });
280
- tenantRoute(api, READ, {
281
- method: "get",
282
- path: "/v1/environments/{environmentId}/secrets/{name}",
283
- tags: ["Environments"],
284
- summary: "Get an Environment's secret",
285
- description: "Its name, type and binding. Never its value.",
286
- request: { params: secretPath },
287
- responses: { 200: json(EnvironmentSecretView, "The secret") },
288
- }, async (c) => {
289
- const id = idOf(c.req.param("environmentId"));
290
- assertReaches(c.get("scope"), id);
291
- return jsonResponse(200, await c.env.tenant.vault.getEnvironmentSecret(id, c.req.param("name") ?? ""));
292
- });
293
- tenantRoute(api, WRITE, {
294
- method: "put",
295
- path: "/v1/environments/{environmentId}/secrets/{name}",
296
- tags: ["Environments"],
297
- summary: "Write an Environment's secret",
298
- description: "Creates or replaces one credential of the Environment's vault: an `environment_secret` (the variable `name` reads the sentinel `nylorun-managed` in the sandbox, and egress-gate sets the value as a header on requests to `allowedHosts`), an `environment_variable` (a plain variable), or a `bearer` token or `headers` map for the MCP servers and HTTP tools at `url`. The same name and type re-seals the value and binding; another type replaces it. A rotated value is used from the next request. A second credential for a host or a URL is `409 credential_conflict`. When what the Environment runs declares secret slots (protocol 14, D64), send only the value (`{ value }`, or `{ headers }` for a `headers` slot): the type and binding come from the slot, and a name it does not declare is `409 secret_not_in_manifest`.",
299
- request: {
300
- params: secretPath,
301
- body: {
302
- required: true,
303
- content: { "application/json": { schema: z.union([PutEnvironmentSecretRequest, PutEnvironmentSecretValueRequest]) } },
304
- },
305
- },
306
- responses: {
307
- 200: json(EnvironmentSecretView, "The secret, without its value"),
308
- 409: { description: "Another credential is bound to one of its hosts or to its URL (`credential_conflict`)" },
309
- },
310
- }, async (c) => {
311
- const id = idOf(c.req.param("environmentId"));
312
- const scope = c.get("scope");
313
- assertReaches(scope, id);
314
- const name = c.req.param("name") ?? "";
315
- const body = await secretRequestFor(c.env.tenant, id, name, await readJson(c.req.raw));
316
- return jsonResponse(200, await c.env.tenant.keys.putEnvironmentSecret(id, name, body, actorOf(scope)));
317
- });
318
- tenantRoute(api, WRITE, {
319
- method: "delete",
320
- path: "/v1/environments/{environmentId}/secrets/{name}",
321
- tags: ["Environments"],
322
- summary: "Delete an Environment's secret",
323
- description: "The next request that would have used it gets none.",
324
- request: { params: secretPath },
325
- responses: { 200: json(DeletedResponse, "Deleted") },
326
- }, async (c) => {
327
- const id = idOf(c.req.param("environmentId"));
328
- const scope = c.get("scope");
329
- assertReaches(scope, id);
330
- return jsonResponse(200, await c.env.tenant.vault.deleteEnvironmentSecret(id, c.req.param("name") ?? "", actorOf(scope)));
331
- });
332
- }
@@ -1,8 +0,0 @@
1
- import type { TenantContext } from "../tenant/context.js";
2
- /** The directory every sandbox's outputs are exported from. */
3
- export declare const OUTPUTS_DIR = "/workspace/outputs";
4
- /**
5
- * Exports the outputs of turn `turnId` of session `sessionId`, if it completed and its sandbox
6
- * has any. Never throws.
7
- */
8
- export declare function exportOutputs(ctx: TenantContext, sessionId: string, turnId: string | null, signal: AbortSignal): Promise<void>;
@@ -1,204 +0,0 @@
1
- /**
2
- * The turn-end export (F8.2): when an agent's turn completes, core reads `/workspace/outputs` of
3
- * the session's sandbox through the `WorkspaceReader` seam and keeps it as a version of the
4
- * session's folder artifact `outputs` (the first export creates it; later turns add versions,
5
- * and a turn whose outputs did not change adds none).
6
- *
7
- * - **After the turn.** The advance calls `exportOutputs` once `settle` has committed the turn,
8
- * outside its transaction and while it still holds the session's lease, so the next turn
9
- * cannot change the workspace under it. Nothing here fails the turn: a sandbox or store
10
- * failure is logged and recorded as `artifact.export.failed`.
11
- * - **Claimed.** The listing and the bytes are what the holder of the workspace supplied: the
12
- * version's source is `export` and its event says `claimed: true`.
13
- * - **Bounded.** At most `EXPORT_MAX_FILES` files and `EXPORT_MAX_BYTES` bytes per export, each
14
- * file within the Tenant's per-file limit, and the new bytes within its total; past any of
15
- * them nothing is stored and `artifact.export.skipped` says why.
16
- * - **Deduplicated.** Each file is stored content-addressed (`folders.ts`); a file the store
17
- * already holds is not stored again.
18
- *
19
- * No sandbox, a sandbox never created, or an empty or missing `/workspace/outputs` exports
20
- * nothing and records nothing. The package's declared outputs are left for later: the manifest
21
- * has no `outputs` field yet.
22
- */
23
- import { posix } from "node:path";
24
- import { EXPORT_MAX_BYTES, EXPORT_MAX_FILES, OUTPUTS_ARTIFACT_NAME, } from "@nylorun/core/contracts";
25
- import { SANDBOX_WORKSPACE } from "@nylorun/core/define";
26
- import { isWorkflowManifest } from "../core/flow-host.js";
27
- import { sandboxWorkspaceOf } from "../sandbox/share.js";
28
- import { SandboxFileTooLargeError } from "../sandbox/types.js";
29
- import { readArtifactLimits } from "./config.js";
30
- import { collectContent, commitFolderVersion, contentEpoch, contentKey, encodeManifest, FolderCommitRefused, sha256Hex, } from "./folders.js";
31
- import { contentTypeFor } from "./media-types.js";
32
- /** The directory every sandbox's outputs are exported from. */
33
- export const OUTPUTS_DIR = `${SANDBOX_WORKSPACE}/outputs`;
34
- /** Stops the export with `artifact.export.skipped`. */
35
- class Skip extends Error {
36
- reason;
37
- limit;
38
- path;
39
- constructor(reason, message, limit, path) {
40
- super(message);
41
- this.reason = reason;
42
- this.limit = limit;
43
- this.path = path;
44
- }
45
- }
46
- /**
47
- * Exports the outputs of turn `turnId` of session `sessionId`, if it completed and its sandbox
48
- * has any. Never throws.
49
- */
50
- export async function exportOutputs(ctx, sessionId, turnId, signal) {
51
- try {
52
- await runExport(ctx, sessionId, turnId, signal);
53
- }
54
- catch (error) {
55
- const message = error instanceof Error ? error.message : String(error);
56
- if (signal.aborted) {
57
- ctx.config.logger.info("outputs export stopped", { sessionId, message });
58
- return;
59
- }
60
- const skip = error instanceof Skip
61
- ? error
62
- : error instanceof FolderCommitRefused && error.reason === "tenant_total"
63
- ? new Skip("tenant_total", error.message, error.limit)
64
- : undefined;
65
- if (!skip)
66
- ctx.config.logger.warn("outputs export failed", { sessionId, message });
67
- try {
68
- await ctx.store.tx(async (t) => {
69
- if (skip)
70
- await t.event(sessionId, turnId, "artifact.export.skipped", {
71
- name: OUTPUTS_ARTIFACT_NAME,
72
- reason: skip.reason,
73
- message: skip.message,
74
- ...(skip.limit === undefined ? {} : { limit: skip.limit }),
75
- ...(skip.path === undefined ? {} : { path: skip.path }),
76
- });
77
- else
78
- await t.event(sessionId, turnId, "artifact.export.failed", {
79
- name: OUTPUTS_ARTIFACT_NAME,
80
- message,
81
- });
82
- });
83
- }
84
- catch {
85
- /* the session is gone (reset) or the store closed */
86
- }
87
- }
88
- }
89
- async function runExport(ctx, sessionId, turnId, signal) {
90
- const found = await ctx.store.tx(async (t) => {
91
- const s = await t.get("sessions", sessionId);
92
- // Only a turn that completed, and only an agent's (a flow agent's sandbox is its agents').
93
- if (!s || s.status !== "completed" || s.lastTurnId !== turnId || isWorkflowManifest(s.manifest))
94
- return undefined;
95
- const workspace = sandboxWorkspaceOf(s);
96
- const prior = (await t.listArtifacts({ sessionId })).find((row) => row.kind === "folder" && row.name === OUTPUTS_ARTIFACT_NAME);
97
- const latest = prior ? await t.artifactVersion(prior.id, prior.latestVersion) : undefined;
98
- return {
99
- session: {
100
- sessionId,
101
- turnId,
102
- ownerId: workspace.ownerId,
103
- ...(workspace.environmentId === undefined ? {} : { environmentId: workspace.environmentId }),
104
- manifest: s.manifest,
105
- },
106
- limits: await readArtifactLimits(t),
107
- priorId: prior?.id,
108
- latestSha: latest?.sha256,
109
- epoch: await contentEpoch(t),
110
- };
111
- });
112
- if (!found)
113
- return;
114
- const { session, limits } = found;
115
- const listing = await ctx.workspaces.list(session, OUTPUTS_DIR, {
116
- maxEntries: EXPORT_MAX_FILES,
117
- signal,
118
- });
119
- if (!listing || listing.entries.length === 0)
120
- return;
121
- if (listing.truncated)
122
- throw new Skip("too_many_files", `${OUTPUTS_DIR} holds more than ${EXPORT_MAX_FILES} files`, EXPORT_MAX_FILES);
123
- const large = listing.entries.find((entry) => entry.size > limits.fileBytes);
124
- if (large)
125
- throw tooLargeFile(large.path, large.size, limits.fileBytes);
126
- const maxBytes = Math.min(EXPORT_MAX_BYTES, limits.totalBytes);
127
- const listed = listing.entries.reduce((sum, entry) => sum + entry.size, 0);
128
- if (listed > maxBytes)
129
- throw tooLarge(listed, maxBytes);
130
- // The files this export stored: removed again if it commits nothing, unless another version
131
- // names them by then.
132
- const put = [];
133
- try {
134
- await storeAndCommit(ctx, found, listing.entries, maxBytes, put, signal);
135
- }
136
- catch (error) {
137
- if (put.length > 0)
138
- await ctx.store
139
- .tx(async (t) => {
140
- await t.lockArtifactQuota();
141
- await collectContent(ctx.blobs, t, put);
142
- })
143
- .catch(() => undefined);
144
- throw error;
145
- }
146
- }
147
- async function storeAndCommit(ctx, found, listed, maxBytes, put, signal) {
148
- const { session, limits } = found;
149
- const { sessionId, turnId } = session;
150
- const entries = [];
151
- const stored = new Set();
152
- let read = 0;
153
- for (const entry of listed) {
154
- let bytes;
155
- try {
156
- bytes = await ctx.workspaces.readBytes(session, posix.join(OUTPUTS_DIR, entry.path), {
157
- maxBytes: limits.fileBytes,
158
- signal,
159
- });
160
- }
161
- catch (error) {
162
- if (error instanceof SandboxFileTooLargeError)
163
- throw tooLargeFile(entry.path, error.size, limits.fileBytes);
164
- throw error;
165
- }
166
- // Gone since the listing: the folder is what is there now.
167
- if (bytes === undefined)
168
- continue;
169
- read += bytes.length;
170
- if (read > maxBytes)
171
- throw tooLarge(read, maxBytes);
172
- const sha256 = sha256Hex(bytes);
173
- const contentType = contentTypeFor(posix.basename(entry.path));
174
- if (!stored.has(sha256)) {
175
- stored.add(sha256);
176
- const key = contentKey(sha256);
177
- if (!(await ctx.blobs.head(key))) {
178
- put.push(sha256);
179
- await ctx.blobs.put(key, bytes, { contentType, signal });
180
- }
181
- }
182
- entries.push({ path: entry.path, size: bytes.length, sha256, contentType });
183
- }
184
- // Every file vanished, or nothing changed since the last export: no new version.
185
- if (entries.length === 0)
186
- return;
187
- if (found.latestSha !== undefined && sha256Hex(encodeManifest(entries)) === found.latestSha)
188
- return;
189
- await commitFolderVersion(ctx, {
190
- sessionId,
191
- turnId,
192
- ...(found.priorId === undefined ? {} : { artifactId: found.priorId }),
193
- name: OUTPUTS_ARTIFACT_NAME,
194
- entries,
195
- epoch: found.epoch,
196
- totalBytes: limits.totalBytes,
197
- });
198
- }
199
- function tooLargeFile(path, size, limit) {
200
- return new Skip("file_too_large", `${OUTPUTS_DIR}/${path} is ${size} bytes; a file may hold at most ${limit}`, limit, path);
201
- }
202
- function tooLarge(bytes, limit) {
203
- return new Skip("too_large", `${OUTPUTS_DIR} holds ${bytes} bytes; an export stores at most ${limit}`, limit);
204
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * Media types of file artifacts: what an upload may declare, what a name's extension implies,
3
- * and which model input a file is (`modelInputOf`, protocol 12): text, an image, a PDF, an Office
4
- * document, audio or video. A model reads a file only when its inputs include that one.
5
- */
6
- import type { ModelInput } from "@nylorun/core/contracts";
7
- /** Images the model reads as images. */
8
- export declare const MODEL_IMAGE_TYPES: readonly string[];
9
- /** The largest image or text file a model call carries inline. */
10
- export declare const MODEL_FILE_MAX_BYTES: number;
11
- /** The type part of a `Content-Type` value, lowercased, without parameters. */
12
- export declare function essence(contentType: string): string;
13
- /** A declared `Content-Type` as stored, or undefined when it is not a media type. */
14
- export declare function normalizeContentType(value: string | undefined): string | undefined;
15
- /** The media type a file name implies, or `application/octet-stream`. */
16
- export declare function contentTypeFor(name: string): string;
17
- export declare function isModelImage(contentType: string): boolean;
18
- /** Files the model reads as text: `text/*`, JSON, XML, YAML and their `+json`/`+xml` kin. */
19
- export declare function isText(contentType: string): boolean;
20
- /** The usual extension of a media type, for a name the Runtime gives a file; undefined if none. */
21
- export declare function extensionFor(contentType: string): string | undefined;
22
- /** The model input a file of `contentType` is, or undefined for a type no model reads. */
23
- export declare function modelInputOf(contentType: string): ModelInput | undefined;
24
- /** An input, in a sentence: "PDFs", "Office documents"; "files of this type" for none. */
25
- export declare function inputLabel(input: ModelInput | undefined): string;
26
- /**
27
- * Why a model that reads `inputs` cannot read a file of `contentType`, or undefined when it can.
28
- * An image must also be PNG, JPEG, GIF or WebP.
29
- */
30
- export declare function unreadableReason(contentType: string, inputs: readonly ModelInput[]): string | undefined;