@nylorun/runtime 0.26.0-beta → 0.28.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/README.md +18 -14
- 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.d.ts +4 -3
- package/dist/api/a2a/card.js +4 -4
- 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 +30 -25
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +8 -11
- package/dist/api/components.d.ts +6789 -3405
- package/dist/api/components.js +43 -30
- 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.js +9 -4
- package/dist/api/http/body.d.ts +17 -3
- package/dist/api/http/body.js +35 -7
- package/dist/api/http/respond.js +6 -2
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +21 -9
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +13 -7
- 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 +3 -3
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +10 -111
- package/dist/api/http/routes/reads.js +51 -29
- package/dist/api/http/routes/sessions.js +26 -54
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +77 -24
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +180 -0
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +73 -47
- package/dist/artifacts/disk.d.ts +10 -14
- package/dist/artifacts/disk.js +99 -95
- 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 +72 -27
- 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/flow-host.d.ts +0 -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 +5 -4
- package/dist/gates/egress.d.ts +13 -13
- package/dist/gates/egress.js +19 -21
- 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 +41 -13
- 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/meter.js +6 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +34 -6
- package/dist/gates/policy.js +66 -18
- 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 +2 -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 +21 -15
- 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 +113 -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 +86 -73
- package/dist/harness-api/start.d.ts +27 -5
- package/dist/harness-api/start.js +102 -19
- 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 +4 -47
- 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 +2 -4
- package/dist/host/http.js +5 -29
- 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 +0 -2
- package/dist/keys/contract.js +0 -2
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +22 -1
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +3043 -1954
- 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 +12 -5
- package/dist/mcp/preview.js +43 -26
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- 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 +13653 -9986
- package/dist/reads/cursor.js +3 -1
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/index.d.ts +2 -2
- package/dist/record/index.js +2 -2
- 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 +8 -7
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +2 -2
- package/dist/sandbox/placement.js +7 -10
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +25 -17
- package/dist/sandbox/pods/lifecycle.js +51 -29
- 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 +14 -3
- 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 +11 -6
- package/dist/sandbox/resolve.js +19 -9
- package/dist/sandbox/setup.d.ts +6 -2
- package/dist/sandbox/setup.js +47 -14
- 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/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
- package/dist/store/postgres/reads.js +17 -82
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1382 -151
- package/dist/store/postgres/schema.js +191 -37
- package/dist/store/postgres/store.js +384 -105
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +172 -57
- package/dist/store/types.js +0 -1
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +41 -39
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +493 -328
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -90
- package/dist/tenant/context.d.ts +57 -33
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +15 -51
- 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 +11 -10
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +29 -6
- package/dist/tenant/identity-file.js +45 -15
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +34 -26
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +87 -0
- package/dist/tenant/limits.js +172 -0
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +11 -14
- package/dist/tenant/outbound.js +10 -15
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +7 -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.d.ts +1 -1
- package/dist/tenant/runtime.js +38 -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.d.ts +28 -45
- package/dist/tenant/sessions.js +89 -209
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +10 -10
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +51 -51
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +9 -14
- package/dist/tenant/worker.d.ts +4 -10
- package/dist/tenant/worker.js +10 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +10 -7
- package/dist/traces/map.js +16 -12
- 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 +390 -0
- package/dist/tuning.js +677 -0
- package/dist/vault/headers.js +4 -2
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- 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/record/environment.d.ts +0 -25
- package/dist/record/environment.js +0 -44
- 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/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -200
- package/dist/tenant/environments.d.ts +0 -174
- 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
package/dist/sandbox/setup.js
CHANGED
|
@@ -1,24 +1,51 @@
|
|
|
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
|
+
* The plan's `setupHash` names its commands and files: the engine runs the setup again on a volume
|
|
14
|
+
* whose marker names another (a new version's setup, track T3).
|
|
15
|
+
*
|
|
16
|
+
* A failed command fails the pod (`observed: failed`, a reason that starts `setup:`, and
|
|
17
|
+
* `agent.failed` with the command, its exit code and the end of its output), or, on a start on
|
|
18
|
+
* a new version, takes the Agent back to the version it ran (`revertStart`, T3): the turn that
|
|
19
|
+
* waits for it fails with that reason (`podUnusable`, `tenant/advance.ts`), the failure stays
|
|
20
|
+
* until a turn start asks the pod to run again (`wantRunning`), and the engine, polling, runs the
|
|
21
|
+
* setup again then.
|
|
22
|
+
*/
|
|
23
|
+
import { ENVIRONMENT_SETUP_TIMEOUT_DEFAULT, } from "@nylorun/core/contracts";
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
1
25
|
import { parseSandboxDuration } from "@nylorun/core/define";
|
|
26
|
+
import { canonical } from "../store/canonical.js";
|
|
2
27
|
import { contentKey, readManifest } from "../artifacts/folders.js";
|
|
3
|
-
import { contentTypeFor } from "
|
|
28
|
+
import { contentTypeFor } from "@nylorun/core/contracts";
|
|
4
29
|
import { definitionFileKey } from "../tenant/definition-files.js";
|
|
5
30
|
import { SETUP_FAILED } from "./pods/lifecycle.js";
|
|
31
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
32
|
+
import { revertStart } from "../tenant/agent-versions.js";
|
|
6
33
|
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
34
|
/** The setup of `recipe`, or undefined when it has neither commands nor files. */
|
|
12
35
|
export async function setupPlanOf(ctx, t, environment) {
|
|
13
36
|
const recipe = environment.applied;
|
|
14
37
|
if (!recipe.setup?.length && !recipe.files?.length)
|
|
15
38
|
return undefined;
|
|
39
|
+
// A command's own timeout, else the default, within the Tenant's `setupCommandMs` as it is now.
|
|
40
|
+
const { setupCommandMs } = await readRuntimeLimits(t);
|
|
41
|
+
const timeoutOf = (timeout) => Math.min(parseSandboxDuration(timeout ?? ENVIRONMENT_SETUP_TIMEOUT_DEFAULT), setupCommandMs);
|
|
16
42
|
return {
|
|
17
43
|
recipeVersion: environment.appliedVersion,
|
|
44
|
+
setupHash: createHash("sha256").update(canonical({ setup: recipe.setup ?? [], files: recipe.files ?? [] })).digest("hex"),
|
|
18
45
|
commands: (recipe.setup ?? []).map((command) => ({
|
|
19
46
|
command: command.command,
|
|
20
47
|
cwd: command.cwd ?? "/workspace",
|
|
21
|
-
timeoutMs: (command.timeout
|
|
48
|
+
timeoutMs: timeoutOf(command.timeout),
|
|
22
49
|
})),
|
|
23
50
|
files: (recipe.files ?? []).map((file, index) => ({ index, path: file.path })),
|
|
24
51
|
environment: await ctx.vault.environmentVariables(t, environment.id),
|
|
@@ -32,7 +59,7 @@ export function parseSetupReport(value) {
|
|
|
32
59
|
return undefined;
|
|
33
60
|
const index = typeof report.index === "number" && Number.isInteger(report.index) ? report.index : undefined;
|
|
34
61
|
const exitCode = typeof report.exitCode === "number" && Number.isInteger(report.exitCode) ? report.exitCode : undefined;
|
|
35
|
-
const output = typeof report.output === "string" ? report.output
|
|
62
|
+
const output = typeof report.output === "string" ? report.output : undefined;
|
|
36
63
|
return {
|
|
37
64
|
status: report.status,
|
|
38
65
|
...(index === undefined ? {} : { index }),
|
|
@@ -42,8 +69,10 @@ export function parseSetupReport(value) {
|
|
|
42
69
|
}
|
|
43
70
|
/**
|
|
44
71
|
* Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
|
|
45
|
-
* the pod is `failed` with a reason that starts `setup:`, and `
|
|
46
|
-
* command failed and how
|
|
72
|
+
* the pod is `failed` with a reason that starts `setup:`, and `agent.failed` says which
|
|
73
|
+
* command failed and how, with the end of its output (the Tenant's `setupOutputChars`; the reason
|
|
74
|
+
* quotes `setupReasonChars` of it and `setupCommandExcerptChars` of the command). A report
|
|
75
|
+
* from an older host, or a success, changes nothing.
|
|
47
76
|
*/
|
|
48
77
|
export async function recordSetup(ctx, id, epoch, report) {
|
|
49
78
|
await ctx.store.tx(async (t) => {
|
|
@@ -57,19 +86,23 @@ export async function recordSetup(ctx, id, epoch, report) {
|
|
|
57
86
|
await t.updateEnvironment(id, { diskSeed: null }, new Date().toISOString());
|
|
58
87
|
return;
|
|
59
88
|
}
|
|
89
|
+
const { setupOutputChars, setupReasonChars, setupCommandExcerptChars } = await readRuntimeLimits(t);
|
|
90
|
+
const output = (report.output ?? "").slice(-setupOutputChars);
|
|
60
91
|
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 =
|
|
92
|
+
const what = command === undefined ? "copying the input files failed" : `command ${report.index + 1} (${command.command.slice(0, setupCommandExcerptChars)}) exited ${report.exitCode ?? "abnormally"}`;
|
|
93
|
+
const tail = output.trim().slice(-setupReasonChars);
|
|
63
94
|
const reason = `${SETUP_FAILED} ${what}${tail ? `: ${tail}` : ""}`;
|
|
64
|
-
await t.
|
|
65
|
-
await t.environmentEvent(id, "environment.failed", {
|
|
95
|
+
await t.agentEvent(id, "agent.failed", {
|
|
66
96
|
reason,
|
|
67
97
|
setup: {
|
|
68
98
|
...(report.index === undefined ? {} : { index: report.index }),
|
|
69
99
|
...(report.exitCode === undefined ? {} : { exitCode: report.exitCode }),
|
|
70
|
-
output
|
|
100
|
+
output,
|
|
71
101
|
},
|
|
72
102
|
});
|
|
103
|
+
// On a start on a new version (track T3), back to the version it ran; else failed.
|
|
104
|
+
if (!(await revertStart(t, environment, reason, new Date().toISOString())))
|
|
105
|
+
await t.updateEnvironmentPod(id, { observed: "failed", reason }, new Date().toISOString());
|
|
73
106
|
});
|
|
74
107
|
if (report.status === "failed")
|
|
75
108
|
ctx.config.logger.warn("environment setup failed", { environmentId: id, index: report.index, exitCode: report.exitCode });
|
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>;
|
package/dist/sandbox/tools.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The six
|
|
3
|
-
*
|
|
2
|
+
* The six sandbox operations, implemented once over `SandboxHandle` so every backend behaves the
|
|
3
|
+
* same, held to the harness's sandbox tool limits (`SandboxToolLimits`, the numbers its tools'
|
|
4
|
+
* schemas name). Expected problems are returned as failed outcomes; only infrastructure errors
|
|
5
|
+
* throw.
|
|
4
6
|
*/
|
|
5
7
|
import { posix } from "node:path";
|
|
6
8
|
import { SANDBOX_WORKSPACE } from "@nylorun/core/define";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
9
|
+
import { DEFAULT_SANDBOX_TOOL_LIMITS } from "@nylorun/harness";
|
|
10
|
+
import { hostTuning } from "../tuning.js";
|
|
11
|
+
/**
|
|
12
|
+
* How long a command the Runtime runs for itself in a sandbox may take (making a file's folder,
|
|
13
|
+
* mounting a skill, a tool's own helper command): `harness.helperCommandTimeoutMs`.
|
|
14
|
+
*/
|
|
15
|
+
export const helperCommandTimeoutMs = () => hostTuning().harness.helperCommandTimeoutMs;
|
|
14
16
|
export function quote(value) {
|
|
15
17
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
16
18
|
}
|
|
@@ -24,7 +26,7 @@ export function resolveSandboxPath(path, workspace = SANDBOX_WORKSPACE) {
|
|
|
24
26
|
return posix.normalize(path.startsWith("/") ? path : posix.join(workspace, path));
|
|
25
27
|
}
|
|
26
28
|
/** Keep the head and tail of long output so the model sees both the start and the error. */
|
|
27
|
-
export function truncateMiddle(value, limit =
|
|
29
|
+
export function truncateMiddle(value, limit = DEFAULT_SANDBOX_TOOL_LIMITS.outputChars) {
|
|
28
30
|
if (value.length <= limit)
|
|
29
31
|
return { text: value, truncated: false };
|
|
30
32
|
const half = Math.floor(limit / 2);
|
|
@@ -39,20 +41,21 @@ const failed = (code, message) => ({
|
|
|
39
41
|
code,
|
|
40
42
|
message,
|
|
41
43
|
});
|
|
42
|
-
export async function runSandboxTool(handle, name, input, signal, report,
|
|
44
|
+
export async function runSandboxTool(handle, name, input, signal, report, limits = DEFAULT_SANDBOX_TOOL_LIMITS,
|
|
43
45
|
/** Variables every command gets (R2c): secret names set to the sentinel, plain values. */
|
|
44
46
|
environment) {
|
|
47
|
+
const lineOf = (line) => (line.length > limits.lineChars ? `${line.slice(0, limits.lineChars)}…` : line);
|
|
45
48
|
const workspace = handle.workspace ?? SANDBOX_WORKSPACE;
|
|
46
49
|
const resolvePath = (path) => resolveSandboxPath(path, workspace);
|
|
47
|
-
const exec = (command, timeoutMs =
|
|
50
|
+
const exec = (command, timeoutMs = helperCommandTimeoutMs()) => handle.exec({ command, cwd: workspace, timeoutMs, ...(environment ? { env: environment } : {}) }, signal);
|
|
48
51
|
switch (name) {
|
|
49
52
|
case "bash": {
|
|
50
53
|
const command = String(input.command);
|
|
51
|
-
const timeout = (input.timeout ??
|
|
54
|
+
const timeout = Math.min(input.timeout ?? limits.defaultTimeoutS, limits.maxTimeoutS) * 1000;
|
|
52
55
|
const result = await exec(command, timeout);
|
|
53
56
|
report({ command, exitCode: result.exitCode, killed: result.killed });
|
|
54
|
-
const stdout = truncateMiddle(result.stdout);
|
|
55
|
-
const stderr = truncateMiddle(result.stderr);
|
|
57
|
+
const stdout = truncateMiddle(result.stdout, limits.outputChars);
|
|
58
|
+
const stderr = truncateMiddle(result.stderr, limits.outputChars);
|
|
56
59
|
return {
|
|
57
60
|
kind: "completed",
|
|
58
61
|
output: {
|
|
@@ -76,14 +79,11 @@ environment) {
|
|
|
76
79
|
if (lines.at(-1) === "")
|
|
77
80
|
lines.pop();
|
|
78
81
|
const start = (input.offset ?? 1) - 1;
|
|
79
|
-
const limit = input.limit ??
|
|
82
|
+
const limit = Math.min(input.limit ?? limits.readLines, limits.maxReadLines);
|
|
80
83
|
const slice = lines.slice(start, start + limit);
|
|
81
84
|
const width = String(start + slice.length).length;
|
|
82
85
|
const text = slice
|
|
83
|
-
.map((line, index) => {
|
|
84
|
-
const clipped = line.length > LINE_LIMIT ? `${line.slice(0, LINE_LIMIT)}…` : line;
|
|
85
|
-
return `${String(start + index + 1).padStart(width, " ")}\t${clipped}`;
|
|
86
|
-
})
|
|
86
|
+
.map((line, index) => `${String(start + index + 1).padStart(width, " ")}\t${lineOf(line)}`)
|
|
87
87
|
.join("\n");
|
|
88
88
|
const remaining = lines.length - (start + slice.length);
|
|
89
89
|
return {
|
|
@@ -124,7 +124,7 @@ environment) {
|
|
|
124
124
|
return { kind: "completed", output: { path, replacements: input.replace_all === true ? count : 1 } };
|
|
125
125
|
}
|
|
126
126
|
case "grep": {
|
|
127
|
-
const max = input.max_results ??
|
|
127
|
+
const max = Math.min(input.max_results ?? limits.grepResults, limits.maxGrepResults);
|
|
128
128
|
const target = input.path ? String(input.path) : ".";
|
|
129
129
|
const flags = ["-rnE", ...(input.ignore_case ? ["-i"] : [])];
|
|
130
130
|
const include = input.glob ? ` --include=${quote(String(input.glob))}` : "";
|
|
@@ -137,7 +137,7 @@ environment) {
|
|
|
137
137
|
return result.stderr.trim()
|
|
138
138
|
? failed("sandbox.grep_failed", result.stderr.trim())
|
|
139
139
|
: { kind: "completed", output: "No matches." };
|
|
140
|
-
const shown = lines.slice(0, max).map(
|
|
140
|
+
const shown = lines.slice(0, max).map(lineOf);
|
|
141
141
|
return {
|
|
142
142
|
kind: "completed",
|
|
143
143
|
output: lines.length > max ? `${shown.join("\n")}\n… more matches omitted; narrow the search.` : shown.join("\n"),
|
|
@@ -146,7 +146,7 @@ environment) {
|
|
|
146
146
|
case "glob": {
|
|
147
147
|
const root = resolvePath(input.path ? String(input.path) : ".");
|
|
148
148
|
const pattern = String(input.pattern);
|
|
149
|
-
const command = `find ${quote(root)} -type f -not -path '*/.git/*' 2>/dev/null | head -n ${
|
|
149
|
+
const command = `find ${quote(root)} -type f -not -path '*/.git/*' 2>/dev/null | head -n ${limits.globScan}`;
|
|
150
150
|
report({ command });
|
|
151
151
|
const result = await exec(command);
|
|
152
152
|
const byBasename = !pattern.includes("/");
|
|
@@ -157,7 +157,7 @@ environment) {
|
|
|
157
157
|
const relative = posix.relative(root, file);
|
|
158
158
|
if (posix.matchesGlob(byBasename ? posix.basename(relative) : relative, pattern)) {
|
|
159
159
|
matches.push(file);
|
|
160
|
-
if (matches.length >
|
|
160
|
+
if (matches.length > limits.globResults)
|
|
161
161
|
break;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
@@ -166,8 +166,8 @@ environment) {
|
|
|
166
166
|
matches.sort();
|
|
167
167
|
return {
|
|
168
168
|
kind: "completed",
|
|
169
|
-
output: matches.length >
|
|
170
|
-
? `${matches.slice(0,
|
|
169
|
+
output: matches.length > limits.globResults
|
|
170
|
+
? `${matches.slice(0, limits.globResults).join("\n")}\n… more files omitted; narrow the pattern.`
|
|
171
171
|
: matches.join("\n"),
|
|
172
172
|
};
|
|
173
173
|
}
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -51,6 +51,10 @@ export interface ExecResult {
|
|
|
51
51
|
readonly killed: boolean;
|
|
52
52
|
readonly timedOut: boolean;
|
|
53
53
|
}
|
|
54
|
+
/** A file's bytes: whole, or as chunks to write as they come. */
|
|
55
|
+
export type SandboxFileBytes = Uint8Array | AsyncIterable<Uint8Array>;
|
|
56
|
+
/** `content` whole, for a backend that writes a file at once. */
|
|
57
|
+
export declare function wholeBytes(content: SandboxFileBytes): Promise<Uint8Array>;
|
|
54
58
|
export interface SandboxHandle {
|
|
55
59
|
/** The directory tools run in and resolve relative paths against. Default: `/workspace`. */
|
|
56
60
|
readonly workspace?: string;
|
|
@@ -61,7 +65,7 @@ export interface SandboxHandle {
|
|
|
61
65
|
readFile(path: string): Promise<string | undefined>;
|
|
62
66
|
/**
|
|
63
67
|
* The file's bytes, or undefined when it does not exist; refuses one larger than `maxBytes`
|
|
64
|
-
* (`SandboxFileTooLargeError`). For `save_artifact
|
|
68
|
+
* (`SandboxFileTooLargeError`). For `save_artifact` and the export, which keep binary files intact.
|
|
65
69
|
*/
|
|
66
70
|
readBytes?(path: string, maxBytes: number): Promise<Uint8Array | undefined>;
|
|
67
71
|
/**
|
|
@@ -70,8 +74,11 @@ export interface SandboxHandle {
|
|
|
70
74
|
* directory. Stops after `maxEntries + 1` files (`truncated`). For the turn-end export (F8.2).
|
|
71
75
|
*/
|
|
72
76
|
listFiles?(dir: string, maxEntries: number): Promise<SandboxListing | undefined>;
|
|
73
|
-
/**
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Text is written as UTF-8, bytes as they are, chunks as they come (a skill's file, which may
|
|
79
|
+
* be too large to hold at once). Parent directories must already exist.
|
|
80
|
+
*/
|
|
81
|
+
writeFile(path: string, content: string | SandboxFileBytes): Promise<void>;
|
|
75
82
|
/** Release compute; files persist. */
|
|
76
83
|
stop(): Promise<void>;
|
|
77
84
|
/**
|
package/dist/sandbox/types.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/** `content` whole, for a backend that writes a file at once. */
|
|
2
|
+
export async function wholeBytes(content) {
|
|
3
|
+
if (content instanceof Uint8Array)
|
|
4
|
+
return content;
|
|
5
|
+
const chunks = [];
|
|
6
|
+
for await (const chunk of content)
|
|
7
|
+
chunks.push(chunk);
|
|
8
|
+
return Buffer.concat(chunks);
|
|
9
|
+
}
|
|
1
10
|
/** A sandbox file read with `readBytes` is larger than the caller allows. */
|
|
2
11
|
export class SandboxFileTooLargeError extends Error {
|
|
3
12
|
path;
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { type Sql } from "postgres";
|
|
2
2
|
export type PostgresClient = Sql;
|
|
3
3
|
export interface PostgresClientOptions {
|
|
4
|
-
/** Pool size. Default 10. */
|
|
4
|
+
/** Pool size. Default `postgres.poolSize` (Host tuning, 10). */
|
|
5
5
|
max?: number;
|
|
6
|
-
/** Seconds before an idle connection closes. Default 30. */
|
|
6
|
+
/** Seconds before an idle connection closes. Default `postgres.idleTimeoutSeconds` (30). */
|
|
7
7
|
idleTimeoutSeconds?: number;
|
|
8
|
-
/** Seconds to wait for a connection. Default 10. */
|
|
8
|
+
/** Seconds to wait for a connection. Default `postgres.connectTimeoutSeconds` (10). */
|
|
9
9
|
connectTimeoutSeconds?: number;
|
|
10
10
|
/** `application_name` on every connection. Default `nylorun-runtime`. */
|
|
11
11
|
applicationName?: string;
|
|
12
12
|
/**
|
|
13
13
|
* `statement_timeout`: a statement running or waiting for a lock longer fails. Default
|
|
14
|
-
* 60 s; 0 turns it off. Migrations lift it (`lockMigrations` in `migrate.ts`).
|
|
14
|
+
* `postgres.statementTimeoutMs` (60 s); 0 turns it off. Migrations lift it (`lockMigrations` in `migrate.ts`).
|
|
15
15
|
*/
|
|
16
16
|
statementTimeoutMs?: number;
|
|
17
17
|
/**
|
|
18
18
|
* `idle_in_transaction_session_timeout`: a transaction left idle this long is ended and its
|
|
19
|
-
* connection closed, releasing its locks. Default 60 s; 0
|
|
19
|
+
* connection closed, releasing its locks. Default `postgres.idleInTransactionTimeoutMs` (60 s); 0
|
|
20
|
+
* turns it off.
|
|
20
21
|
*/
|
|
21
22
|
idleInTransactionTimeoutMs?: number;
|
|
22
23
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import postgres, {} from "postgres";
|
|
2
|
+
import { hostTuning } from "../../tuning.js";
|
|
2
3
|
/**
|
|
3
4
|
* Opens a connection pool on the Tenant's database, for its Session Store and the Tenant
|
|
4
5
|
* bootstrap (`tenant.ts`). Stores never end it, so the caller calls `client.end()` at
|
|
@@ -13,15 +14,16 @@ import postgres, {} from "postgres";
|
|
|
13
14
|
* not hold a connection forever: both are bounded by default.
|
|
14
15
|
*/
|
|
15
16
|
export function createPostgresClient(url, options = {}) {
|
|
17
|
+
const tuning = hostTuning().postgres;
|
|
16
18
|
return postgres(url, {
|
|
17
|
-
max: options.max ??
|
|
18
|
-
idle_timeout: options.idleTimeoutSeconds ??
|
|
19
|
-
connect_timeout: options.connectTimeoutSeconds ??
|
|
19
|
+
max: options.max ?? tuning.poolSize,
|
|
20
|
+
idle_timeout: options.idleTimeoutSeconds ?? tuning.idleTimeoutSeconds,
|
|
21
|
+
connect_timeout: options.connectTimeoutSeconds ?? tuning.connectTimeoutSeconds,
|
|
20
22
|
onnotice: () => { },
|
|
21
23
|
connection: {
|
|
22
24
|
application_name: options.applicationName ?? "nylorun-runtime",
|
|
23
|
-
statement_timeout: options.statementTimeoutMs ??
|
|
24
|
-
idle_in_transaction_session_timeout: options.idleInTransactionTimeoutMs ??
|
|
25
|
+
statement_timeout: options.statementTimeoutMs ?? tuning.statementTimeoutMs,
|
|
26
|
+
idle_in_transaction_session_timeout: options.idleInTransactionTimeoutMs ?? tuning.idleInTransactionTimeoutMs,
|
|
25
27
|
},
|
|
26
28
|
});
|
|
27
29
|
}
|
|
@@ -58,17 +60,18 @@ function connectionOf(source) {
|
|
|
58
60
|
}
|
|
59
61
|
/** Separate, bounded pool for read projections; inherits the Tenant connection credentials. */
|
|
60
62
|
export function createPostgresReadClient(source) {
|
|
63
|
+
const tuning = hostTuning().postgres;
|
|
61
64
|
const options = {
|
|
62
65
|
...connectionOf(source),
|
|
63
|
-
max:
|
|
64
|
-
idle_timeout:
|
|
65
|
-
connect_timeout:
|
|
66
|
+
max: tuning.readPoolSize,
|
|
67
|
+
idle_timeout: tuning.idleTimeoutSeconds,
|
|
68
|
+
connect_timeout: tuning.readConnectTimeoutSeconds,
|
|
66
69
|
onnotice() { },
|
|
67
70
|
connection: {
|
|
68
71
|
...source.options.connection,
|
|
69
72
|
application_name: "nylorun-reads",
|
|
70
|
-
statement_timeout:
|
|
71
|
-
idle_in_transaction_session_timeout:
|
|
73
|
+
statement_timeout: tuning.readStatementTimeoutMs,
|
|
74
|
+
idle_in_transaction_session_timeout: tuning.readStatementTimeoutMs,
|
|
72
75
|
default_transaction_read_only: true,
|
|
73
76
|
},
|
|
74
77
|
};
|
|
@@ -87,7 +90,7 @@ export function createPostgresListenClient(source, handlers) {
|
|
|
87
90
|
max: 1,
|
|
88
91
|
idle_timeout: 0,
|
|
89
92
|
max_lifetime: null,
|
|
90
|
-
connect_timeout:
|
|
93
|
+
connect_timeout: hostTuning().postgres.connectTimeoutSeconds,
|
|
91
94
|
fetch_types: false,
|
|
92
95
|
onnotice() { },
|
|
93
96
|
onnotify: handlers.onNotify,
|