@nylorun/runtime 0.25.0-beta → 0.27.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +169 -0
- package/README.md +26 -16
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.js +1 -1
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +12 -18
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +4 -4
- package/dist/api/components.d.ts +326 -45
- package/dist/api/components.js +5 -1
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.d.ts +2 -2
- package/dist/api/http/app.js +2 -2
- package/dist/api/http/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/define.d.ts +1 -1
- package/dist/api/http/respond.js +7 -3
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/artifacts.js +19 -7
- package/dist/api/http/routes/coverage.js +1 -1
- package/dist/api/http/routes/environment-manifests.js +1 -1
- package/dist/api/http/routes/environments.js +6 -6
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +1 -1
- package/dist/api/http/routes/reads.d.ts +35 -0
- package/dist/api/http/routes/reads.js +50 -22
- package/dist/api/http/routes/sessions.js +12 -11
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +69 -16
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +6 -2
- package/dist/artifacts/disk.d.ts +0 -4
- package/dist/artifacts/disk.js +77 -73
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +70 -25
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +4 -3
- package/dist/gates/egress.d.ts +9 -9
- package/dist/gates/egress.js +19 -19
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +53 -17
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +28 -5
- package/dist/gates/policy.js +60 -17
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +0 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +4 -9
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +102 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +82 -73
- package/dist/harness-api/start.d.ts +20 -2
- package/dist/harness-api/start.js +57 -18
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +2 -45
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +0 -2
- package/dist/host/http.js +0 -27
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +1 -3
- package/dist/keys/contract.js +1 -3
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.js +6 -1
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +1961 -994
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +13 -6
- package/dist/mcp/preview.js +37 -20
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +866 -1218
- package/dist/reads/cursor.js +3 -1
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/environment.d.ts +2 -0
- package/dist/record/environment.js +2 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +3 -2
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +5 -8
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +19 -11
- package/dist/sandbox/pods/lifecycle.js +45 -23
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +4 -1
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +8 -3
- package/dist/sandbox/resolve.js +12 -2
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +34 -11
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/migrate.js +1 -1
- package/dist/store/postgres/reads.js +10 -2
- package/dist/store/postgres/schema.d.ts +19 -0
- package/dist/store/postgres/schema.js +7 -0
- package/dist/store/postgres/store.js +55 -26
- package/dist/store/types.d.ts +17 -7
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +21 -31
- package/dist/tenant/commands.js +9 -16
- package/dist/tenant/context.d.ts +14 -17
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +9 -36
- package/dist/tenant/environment-manifests.js +9 -1
- package/dist/tenant/environments.d.ts +0 -2
- package/dist/tenant/environments.js +54 -30
- package/dist/tenant/ephemeral.d.ts +1 -1
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +12 -11
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +24 -1
- package/dist/tenant/identity-file.js +35 -7
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +31 -23
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +90 -0
- package/dist/tenant/limits.js +175 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +17 -19
- package/dist/tenant/outbound.js +18 -21
- package/dist/tenant/run-grants.js +3 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.js +30 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.js +32 -29
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +4 -3
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +39 -48
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/types.d.ts +11 -16
- package/dist/tenant/worker.d.ts +2 -10
- package/dist/tenant/worker.js +5 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +9 -6
- package/dist/traces/map.js +9 -11
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +383 -0
- package/dist/tuning.js +658 -0
- package/dist/vault/headers.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
* (`10.96.0.1:443`) for 0.5 to 5 s after it started (8a). So before the engine joins, or runs
|
|
5
5
|
* anything, it connects to addresses the policy must block (the API server's service address,
|
|
6
6
|
* `KUBERNETES_SERVICE_HOST:KUBERNETES_SERVICE_PORT`, which the kubelet always sets) until each
|
|
7
|
-
* fails `
|
|
7
|
+
* fails `harness.networkConfirmations` times in a row (Host tuning) (refused, unreachable or timed out). A connection that
|
|
8
8
|
* succeeds starts the count again. Past `timeoutMs` the engine exits, and the pod restarts it.
|
|
9
9
|
*/
|
|
10
10
|
import { connect } from "node:net";
|
|
11
|
-
|
|
12
|
-
export const CONFIRMATIONS = 3;
|
|
11
|
+
import { hostTuning } from "../../tuning.js";
|
|
13
12
|
/** The addresses a pod's NetworkPolicy must block: the API server's service address. */
|
|
14
13
|
export function blockedAddresses(env) {
|
|
15
14
|
const override = env.NYLORUN_SANDBOX_BLOCKED_PROBE;
|
|
@@ -45,19 +44,20 @@ export function opens(address, timeoutMs) {
|
|
|
45
44
|
}
|
|
46
45
|
/** Resolves once every address is blocked; rejects past the timeout. */
|
|
47
46
|
export async function awaitNetworkPolicy(options) {
|
|
47
|
+
const tuning = hostTuning().harness;
|
|
48
48
|
const started = Date.now();
|
|
49
|
-
const deadline = started + (options.timeoutMs ??
|
|
50
|
-
const needed = options.confirmations ??
|
|
49
|
+
const deadline = started + (options.timeoutMs ?? tuning.networkPolicyTimeoutMs);
|
|
50
|
+
const needed = options.confirmations ?? tuning.networkConfirmations;
|
|
51
51
|
for (const address of options.addresses) {
|
|
52
52
|
let failures = 0;
|
|
53
53
|
let opened = 0;
|
|
54
54
|
while (failures < needed) {
|
|
55
55
|
if (Date.now() > deadline)
|
|
56
56
|
throw new Error(`The pod's NetworkPolicy is not in force: ${address.host}:${address.port} still answered ${opened} time(s)`);
|
|
57
|
-
if (await opens(address, options.attemptMs ??
|
|
57
|
+
if (await opens(address, options.attemptMs ?? tuning.networkProbeMs)) {
|
|
58
58
|
failures = 0;
|
|
59
59
|
opened += 1;
|
|
60
|
-
await new Promise((resolve) => setTimeout(resolve, options.pauseMs ??
|
|
60
|
+
await new Promise((resolve) => setTimeout(resolve, options.pauseMs ?? tuning.networkPauseMs));
|
|
61
61
|
}
|
|
62
62
|
else
|
|
63
63
|
failures += 1;
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
* infrastructure errors throw; Restate retries those.
|
|
16
16
|
*/
|
|
17
17
|
import { randomBytes } from "node:crypto";
|
|
18
|
+
import { DEFAULT_SANDBOX_IMAGE } from "../policy.js";
|
|
18
19
|
import { effectiveSandboxConfig, readSandboxConfig } from "../tenant-config.js";
|
|
19
20
|
import { podSandboxSpecOf } from "../resolve.js";
|
|
20
21
|
import { sha256Hex } from "../join.js";
|
|
21
22
|
import { recoverLostVolume } from "../../artifacts/disk.js";
|
|
22
23
|
import { decide } from "./lifecycle.js";
|
|
23
24
|
import { podLifecycleConfig, podSpecOf } from "./spec.js";
|
|
25
|
+
import { hostTuning } from "../../tuning.js";
|
|
24
26
|
export async function reconcileSandbox(ctx, sandboxId, trigger) {
|
|
25
27
|
const pods = ctx.pods;
|
|
26
28
|
if (!pods || ctx.closed)
|
|
@@ -52,6 +54,7 @@ export async function reconcileSandbox(ctx, sandboxId, trigger) {
|
|
|
52
54
|
const lifecycle = podLifecycleConfig(config, row.recipe);
|
|
53
55
|
const decision = decide({
|
|
54
56
|
row,
|
|
57
|
+
image: row.applied.image ?? DEFAULT_SANDBOX_IMAGE,
|
|
55
58
|
config: lifecycle,
|
|
56
59
|
status: await status(row.pod.k8sName),
|
|
57
60
|
...(row.pod.retiring ? { retiring: await status(row.pod.retiring) } : {}),
|
|
@@ -116,7 +119,7 @@ export async function reconcileSandbox(ctx, sandboxId, trigger) {
|
|
|
116
119
|
message: error instanceof Error ? error.message : String(error),
|
|
117
120
|
});
|
|
118
121
|
// Nothing of the decision is written: the next reconcile decides again.
|
|
119
|
-
return { retryAfterMs: decision.retryAfterMs ??
|
|
122
|
+
return { retryAfterMs: decision.retryAfterMs ?? hostTuning().pods.reconcileRetryMs };
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
const written = await ctx.store.tx(async (t) => {
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { parseSandboxDuration, parseSandboxSize } from "@nylorun/core/define";
|
|
7
7
|
import { DEFAULT_HARNESS } from "@nylorun/core/contracts";
|
|
8
|
-
import { memoryMiBOf } from "../policy.js";
|
|
8
|
+
import { idleMsOf, memoryMiBOf } from "../policy.js";
|
|
9
|
+
import { podTuningEnv } from "../../tuning.js";
|
|
9
10
|
import { opIdOf } from "./lifecycle.js";
|
|
10
11
|
/** The pod's grace period to stop when the Tenant sets none (D34). */
|
|
11
12
|
export const DEFAULT_STOP_GRACE_SECONDS = 10;
|
|
@@ -19,13 +20,13 @@ export const POD_HARNESS_ROOT = "/harness";
|
|
|
19
20
|
*/
|
|
20
21
|
export function podLifecycleConfig(config, recipe) {
|
|
21
22
|
const stopGrace = config.lifecycle?.stopGrace;
|
|
22
|
-
const tenantIdle =
|
|
23
|
+
const tenantIdle = idleMsOf({ ...(config.limits?.idle === undefined ? {} : { idle: config.limits.idle }) });
|
|
23
24
|
const own = recipe?.lifecycle.idle === undefined ? undefined : parseSandboxDuration(recipe.lifecycle.idle);
|
|
25
|
+
const graceMs = stopGrace === undefined ? undefined : parseSandboxDuration(stopGrace);
|
|
24
26
|
return {
|
|
25
27
|
idleMs: own === undefined ? tenantIdle : Math.min(own, tenantIdle),
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
: Math.max(1, Math.min(30, Math.round((parseSandboxDuration(stopGrace) ?? 10_000) / 1000))),
|
|
28
|
+
// Kubernetes counts the grace in whole seconds, at least one.
|
|
29
|
+
stopGraceSeconds: graceMs === undefined ? DEFAULT_STOP_GRACE_SECONDS : Math.max(1, Math.round(graceMs / 1000)),
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
/** The volume's size in GiB. */
|
|
@@ -53,6 +54,8 @@ export function podSpecOf(input) {
|
|
|
53
54
|
env: {
|
|
54
55
|
NYLORUN_SANDBOX_ID: input.environmentId,
|
|
55
56
|
NYLORUN_HARNESS_ROOT: POD_HARNESS_ROOT,
|
|
57
|
+
// The `harness` and `engine` tuning the operator set: the pod's engine runs with them.
|
|
58
|
+
...podTuningEnv(),
|
|
56
59
|
},
|
|
57
60
|
...(input.joinToken ? { joinToken: input.joinToken } : {}),
|
|
58
61
|
};
|
package/dist/sandbox/policy.d.ts
CHANGED
|
@@ -4,10 +4,16 @@ import type { ResolvedNetwork } from "./types.js";
|
|
|
4
4
|
export declare const DEFAULT_SANDBOX_IMAGE = "python:3.13-slim";
|
|
5
5
|
export declare const DEFAULT_SANDBOX_CPUS = 2;
|
|
6
6
|
export declare const DEFAULT_SANDBOX_MEMORY_MIB = 1024;
|
|
7
|
-
|
|
7
|
+
/** The default of the Tenant's Environment setting `limits.idle` (`environments.config`, D36). */
|
|
8
|
+
export declare const DEFAULT_SANDBOX_IDLE = "15m";
|
|
8
9
|
/** Package registries and code hosts. Concrete names so every backend can express them. */
|
|
9
10
|
export declare const DEV_PRESET_HOSTS: readonly string[];
|
|
10
11
|
export declare function resolveNetwork(manifest: SandboxManifest | undefined): ResolvedNetwork;
|
|
12
|
+
/**
|
|
13
|
+
* How long the sandbox may sit idle: its spec's `idle`, which `sandboxSpecOf` sets from the
|
|
14
|
+
* Environment's `lifecycle.idle` within the Tenant's `limits.idle`; else that setting's default
|
|
15
|
+
* (`DEFAULT_SANDBOX_IDLE`, D36).
|
|
16
|
+
*/
|
|
11
17
|
export declare function idleMsOf(manifest: SandboxManifest | undefined): number;
|
|
12
18
|
export declare function memoryMiBOf(manifest: SandboxManifest | undefined): number;
|
|
13
19
|
export declare function describeNetwork(network: ResolvedNetwork): string;
|
package/dist/sandbox/policy.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { parseSandboxDuration, parseSandboxSize, } from "@nylorun/core/define";
|
|
2
|
+
import { hostTuning } from "../tuning.js";
|
|
2
3
|
/** Docker Official Image: Debian with bash, coreutils and Python 3; small first pull. */
|
|
3
4
|
export const DEFAULT_SANDBOX_IMAGE = "python:3.13-slim";
|
|
4
5
|
export const DEFAULT_SANDBOX_CPUS = 2;
|
|
5
6
|
export const DEFAULT_SANDBOX_MEMORY_MIB = 1024;
|
|
6
|
-
|
|
7
|
+
/** The default of the Tenant's Environment setting `limits.idle` (`environments.config`, D36). */
|
|
8
|
+
export const DEFAULT_SANDBOX_IDLE = "15m";
|
|
7
9
|
/** Package registries and code hosts. Concrete names so every backend can express them. */
|
|
8
10
|
export const DEV_PRESET_HOSTS = Object.freeze([
|
|
9
11
|
// npm and yarn
|
|
@@ -50,14 +52,19 @@ export function resolveNetwork(manifest) {
|
|
|
50
52
|
suffixes: Object.freeze([...suffixes].sort()),
|
|
51
53
|
});
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* How long the sandbox may sit idle: its spec's `idle`, which `sandboxSpecOf` sets from the
|
|
57
|
+
* Environment's `lifecycle.idle` within the Tenant's `limits.idle`; else that setting's default
|
|
58
|
+
* (`DEFAULT_SANDBOX_IDLE`, D36).
|
|
59
|
+
*/
|
|
53
60
|
export function idleMsOf(manifest) {
|
|
54
|
-
return (manifest?.idle
|
|
61
|
+
return parseSandboxDuration(manifest?.idle ?? DEFAULT_SANDBOX_IDLE) ?? parseSandboxDuration(DEFAULT_SANDBOX_IDLE);
|
|
55
62
|
}
|
|
56
63
|
export function memoryMiBOf(manifest) {
|
|
57
64
|
const bytes = manifest?.resources?.memory
|
|
58
65
|
? parseSandboxSize(manifest.resources.memory)
|
|
59
66
|
: undefined;
|
|
60
|
-
return bytes ? Math.max(
|
|
67
|
+
return bytes ? Math.max(hostTuning().pods.memoryFloorMiB, Math.ceil(bytes / 1024 ** 2)) : DEFAULT_SANDBOX_MEMORY_MIB;
|
|
61
68
|
}
|
|
62
69
|
export function describeNetwork(network) {
|
|
63
70
|
const extra = network.preset === "dev"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolves an Environment's recipe (`PUT /v1/environments/{id}`) against its Tenant's limits,
|
|
3
|
-
* and the sandbox spec a recipe gives a session
|
|
3
|
+
* and the sandbox spec a recipe gives a session (`Session.sandbox`) and the pod. Pure: the
|
|
4
4
|
* caller supplies the configuration. Every problem is reported, not only the first, so one
|
|
5
5
|
* response lists all of them.
|
|
6
6
|
*/
|
|
7
|
-
import type { EnvironmentFile, EnvironmentKind, EnvironmentLifecycle, EnvironmentRecipe, EnvironmentSecretSlots, EnvironmentSetupCommand } from "@nylorun/core/contracts";
|
|
7
|
+
import type { EnvironmentFile, EnvironmentKind, EnvironmentLifecycle, EnvironmentLimits, EnvironmentRecipe, EnvironmentSecretSlots, EnvironmentSetupCommand } from "@nylorun/core/contracts";
|
|
8
8
|
import { type SandboxManifest } from "@nylorun/core/define";
|
|
9
9
|
import type { PodSandboxSpec } from "./pods/spec.js";
|
|
10
10
|
import { type EffectiveSandboxConfig } from "./tenant-config.js";
|
|
@@ -25,6 +25,11 @@ export interface RecipeRequest {
|
|
|
25
25
|
readonly files?: readonly EnvironmentFile[];
|
|
26
26
|
/** The secret slots (D64): passed through for every kind. */
|
|
27
27
|
readonly secrets?: EnvironmentSecretSlots;
|
|
28
|
+
/**
|
|
29
|
+
* Artifact and skill limits: passed through for every kind, as set. The caller checks them
|
|
30
|
+
* against the Tenant's (`tenant/limits.ts`), which this module does not read.
|
|
31
|
+
*/
|
|
32
|
+
readonly limits?: EnvironmentLimits;
|
|
28
33
|
}
|
|
29
34
|
export type RecipeResolution = {
|
|
30
35
|
readonly kind: "recipe";
|
|
@@ -36,7 +41,7 @@ export type RecipeResolution = {
|
|
|
36
41
|
/** The recipe `request` asks for an Environment of `kind`, or every reason it cannot have it. */
|
|
37
42
|
export declare function resolveRecipe(kind: EnvironmentKind, request: RecipeRequest, config: EffectiveSandboxConfig): RecipeResolution;
|
|
38
43
|
/**
|
|
39
|
-
* The sandbox spec of a recipe: what a session
|
|
44
|
+
* The sandbox spec of a recipe: what a session pins when it opens and the virtual
|
|
40
45
|
* workspace runs. Idle is the Environment's `lifecycle.idle`, else the Tenant's.
|
|
41
46
|
*/
|
|
42
47
|
export declare function sandboxSpecOf(recipe: EnvironmentRecipe, tenantIdle: string): SandboxManifest;
|
package/dist/sandbox/resolve.js
CHANGED
|
@@ -20,7 +20,10 @@ export function resolveRecipe(kind, request, config) {
|
|
|
20
20
|
errors.push(`${field} is for an Environment with a sandbox: kind none has none.`);
|
|
21
21
|
if (errors.length > 0)
|
|
22
22
|
return { kind: "error", errors };
|
|
23
|
-
return {
|
|
23
|
+
return {
|
|
24
|
+
kind: "recipe",
|
|
25
|
+
recipe: { harness, lifecycle: { ...lifecycle }, ...limitsOf(request), ...slotsOf(request) },
|
|
26
|
+
};
|
|
24
27
|
}
|
|
25
28
|
if (request.image !== undefined && !pod)
|
|
26
29
|
errors.push("image is for kind pod: a virtual Environment runs no images.");
|
|
@@ -57,10 +60,17 @@ export function resolveRecipe(kind, request, config) {
|
|
|
57
60
|
lifecycle: { ...lifecycle },
|
|
58
61
|
...(pod && request.setup?.length ? { setup: request.setup.map((command) => ({ ...command })) } : {}),
|
|
59
62
|
...(pod && request.files?.length ? { files: request.files.map((file) => ({ ...file })) } : {}),
|
|
63
|
+
...limitsOf(request),
|
|
60
64
|
...slotsOf(request),
|
|
61
65
|
},
|
|
62
66
|
};
|
|
63
67
|
}
|
|
68
|
+
/** The request's limits, when it sets any. */
|
|
69
|
+
function limitsOf(request) {
|
|
70
|
+
return request.limits !== undefined && Object.keys(request.limits).length > 0
|
|
71
|
+
? { limits: structuredClone(request.limits) }
|
|
72
|
+
: {};
|
|
73
|
+
}
|
|
64
74
|
/** The request's secret slots, when it declares any. */
|
|
65
75
|
function slotsOf(request) {
|
|
66
76
|
return request.secrets !== undefined && Object.keys(request.secrets).length > 0
|
|
@@ -68,7 +78,7 @@ function slotsOf(request) {
|
|
|
68
78
|
: {};
|
|
69
79
|
}
|
|
70
80
|
/**
|
|
71
|
-
* The sandbox spec of a recipe: what a session
|
|
81
|
+
* The sandbox spec of a recipe: what a session pins when it opens and the virtual
|
|
72
82
|
* workspace runs. Idle is the Environment's `lifecycle.idle`, else the Tenant's.
|
|
73
83
|
*/
|
|
74
84
|
export function sandboxSpecOf(recipe, tenantIdle) {
|
package/dist/sandbox/setup.d.ts
CHANGED
|
@@ -34,7 +34,9 @@ export declare function parseSetupReport(value: unknown): SetupReport | undefine
|
|
|
34
34
|
/**
|
|
35
35
|
* Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
|
|
36
36
|
* the pod is `failed` with a reason that starts `setup:`, and `environment.failed` says which
|
|
37
|
-
* command failed and how
|
|
37
|
+
* command failed and how, with the end of its output (the Tenant's `setupOutputChars`; the reason
|
|
38
|
+
* quotes `setupReasonChars` of it and `setupCommandExcerptChars` of the command). A report
|
|
39
|
+
* from an older host, or a success, changes nothing.
|
|
38
40
|
*/
|
|
39
41
|
export declare function recordSetup(ctx: TenantContext, id: string, epoch: number, report: SetupReport): Promise<void>;
|
|
40
42
|
/**
|
package/dist/sandbox/setup.js
CHANGED
|
@@ -1,24 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup and input files (R2d step 2, Host feature `environment-setup`): what a pod Environment's
|
|
3
|
+
* engine runs and copies when its disk is made or reset, before it takes any turn. The plan is
|
|
4
|
+
* the applied recipe's `setup` and `files`, handed to the engine with its host token (the join
|
|
5
|
+
* answer, and `GET host/setup` while it waits to retry, or, on a fork onto other setup, for its
|
|
6
|
+
* disk version to be written: Q17); the engine keeps a marker on the volume
|
|
7
|
+
* (`/harness/setup.json`), so a resume never runs it again and a reset (a new volume) does.
|
|
8
|
+
*
|
|
9
|
+
* Input files come only through the routes here, indexed into the applied recipe's `files`:
|
|
10
|
+
* a pod reads the artifact versions and the definition files by hash (Q6) its own recipe names,
|
|
11
|
+
* and nothing else of the Object store.
|
|
12
|
+
*
|
|
13
|
+
* A failed command fails the pod (`observed: failed`, a reason that starts `setup:`, and
|
|
14
|
+
* `environment.failed` with the command, its exit code and the end of its output): the turn that
|
|
15
|
+
* waits for it fails with that reason (`podUnusable`, `tenant/advance.ts`), the failure stays
|
|
16
|
+
* until a turn start asks the pod to run again (`wantRunning`), and the engine, polling, runs the
|
|
17
|
+
* setup again then.
|
|
18
|
+
*/
|
|
19
|
+
import { ENVIRONMENT_SETUP_TIMEOUT_DEFAULT, } from "@nylorun/core/contracts";
|
|
1
20
|
import { parseSandboxDuration } from "@nylorun/core/define";
|
|
2
21
|
import { contentKey, readManifest } from "../artifacts/folders.js";
|
|
3
|
-
import { contentTypeFor } from "
|
|
22
|
+
import { contentTypeFor } from "@nylorun/core/contracts";
|
|
4
23
|
import { definitionFileKey } from "../tenant/definition-files.js";
|
|
5
24
|
import { SETUP_FAILED } from "./pods/lifecycle.js";
|
|
25
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
6
26
|
export { SETUP_FAILED };
|
|
7
|
-
/** The most of a failed command's output kept in its event and reason. */
|
|
8
|
-
const OUTPUT_TAIL = 4 * 1024;
|
|
9
|
-
/** A command's timeout when the recipe gives none. */
|
|
10
|
-
const DEFAULT_TIMEOUT_MS = 10 * 60_000;
|
|
11
27
|
/** The setup of `recipe`, or undefined when it has neither commands nor files. */
|
|
12
28
|
export async function setupPlanOf(ctx, t, environment) {
|
|
13
29
|
const recipe = environment.applied;
|
|
14
30
|
if (!recipe.setup?.length && !recipe.files?.length)
|
|
15
31
|
return undefined;
|
|
32
|
+
// A command's own timeout, else the default, within the Tenant's `setupCommandMs` as it is now.
|
|
33
|
+
const { setupCommandMs } = await readRuntimeLimits(t);
|
|
34
|
+
const timeoutOf = (timeout) => Math.min(parseSandboxDuration(timeout ?? ENVIRONMENT_SETUP_TIMEOUT_DEFAULT), setupCommandMs);
|
|
16
35
|
return {
|
|
17
36
|
recipeVersion: environment.appliedVersion,
|
|
18
37
|
commands: (recipe.setup ?? []).map((command) => ({
|
|
19
38
|
command: command.command,
|
|
20
39
|
cwd: command.cwd ?? "/workspace",
|
|
21
|
-
timeoutMs: (command.timeout
|
|
40
|
+
timeoutMs: timeoutOf(command.timeout),
|
|
22
41
|
})),
|
|
23
42
|
files: (recipe.files ?? []).map((file, index) => ({ index, path: file.path })),
|
|
24
43
|
environment: await ctx.vault.environmentVariables(t, environment.id),
|
|
@@ -32,7 +51,7 @@ export function parseSetupReport(value) {
|
|
|
32
51
|
return undefined;
|
|
33
52
|
const index = typeof report.index === "number" && Number.isInteger(report.index) ? report.index : undefined;
|
|
34
53
|
const exitCode = typeof report.exitCode === "number" && Number.isInteger(report.exitCode) ? report.exitCode : undefined;
|
|
35
|
-
const output = typeof report.output === "string" ? report.output
|
|
54
|
+
const output = typeof report.output === "string" ? report.output : undefined;
|
|
36
55
|
return {
|
|
37
56
|
status: report.status,
|
|
38
57
|
...(index === undefined ? {} : { index }),
|
|
@@ -43,7 +62,9 @@ export function parseSetupReport(value) {
|
|
|
43
62
|
/**
|
|
44
63
|
* Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
|
|
45
64
|
* the pod is `failed` with a reason that starts `setup:`, and `environment.failed` says which
|
|
46
|
-
* command failed and how
|
|
65
|
+
* command failed and how, with the end of its output (the Tenant's `setupOutputChars`; the reason
|
|
66
|
+
* quotes `setupReasonChars` of it and `setupCommandExcerptChars` of the command). A report
|
|
67
|
+
* from an older host, or a success, changes nothing.
|
|
47
68
|
*/
|
|
48
69
|
export async function recordSetup(ctx, id, epoch, report) {
|
|
49
70
|
await ctx.store.tx(async (t) => {
|
|
@@ -57,9 +78,11 @@ export async function recordSetup(ctx, id, epoch, report) {
|
|
|
57
78
|
await t.updateEnvironment(id, { diskSeed: null }, new Date().toISOString());
|
|
58
79
|
return;
|
|
59
80
|
}
|
|
81
|
+
const { setupOutputChars, setupReasonChars, setupCommandExcerptChars } = await readRuntimeLimits(t);
|
|
82
|
+
const output = (report.output ?? "").slice(-setupOutputChars);
|
|
60
83
|
const command = report.index === undefined ? undefined : environment.applied.setup?.[report.index];
|
|
61
|
-
const what = command === undefined ? "copying the input files failed" : `command ${report.index + 1} (${command.command.slice(0,
|
|
62
|
-
const tail =
|
|
84
|
+
const what = command === undefined ? "copying the input files failed" : `command ${report.index + 1} (${command.command.slice(0, setupCommandExcerptChars)}) exited ${report.exitCode ?? "abnormally"}`;
|
|
85
|
+
const tail = output.trim().slice(-setupReasonChars);
|
|
63
86
|
const reason = `${SETUP_FAILED} ${what}${tail ? `: ${tail}` : ""}`;
|
|
64
87
|
await t.updateEnvironmentPod(id, { observed: "failed", reason }, new Date().toISOString());
|
|
65
88
|
await t.environmentEvent(id, "environment.failed", {
|
|
@@ -67,7 +90,7 @@ export async function recordSetup(ctx, id, epoch, report) {
|
|
|
67
90
|
setup: {
|
|
68
91
|
...(report.index === undefined ? {} : { index: report.index }),
|
|
69
92
|
...(report.exitCode === undefined ? {} : { exitCode: report.exitCode }),
|
|
70
|
-
output
|
|
93
|
+
output,
|
|
71
94
|
},
|
|
72
95
|
});
|
|
73
96
|
});
|
package/dist/sandbox/share.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
4
|
-
*
|
|
3
|
+
* workspace, with the spec pinned when the session was opened (`Session.sandbox`, the one place
|
|
4
|
+
* it is kept since protocol 15). Definitions declare none (Sandboxes v3).
|
|
5
5
|
*/
|
|
6
6
|
import { type AgentManifest, type SandboxManifest, type WorkflowManifest } from "@nylorun/core/define";
|
|
7
7
|
export type SessionSandboxRef = {
|
|
@@ -13,23 +13,14 @@ export type SessionSandboxRef = {
|
|
|
13
13
|
/** The Environment's sandbox spec, pinned when the session was opened. */
|
|
14
14
|
readonly sandbox?: SandboxManifest;
|
|
15
15
|
};
|
|
16
|
-
/** The session's sandbox spec
|
|
17
|
-
export declare function sessionSandboxSpec(session: SessionSandboxRef): SandboxManifest | undefined;
|
|
18
|
-
/** The sandbox spec of a pinned agent manifest (its `runtime` block) or a workflow manifest. */
|
|
19
|
-
export declare function sandboxSpecOf(manifest: AgentManifest | WorkflowManifest | Record<string, unknown> | undefined): SandboxManifest | undefined;
|
|
16
|
+
/** The session's sandbox spec, pinned when it was opened (or inherited from its tree's owner). */
|
|
17
|
+
export declare function sessionSandboxSpec(session: Pick<SessionSandboxRef, "sandbox">): SandboxManifest | undefined;
|
|
20
18
|
/**
|
|
21
19
|
* Where a definition document declares a sandbox: a workflow's `sandbox`, and the agents embedded
|
|
22
20
|
* in a flow agent. Definitions no longer declare one (Sandboxes v3), so registration refuses any
|
|
23
|
-
* of these. An agent manifest
|
|
24
|
-
* refuses at registration.
|
|
21
|
+
* of these. An agent manifest has no field for one.
|
|
25
22
|
*/
|
|
26
23
|
export declare function declaredSandboxes(manifest: unknown, into?: string[]): string[];
|
|
27
|
-
/**
|
|
28
|
-
* The agents of a definition document that set `runtime`: the document itself, the agents it
|
|
29
|
-
* uses as tools and those embedded in a flow agent. Only the Runtime writes that block, when it
|
|
30
|
-
* pins a session's manifest, so registration refuses any of these.
|
|
31
|
-
*/
|
|
32
|
-
export declare function authoredRuntimeBlocks(manifest: unknown, into?: string[]): string[];
|
|
33
24
|
/**
|
|
34
25
|
* The workspace a session's sandbox tools run in: the session, whose log records the
|
|
35
26
|
* workspace's events, and its Environment, whose id keys the workspace.
|
package/dist/sandbox/share.js
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
4
|
-
*
|
|
3
|
+
* workspace, with the spec pinned when the session was opened (`Session.sandbox`, the one place
|
|
4
|
+
* it is kept since protocol 15). Definitions declare none (Sandboxes v3).
|
|
5
5
|
*/
|
|
6
6
|
import {} from "@nylorun/core/define";
|
|
7
|
-
/** The session's sandbox spec
|
|
7
|
+
/** The session's sandbox spec, pinned when it was opened (or inherited from its tree's owner). */
|
|
8
8
|
export function sessionSandboxSpec(session) {
|
|
9
|
-
return session.sandbox
|
|
10
|
-
}
|
|
11
|
-
/** The sandbox spec of a pinned agent manifest (its `runtime` block) or a workflow manifest. */
|
|
12
|
-
export function sandboxSpecOf(manifest) {
|
|
13
|
-
if (!manifest || typeof manifest !== "object")
|
|
14
|
-
return undefined;
|
|
15
|
-
if (manifest.kind === "workflow")
|
|
16
|
-
return manifest.sandbox;
|
|
17
|
-
return manifest.runtime?.sandbox;
|
|
9
|
+
return session.sandbox;
|
|
18
10
|
}
|
|
19
11
|
/**
|
|
20
12
|
* Where a definition document declares a sandbox: a workflow's `sandbox`, and the agents embedded
|
|
21
13
|
* in a flow agent. Definitions no longer declare one (Sandboxes v3), so registration refuses any
|
|
22
|
-
* of these. An agent manifest
|
|
23
|
-
* refuses at registration.
|
|
14
|
+
* of these. An agent manifest has no field for one.
|
|
24
15
|
*/
|
|
25
16
|
export function declaredSandboxes(manifest, into = []) {
|
|
26
17
|
if (!manifest || typeof manifest !== "object")
|
|
@@ -37,26 +28,6 @@ export function declaredSandboxes(manifest, into = []) {
|
|
|
37
28
|
declaredSandboxes(tool.agent, into);
|
|
38
29
|
return into;
|
|
39
30
|
}
|
|
40
|
-
/**
|
|
41
|
-
* The agents of a definition document that set `runtime`: the document itself, the agents it
|
|
42
|
-
* uses as tools and those embedded in a flow agent. Only the Runtime writes that block, when it
|
|
43
|
-
* pins a session's manifest, so registration refuses any of these.
|
|
44
|
-
*/
|
|
45
|
-
export function authoredRuntimeBlocks(manifest, into = []) {
|
|
46
|
-
if (!manifest || typeof manifest !== "object")
|
|
47
|
-
return into;
|
|
48
|
-
const document = manifest;
|
|
49
|
-
if (document.kind === "workflow") {
|
|
50
|
-
for (const agent of Object.values(document.agents ?? {}))
|
|
51
|
-
authoredRuntimeBlocks(agent, into);
|
|
52
|
-
return into;
|
|
53
|
-
}
|
|
54
|
-
if (document.runtime !== undefined)
|
|
55
|
-
into.push(`'${document.id}'`);
|
|
56
|
-
for (const tool of document.tools ?? [])
|
|
57
|
-
authoredRuntimeBlocks(tool.agent, into);
|
|
58
|
-
return into;
|
|
59
|
-
}
|
|
60
31
|
/**
|
|
61
32
|
* The workspace a session's sandbox tools run in: the session, whose log records the
|
|
62
33
|
* workspace's events, and its Environment, whose id keys the workspace.
|
package/dist/sandbox/skills.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type SkillFiles = Readonly<Record<string, string>>;
|
|
1
|
+
import type { SandboxFileBytes, SandboxHandle } from "./types.js";
|
|
2
|
+
/** A folder's files, by path in it, as `sha256:<hex>`. */
|
|
3
|
+
export type FolderFiles = Readonly<Record<string, string>>;
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Writes one folder's files to `root`, replacing a partial copy a failed mount left, and makes
|
|
6
|
+
* them read-only. `read` gives a file's bytes by `sha256:<hex>`, whole or as chunks, which are
|
|
7
|
+
* written as they come.
|
|
8
8
|
*/
|
|
9
|
-
export declare function
|
|
10
|
-
/**
|
|
11
|
-
* Writes one skill version's files to its folder `root` (`/skills/<name>@<hash>`), replacing a
|
|
12
|
-
* partial copy a failed mount left, and makes them read-only. `read` gives a file's bytes by
|
|
13
|
-
* `sha256:<hex>`.
|
|
14
|
-
*/
|
|
15
|
-
export declare function mountSkill(handle: SandboxHandle, root: string, files: SkillFiles, read: (sha256: string) => Promise<Uint8Array>, signal: AbortSignal): Promise<void>;
|
|
9
|
+
export declare function mountFolderFiles(handle: SandboxHandle, root: string, files: FolderFiles, read: (sha256: string) => Promise<SandboxFileBytes>, signal: AbortSignal): Promise<void>;
|
package/dist/sandbox/skills.js
CHANGED
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
2
|
+
* Folders a harness mounts in a sandbox (protocol 15): our engine's skill folders,
|
|
3
|
+
* `/skills/<name>@<hash>/` (`skillFolderOf`, track R2 M4, D66), each one version's files,
|
|
4
|
+
* read-only, so the model can read them and run their scripts with bash. Our engine decides which
|
|
5
|
+
* folders and when (before each sandbox call); the SandboxManager writes them once per open
|
|
6
|
+
* handle (`mountFolder`). A version's folder is written once and never replaced: sessions whose
|
|
7
|
+
* agents carry different versions of one skill share an Environment, and run turns at once,
|
|
8
|
+
* without changing each other's files. Folders live outside the persisted disk, so a stop clears
|
|
9
|
+
* them and the next call mounts them again.
|
|
10
10
|
*/
|
|
11
11
|
import { posix } from "node:path";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { helperCommandTimeoutMs, quote } from "./tools.js";
|
|
13
|
+
import { hostTuning } from "../tuning.js";
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Writes one folder's files to `root`, replacing a partial copy a failed mount left, and makes
|
|
16
|
+
* them read-only. `read` gives a file's bytes by `sha256:<hex>`, whole or as chunks, which are
|
|
17
|
+
* written as they come.
|
|
17
18
|
*/
|
|
18
|
-
export function
|
|
19
|
-
for (const skill of Object.values(manifest?.skills ?? {})) {
|
|
20
|
-
const folder = skillFolderOf(skill);
|
|
21
|
-
if (!into.has(folder))
|
|
22
|
-
into.set(folder, skill.files);
|
|
23
|
-
}
|
|
24
|
-
for (const tool of manifest?.tools ?? [])
|
|
25
|
-
if (tool.agent !== undefined && !("kind" in tool.agent))
|
|
26
|
-
sandboxSkillsOf(tool.agent, into);
|
|
27
|
-
return into;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Writes one skill version's files to its folder `root` (`/skills/<name>@<hash>`), replacing a
|
|
31
|
-
* partial copy a failed mount left, and makes them read-only. `read` gives a file's bytes by
|
|
32
|
-
* `sha256:<hex>`.
|
|
33
|
-
*/
|
|
34
|
-
export async function mountSkill(handle, root, files, read, signal) {
|
|
19
|
+
export async function mountFolderFiles(handle, root, files, read, signal) {
|
|
35
20
|
const exec = async (command) => {
|
|
36
|
-
const result = await handle.exec({ command, cwd: "/", timeoutMs:
|
|
21
|
+
const result = await handle.exec({ command, cwd: "/", timeoutMs: helperCommandTimeoutMs() }, signal);
|
|
37
22
|
if (result.exitCode !== 0)
|
|
38
|
-
throw new Error(`${command.slice(0,
|
|
23
|
+
throw new Error(`${command.slice(0, hostTuning().harness.helperCommandExcerptChars)} failed: ${result.stderr.trim() || `exit ${result.exitCode}`}`);
|
|
39
24
|
};
|
|
40
25
|
const directories = new Set([root]);
|
|
41
26
|
for (const path of Object.keys(files))
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* fields take the defaults below.
|
|
5
5
|
*/
|
|
6
6
|
import { type PlacementHost, type TenantEnvironmentsConfig } from "@nylorun/core/contracts";
|
|
7
|
+
import { DEFAULT_SANDBOX_IDLE } from "./policy.js";
|
|
8
|
+
export { DEFAULT_SANDBOX_IDLE };
|
|
7
9
|
export declare const ENVIRONMENTS_CONFIG_SETTING = "environments.config";
|
|
8
10
|
/** The Tenant's settings, as stored (`environments.config`). */
|
|
9
11
|
export type TenantSandboxConfig = TenantEnvironmentsConfig;
|
|
10
12
|
/** The most an Environment may ask for when the Tenant sets no maximum. */
|
|
11
13
|
export declare const DEFAULT_MAX_CPUS = 4;
|
|
12
14
|
export declare const DEFAULT_MAX_MEMORY_MIB = 8192;
|
|
13
|
-
export declare const DEFAULT_SANDBOX_IDLE = "15m";
|
|
14
15
|
/** The most Environments a Tenant holds when it sets no limit. */
|
|
15
16
|
export declare const DEFAULT_MAX_ENVIRONMENTS = 100;
|
|
16
17
|
export interface SandboxResourcesLimit {
|
|
@@ -47,4 +48,3 @@ export declare function effectiveSandboxConfig(config: TenantSandboxConfig): Eff
|
|
|
47
48
|
/** The stored configuration, or `{}` when the Tenant has none. */
|
|
48
49
|
export declare function readSandboxConfig(t: SettingsReader): Promise<TenantSandboxConfig>;
|
|
49
50
|
export declare function writeSandboxConfig(t: SettingsWriter, config: TenantSandboxConfig): Promise<void>;
|
|
50
|
-
export {};
|
|
@@ -5,17 +5,18 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { TenantEnvironmentsConfigSchema, } from "@nylorun/core/contracts";
|
|
7
7
|
import { parseSandboxSize } from "@nylorun/core/define";
|
|
8
|
-
import { DEFAULT_SANDBOX_CPUS, DEFAULT_SANDBOX_MEMORY_MIB, DEV_PRESET_HOSTS, } from "./policy.js";
|
|
8
|
+
import { DEFAULT_SANDBOX_CPUS, DEFAULT_SANDBOX_IDLE, DEFAULT_SANDBOX_MEMORY_MIB, DEV_PRESET_HOSTS, } from "./policy.js";
|
|
9
|
+
import { hostTuning } from "../tuning.js";
|
|
10
|
+
export { DEFAULT_SANDBOX_IDLE };
|
|
9
11
|
export const ENVIRONMENTS_CONFIG_SETTING = "environments.config";
|
|
10
12
|
/** The most an Environment may ask for when the Tenant sets no maximum. */
|
|
11
13
|
export const DEFAULT_MAX_CPUS = 4;
|
|
12
14
|
export const DEFAULT_MAX_MEMORY_MIB = 8192;
|
|
13
|
-
export const DEFAULT_SANDBOX_IDLE = "15m";
|
|
14
15
|
/** The most Environments a Tenant holds when it sets no limit. */
|
|
15
16
|
export const DEFAULT_MAX_ENVIRONMENTS = 100;
|
|
16
17
|
export function memoryMiB(value) {
|
|
17
18
|
const bytes = parseSandboxSize(value);
|
|
18
|
-
return bytes === undefined ? 0 : Math.max(
|
|
19
|
+
return bytes === undefined ? 0 : Math.max(hostTuning().pods.memoryFloorMiB, Math.ceil(bytes / 1024 ** 2));
|
|
19
20
|
}
|
|
20
21
|
function resources(value, fallback) {
|
|
21
22
|
return {
|
package/dist/sandbox/tools.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SandboxToolOutcome } from "@nylorun/core/contracts";
|
|
2
2
|
import { type SandboxToolName } from "@nylorun/core/define";
|
|
3
|
+
import { type SandboxToolLimits } from "@nylorun/harness";
|
|
3
4
|
import type { SandboxHandle } from "./types.js";
|
|
4
5
|
export type { SandboxToolOutcome };
|
|
5
6
|
/** What the Runtime reports about a tool call on the session stream. */
|
|
@@ -9,6 +10,11 @@ export interface SandboxToolReport {
|
|
|
9
10
|
readonly exitCode?: number;
|
|
10
11
|
readonly killed?: boolean;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* How long a command the Runtime runs for itself in a sandbox may take (making a file's folder,
|
|
15
|
+
* mounting a skill, a tool's own helper command): `harness.helperCommandTimeoutMs`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const helperCommandTimeoutMs: () => number;
|
|
12
18
|
export declare function quote(value: string): string;
|
|
13
19
|
/**
|
|
14
20
|
* An absolute path inside the sandbox. Relative paths are relative to the workspace; on a backend
|
|
@@ -20,6 +26,6 @@ export declare function truncateMiddle(value: string, limit?: number): {
|
|
|
20
26
|
text: string;
|
|
21
27
|
truncated: boolean;
|
|
22
28
|
};
|
|
23
|
-
export declare function runSandboxTool(handle: SandboxHandle, name: SandboxToolName, input: Record<string, any>, signal: AbortSignal, report: (value: SandboxToolReport) => void,
|
|
29
|
+
export declare function runSandboxTool(handle: SandboxHandle, name: SandboxToolName, input: Record<string, any>, signal: AbortSignal, report: (value: SandboxToolReport) => void, limits?: SandboxToolLimits,
|
|
24
30
|
/** Variables every command gets (R2c): secret names set to the sentinel, plain values. */
|
|
25
31
|
environment?: Readonly<Record<string, string>>): Promise<SandboxToolOutcome>;
|