@nylorun/runtime 0.23.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 (252) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/README.md +79 -28
  3. package/dist/adapters/execution/restate.js +1 -1
  4. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  5. package/dist/adapters/replication/pgoutput.js +6 -3
  6. package/dist/adapters/sandbox/local.d.ts +9 -1
  7. package/dist/adapters/sandbox/local.js +16 -4
  8. package/dist/adapters/sandbox/virtual.js +4 -1
  9. package/dist/api/a2a/endpoint.js +5 -1
  10. package/dist/api/a2a/routes.d.ts +2 -0
  11. package/dist/api/a2a/routes.js +2 -2
  12. package/dist/api/a2a/tasks.js +2 -1
  13. package/dist/api/ag-ui/endpoint.js +4 -2
  14. package/dist/api/ag-ui/history.js +4 -1
  15. package/dist/api/ag-ui/routes.d.ts +4 -1
  16. package/dist/api/ag-ui/routes.js +11 -10
  17. package/dist/api/ag-ui/translate.js +3 -2
  18. package/dist/api/components.d.ts +4953 -1204
  19. package/dist/api/components.js +25 -16
  20. package/dist/api/gate/keys.js +7 -1
  21. package/dist/api/gate/routes.d.ts +6 -0
  22. package/dist/api/gate/routes.js +39 -3
  23. package/dist/api/http/app.js +4 -4
  24. package/dist/api/http/define.d.ts +6 -0
  25. package/dist/api/http/define.js +13 -1
  26. package/dist/api/http/respond.js +1 -1
  27. package/dist/api/http/routes/artifacts.js +35 -8
  28. package/dist/api/http/routes/coverage.d.ts +9 -0
  29. package/dist/api/http/routes/coverage.js +34 -0
  30. package/dist/api/http/routes/environments.d.ts +11 -0
  31. package/dist/api/http/routes/environments.js +283 -0
  32. package/dist/api/http/routes/mcp.d.ts +1 -1
  33. package/dist/api/http/routes/mcp.js +2 -2
  34. package/dist/api/http/routes/me.js +1 -1
  35. package/dist/api/http/routes/reads.d.ts +45 -9
  36. package/dist/api/http/routes/reads.js +54 -5
  37. package/dist/api/http/routes/sessions.js +11 -6
  38. package/dist/api/http/routes/tenant.js +32 -10
  39. package/dist/api/openapi.js +31 -26
  40. package/dist/artifacts/disk.d.ts +59 -0
  41. package/dist/artifacts/disk.js +563 -0
  42. package/dist/artifacts/export.d.ts +1 -1
  43. package/dist/artifacts/export.js +2 -3
  44. package/dist/artifacts/folders.d.ts +6 -3
  45. package/dist/artifacts/folders.js +17 -12
  46. package/dist/artifacts/service.d.ts +2 -1
  47. package/dist/artifacts/service.js +6 -1
  48. package/dist/artifacts/workspace.d.ts +1 -1
  49. package/dist/artifacts/workspace.js +1 -1
  50. package/dist/core/flow-host.d.ts +2 -3
  51. package/dist/core/flow-host.js +3 -0
  52. package/dist/core/provider.d.ts +3 -2
  53. package/dist/core/provider.js +5 -37
  54. package/dist/core/sandbox-routes.d.ts +2 -21
  55. package/dist/core/sandbox-routes.js +10 -43
  56. package/dist/execution/types.d.ts +1 -1
  57. package/dist/gates/contract.d.ts +3 -24
  58. package/dist/gates/contract.js +1 -36
  59. package/dist/gates/egress-credentials.d.ts +9 -0
  60. package/dist/gates/egress-credentials.js +11 -4
  61. package/dist/gates/egress.d.ts +28 -1
  62. package/dist/gates/egress.js +115 -10
  63. package/dist/gates/fixture-model.d.ts +28 -0
  64. package/dist/gates/fixture-model.js +92 -0
  65. package/dist/gates/handler.js +4 -4
  66. package/dist/gates/http-tool.d.ts +6 -2
  67. package/dist/gates/http-tool.js +3 -5
  68. package/dist/gates/in-process.d.ts +5 -0
  69. package/dist/gates/in-process.js +12 -1
  70. package/dist/gates/journal.d.ts +30 -0
  71. package/dist/gates/journal.js +52 -0
  72. package/dist/gates/mcp-handler.d.ts +4 -0
  73. package/dist/gates/mcp-handler.js +5 -1
  74. package/dist/gates/meter.d.ts +10 -2
  75. package/dist/gates/meter.js +20 -5
  76. package/dist/gates/policy.d.ts +49 -0
  77. package/dist/gates/policy.js +134 -0
  78. package/dist/gates/tenant-vaults.d.ts +7 -7
  79. package/dist/gates/tenant-vaults.js +5 -5
  80. package/dist/gates/tool-calls.d.ts +10 -5
  81. package/dist/gates/tool-calls.js +28 -10
  82. package/dist/gates/tool-client.d.ts +8 -0
  83. package/dist/gates/tool-client.js +27 -11
  84. package/dist/gates/tool-contract.d.ts +10 -68
  85. package/dist/gates/tool-contract.js +1 -85
  86. package/dist/gates/tool-gate.d.ts +1 -1
  87. package/dist/harness/calls.d.ts +12 -5
  88. package/dist/harness/calls.js +21 -12
  89. package/dist/harness/executors.js +12 -9
  90. package/dist/harness/main.js +18 -5
  91. package/dist/harness/pod.d.ts +34 -0
  92. package/dist/harness/pod.js +41 -0
  93. package/dist/harness/service.d.ts +0 -1
  94. package/dist/harness/service.js +13 -5
  95. package/dist/harness/setup.d.ts +27 -0
  96. package/dist/harness/setup.js +150 -0
  97. package/dist/harness/ws-client.js +3 -2
  98. package/dist/harness/ws-port.d.ts +1 -5
  99. package/dist/harness/ws-port.js +1 -5
  100. package/dist/harness-api/in-process.js +3 -3
  101. package/dist/harness-api/record.d.ts +15 -1
  102. package/dist/harness-api/record.js +174 -44
  103. package/dist/harness-api/redact.d.ts +3 -0
  104. package/dist/harness-api/redact.js +45 -0
  105. package/dist/harness-api/server.d.ts +8 -0
  106. package/dist/harness-api/server.js +93 -13
  107. package/dist/harness-api/start.d.ts +10 -3
  108. package/dist/harness-api/start.js +40 -10
  109. package/dist/harness-api/tool-journal.d.ts +96 -0
  110. package/dist/harness-api/tool-journal.js +102 -0
  111. package/dist/harness-api/tool-status.d.ts +13 -0
  112. package/dist/harness-api/tool-status.js +26 -0
  113. package/dist/harness-api/workspace.d.ts +6 -3
  114. package/dist/harness-api/workspace.js +27 -11
  115. package/dist/harness-api/ws-server.d.ts +11 -3
  116. package/dist/harness-api/ws-server.js +94 -15
  117. package/dist/host/gates.d.ts +2 -1
  118. package/dist/host/gates.js +10 -2
  119. package/dist/host/main.js +54 -5
  120. package/dist/host/operate.js +67 -5
  121. package/dist/host/stack-config.d.ts +64 -0
  122. package/dist/host/stack-config.js +61 -1
  123. package/dist/keys/client.js +4 -1
  124. package/dist/keys/keys.d.ts +5 -5
  125. package/dist/keys/keys.js +5 -7
  126. package/dist/management-openapi.json +999 -2211
  127. package/dist/mcp/connect.d.ts +25 -20
  128. package/dist/mcp/connect.js +29 -30
  129. package/dist/mcp/pool.d.ts +2 -2
  130. package/dist/mcp/pool.js +1 -1
  131. package/dist/mcp/preview.d.ts +3 -3
  132. package/dist/mcp/preview.js +10 -20
  133. package/dist/mcp/snapshot.d.ts +5 -11
  134. package/dist/mcp/snapshot.js +6 -7
  135. package/dist/model/pi-model.js +8 -0
  136. package/dist/openapi.json +6955 -2526
  137. package/dist/reads/types.d.ts +16 -3
  138. package/dist/record/append.d.ts +3 -1
  139. package/dist/record/append.js +1 -0
  140. package/dist/record/envelope.d.ts +5 -1
  141. package/dist/record/envelope.js +7 -3
  142. package/dist/record/environment.d.ts +25 -0
  143. package/dist/record/environment.js +44 -0
  144. package/dist/record/index.d.ts +1 -1
  145. package/dist/record/index.js +1 -1
  146. package/dist/sandbox/disk-fs.d.ts +95 -0
  147. package/dist/sandbox/disk-fs.js +206 -0
  148. package/dist/sandbox/join.d.ts +17 -18
  149. package/dist/sandbox/join.js +42 -15
  150. package/dist/sandbox/manager.d.ts +16 -8
  151. package/dist/sandbox/manager.js +33 -17
  152. package/dist/sandbox/placement.d.ts +13 -12
  153. package/dist/sandbox/placement.js +29 -12
  154. package/dist/sandbox/pods/client.d.ts +6 -1
  155. package/dist/sandbox/pods/lifecycle.d.ts +28 -27
  156. package/dist/sandbox/pods/lifecycle.js +14 -26
  157. package/dist/sandbox/pods/reconcile.js +18 -12
  158. package/dist/sandbox/pods/spec.d.ts +15 -13
  159. package/dist/sandbox/pods/spec.js +16 -20
  160. package/dist/sandbox/records.d.ts +2 -2
  161. package/dist/sandbox/records.js +3 -3
  162. package/dist/sandbox/resolve.d.ts +35 -27
  163. package/dist/sandbox/resolve.js +66 -41
  164. package/dist/sandbox/session-sandbox.js +1 -1
  165. package/dist/sandbox/setup.d.ts +61 -0
  166. package/dist/sandbox/setup.js +123 -0
  167. package/dist/sandbox/share.d.ts +11 -21
  168. package/dist/sandbox/share.js +12 -41
  169. package/dist/sandbox/tenant-config.d.ts +12 -14
  170. package/dist/sandbox/tenant-config.js +12 -17
  171. package/dist/sandbox/types.d.ts +6 -0
  172. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  173. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  174. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  175. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  176. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  177. package/dist/store/postgres/reads.js +117 -17
  178. package/dist/store/postgres/record-writer.d.ts +4 -4
  179. package/dist/store/postgres/record-writer.js +9 -9
  180. package/dist/store/postgres/record.d.ts +7 -0
  181. package/dist/store/postgres/record.js +23 -2
  182. package/dist/store/postgres/schema.d.ts +934 -761
  183. package/dist/store/postgres/schema.js +102 -73
  184. package/dist/store/postgres/store.js +119 -66
  185. package/dist/store/types.d.ts +109 -48
  186. package/dist/tenant/advance.d.ts +7 -6
  187. package/dist/tenant/advance.js +115 -42
  188. package/dist/tenant/artifact-tool.js +3 -4
  189. package/dist/tenant/commands.d.ts +15 -2
  190. package/dist/tenant/commands.js +93 -12
  191. package/dist/tenant/context.d.ts +48 -22
  192. package/dist/tenant/context.js +4 -15
  193. package/dist/tenant/effects.d.ts +1 -1
  194. package/dist/tenant/effects.js +37 -20
  195. package/dist/tenant/environments.d.ts +167 -0
  196. package/dist/tenant/environments.js +823 -0
  197. package/dist/tenant/harness-tokens.d.ts +21 -0
  198. package/dist/tenant/harness-tokens.js +61 -0
  199. package/dist/tenant/host-token.d.ts +1 -1
  200. package/dist/tenant/host-token.js +2 -4
  201. package/dist/tenant/identity-file.d.ts +4 -4
  202. package/dist/tenant/identity-file.js +17 -8
  203. package/dist/tenant/issuers.js +3 -3
  204. package/dist/tenant/principals.js +1 -1
  205. package/dist/tenant/reset.js +3 -3
  206. package/dist/tenant/runtime.d.ts +6 -1
  207. package/dist/tenant/runtime.js +7 -1
  208. package/dist/tenant/session.d.ts +3 -3
  209. package/dist/tenant/session.js +0 -14
  210. package/dist/tenant/sessions.d.ts +13 -13
  211. package/dist/tenant/sessions.js +61 -138
  212. package/dist/tenant/signing-keys.d.ts +5 -1
  213. package/dist/tenant/status.js +3 -3
  214. package/dist/tenant/sweep.js +12 -6
  215. package/dist/tenant/transcript.d.ts +210 -20
  216. package/dist/tenant/transcript.js +62 -11
  217. package/dist/tenant/turn-timing.d.ts +43 -0
  218. package/dist/tenant/turn-timing.js +39 -0
  219. package/dist/tenant/types.d.ts +2 -0
  220. package/dist/traces/ids.d.ts +42 -0
  221. package/dist/traces/ids.js +85 -0
  222. package/dist/traces/key.d.ts +6 -0
  223. package/dist/traces/key.js +37 -0
  224. package/dist/traces/map.d.ts +113 -0
  225. package/dist/traces/map.js +317 -0
  226. package/dist/traces/options.d.ts +7 -0
  227. package/dist/traces/options.js +1 -0
  228. package/dist/traces/otlp.d.ts +42 -0
  229. package/dist/traces/otlp.js +84 -0
  230. package/dist/traces/projector.d.ts +58 -0
  231. package/dist/traces/projector.js +200 -0
  232. package/dist/traces/read.d.ts +23 -0
  233. package/dist/traces/read.js +16 -0
  234. package/dist/vault/coverage.d.ts +26 -0
  235. package/dist/vault/coverage.js +64 -0
  236. package/dist/vault/error.d.ts +11 -1
  237. package/dist/vault/error.js +5 -1
  238. package/dist/vault/service.d.ts +47 -48
  239. package/dist/vault/service.js +321 -382
  240. package/dist/vault/sources.d.ts +5 -16
  241. package/dist/vault/sources.js +6 -8
  242. package/dist/version.d.ts +1 -1
  243. package/dist/version.js +2 -2
  244. package/package.json +13 -13
  245. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  246. package/dist/api/http/routes/sandboxes.js +0 -153
  247. package/dist/api/http/routes/vaults.d.ts +0 -10
  248. package/dist/api/http/routes/vaults.js +0 -139
  249. package/dist/record/sandbox.d.ts +0 -25
  250. package/dist/record/sandbox.js +0 -43
  251. package/dist/tenant/sandboxes.d.ts +0 -123
  252. package/dist/tenant/sandboxes.js +0 -566
@@ -0,0 +1,123 @@
1
+ import { parseSandboxDuration } from "@nylorun/core/define";
2
+ import { contentKey, readManifest } from "../artifacts/folders.js";
3
+ import { SETUP_FAILED } from "./pods/lifecycle.js";
4
+ export { SETUP_FAILED };
5
+ /** The most of a failed command's output kept in its event and reason. */
6
+ const OUTPUT_TAIL = 4 * 1024;
7
+ /** A command's timeout when the recipe gives none. */
8
+ const DEFAULT_TIMEOUT_MS = 10 * 60_000;
9
+ /** The setup of `recipe`, or undefined when it has neither commands nor files. */
10
+ export async function setupPlanOf(ctx, t, environment) {
11
+ const recipe = environment.applied;
12
+ if (!recipe.setup?.length && !recipe.files?.length)
13
+ return undefined;
14
+ return {
15
+ recipeVersion: environment.appliedVersion,
16
+ commands: (recipe.setup ?? []).map((command) => ({
17
+ command: command.command,
18
+ cwd: command.cwd ?? "/workspace",
19
+ timeoutMs: (command.timeout && parseSandboxDuration(command.timeout)) || DEFAULT_TIMEOUT_MS,
20
+ })),
21
+ files: (recipe.files ?? []).map((file, index) => ({ index, path: file.path })),
22
+ environment: await ctx.vault.environmentVariables(t, environment.id),
23
+ };
24
+ }
25
+ export function parseSetupReport(value) {
26
+ if (!value || typeof value !== "object")
27
+ return undefined;
28
+ const report = value;
29
+ if (report.status !== "ok" && report.status !== "failed")
30
+ return undefined;
31
+ const index = typeof report.index === "number" && Number.isInteger(report.index) ? report.index : undefined;
32
+ const exitCode = typeof report.exitCode === "number" && Number.isInteger(report.exitCode) ? report.exitCode : undefined;
33
+ const output = typeof report.output === "string" ? report.output.slice(-OUTPUT_TAIL) : undefined;
34
+ return {
35
+ status: report.status,
36
+ ...(index === undefined ? {} : { index }),
37
+ ...(exitCode === undefined ? {} : { exitCode }),
38
+ ...(output === undefined ? {} : { output }),
39
+ };
40
+ }
41
+ /**
42
+ * Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
43
+ * the pod is `failed` with a reason that starts `setup:`, and `environment.failed` says which
44
+ * command failed and how. A report from an older host, or a success, changes nothing.
45
+ */
46
+ export async function recordSetup(ctx, id, epoch, report) {
47
+ await ctx.store.tx(async (t) => {
48
+ const environment = await t.environment(id, { lock: true });
49
+ const pod = environment?.pod;
50
+ if (!environment || !pod || pod.hostEpoch !== epoch || pod.desired !== "running")
51
+ return;
52
+ if (report.status === "ok")
53
+ return;
54
+ const command = report.index === undefined ? undefined : environment.applied.setup?.[report.index];
55
+ const what = command === undefined ? "copying the input files failed" : `command ${report.index + 1} (${command.command.slice(0, 80)}) exited ${report.exitCode ?? "abnormally"}`;
56
+ const tail = (report.output ?? "").trim().slice(-500);
57
+ const reason = `${SETUP_FAILED} ${what}${tail ? `: ${tail}` : ""}`;
58
+ await t.updateEnvironmentPod(id, { observed: "failed", reason }, new Date().toISOString());
59
+ await t.environmentEvent(id, "environment.failed", {
60
+ reason,
61
+ setup: {
62
+ ...(report.index === undefined ? {} : { index: report.index }),
63
+ ...(report.exitCode === undefined ? {} : { exitCode: report.exitCode }),
64
+ output: report.output ?? "",
65
+ },
66
+ });
67
+ });
68
+ if (report.status === "failed")
69
+ ctx.config.logger.warn("environment setup failed", { environmentId: id, index: report.index, exitCode: report.exitCode });
70
+ else
71
+ ctx.config.logger.info("environment setup done", { environmentId: id });
72
+ }
73
+ /** Whether the engine of `id` should run its setup again: the pod is no longer `failed`. */
74
+ export async function setupState(ctx, id) {
75
+ return ctx.store.tx(async (t) => {
76
+ const environment = await t.environment(id);
77
+ const pod = environment?.pod;
78
+ if (!environment || !pod)
79
+ return { retry: false };
80
+ if (pod.observed === "failed")
81
+ return { retry: false };
82
+ const setup = await setupPlanOf(ctx, t, environment);
83
+ return { retry: true, ...(setup ? { setup } : {}) };
84
+ });
85
+ }
86
+ /**
87
+ * Input file `index` of Environment `id`'s applied recipe: a file artifact's bytes, a folder's
88
+ * listing, or (with `path`) one of the folder's files. Undefined when there is no such file.
89
+ */
90
+ export async function setupFile(ctx, id, index, path) {
91
+ const found = await ctx.store.tx(async (t) => {
92
+ const environment = await t.environment(id);
93
+ const file = environment?.applied.files?.[index];
94
+ if (!file)
95
+ return undefined;
96
+ const artifact = await t.artifact(file.artifactId);
97
+ if (!artifact || artifact.sessionId !== null)
98
+ return undefined;
99
+ const version = await t.artifactVersion(file.artifactId, file.version);
100
+ return version ? { artifact, version } : undefined;
101
+ });
102
+ if (!found)
103
+ return undefined;
104
+ if (found.artifact.kind === "file") {
105
+ if (path !== undefined)
106
+ return undefined;
107
+ const got = await ctx.blobs.get(found.version.blobKey);
108
+ return got ? { kind: "file", contentType: found.version.contentType ?? found.artifact.contentType, body: got.body } : undefined;
109
+ }
110
+ const manifest = await readManifest(ctx.blobs, found.version);
111
+ if (path === undefined)
112
+ return {
113
+ kind: "folder",
114
+ entries: manifest.entries
115
+ .filter((entry) => entry.type === undefined)
116
+ .map((entry) => ({ path: entry.path, size: entry.size })),
117
+ };
118
+ const entry = manifest.entries.find((item) => item.path === path && item.type === undefined);
119
+ if (!entry)
120
+ return undefined;
121
+ const got = await ctx.blobs.get(contentKey(entry.sha256));
122
+ return got ? { kind: "file", contentType: entry.contentType, body: got.body } : undefined;
123
+ }
@@ -1,19 +1,16 @@
1
1
  /**
2
- * Shared sandboxes: one sandbox per owning session, attached via PutSession.sandbox =
3
- * { session }; keys use the owner id. A sandbox is either declared by the definition (an agent
4
- * capability or a workflow's `sandbox`) or chosen when the session was opened and pinned on the
5
- * session record (`sandbox`).
2
+ * A session's sandbox: the one of the Environment it names (protocol 11), whose id keys the
3
+ * workspace, with the spec pinned when the session was opened (`Session.sandbox`). Definitions
4
+ * no longer declare one (Sandboxes v3); `sandboxSpecOf` still reads a pinned manifest's.
6
5
  */
7
6
  import { type AgentManifest, type CapabilityManifest, type SandboxManifest, type WorkflowManifest } from "@nylorun/core/define";
8
7
  export type SessionSandboxRef = {
9
8
  readonly id: string;
10
9
  readonly ownerUserId: string;
11
10
  readonly manifest: AgentManifest | WorkflowManifest | Record<string, unknown>;
12
- /** Session whose id keys the sandbox. Absent means this session owns its own. */
13
- readonly sandboxOwnerId?: string | null;
14
- /** The sandbox resource the session is attached to; its id keys the workspace. */
15
- readonly sandboxId?: string;
16
- /** The sandbox chosen when the session was opened (or inherited from its owner), resolved. */
11
+ /** The Environment the session names; its id keys the workspace. */
12
+ readonly environmentId?: string;
13
+ /** The Environment's sandbox spec, pinned when the session was opened. */
17
14
  readonly sandbox?: SandboxManifest;
18
15
  };
19
16
  /** The session's sandbox spec: the one pinned at open, else the one its definition declares. */
@@ -26,21 +23,14 @@ export declare function sandboxSpecOf(manifest: AgentManifest | WorkflowManifest
26
23
  * one (Sandboxes v3), so registration refuses any of these.
27
24
  */
28
25
  export declare function declaredSandboxes(manifest: unknown, into?: string[]): string[];
29
- export declare function sandboxSpecsEqual(a: SandboxManifest | undefined, b: SandboxManifest | undefined): boolean;
30
26
  /** Synthetic capability so SandboxManager.run can use a workflow (or shared) spec. */
31
27
  export declare function capabilityForSandbox(sandbox: SandboxManifest): CapabilityManifest;
32
28
  /**
33
- * Resolve the session id that keys the sandbox. Walks one hop via sandboxOwnerId;
34
- * cycles fall back to the starting session.
29
+ * The workspace a session's sandbox tools run in: the session, whose log records the
30
+ * workspace's events, and its Environment, whose id keys the workspace.
35
31
  */
36
- export declare function owningSandboxSessionId(session: SessionSandboxRef, lookup: (id: string) => SessionSandboxRef | undefined): string;
37
- /**
38
- * The workspace a session's sandbox tools run in: the owning session (one hop through
39
- * `sandboxOwnerId`, whose log records the workspace's events) and, when that session is
40
- * attached to a sandbox resource, the resource's id, which keys the workspace.
41
- */
42
- export declare function sandboxWorkspaceOf(session: SessionSandboxRef, lookup: (id: string) => SessionSandboxRef | undefined): {
32
+ export declare function sandboxWorkspaceOf(session: SessionSandboxRef): {
43
33
  readonly ownerId: string;
44
- readonly sandboxId?: string;
34
+ readonly environmentId?: string;
45
35
  };
46
- export declare function sessionHasSandbox(session: SessionSandboxRef, lookup: (id: string) => SessionSandboxRef | undefined): boolean;
36
+ export declare function sessionHasSandbox(session: SessionSandboxRef): boolean;
@@ -1,10 +1,9 @@
1
1
  /**
2
- * Shared sandboxes: one sandbox per owning session, attached via PutSession.sandbox =
3
- * { session }; keys use the owner id. A sandbox is either declared by the definition (an agent
4
- * capability or a workflow's `sandbox`) or chosen when the session was opened and pinned on the
5
- * session record (`sandbox`).
2
+ * A session's sandbox: the one of the Environment it names (protocol 11), whose id keys the
3
+ * workspace, with the spec pinned when the session was opened (`Session.sandbox`). Definitions
4
+ * no longer declare one (Sandboxes v3); `sandboxSpecOf` still reads a pinned manifest's.
6
5
  */
7
- import { canonical, } from "@nylorun/core/define";
6
+ import {} from "@nylorun/core/define";
8
7
  /** The session's sandbox spec: the one pinned at open, else the one its definition declares. */
9
8
  export function sessionSandboxSpec(session) {
10
9
  return session.sandbox ?? sandboxSpecOf(session.manifest);
@@ -47,13 +46,6 @@ export function declaredSandboxes(manifest, into = []) {
47
46
  }
48
47
  return into;
49
48
  }
50
- export function sandboxSpecsEqual(a, b) {
51
- if (a === undefined && b === undefined)
52
- return true;
53
- if (a === undefined || b === undefined)
54
- return false;
55
- return canonical(a) === canonical(b);
56
- }
57
49
  /** Synthetic capability so SandboxManager.run can use a workflow (or shared) spec. */
58
50
  export function capabilityForSandbox(sandbox) {
59
51
  return {
@@ -64,35 +56,14 @@ export function capabilityForSandbox(sandbox) {
64
56
  };
65
57
  }
66
58
  /**
67
- * Resolve the session id that keys the sandbox. Walks one hop via sandboxOwnerId;
68
- * cycles fall back to the starting session.
69
- */
70
- export function owningSandboxSessionId(session, lookup) {
71
- const seen = new Set();
72
- let current = session;
73
- while (current) {
74
- if (seen.has(current.id))
75
- return session.id;
76
- seen.add(current.id);
77
- const ownerId = current.sandboxOwnerId;
78
- if (!ownerId || ownerId === current.id)
79
- return current.id;
80
- current = lookup(ownerId);
81
- }
82
- return session.id;
83
- }
84
- /**
85
- * The workspace a session's sandbox tools run in: the owning session (one hop through
86
- * `sandboxOwnerId`, whose log records the workspace's events) and, when that session is
87
- * attached to a sandbox resource, the resource's id, which keys the workspace.
59
+ * The workspace a session's sandbox tools run in: the session, whose log records the
60
+ * workspace's events, and its Environment, whose id keys the workspace.
88
61
  */
89
- export function sandboxWorkspaceOf(session, lookup) {
90
- const ownerId = owningSandboxSessionId(session, lookup);
91
- const owner = ownerId === session.id ? session : lookup(ownerId) ?? session;
92
- return owner.sandboxId === undefined ? { ownerId } : { ownerId, sandboxId: owner.sandboxId };
62
+ export function sandboxWorkspaceOf(session) {
63
+ return session.environmentId === undefined
64
+ ? { ownerId: session.id }
65
+ : { ownerId: session.id, environmentId: session.environmentId };
93
66
  }
94
- export function sessionHasSandbox(session, lookup) {
95
- const ownerId = owningSandboxSessionId(session, lookup);
96
- const owner = ownerId === session.id ? session : lookup(ownerId) ?? session;
97
- return sessionSandboxSpec(owner) !== undefined || sessionSandboxSpec(session) !== undefined;
67
+ export function sessionHasSandbox(session) {
68
+ return sessionSandboxSpec(session) !== undefined;
98
69
  }
@@ -1,41 +1,39 @@
1
1
  /**
2
- * A Tenant's sandbox configuration, stored as Tenant setting `sandbox.config`: the sandbox a
3
- * session gets when it names none, and the limits every session's sandbox must fit. Unset
2
+ * A Tenant's Environment settings, stored as Tenant setting `environments.config`: the limits
3
+ * every Environment's recipe must fit, the pods' stop grace and the harness placement. Unset
4
4
  * fields take the defaults below.
5
5
  */
6
- import { type SandboxInlineRequest, type SandboxPlacementHost, type TenantSandboxConfig } from "@nylorun/core/contracts";
7
- export declare const SANDBOX_CONFIG_SETTING = "sandbox.config";
8
- /** The most a session may ask for when the Tenant sets no maximum. */
6
+ import { type PlacementHost, type TenantEnvironmentsConfig } from "@nylorun/core/contracts";
7
+ export declare const ENVIRONMENTS_CONFIG_SETTING = "environments.config";
8
+ /** The Tenant's settings, as stored (`environments.config`). */
9
+ export type TenantSandboxConfig = TenantEnvironmentsConfig;
10
+ /** The most an Environment may ask for when the Tenant sets no maximum. */
9
11
  export declare const DEFAULT_MAX_CPUS = 4;
10
12
  export declare const DEFAULT_MAX_MEMORY_MIB = 8192;
11
13
  export declare const DEFAULT_SANDBOX_IDLE = "15m";
12
- /** The most sandbox resources a Tenant holds when it sets no limit. */
13
- export declare const DEFAULT_MAX_SANDBOXES = 100;
14
+ /** The most Environments a Tenant holds when it sets no limit. */
15
+ export declare const DEFAULT_MAX_ENVIRONMENTS = 100;
14
16
  export interface SandboxResourcesLimit {
15
17
  readonly cpus: number;
16
18
  readonly memoryMiB: number;
17
19
  }
18
20
  /** A configuration with every default applied. */
19
21
  export interface EffectiveSandboxConfig {
20
- readonly default: "none" | "virtual" | SandboxInlineRequest;
21
22
  readonly limits: {
22
23
  readonly network: readonly string[];
23
24
  readonly resources: SandboxResourcesLimit;
24
25
  readonly defaultResources: SandboxResourcesLimit;
25
26
  readonly idle: string;
26
- /** The most sandbox resources (`PUT /v1/sandboxes/{id}`) the Tenant may hold. */
27
- readonly sandboxes: number;
28
- /** Pods: the longest `lifecycle.ttl`. */
29
- readonly ttl?: string;
27
+ /** The most Environments the Tenant may hold. */
28
+ readonly environments: number;
30
29
  };
31
30
  /** Pods (F7.2, D36). */
32
31
  readonly lifecycle: {
33
- readonly onExpiry: "retain" | "delete";
34
32
  readonly stopGrace: string;
35
33
  };
36
34
  /** Where each harness may run (D38). */
37
35
  readonly placement: Readonly<Record<string, {
38
- readonly hosts: readonly SandboxPlacementHost[];
36
+ readonly hosts: readonly PlacementHost[];
39
37
  }>>;
40
38
  }
41
39
  interface SettingsReader {
@@ -1,18 +1,18 @@
1
1
  /**
2
- * A Tenant's sandbox configuration, stored as Tenant setting `sandbox.config`: the sandbox a
3
- * session gets when it names none, and the limits every session's sandbox must fit. Unset
2
+ * A Tenant's Environment settings, stored as Tenant setting `environments.config`: the limits
3
+ * every Environment's recipe must fit, the pods' stop grace and the harness placement. Unset
4
4
  * fields take the defaults below.
5
5
  */
6
- import { TenantSandboxConfigSchema, } from "@nylorun/core/contracts";
6
+ import { TenantEnvironmentsConfigSchema, } from "@nylorun/core/contracts";
7
7
  import { parseSandboxSize } from "@nylorun/core/define";
8
8
  import { DEFAULT_SANDBOX_CPUS, DEFAULT_SANDBOX_MEMORY_MIB, DEV_PRESET_HOSTS, } from "./policy.js";
9
- export const SANDBOX_CONFIG_SETTING = "sandbox.config";
10
- /** The most a session may ask for when the Tenant sets no maximum. */
9
+ export const ENVIRONMENTS_CONFIG_SETTING = "environments.config";
10
+ /** The most an Environment may ask for when the Tenant sets no maximum. */
11
11
  export const DEFAULT_MAX_CPUS = 4;
12
12
  export const DEFAULT_MAX_MEMORY_MIB = 8192;
13
13
  export const DEFAULT_SANDBOX_IDLE = "15m";
14
- /** The most sandbox resources a Tenant holds when it sets no limit. */
15
- export const DEFAULT_MAX_SANDBOXES = 100;
14
+ /** The most Environments a Tenant holds when it sets no limit. */
15
+ export const DEFAULT_MAX_ENVIRONMENTS = 100;
16
16
  export function memoryMiB(value) {
17
17
  const bytes = parseSandboxSize(value);
18
18
  return bytes === undefined ? 0 : Math.max(128, Math.ceil(bytes / 1024 ** 2));
@@ -26,7 +26,6 @@ function resources(value, fallback) {
26
26
  export function effectiveSandboxConfig(config) {
27
27
  const limits = config.limits ?? {};
28
28
  return {
29
- default: config.default ?? "none",
30
29
  limits: {
31
30
  network: limits.network ?? DEV_PRESET_HOSTS,
32
31
  resources: resources(limits.resources, {
@@ -38,23 +37,19 @@ export function effectiveSandboxConfig(config) {
38
37
  memoryMiB: DEFAULT_SANDBOX_MEMORY_MIB,
39
38
  }),
40
39
  idle: limits.idle ?? DEFAULT_SANDBOX_IDLE,
41
- sandboxes: limits.sandboxes ?? DEFAULT_MAX_SANDBOXES,
42
- ...(limits.ttl === undefined ? {} : { ttl: limits.ttl }),
43
- },
44
- lifecycle: {
45
- onExpiry: config.lifecycle?.onExpiry ?? "retain",
46
- stopGrace: config.lifecycle?.stopGrace ?? "10s",
40
+ environments: limits.environments ?? DEFAULT_MAX_ENVIRONMENTS,
47
41
  },
42
+ lifecycle: { stopGrace: config.lifecycle?.stopGrace ?? "10s" },
48
43
  placement: config.placement ?? { "*": { hosts: ["harness-container", "sandbox"] } },
49
44
  };
50
45
  }
51
46
  /** The stored configuration, or `{}` when the Tenant has none. */
52
47
  export async function readSandboxConfig(t) {
53
- const raw = await t.getSetting(SANDBOX_CONFIG_SETTING);
48
+ const raw = await t.getSetting(ENVIRONMENTS_CONFIG_SETTING);
54
49
  if (raw === undefined)
55
50
  return {};
56
- return TenantSandboxConfigSchema.parse(JSON.parse(raw));
51
+ return TenantEnvironmentsConfigSchema.parse(JSON.parse(raw));
57
52
  }
58
53
  export async function writeSandboxConfig(t, config) {
59
- await t.putSetting(SANDBOX_CONFIG_SETTING, JSON.stringify(config));
54
+ await t.putSetting(ENVIRONMENTS_CONFIG_SETTING, JSON.stringify(config));
60
55
  }
@@ -32,6 +32,7 @@ export interface SandboxSpec {
32
32
  readonly memoryMiB: number;
33
33
  readonly network: ResolvedNetwork;
34
34
  }
35
+ import type { DiskRoots } from "./disk-fs.js";
35
36
  export interface ExecRequest {
36
37
  readonly command: string;
37
38
  readonly cwd: string;
@@ -73,6 +74,11 @@ export interface SandboxHandle {
73
74
  writeFile(path: string, content: string | Uint8Array): Promise<void>;
74
75
  /** Release compute; files persist. */
75
76
  stop(): Promise<void>;
77
+ /**
78
+ * Where the disk's roots are, for disk versions (R2d step 3, `sandbox/disk-fs.ts`): the
79
+ * workspace, and in a pod the engine's `/harness`. Absent for a backend without a disk.
80
+ */
81
+ readonly diskRoots?: DiskRoots;
76
82
  }
77
83
  /** The files `listFiles` found: paths relative to the directory listed. */
78
84
  export interface SandboxListing {
@@ -0,0 +1,63 @@
1
+ -- Protocol 11: Environments (R2d), a fresh start (Q2). Sandbox resources and every vault but
2
+ -- the host's are dropped, not mapped: which Environment a person's key belongs in is the app's
3
+ -- mapping, and each seal binds its vault id. Sessions opened before protocol 11 are closed
4
+ -- (Q12): their history stays readable and a new turn is refused (`session_closed`).
5
+ CREATE TABLE "nylorun_streams"."environment_events" (
6
+ "environment_id" text COLLATE "C" NOT NULL,
7
+ "seq" bigint NOT NULL,
8
+ "type" text NOT NULL,
9
+ "body" json NOT NULL,
10
+ "committed_at" timestamp with time zone DEFAULT now() NOT NULL,
11
+ CONSTRAINT "environment_events_pkey" PRIMARY KEY("environment_id","seq")
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE TABLE "nylorun"."environments" (
15
+ "id" text COLLATE "C" PRIMARY KEY NOT NULL,
16
+ "kind" text NOT NULL,
17
+ "labels" json NOT NULL,
18
+ "recipe" json NOT NULL,
19
+ "applied" json NOT NULL,
20
+ "version" integer DEFAULT 1 NOT NULL,
21
+ "applied_version" integer DEFAULT 1 NOT NULL,
22
+ "owner_user_id" text,
23
+ "last_used_at" timestamp with time zone DEFAULT now() NOT NULL,
24
+ "created_at" text COLLATE "C" NOT NULL,
25
+ "updated_at" text COLLATE "C" NOT NULL,
26
+ "k8s_name" text COLLATE "C",
27
+ "volume_gen" integer DEFAULT 0 NOT NULL,
28
+ "desired" text,
29
+ "observed" text,
30
+ "pod_uid" text,
31
+ "host_epoch" integer DEFAULT 0 NOT NULL,
32
+ "join_token_hash" text,
33
+ "rev" integer DEFAULT 0 NOT NULL,
34
+ "last_active_at" timestamp with time zone,
35
+ "started_at" timestamp with time zone,
36
+ "reason" text,
37
+ "retiring" text COLLATE "C",
38
+ "volume_id" text,
39
+ CONSTRAINT "environments_kind_check" CHECK ("nylorun"."environments"."kind" IN ('virtual', 'pod', 'none'))
40
+ );
41
+ --> statement-breakpoint
42
+ DROP TABLE "nylorun_streams"."sandbox_events" CASCADE;--> statement-breakpoint
43
+ DROP TABLE "nylorun"."sandbox_resources" CASCADE;--> statement-breakpoint
44
+ DELETE FROM "nylorun"."vaults" WHERE "id" <> 'host';--> statement-breakpoint
45
+ DELETE FROM "nylorun"."vault_idempotency" WHERE "id" NOT LIKE 'host-model%';--> statement-breakpoint
46
+ DELETE FROM "nylorun"."sandboxes";--> statement-breakpoint
47
+ DELETE FROM "nylorun"."sandbox_signals";--> statement-breakpoint
48
+ UPDATE "nylorun"."sessions" SET "body" = (("body"::jsonb - 'sandboxId' - 'sandboxSource' - 'sandboxOwnerId' - 'sandbox' - 'vaultIds' - 'credentialSelections') || '{"closedReason":"protocol_11"}'::jsonb)::json;--> statement-breakpoint
49
+ UPDATE "nylorun"."tenant_settings" SET "key" = 'environments.config', "value" = ((("value"::jsonb - 'default') #- '{limits,ttl}' #- '{lifecycle,onExpiry}' #- '{limits,sandboxes}') || CASE WHEN ("value"::jsonb #> '{limits,sandboxes}') IS NULL THEN '{}'::jsonb ELSE jsonb_build_object('limits', (("value"::jsonb -> 'limits') - 'sandboxes' - 'ttl') || jsonb_build_object('environments', "value"::jsonb #> '{limits,sandboxes}')) END)::text WHERE "key" = 'sandbox.config';--> statement-breakpoint
50
+ ALTER TABLE "nylorun"."vaults" DROP CONSTRAINT "vaults_scope_check";--> statement-breakpoint
51
+ DROP INDEX "nylorun"."sessions_sandbox";--> statement-breakpoint
52
+ DROP INDEX "nylorun"."vaults_owner";--> statement-breakpoint
53
+ DROP INDEX "nylorun"."vaults_one_host";--> statement-breakpoint
54
+ ALTER TABLE "nylorun"."sessions" ADD COLUMN "environment_id" text GENERATED ALWAYS AS (nylorun.doc(body)->>'environmentId') STORED;--> statement-breakpoint
55
+ ALTER TABLE "nylorun"."vaults" ADD COLUMN "environment_id" text COLLATE "C";--> statement-breakpoint
56
+ ALTER TABLE "nylorun"."vaults" ADD CONSTRAINT "vaults_environment_id_fkey" FOREIGN KEY ("environment_id") REFERENCES "nylorun"."environments"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
57
+ CREATE INDEX "sessions_environment" ON "nylorun"."sessions" USING btree ("environment_id");--> statement-breakpoint
58
+ CREATE UNIQUE INDEX "vault_credentials_name" ON "nylorun"."vault_credentials" USING btree ("vault_id","name");--> statement-breakpoint
59
+ CREATE UNIQUE INDEX "vaults_environment" ON "nylorun"."vaults" USING btree ("environment_id");--> statement-breakpoint
60
+ ALTER TABLE "nylorun"."sessions" DROP COLUMN "sandbox_id";--> statement-breakpoint
61
+ ALTER TABLE "nylorun"."vaults" DROP COLUMN "owner_user_id";--> statement-breakpoint
62
+ ALTER TABLE "nylorun"."vaults" DROP COLUMN "scope";--> statement-breakpoint
63
+ ALTER TABLE "nylorun"."vaults" ADD CONSTRAINT "vaults_owner_check" CHECK (("nylorun"."vaults"."id" = 'host') = ("nylorun"."vaults"."environment_id" IS NULL));
@@ -0,0 +1,7 @@
1
+ -- R2d step 3: an Environment's disk versions are one folder artifact it owns (environment_id),
2
+ -- and its row holds the disk lease (disk_op) and the version its next volume starts from
3
+ -- (disk_seed).
4
+ ALTER TABLE "nylorun"."artifacts" ADD COLUMN "environment_id" text COLLATE "C";--> statement-breakpoint
5
+ ALTER TABLE "nylorun"."environments" ADD COLUMN "disk_op" json;--> statement-breakpoint
6
+ ALTER TABLE "nylorun"."environments" ADD COLUMN "disk_seed" json;--> statement-breakpoint
7
+ CREATE UNIQUE INDEX "artifacts_environment_disk" ON "nylorun"."artifacts" USING btree ("environment_id") WHERE environment_id IS NOT NULL;
@@ -0,0 +1,5 @@
1
+ -- R7.1: the gate's own timing of a model call (its span): when it sent the call, when the answer
2
+ -- was complete, and how long the provider took to its first stream event. Null on older rows.
3
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "started_at" text;--> statement-breakpoint
4
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "ended_at" text;--> statement-breakpoint
5
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "first_token_ms" integer;
@@ -0,0 +1 @@
1
+ CREATE INDEX "artifacts_creation_order" ON "nylorun"."artifacts" USING btree ("created_at","id");
@@ -141,6 +141,34 @@
141
141
  "when": 1791530515498,
142
142
  "tag": "0019_sandbox_signal_outbox",
143
143
  "breakpoints": true
144
+ },
145
+ {
146
+ "idx": 20,
147
+ "version": "7",
148
+ "when": 1791702110945,
149
+ "tag": "0020_environments",
150
+ "breakpoints": true
151
+ },
152
+ {
153
+ "idx": 21,
154
+ "version": "7",
155
+ "when": 1791709466639,
156
+ "tag": "0021_disk_versions",
157
+ "breakpoints": true
158
+ },
159
+ {
160
+ "idx": 22,
161
+ "version": "7",
162
+ "when": 1791717547905,
163
+ "tag": "0022_model_usage_timing",
164
+ "breakpoints": true
165
+ },
166
+ {
167
+ "idx": 23,
168
+ "version": "7",
169
+ "when": 1791730977999,
170
+ "tag": "0023_artifact_pages",
171
+ "breakpoints": true
144
172
  }
145
173
  ]
146
174
  }