@nylorun/runtime 0.22.0-beta → 0.24.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 (273) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +95 -30
  3. package/dist/adapters/execution/restate.d.ts +15 -3
  4. package/dist/adapters/execution/restate.js +78 -84
  5. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  6. package/dist/adapters/replication/pgoutput.js +6 -3
  7. package/dist/adapters/sandbox/local.d.ts +9 -1
  8. package/dist/adapters/sandbox/local.js +16 -4
  9. package/dist/adapters/sandbox/virtual.js +4 -1
  10. package/dist/adapters/streams/s2.js +1 -13
  11. package/dist/api/a2a/endpoint.js +5 -1
  12. package/dist/api/a2a/routes.d.ts +2 -0
  13. package/dist/api/a2a/routes.js +2 -2
  14. package/dist/api/a2a/tasks.js +2 -1
  15. package/dist/api/ag-ui/endpoint.js +4 -2
  16. package/dist/api/ag-ui/history.js +4 -1
  17. package/dist/api/ag-ui/routes.d.ts +4 -1
  18. package/dist/api/ag-ui/routes.js +11 -10
  19. package/dist/api/ag-ui/translate.js +3 -2
  20. package/dist/api/components.d.ts +4953 -1204
  21. package/dist/api/components.js +25 -16
  22. package/dist/api/gate/keys.js +7 -1
  23. package/dist/api/gate/routes.d.ts +6 -0
  24. package/dist/api/gate/routes.js +39 -3
  25. package/dist/api/http/app.js +4 -4
  26. package/dist/api/http/define.d.ts +6 -0
  27. package/dist/api/http/define.js +13 -1
  28. package/dist/api/http/respond.js +1 -1
  29. package/dist/api/http/routes/artifacts.js +35 -8
  30. package/dist/api/http/routes/coverage.d.ts +9 -0
  31. package/dist/api/http/routes/coverage.js +34 -0
  32. package/dist/api/http/routes/environments.d.ts +11 -0
  33. package/dist/api/http/routes/environments.js +283 -0
  34. package/dist/api/http/routes/mcp.d.ts +1 -1
  35. package/dist/api/http/routes/mcp.js +2 -2
  36. package/dist/api/http/routes/me.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +45 -9
  38. package/dist/api/http/routes/reads.js +54 -5
  39. package/dist/api/http/routes/sessions.js +11 -6
  40. package/dist/api/http/routes/tenant.js +32 -10
  41. package/dist/api/openapi.js +31 -26
  42. package/dist/artifacts/disk.d.ts +59 -0
  43. package/dist/artifacts/disk.js +563 -0
  44. package/dist/artifacts/export.d.ts +1 -1
  45. package/dist/artifacts/export.js +2 -3
  46. package/dist/artifacts/folders.d.ts +6 -3
  47. package/dist/artifacts/folders.js +17 -12
  48. package/dist/artifacts/service.d.ts +2 -1
  49. package/dist/artifacts/service.js +6 -1
  50. package/dist/artifacts/workspace.d.ts +1 -1
  51. package/dist/artifacts/workspace.js +1 -1
  52. package/dist/core/flow-host.d.ts +3 -22
  53. package/dist/core/flow-host.js +11 -37
  54. package/dist/core/provider.d.ts +3 -2
  55. package/dist/core/provider.js +5 -37
  56. package/dist/core/sandbox-routes.d.ts +2 -21
  57. package/dist/core/sandbox-routes.js +10 -43
  58. package/dist/execution/types.d.ts +11 -9
  59. package/dist/execution/types.js +5 -7
  60. package/dist/gates/contract.d.ts +3 -24
  61. package/dist/gates/contract.js +1 -36
  62. package/dist/gates/egress-credentials.d.ts +9 -0
  63. package/dist/gates/egress-credentials.js +11 -4
  64. package/dist/gates/egress.d.ts +28 -1
  65. package/dist/gates/egress.js +115 -10
  66. package/dist/gates/fixture-model.d.ts +28 -0
  67. package/dist/gates/fixture-model.js +92 -0
  68. package/dist/gates/handler.js +4 -4
  69. package/dist/gates/http-tool.d.ts +6 -2
  70. package/dist/gates/http-tool.js +3 -5
  71. package/dist/gates/in-process.d.ts +5 -0
  72. package/dist/gates/in-process.js +12 -1
  73. package/dist/gates/journal.d.ts +30 -0
  74. package/dist/gates/journal.js +52 -0
  75. package/dist/gates/mcp-handler.d.ts +4 -0
  76. package/dist/gates/mcp-handler.js +5 -1
  77. package/dist/gates/meter.d.ts +10 -2
  78. package/dist/gates/meter.js +20 -5
  79. package/dist/gates/policy.d.ts +49 -0
  80. package/dist/gates/policy.js +134 -0
  81. package/dist/gates/tenant-vaults.d.ts +7 -7
  82. package/dist/gates/tenant-vaults.js +5 -5
  83. package/dist/gates/tool-calls.d.ts +10 -5
  84. package/dist/gates/tool-calls.js +28 -10
  85. package/dist/gates/tool-client.d.ts +8 -0
  86. package/dist/gates/tool-client.js +27 -11
  87. package/dist/gates/tool-contract.d.ts +10 -68
  88. package/dist/gates/tool-contract.js +1 -85
  89. package/dist/gates/tool-gate.d.ts +1 -1
  90. package/dist/harness/calls.d.ts +12 -5
  91. package/dist/harness/calls.js +21 -12
  92. package/dist/harness/executors.js +12 -9
  93. package/dist/harness/main.js +18 -5
  94. package/dist/harness/pod.d.ts +34 -0
  95. package/dist/harness/pod.js +41 -0
  96. package/dist/harness/service.d.ts +0 -1
  97. package/dist/harness/service.js +13 -5
  98. package/dist/harness/setup.d.ts +27 -0
  99. package/dist/harness/setup.js +150 -0
  100. package/dist/harness/ws-client.js +3 -2
  101. package/dist/harness/ws-port.d.ts +1 -5
  102. package/dist/harness/ws-port.js +1 -5
  103. package/dist/harness-api/in-process.js +3 -3
  104. package/dist/harness-api/record.d.ts +15 -1
  105. package/dist/harness-api/record.js +174 -44
  106. package/dist/harness-api/redact.d.ts +3 -0
  107. package/dist/harness-api/redact.js +45 -0
  108. package/dist/harness-api/server.d.ts +8 -0
  109. package/dist/harness-api/server.js +93 -13
  110. package/dist/harness-api/start.d.ts +10 -3
  111. package/dist/harness-api/start.js +40 -10
  112. package/dist/harness-api/tool-journal.d.ts +96 -0
  113. package/dist/harness-api/tool-journal.js +102 -0
  114. package/dist/harness-api/tool-status.d.ts +13 -0
  115. package/dist/harness-api/tool-status.js +26 -0
  116. package/dist/harness-api/workspace.d.ts +6 -3
  117. package/dist/harness-api/workspace.js +27 -11
  118. package/dist/harness-api/ws-server.d.ts +11 -3
  119. package/dist/harness-api/ws-server.js +94 -15
  120. package/dist/host/execution.d.ts +8 -6
  121. package/dist/host/gates.d.ts +2 -1
  122. package/dist/host/gates.js +10 -2
  123. package/dist/host/main.js +64 -21
  124. package/dist/host/operate.js +67 -5
  125. package/dist/host/stack-config.d.ts +70 -0
  126. package/dist/host/stack-config.js +67 -1
  127. package/dist/infra/execution.d.ts +18 -1
  128. package/dist/infra/execution.js +28 -4
  129. package/dist/infra/index.d.ts +1 -1
  130. package/dist/keys/client.js +4 -1
  131. package/dist/keys/keys.d.ts +5 -5
  132. package/dist/keys/keys.js +5 -7
  133. package/dist/management-openapi.json +999 -2211
  134. package/dist/mcp/connect.d.ts +25 -20
  135. package/dist/mcp/connect.js +29 -30
  136. package/dist/mcp/pool.d.ts +2 -2
  137. package/dist/mcp/pool.js +1 -1
  138. package/dist/mcp/preview.d.ts +3 -3
  139. package/dist/mcp/preview.js +10 -20
  140. package/dist/mcp/snapshot.d.ts +5 -11
  141. package/dist/mcp/snapshot.js +6 -7
  142. package/dist/model/pi-model.js +8 -0
  143. package/dist/openapi.json +6955 -2526
  144. package/dist/reads/types.d.ts +16 -3
  145. package/dist/record/append.d.ts +3 -1
  146. package/dist/record/append.js +1 -0
  147. package/dist/record/envelope.d.ts +5 -1
  148. package/dist/record/envelope.js +7 -3
  149. package/dist/record/environment.d.ts +25 -0
  150. package/dist/record/environment.js +44 -0
  151. package/dist/record/index.d.ts +1 -1
  152. package/dist/record/index.js +1 -1
  153. package/dist/retry.d.ts +21 -0
  154. package/dist/retry.js +34 -0
  155. package/dist/sandbox/disk-fs.d.ts +95 -0
  156. package/dist/sandbox/disk-fs.js +206 -0
  157. package/dist/sandbox/join.d.ts +17 -18
  158. package/dist/sandbox/join.js +46 -20
  159. package/dist/sandbox/manager.d.ts +16 -8
  160. package/dist/sandbox/manager.js +33 -17
  161. package/dist/sandbox/placement.d.ts +13 -12
  162. package/dist/sandbox/placement.js +29 -12
  163. package/dist/sandbox/pods/client.d.ts +6 -1
  164. package/dist/sandbox/pods/lifecycle.d.ts +28 -27
  165. package/dist/sandbox/pods/lifecycle.js +14 -26
  166. package/dist/sandbox/pods/reconcile.js +21 -15
  167. package/dist/sandbox/pods/spec.d.ts +15 -13
  168. package/dist/sandbox/pods/spec.js +16 -20
  169. package/dist/sandbox/records.d.ts +2 -2
  170. package/dist/sandbox/records.js +3 -3
  171. package/dist/sandbox/resolve.d.ts +35 -27
  172. package/dist/sandbox/resolve.js +66 -41
  173. package/dist/sandbox/session-sandbox.js +1 -1
  174. package/dist/sandbox/setup.d.ts +61 -0
  175. package/dist/sandbox/setup.js +123 -0
  176. package/dist/sandbox/share.d.ts +11 -21
  177. package/dist/sandbox/share.js +12 -41
  178. package/dist/sandbox/tenant-config.d.ts +12 -14
  179. package/dist/sandbox/tenant-config.js +12 -17
  180. package/dist/sandbox/types.d.ts +6 -0
  181. package/dist/store/postgres/control.js +8 -1
  182. package/dist/store/postgres/drizzle/0018_wake_outbox_retries.sql +3 -0
  183. package/dist/store/postgres/drizzle/0019_sandbox_signal_outbox.sql +18 -0
  184. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  185. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  186. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  187. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  188. package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
  189. package/dist/store/postgres/reads.js +117 -17
  190. package/dist/store/postgres/record-writer.d.ts +4 -4
  191. package/dist/store/postgres/record-writer.js +9 -9
  192. package/dist/store/postgres/record.d.ts +7 -0
  193. package/dist/store/postgres/record.js +23 -2
  194. package/dist/store/postgres/schema.d.ts +1213 -779
  195. package/dist/store/postgres/schema.js +136 -76
  196. package/dist/store/postgres/store.js +283 -117
  197. package/dist/store/types.d.ts +206 -88
  198. package/dist/streams/relay/core.js +1 -13
  199. package/dist/tenant/advance.d.ts +7 -6
  200. package/dist/tenant/advance.js +121 -49
  201. package/dist/tenant/artifact-tool.js +3 -4
  202. package/dist/tenant/commands.d.ts +15 -2
  203. package/dist/tenant/commands.js +94 -14
  204. package/dist/tenant/context.d.ts +67 -33
  205. package/dist/tenant/context.js +4 -15
  206. package/dist/tenant/control.d.ts +8 -1
  207. package/dist/tenant/control.js +2 -0
  208. package/dist/tenant/effects.d.ts +1 -1
  209. package/dist/tenant/effects.js +37 -20
  210. package/dist/tenant/environments.d.ts +167 -0
  211. package/dist/tenant/environments.js +823 -0
  212. package/dist/tenant/harness-tokens.d.ts +21 -0
  213. package/dist/tenant/harness-tokens.js +61 -0
  214. package/dist/tenant/host-token.d.ts +1 -1
  215. package/dist/tenant/host-token.js +2 -4
  216. package/dist/tenant/identity-file.d.ts +4 -4
  217. package/dist/tenant/identity-file.js +17 -8
  218. package/dist/tenant/issuers.js +3 -3
  219. package/dist/tenant/principals.js +1 -1
  220. package/dist/tenant/reset.js +3 -3
  221. package/dist/tenant/runtime.d.ts +6 -1
  222. package/dist/tenant/runtime.js +30 -4
  223. package/dist/tenant/scheduler.d.ts +7 -0
  224. package/dist/tenant/scheduler.js +38 -7
  225. package/dist/tenant/session-streams.d.ts +0 -1
  226. package/dist/tenant/session-streams.js +1 -13
  227. package/dist/tenant/session.d.ts +3 -3
  228. package/dist/tenant/session.js +0 -14
  229. package/dist/tenant/sessions.d.ts +13 -13
  230. package/dist/tenant/sessions.js +61 -138
  231. package/dist/tenant/signing-keys.d.ts +5 -1
  232. package/dist/tenant/status.js +3 -3
  233. package/dist/tenant/streams.js +14 -29
  234. package/dist/tenant/sweep.d.ts +39 -12
  235. package/dist/tenant/sweep.js +168 -63
  236. package/dist/tenant/transcript.d.ts +210 -20
  237. package/dist/tenant/transcript.js +62 -11
  238. package/dist/tenant/turn-timing.d.ts +43 -0
  239. package/dist/tenant/turn-timing.js +39 -0
  240. package/dist/tenant/types.d.ts +2 -0
  241. package/dist/traces/ids.d.ts +42 -0
  242. package/dist/traces/ids.js +85 -0
  243. package/dist/traces/key.d.ts +6 -0
  244. package/dist/traces/key.js +37 -0
  245. package/dist/traces/map.d.ts +113 -0
  246. package/dist/traces/map.js +317 -0
  247. package/dist/traces/options.d.ts +7 -0
  248. package/dist/traces/options.js +1 -0
  249. package/dist/traces/otlp.d.ts +42 -0
  250. package/dist/traces/otlp.js +84 -0
  251. package/dist/traces/projector.d.ts +58 -0
  252. package/dist/traces/projector.js +200 -0
  253. package/dist/traces/read.d.ts +23 -0
  254. package/dist/traces/read.js +16 -0
  255. package/dist/vault/coverage.d.ts +26 -0
  256. package/dist/vault/coverage.js +64 -0
  257. package/dist/vault/error.d.ts +11 -1
  258. package/dist/vault/error.js +5 -1
  259. package/dist/vault/service.d.ts +47 -48
  260. package/dist/vault/service.js +321 -382
  261. package/dist/vault/sources.d.ts +5 -16
  262. package/dist/vault/sources.js +6 -8
  263. package/dist/version.d.ts +1 -1
  264. package/dist/version.js +2 -2
  265. package/package.json +14 -13
  266. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  267. package/dist/api/http/routes/sandboxes.js +0 -153
  268. package/dist/api/http/routes/vaults.d.ts +0 -10
  269. package/dist/api/http/routes/vaults.js +0 -139
  270. package/dist/record/sandbox.d.ts +0 -25
  271. package/dist/record/sandbox.js +0 -43
  272. package/dist/tenant/sandboxes.d.ts +0 -122
  273. package/dist/tenant/sandboxes.js +0 -562
@@ -32,7 +32,9 @@
32
32
  * which check failed.
33
33
  */
34
34
  import { createHash, timingSafeEqual } from "node:crypto";
35
- import { isSandboxId } from "@nylorun/core/contracts";
35
+ import { isEnvironmentId } from "@nylorun/core/contracts";
36
+ import { recoverLostVolume, runPendingSeed } from "../artifacts/disk.js";
37
+ import { recordSetup, setupFile, setupPlanOf, setupState, } from "./setup.js";
36
38
  import { mintHostTokens, staleHost, verifyHostToken, } from "../tenant/host-token.js";
37
39
  /** A refused join, renewal or host connection. */
38
40
  export class HostAuthError extends Error {
@@ -84,7 +86,7 @@ export function hostAuthority(ctx) {
84
86
  return {
85
87
  async join(request) {
86
88
  const { sandboxId, podUid, joinToken, volumeId } = request;
87
- if (!isSandboxId(sandboxId) ||
89
+ if (!isEnvironmentId(sandboxId) ||
88
90
  typeof podUid !== "string" ||
89
91
  !POD_UID.test(podUid) ||
90
92
  typeof joinToken !== "string" ||
@@ -107,11 +109,11 @@ export function hostAuthority(ctx) {
107
109
  // A pod being stopped joins nothing: its sandbox runs again only when asked to.
108
110
  if (pod.desired !== "running")
109
111
  return refuse("the sandbox is stopped");
110
- if (pod.observed === "lost" || pod.observed === "expired")
111
- return refuse(`the sandbox is ${pod.observed}`);
112
+ if (pod.observed === "lost")
113
+ return refuse("the sandbox is lost");
112
114
  return pod;
113
115
  };
114
- const readRow = () => ctx.store.tx((t) => t.sandboxResource(sandboxId));
116
+ const readRow = () => ctx.store.tx((t) => t.environment(sandboxId));
115
117
  const before = usable(await readRow());
116
118
  let current;
117
119
  try {
@@ -123,7 +125,7 @@ export function hostAuthority(ctx) {
123
125
  if (current !== podUid)
124
126
  refuse("the pod is not the sandbox's current pod");
125
127
  const joined = await ctx.store.tx(async (t) => {
126
- const row = await t.sandboxResource(sandboxId, { lock: true });
128
+ const row = await t.environment(sandboxId, { lock: true });
127
129
  const pod = usable(row);
128
130
  if (pod.k8sName !== before.k8sName)
129
131
  refuse("the sandbox was reset during the join");
@@ -132,11 +134,14 @@ export function hostAuthority(ctx) {
132
134
  if (pod.volumeId !== undefined && volumeId !== pod.volumeId) {
133
135
  // A new, empty volume under the same Sandbox: the sandbox's files are gone.
134
136
  const reason = "The sandbox's volume was replaced: its files are gone";
135
- await t.updateSandboxPod(sandboxId, { observed: "lost", reason, hostEpoch: epoch }, now);
136
- await t.sandboxEvent(sandboxId, "sandbox.lost", { reason });
137
+ await t.updateEnvironmentPod(sandboxId, { observed: "lost", reason, hostEpoch: epoch }, now);
138
+ await t.environmentEvent(sandboxId, "environment.lost", { reason });
139
+ // Its Sandbox is deleted; only a reset brings the sandbox back.
140
+ await t.signal({ type: "host.revoked", sandboxId, epoch });
141
+ await t.signalSandbox(sandboxId, { kind: "reconcile" });
137
142
  return { lost: epoch };
138
143
  }
139
- await t.updateSandboxPod(sandboxId, {
144
+ await t.updateEnvironmentPod(sandboxId, {
140
145
  hostEpoch: epoch,
141
146
  podUid,
142
147
  ...(pod.volumeId === undefined && volumeId !== undefined ? { volumeId } : {}),
@@ -144,27 +149,48 @@ export function hostAuthority(ctx) {
144
149
  }, now);
145
150
  if (pod.podUid !== podUid) {
146
151
  if (pod.podUid !== undefined)
147
- await t.sandboxEvent(sandboxId, "sandbox.relaunched", { hostEpoch: epoch });
152
+ await t.environmentEvent(sandboxId, "environment.relaunched", { hostEpoch: epoch });
148
153
  else
149
- await t.sandboxEvent(sandboxId, "sandbox.running", { hostEpoch: epoch });
154
+ await t.environmentEvent(sandboxId, "environment.running", { hostEpoch: epoch });
150
155
  }
151
- return epoch;
156
+ // The previous host's connection (an older epoch) serves nothing more, on any process.
157
+ await t.signal({ type: "host.revoked", sandboxId, epoch });
158
+ return {
159
+ epoch,
160
+ setup: row ? await setupPlanOf(ctx, t, row) : undefined,
161
+ seeded: row?.diskSeed !== undefined,
162
+ };
152
163
  });
153
- if (typeof joined === "object") {
154
- ctx.harness.revokeHost(sandboxId, joined.lost);
155
- // Its Sandbox is deleted; only a reset brings the sandbox back.
156
- await ctx.sandboxSignal(sandboxId, { kind: "reconcile" });
164
+ if ("lost" in joined) {
165
+ // With a disk version, a new volume seeded from the latest one (R2d step 3).
166
+ void recoverLostVolume(ctx, sandboxId).catch(() => undefined);
157
167
  return refuse("the volume was replaced: the sandbox is lost");
158
168
  }
159
- // The previous host's connection (an older epoch) serves nothing more.
160
- ctx.harness.revokeHost(sandboxId, joined);
161
- ctx.config.logger.info("sandbox host joined", { sandboxId, epoch: joined });
162
- return answerOf(await mintHostTokens(ctx, { sandboxId, epoch: joined, podUid }), await ctx.keys.egressCa());
169
+ ctx.config.logger.info("sandbox host joined", { sandboxId, epoch: joined.epoch });
170
+ const answer = answerOf(await mintHostTokens(ctx, { sandboxId, epoch: joined.epoch, podUid }), await ctx.keys.egressCa());
171
+ if (joined.seeded) {
172
+ // The engine connects next; the seed waits for it, then writes the version.
173
+ void runPendingSeed(ctx, sandboxId);
174
+ return { ...answer, seeded: true };
175
+ }
176
+ return joined.setup ? { ...answer, setup: joined.setup } : answer;
163
177
  },
164
178
  async renew(hostToken) {
165
179
  const claims = await verified(hostToken);
166
180
  return answerOf(await mintHostTokens(ctx, claims), await ctx.keys.egressCa());
167
181
  },
168
182
  verify: verified,
183
+ async setup(hostToken) {
184
+ const claims = await verified(hostToken);
185
+ return setupState(ctx, claims.sandboxId);
186
+ },
187
+ async reportSetup(hostToken, report) {
188
+ const claims = await verified(hostToken);
189
+ await recordSetup(ctx, claims.sandboxId, claims.epoch, report);
190
+ },
191
+ async setupFile(hostToken, index, path) {
192
+ const claims = await verified(hostToken);
193
+ return setupFile(ctx, claims.sandboxId, index, path);
194
+ },
169
195
  };
170
196
  }
@@ -20,6 +20,7 @@ import type { EventPayload } from "@nylorun/core/contracts";
20
20
  import { type AgentManifest, type CapabilityManifest, type SandboxToolName } from "@nylorun/core/define";
21
21
  import { type SandboxRecord, type SandboxRecords } from "./records.js";
22
22
  import { type SandboxSelection, type SandboxSelectionReport } from "./select.js";
23
+ import { type DiskAnswer, type DiskOp } from "./disk-fs.js";
23
24
  import { type SandboxToolOutcome } from "./tools.js";
24
25
  import { type SandboxBackend, type SandboxListing } from "./types.js";
25
26
  export { sandboxCapabilityOf } from "./capability.js";
@@ -33,7 +34,7 @@ export interface SandboxSessionRef {
33
34
  * The sandbox resource the owning session is attached to. Its workspace is keyed by this id,
34
35
  * so every session attached to it shares the files; absent, the workspace is the session's.
35
36
  */
36
- readonly sandboxId?: string;
37
+ readonly environmentId?: string;
37
38
  /** Variables every command of the call gets (R2c): secret names set to the sentinel, plain values. */
38
39
  readonly environment?: Readonly<Record<string, string>>;
39
40
  /**
@@ -55,7 +56,7 @@ export interface SandboxSweepOptions {
55
56
  /** Whether a session still exists; sandboxes of deleted sessions are removed. */
56
57
  readonly sessionExists: (sessionId: string) => boolean | Promise<boolean>;
57
58
  /** Whether a sandbox resource still exists. Default: every one does. */
58
- readonly sandboxExists?: (sandboxId: string) => boolean | Promise<boolean>;
59
+ readonly sandboxExists?: (environmentId: string) => boolean | Promise<boolean>;
59
60
  }
60
61
  export interface SandboxManagerOptions {
61
62
  /** Distinguishes this Runtime's sandboxes from other Runtimes on the same machine: the Tenant id. */
@@ -77,7 +78,7 @@ export interface SandboxManagerOptions {
77
78
  readonly emit: <T extends SandboxEventType>(sessionId: string, turnId: string | null, type: T, payload: EventPayload<T>, meta: SandboxEventMeta) => void | Promise<void>;
78
79
  }
79
80
  /** The session events a sandbox writes. */
80
- export type SandboxEventType = "sandbox.state" | "sandbox.exec";
81
+ export type SandboxEventType = "environment.state" | "environment.exec";
81
82
  export declare class SandboxManager {
82
83
  private readonly options;
83
84
  private selection?;
@@ -104,8 +105,8 @@ export declare class SandboxManager {
104
105
  /** Whether any sandbox was ever recorded, so startup can skip probing for agents without one. */
105
106
  hasRecords(): Promise<boolean>;
106
107
  keyOf(sessionId: string): string;
107
- /** The workspace key of sandbox resource `sandboxId` (`sandboxWorkspaceKey`). */
108
- sandboxKeyOf(sandboxId: string): string;
108
+ /** The workspace key of sandbox resource `environmentId` (`sandboxWorkspaceKey`). */
109
+ sandboxKeyOf(environmentId: string): string;
109
110
  private keyFor;
110
111
  report(): Promise<SandboxSelectionReport & {
111
112
  readonly defaultImage: string;
@@ -141,6 +142,13 @@ export declare class SandboxManager {
141
142
  } | Extract<SandboxToolOutcome, {
142
143
  kind: "failed";
143
144
  }>>;
145
+ /**
146
+ * One operation on the disk of the session's sandbox (R2d step 3, `sandbox/disk-fs.ts`), in the
147
+ * same queue as its tool calls, starting it first when it is stopped.
148
+ */
149
+ disk(session: SandboxSessionRef, capability: CapabilityManifest, op: DiskOp, signal: AbortSignal): Promise<DiskAnswer | Extract<SandboxToolOutcome, {
150
+ kind: "failed";
151
+ }>>;
144
152
  /** Runs `use` on the session's sandbox in its queue, starting it first when it is stopped. */
145
153
  private withHandle;
146
154
  /** The session's live sandbox entry and spec, or why it cannot run here. */
@@ -159,16 +167,16 @@ export declare class SandboxManager {
159
167
  /** The sandbox record for a session, if one was ever created. */
160
168
  status(sessionId: string): Promise<SandboxRecord | undefined>;
161
169
  /**
162
- * Deletes sandbox resource `sandboxId`'s workspace, its files and its compute record, after
170
+ * Deletes sandbox resource `environmentId`'s workspace, its files and its compute record, after
163
171
  * any command queued on it. Called once the resource is deleted.
164
172
  */
165
- removeSandbox(sandboxId: string): Promise<void>;
173
+ removeSandbox(environmentId: string): Promise<void>;
166
174
  /** Deletes the workspace `key`, its files and its record, after any command queued on it. */
167
175
  remove(key: string): Promise<void>;
168
176
  /**
169
177
  * Delete workspaces this Runtime owns whose owner is gone: a session's when `sessionExists`
170
178
  * says the session is gone, a sandbox resource's when `sandboxExists` says the resource is.
171
179
  */
172
- reconcile(sessionExists: (sessionId: string) => boolean | Promise<boolean>, sandboxExists: (sandboxId: string) => boolean | Promise<boolean>): Promise<void>;
180
+ reconcile(sessionExists: (sessionId: string) => boolean | Promise<boolean>, sandboxExists: (environmentId: string) => boolean | Promise<boolean>): Promise<void>;
173
181
  close(): Promise<void>;
174
182
  }
@@ -3,6 +3,7 @@ import { sandboxWorkspaceKey, sessionWorkspaceKey, workspacePrefix, } from "./re
3
3
  import { DEFAULT_SANDBOX_CPUS, DEFAULT_SANDBOX_IMAGE, describeNetwork, idleMsOf, memoryMiBOf, resolveNetwork, } from "./policy.js";
4
4
  import { parseSandboxPreference, reportSelection, selectSandboxBackend, } from "./select.js";
5
5
  import { mountSkill, sandboxSkillsOf, skillSignature } from "./skills.js";
6
+ import { runDiskOp } from "./disk-fs.js";
6
7
  import { resolveSandboxPath, runSandboxTool, } from "./tools.js";
7
8
  import { SandboxFileTooLargeError, } from "./types.js";
8
9
  export { sandboxCapabilityOf } from "./capability.js";
@@ -89,14 +90,14 @@ export class SandboxManager {
89
90
  keyOf(sessionId) {
90
91
  return sessionWorkspaceKey(this.options.scope, sessionId);
91
92
  }
92
- /** The workspace key of sandbox resource `sandboxId` (`sandboxWorkspaceKey`). */
93
- sandboxKeyOf(sandboxId) {
94
- return sandboxWorkspaceKey(this.options.scope, sandboxId);
93
+ /** The workspace key of sandbox resource `environmentId` (`sandboxWorkspaceKey`). */
94
+ sandboxKeyOf(environmentId) {
95
+ return sandboxWorkspaceKey(this.options.scope, environmentId);
95
96
  }
96
97
  keyFor(session) {
97
- return session.sandboxId === undefined
98
+ return session.environmentId === undefined
98
99
  ? this.keyOf(session.id)
99
- : this.sandboxKeyOf(session.sandboxId);
100
+ : this.sandboxKeyOf(session.environmentId);
100
101
  }
101
102
  async report() {
102
103
  return { ...reportSelection(await this.ready), defaultImage: DEFAULT_SANDBOX_IMAGE };
@@ -167,6 +168,21 @@ export class SandboxManager {
167
168
  : { kind: "listed", path: resolved, listing };
168
169
  });
169
170
  }
171
+ /**
172
+ * One operation on the disk of the session's sandbox (R2d step 3, `sandbox/disk-fs.ts`), in the
173
+ * same queue as its tool calls, starting it first when it is stopped.
174
+ */
175
+ async disk(session, capability, op, signal) {
176
+ return this.withHandle(session, capability, signal, async (handle, live) => {
177
+ if (!handle.diskRoots)
178
+ return {
179
+ kind: "failed",
180
+ code: "sandbox.unsupported",
181
+ message: `The ${live.backend.name} backend has no disk to version`,
182
+ };
183
+ return runDiskOp(handle.diskRoots, op);
184
+ });
185
+ }
170
186
  /** Runs `use` on the session's sandbox in its queue, starting it first when it is stopped. */
171
187
  async withHandle(session, capability, signal, use) {
172
188
  const prepared = await this.prepare(session, capability);
@@ -234,7 +250,7 @@ export class SandboxManager {
234
250
  if (!live) {
235
251
  live = {
236
252
  sessionId: session.id,
237
- ...(session.sandboxId === undefined ? {} : { sandboxId: session.sandboxId }),
253
+ ...(session.environmentId === undefined ? {} : { environmentId: session.environmentId }),
238
254
  backend,
239
255
  idleMs: idleMsOf(capability.sandbox),
240
256
  active: 0,
@@ -268,7 +284,7 @@ export class SandboxManager {
268
284
  catch (error) {
269
285
  const message = error instanceof Error ? error.message : String(error);
270
286
  const record = await this.record(spec.key, live, session.id, spec.image, "stopped");
271
- await this.options.emit(session.id, session.activeTurnId, "sandbox.state", { state: "stopped", backend: live.backend.name, error: message }, { record, ...claimOf(session) });
287
+ await this.options.emit(session.id, session.activeTurnId, "environment.state", { state: "stopped", backend: live.backend.name, error: message }, { record, ...claimOf(session) });
272
288
  // Nothing ran yet, so a failed start is a definite outcome, not an uncertain one.
273
289
  return {
274
290
  kind: "failed",
@@ -286,7 +302,7 @@ export class SandboxManager {
286
302
  const outcome = await runSandboxTool(live.handle, toolName, (input ?? {}), signal, (value) => {
287
303
  report = value;
288
304
  }, session.environment);
289
- await this.options.emit(session.id, session.activeTurnId, "sandbox.exec", {
305
+ await this.options.emit(session.id, session.activeTurnId, "environment.exec", {
290
306
  tool: toolName,
291
307
  ...report,
292
308
  ...(report.command ? { command: report.command.slice(0, 500) } : {}),
@@ -347,12 +363,12 @@ export class SandboxManager {
347
363
  network: describeNetwork(spec.network),
348
364
  };
349
365
  const creating = await this.record(spec.key, live, session.id, spec.image, "creating", existing);
350
- await this.options.emit(session.id, session.activeTurnId, "sandbox.state", { state: "creating", ...payload, ...(existing ? { reattach: true } : {}) }, { record: creating, ...claimOf(session) });
366
+ await this.options.emit(session.id, session.activeTurnId, "environment.state", { state: "creating", ...payload, ...(existing ? { reattach: true } : {}) }, { record: creating, ...claimOf(session) });
351
367
  const handle = await live.backend.open(spec);
352
368
  // A handle opened afresh holds no skills this manager knows of.
353
369
  live.skills.clear();
354
370
  const running = await this.record(spec.key, live, session.id, spec.image, "running", existing);
355
- await this.options.emit(session.id, session.activeTurnId, "sandbox.state", {
371
+ await this.options.emit(session.id, session.activeTurnId, "environment.state", {
356
372
  state: "running",
357
373
  ...payload,
358
374
  ...(existing && handle.created
@@ -371,7 +387,7 @@ export class SandboxManager {
371
387
  }
372
388
  finally {
373
389
  const record = await this.record(key, live, live.sessionId, undefined, "stopped");
374
- await this.options.emit(live.sessionId, null, "sandbox.state", { state: "stopped", backend: live.backend.name }, { record });
390
+ await this.options.emit(live.sessionId, null, "environment.state", { state: "stopped", backend: live.backend.name }, { record });
375
391
  }
376
392
  }
377
393
  async record(key, live, sessionId, image, state, known) {
@@ -380,7 +396,7 @@ export class SandboxManager {
380
396
  const record = {
381
397
  key,
382
398
  sessionId,
383
- ...(live.sandboxId === undefined ? {} : { sandboxId: live.sandboxId }),
399
+ ...(live.environmentId === undefined ? {} : { environmentId: live.environmentId }),
384
400
  backend: live.backend.name,
385
401
  image: image ?? existing?.image ?? "",
386
402
  state,
@@ -395,11 +411,11 @@ export class SandboxManager {
395
411
  return this.options.records.get(this.keyOf(sessionId));
396
412
  }
397
413
  /**
398
- * Deletes sandbox resource `sandboxId`'s workspace, its files and its compute record, after
414
+ * Deletes sandbox resource `environmentId`'s workspace, its files and its compute record, after
399
415
  * any command queued on it. Called once the resource is deleted.
400
416
  */
401
- async removeSandbox(sandboxId) {
402
- await this.remove(this.sandboxKeyOf(sandboxId));
417
+ async removeSandbox(environmentId) {
418
+ await this.remove(this.sandboxKeyOf(environmentId));
403
419
  }
404
420
  /** Deletes the workspace `key`, its files and its record, after any command queued on it. */
405
421
  async remove(key) {
@@ -425,9 +441,9 @@ export class SandboxManager {
425
441
  for (const key of await backend.list(this.prefix)) {
426
442
  const record = await this.options.records.get(key);
427
443
  if (record) {
428
- const kept = record.sandboxId === undefined
444
+ const kept = record.environmentId === undefined
429
445
  ? await sessionExists(record.sessionId)
430
- : await sandboxExists(record.sandboxId);
446
+ : await sandboxExists(record.environmentId);
431
447
  if (kept)
432
448
  continue;
433
449
  }
@@ -1,27 +1,28 @@
1
1
  /**
2
2
  * Placement (F7.2, D38): where a session's harness runs, checked when the session opens and
3
3
  * never downgraded afterwards. The Tenant maps harness ids (`nylorun`, or `*` for the rest) to
4
- * the hosts they may run on (`sandbox.config` `placement`): `harness-container` (the Runtime's
5
- * harness: in process, or the harness container) and `sandbox` (the engine in a pod sandbox).
6
- * Default: both.
4
+ * the hosts they may run on (`environments.config` `placement`): `harness-container` (the
5
+ * Runtime's harness: in process, or the harness container) and `sandbox` (the engine in a pod).
6
+ * Default: both. `"*": { hosts: [] }` refuses every harness the map does not name (R4.1).
7
7
  *
8
- * A session attached to a pod sandbox runs on `sandbox`; every other session on
9
- * `harness-container`. A host the Tenant does not allow is `409 placement_refused`. A pod
10
- * sandbox needs sandbox pods: without a cluster (`nylorun sandbox enable`), or with the
11
- * sandboxes service not ready, it is `409 sandbox_unavailable`.
8
+ * A session on a pod Environment runs on `sandbox`; every other session (a virtual or `none`
9
+ * Environment, or none) on `harness-container` (D52). A host the Tenant does not allow is
10
+ * `409 placement_refused`. A pod Environment needs sandbox pods: without a cluster (`nylorun
11
+ * sandbox enable`), or with the sandboxes service not ready, it is `409 environment_unavailable`.
12
12
  */
13
- import type { SandboxPlacementHost, TenantSandboxConfig } from "@nylorun/core/contracts";
13
+ import { type EnvironmentKind, type PlacementHost as SandboxPlacementHost } from "@nylorun/core/contracts";
14
+ import type { TenantSandboxConfig } from "./tenant-config.js";
14
15
  import type { TenantPods } from "../tenant/context.js";
15
- /** The harness every session of this Runtime runs (blueprint D38). */
16
+ /** Our harness's id (blueprint D38): what a session runs unless its Environment names another. */
16
17
  export declare const HARNESS_ID = "nylorun";
17
18
  export declare const DEFAULT_PLACEMENT: readonly SandboxPlacementHost[];
18
19
  /** The hosts harness `harnessId` may run on. */
19
20
  export declare function placementOf(config: TenantSandboxConfig, harnessId?: string): readonly SandboxPlacementHost[];
20
21
  /** How to enable sandbox pods, for the messages that need them. */
21
22
  export declare const ENABLE_PODS = "run `nylorun sandbox enable --context <name>` to give this Tenant a cluster";
22
- /** Refuses a session whose sandbox needs a host the Tenant does not allow. */
23
- export declare function checkPlacement(config: TenantSandboxConfig, kind: "virtual" | "pod" | undefined): void;
23
+ /** Refuses a session whose harness, on the host its Environment needs, the Tenant does not allow. */
24
+ export declare function checkPlacement(config: TenantSandboxConfig, kind: EnvironmentKind | undefined, harness?: string): void;
24
25
  /** Whether the sandboxes service is up, cached for a few seconds. */
25
26
  export declare function podsReady(pods: TenantPods): Promise<boolean>;
26
- /** Refuses a pod sandbox without sandbox pods, or with the sandboxes service down. */
27
+ /** Refuses a pod Environment without sandbox pods, or with the sandboxes service down. */
27
28
  export declare function requirePods(pods: TenantPods | undefined, what: string): Promise<TenantPods>;
@@ -1,6 +1,19 @@
1
+ /**
2
+ * Placement (F7.2, D38): where a session's harness runs, checked when the session opens and
3
+ * never downgraded afterwards. The Tenant maps harness ids (`nylorun`, or `*` for the rest) to
4
+ * the hosts they may run on (`environments.config` `placement`): `harness-container` (the
5
+ * Runtime's harness: in process, or the harness container) and `sandbox` (the engine in a pod).
6
+ * Default: both. `"*": { hosts: [] }` refuses every harness the map does not name (R4.1).
7
+ *
8
+ * A session on a pod Environment runs on `sandbox`; every other session (a virtual or `none`
9
+ * Environment, or none) on `harness-container` (D52). A host the Tenant does not allow is
10
+ * `409 placement_refused`. A pod Environment needs sandbox pods: without a cluster (`nylorun
11
+ * sandbox enable`), or with the sandboxes service not ready, it is `409 environment_unavailable`.
12
+ */
13
+ import { DEFAULT_HARNESS, } from "@nylorun/core/contracts";
1
14
  import { fail } from "../tenant/http.js";
2
- /** The harness every session of this Runtime runs (blueprint D38). */
3
- export const HARNESS_ID = "nylorun";
15
+ /** Our harness's id (blueprint D38): what a session runs unless its Environment names another. */
16
+ export const HARNESS_ID = DEFAULT_HARNESS;
4
17
  export const DEFAULT_PLACEMENT = ["harness-container", "sandbox"];
5
18
  /** The hosts harness `harnessId` may run on. */
6
19
  export function placementOf(config, harnessId = HARNESS_ID) {
@@ -9,14 +22,18 @@ export function placementOf(config, harnessId = HARNESS_ID) {
9
22
  }
10
23
  /** How to enable sandbox pods, for the messages that need them. */
11
24
  export const ENABLE_PODS = "run `nylorun sandbox enable --context <name>` to give this Tenant a cluster";
12
- /** Refuses a session whose sandbox needs a host the Tenant does not allow. */
13
- export function checkPlacement(config, kind) {
25
+ /** Refuses a session whose harness, on the host its Environment needs, the Tenant does not allow. */
26
+ export function checkPlacement(config, kind, harness = HARNESS_ID) {
14
27
  const host = kind === "pod" ? "sandbox" : "harness-container";
15
- const allowed = placementOf(config);
16
- if (!allowed.includes(host))
17
- fail(409, kind === "pod"
18
- ? `This Tenant does not let the ${HARNESS_ID} harness run in a sandbox pod (placement allows ${allowed.join(", ")}).`
19
- : `This Tenant runs the ${HARNESS_ID} harness only in sandbox pods (placement allows ${allowed.join(", ")}): attach a pod sandbox with sandbox: { id }.`, { code: "placement_refused", details: { harness: HARNESS_ID, host, allowed: [...allowed] } });
28
+ const allowed = placementOf(config, harness);
29
+ if (allowed.includes(host))
30
+ return;
31
+ const where = allowed.length === 0 ? "it may run nowhere" : `placement allows ${allowed.join(", ")}`;
32
+ fail(409, allowed.length === 0
33
+ ? `This Tenant does not run the ${harness} harness (${where}).`
34
+ : kind === "pod"
35
+ ? `This Tenant does not let the ${harness} harness run in a sandbox pod (${where}).`
36
+ : `This Tenant runs the ${harness} harness only in sandbox pods (${where}): open the session on a pod Environment.`, { code: "placement_refused", details: { harness, host, allowed: [...allowed] } });
20
37
  }
21
38
  const READY_CACHE_MS = 5_000;
22
39
  const ready = new WeakMap();
@@ -29,15 +46,15 @@ export async function podsReady(pods) {
29
46
  ready.set(pods, { at: Date.now(), ready: answer });
30
47
  return answer;
31
48
  }
32
- /** Refuses a pod sandbox without sandbox pods, or with the sandboxes service down. */
49
+ /** Refuses a pod Environment without sandbox pods, or with the sandboxes service down. */
33
50
  export async function requirePods(pods, what) {
34
51
  if (!pods)
35
52
  return fail(409, `${what} needs sandbox pods, and this Runtime has no cluster: ${ENABLE_PODS}.`, {
36
- code: "sandbox_unavailable",
53
+ code: "environment_unavailable",
37
54
  });
38
55
  if (!(await podsReady(pods)))
39
56
  fail(409, `${what} needs sandbox pods, and the sandboxes service is not ready. Check \`nylorun sandbox status\`.`, {
40
- code: "sandbox_unavailable",
57
+ code: "environment_unavailable",
41
58
  details: { retryAfterSeconds: 5 },
42
59
  }, { "retry-after": "5" });
43
60
  return pods;
@@ -13,7 +13,12 @@ export interface PodSpec {
13
13
  readonly mode: "Running" | "Suspended";
14
14
  /** The workload image; the service defaults it to `python:3.13-slim`. */
15
15
  readonly image?: string;
16
- /** The Runtime image the engine is copied from. */
16
+ /**
17
+ * What the workload runs: `engine` (default), our engine copied from `harnessImage`, or
18
+ * `image`, the image's own entrypoint, for a custom harness built into it (R4.4).
19
+ */
20
+ readonly entrypoint?: "engine" | "image";
21
+ /** The Runtime image the engine is copied from; not with `entrypoint: "image"`. */
17
22
  readonly harnessImage?: string;
18
23
  /** Replaces the engine command (diagnostics). */
19
24
  readonly command?: readonly string[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * A pod sandbox's lifecycle (blueprint D34, D36), as one pure decision: from the row, the
2
+ * A pod Environment's lifecycle (blueprint D34, D36), as one pure decision: from the row, the
3
3
  * Tenant's lifecycle settings, the Sandbox's status as the sandboxes service sees it and the
4
4
  * clock, what to ask of the service, what to write to the row, which events to record, and
5
5
  * when to look again. `reconcile.ts` gathers the facts, carries the decision out and runs it
@@ -12,42 +12,43 @@
12
12
  * - **Every change** the Runtime asks for bumps `rev`; the `PUT` carries `opId =
13
13
  * <name>.r<rev>`, so a repeated apply is a no-op at the service, and a Sandbox whose `opId`
14
14
  * is not the row's is applied again.
15
- * - **Idle** (`trigger: idle`): no turn on an attached session, and none ended within the
16
- * Tenant's `limits.idle` → `Suspended` (`sandbox.suspended`, reason `idle`). The volume is
17
- * kept; the next turn start asks for `Running` again.
18
- * - **TTL**: the Sandbox carries `shutdownTime = createdAt + lifecycle.ttl` and the Tenant's
19
- * `onExpiry` as its `shutdownPolicy`. Once it passes, the sandbox is `expired`
20
- * (`sandbox.expired`), its host epoch moves (its tokens stop working) and turns are refused
21
- * (`sandbox_expired`); with `onExpiry: delete` it is deleted. A `PUT` with a longer TTL moves
22
- * `expiresAt`, and the next apply revives it with the later `shutdownTime`.
15
+ * - **Idle** (`trigger: idle`): no turn on a session that names it, and none ended within its
16
+ * `lifecycle.idle` (at most the Tenant's `limits.idle`) → `Suspended` (`environment.suspended`,
17
+ * reason `idle`). The volume is kept; the next turn start asks for `Running` again.
23
18
  * - **Lost**: an incarnation that has run (a pod joined) whose Sandbox is gone or whose volume
24
- * is missing is `lost` (`sandbox.lost`): its host epoch moves, its Sandbox is deleted, and
25
- * only a reset brings the sandbox back. agent-sandbox recreates a deleted claim empty, so the
26
- * engine's join also checks the id it keeps on the volume (`join.ts`): a new volume is lost
27
- * too. A pod replaced on the same volume is not lost: the new pod's join bumps the epoch
28
- * (`sandbox.relaunched`).
19
+ * is missing is `lost` (`environment.lost`): its host epoch moves, its Sandbox is deleted, and
20
+ * only a reset brings the Environment back. agent-sandbox recreates a deleted claim empty, so
21
+ * the engine's join also checks the id it keeps on the volume (`join.ts`): a new volume is
22
+ * lost too. A pod replaced on the same volume is not lost: the new pod's join bumps the epoch
23
+ * (`environment.relaunched`).
29
24
  * - **Delete** (`desired: deleted`): the Sandbox is deleted, and the row once it is gone.
30
- * - **Reset** (`tenant/sandboxes.ts`) bumps the volume generation: a new name, so a new
25
+ * - **Reset** (`tenant/environments.ts`) bumps the volume generation: a new name, so a new
31
26
  * Sandbox and volume; the old name is `retiring` and deleted here.
27
+ *
28
+ * - **Setup** (R2d step 2): a failed setup (`sandbox/setup.ts`) leaves the pod `failed` with a
29
+ * reason that starts `setup:`, even while the pod is ready, until a turn start asks it to run.
30
+ *
31
+ * An unused Environment is deleted by the Tenant sweep (`lifecycle.deleteAfter`), not here.
32
32
  */
33
- import type { SandboxEventPayload, SandboxEventType } from "@nylorun/core/contracts";
34
- import type { SandboxPodPatch, SandboxPodState, SandboxResource } from "../../store/types.js";
33
+ import type { EnvironmentEventPayload, EnvironmentEventType } from "@nylorun/core/contracts";
34
+ import type { Environment, SandboxPodPatch, SandboxPodState } from "../../store/types.js";
35
35
  import type { PodStatus } from "./client.js";
36
+ /** A setup failure's `reason` starts with this (`sandbox/setup.ts`); it is sticky. */
37
+ export declare const SETUP_FAILED = "setup:";
36
38
  /** A pod not ready this long after it was asked to run has failed. */
37
39
  export declare const READY_DEADLINE_MS: number;
38
40
  /** How soon to look again while the pod changes. */
39
41
  export declare const POLL_MS = 2000;
40
42
  /** How soon to look again when the sandboxes service does not answer. */
41
43
  export declare const UNREACHABLE_RETRY_MS = 5000;
42
- /** The Tenant's lifecycle settings (D36), read each time a decision is made. */
44
+ /** The lifecycle settings (D36), read each time a decision is made. */
43
45
  export interface PodLifecycleConfig {
44
- readonly onExpiry: "retain" | "delete";
45
46
  readonly idleMs: number;
46
47
  }
47
- /** Why the decision is made: a reconcile, or one of the sandbox's timers firing. */
48
- export type PodTrigger = "reconcile" | "idle" | "ttl";
49
- export type PodTimer = "idle" | "ttl";
50
- export type PodRow = SandboxResource & {
48
+ /** Why the decision is made: a reconcile, or the idle timer firing. */
49
+ export type PodTrigger = "reconcile" | "idle";
50
+ export type PodTimer = "idle";
51
+ export type PodRow = Environment & {
51
52
  readonly pod: SandboxPodState;
52
53
  };
53
54
  export interface PodFacts {
@@ -57,14 +58,14 @@ export interface PodFacts {
57
58
  readonly status: PodStatus | undefined;
58
59
  /** The retiring Sandbox's status, when the row names one. */
59
60
  readonly retiring?: PodStatus | undefined;
60
- /** A session attached to the sandbox has a turn running. */
61
+ /** A session that names the Environment has a turn running. */
61
62
  readonly busy: boolean;
62
63
  readonly now: number;
63
64
  readonly trigger: PodTrigger;
64
65
  }
65
- export interface PodEvent<T extends SandboxEventType = SandboxEventType> {
66
+ export interface PodEvent<T extends EnvironmentEventType = EnvironmentEventType> {
66
67
  readonly type: T;
67
- readonly payload: SandboxEventPayload<T>;
68
+ readonly payload: EnvironmentEventPayload<T>;
68
69
  }
69
70
  export interface PodDecision {
70
71
  /** Apply the row's spec (after `patch`) in this mode; `rotateJoin` mints a new join token. */
@@ -76,7 +77,7 @@ export interface PodDecision {
76
77
  readonly delete: readonly string[];
77
78
  readonly patch: SandboxPodPatch;
78
79
  readonly events: readonly PodEvent[];
79
- /** The Sandbox is gone and the sandbox deleted: remove the row. */
80
+ /** The Sandbox is gone and the Environment deleted: remove the row. */
80
81
  readonly removeRow?: true;
81
82
  /** Look again this soon. */
82
83
  readonly retryAfterMs?: number;