@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
package/dist/vault/headers.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Runtime sets itself, so a credential can never replace them.
|
|
5
5
|
*/
|
|
6
6
|
import { VaultError } from "./error.js";
|
|
7
|
+
import { hostTuning } from "../tuning.js";
|
|
7
8
|
/** Headers the MCP transport and the HTTP tool call own. */
|
|
8
9
|
const RESERVED_HEADERS = new Set([
|
|
9
10
|
"host",
|
|
@@ -25,13 +26,14 @@ const RESERVED_HEADERS = new Set([
|
|
|
25
26
|
export function credentialSecrets(headers, identity) {
|
|
26
27
|
const skip = identity?.header.toLowerCase();
|
|
27
28
|
const secrets = new Set();
|
|
29
|
+
const least = hostTuning().harness.scrubMinChars;
|
|
28
30
|
for (const [name, value] of Object.entries(headers)) {
|
|
29
31
|
if (name.toLowerCase() === skip)
|
|
30
32
|
continue;
|
|
31
|
-
if (value.length >=
|
|
33
|
+
if (value.length >= least)
|
|
32
34
|
secrets.add(value);
|
|
33
35
|
const bearer = /^bearer\s+(\S+)\s*$/iu.exec(value)?.[1];
|
|
34
|
-
if (bearer !== undefined && bearer.length >=
|
|
36
|
+
if (bearer !== undefined && bearer.length >= least)
|
|
35
37
|
secrets.add(bearer);
|
|
36
38
|
}
|
|
37
39
|
return [...secrets];
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_VERSION = "0.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.27.0-beta";
|
package/dist/version.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Kept in step with package.json by runtime/server/scripts/check-package.mjs, which runs in npm run check.
|
|
2
2
|
// release:prepare also rewrites this when it bumps @nylorun/runtime.
|
|
3
|
-
export const RUNTIME_VERSION = "0.
|
|
3
|
+
export const RUNTIME_VERSION = "0.27.0-beta";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0-beta",
|
|
4
4
|
"description": "Nylorun Runtime: the agent execution Host on Postgres, Restate and S2, and model providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@hono/node-server": "^2.1.3",
|
|
66
66
|
"@hono/zod-openapi": "^1.6.3",
|
|
67
67
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
68
|
-
"@nylorun/core": "0.
|
|
69
|
-
"@nylorun/harness": "0.
|
|
68
|
+
"@nylorun/core": "0.23.0-beta",
|
|
69
|
+
"@nylorun/harness": "0.28.0-beta",
|
|
70
70
|
"@restatedev/restate-sdk": "1.17.2",
|
|
71
71
|
"@restatedev/restate-sdk-clients": "1.17.2",
|
|
72
72
|
"@s2-dev/streamstore": "0.28.0",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { TenantContext } from "../tenant/context.js";
|
|
2
|
-
/** The directory every sandbox's outputs are exported from. */
|
|
3
|
-
export declare const OUTPUTS_DIR = "/workspace/outputs";
|
|
4
|
-
/**
|
|
5
|
-
* Exports the outputs of turn `turnId` of session `sessionId`, if it completed and its sandbox
|
|
6
|
-
* has any. Never throws.
|
|
7
|
-
*/
|
|
8
|
-
export declare function exportOutputs(ctx: TenantContext, sessionId: string, turnId: string | null, signal: AbortSignal): Promise<void>;
|
package/dist/artifacts/export.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The turn-end export (F8.2): when an agent's turn completes, core reads `/workspace/outputs` of
|
|
3
|
-
* the session's sandbox through the `WorkspaceReader` seam and keeps it as a version of the
|
|
4
|
-
* session's folder artifact `outputs` (the first export creates it; later turns add versions,
|
|
5
|
-
* and a turn whose outputs did not change adds none).
|
|
6
|
-
*
|
|
7
|
-
* - **After the turn.** The advance calls `exportOutputs` once `settle` has committed the turn,
|
|
8
|
-
* outside its transaction and while it still holds the session's lease, so the next turn
|
|
9
|
-
* cannot change the workspace under it. Nothing here fails the turn: a sandbox or store
|
|
10
|
-
* failure is logged and recorded as `artifact.export.failed`.
|
|
11
|
-
* - **Claimed.** The listing and the bytes are what the holder of the workspace supplied: the
|
|
12
|
-
* version's source is `export` and its event says `claimed: true`.
|
|
13
|
-
* - **Bounded.** At most `EXPORT_MAX_FILES` files and `EXPORT_MAX_BYTES` bytes per export, each
|
|
14
|
-
* file within the Tenant's per-file limit, and the new bytes within its total; past any of
|
|
15
|
-
* them nothing is stored and `artifact.export.skipped` says why.
|
|
16
|
-
* - **Deduplicated.** Each file is stored content-addressed (`folders.ts`); a file the store
|
|
17
|
-
* already holds is not stored again.
|
|
18
|
-
*
|
|
19
|
-
* No sandbox, a sandbox never created, or an empty or missing `/workspace/outputs` exports
|
|
20
|
-
* nothing and records nothing. The package's declared outputs are left for later: the manifest
|
|
21
|
-
* has no `outputs` field yet.
|
|
22
|
-
*/
|
|
23
|
-
import { posix } from "node:path";
|
|
24
|
-
import { EXPORT_MAX_BYTES, EXPORT_MAX_FILES, OUTPUTS_ARTIFACT_NAME, } from "@nylorun/core/contracts";
|
|
25
|
-
import { SANDBOX_WORKSPACE } from "@nylorun/core/define";
|
|
26
|
-
import { isWorkflowManifest } from "../core/flow-host.js";
|
|
27
|
-
import { sandboxWorkspaceOf } from "../sandbox/share.js";
|
|
28
|
-
import { SandboxFileTooLargeError } from "../sandbox/types.js";
|
|
29
|
-
import { readArtifactLimits } from "./config.js";
|
|
30
|
-
import { collectContent, commitFolderVersion, contentEpoch, contentKey, encodeManifest, FolderCommitRefused, sha256Hex, } from "./folders.js";
|
|
31
|
-
import { contentTypeFor } from "./media-types.js";
|
|
32
|
-
/** The directory every sandbox's outputs are exported from. */
|
|
33
|
-
export const OUTPUTS_DIR = `${SANDBOX_WORKSPACE}/outputs`;
|
|
34
|
-
/** Stops the export with `artifact.export.skipped`. */
|
|
35
|
-
class Skip extends Error {
|
|
36
|
-
reason;
|
|
37
|
-
limit;
|
|
38
|
-
path;
|
|
39
|
-
constructor(reason, message, limit, path) {
|
|
40
|
-
super(message);
|
|
41
|
-
this.reason = reason;
|
|
42
|
-
this.limit = limit;
|
|
43
|
-
this.path = path;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Exports the outputs of turn `turnId` of session `sessionId`, if it completed and its sandbox
|
|
48
|
-
* has any. Never throws.
|
|
49
|
-
*/
|
|
50
|
-
export async function exportOutputs(ctx, sessionId, turnId, signal) {
|
|
51
|
-
try {
|
|
52
|
-
await runExport(ctx, sessionId, turnId, signal);
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
-
if (signal.aborted) {
|
|
57
|
-
ctx.config.logger.info("outputs export stopped", { sessionId, message });
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const skip = error instanceof Skip
|
|
61
|
-
? error
|
|
62
|
-
: error instanceof FolderCommitRefused && error.reason === "tenant_total"
|
|
63
|
-
? new Skip("tenant_total", error.message, error.limit)
|
|
64
|
-
: undefined;
|
|
65
|
-
if (!skip)
|
|
66
|
-
ctx.config.logger.warn("outputs export failed", { sessionId, message });
|
|
67
|
-
try {
|
|
68
|
-
await ctx.store.tx(async (t) => {
|
|
69
|
-
if (skip)
|
|
70
|
-
await t.event(sessionId, turnId, "artifact.export.skipped", {
|
|
71
|
-
name: OUTPUTS_ARTIFACT_NAME,
|
|
72
|
-
reason: skip.reason,
|
|
73
|
-
message: skip.message,
|
|
74
|
-
...(skip.limit === undefined ? {} : { limit: skip.limit }),
|
|
75
|
-
...(skip.path === undefined ? {} : { path: skip.path }),
|
|
76
|
-
});
|
|
77
|
-
else
|
|
78
|
-
await t.event(sessionId, turnId, "artifact.export.failed", {
|
|
79
|
-
name: OUTPUTS_ARTIFACT_NAME,
|
|
80
|
-
message,
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
/* the session is gone (reset) or the store closed */
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
async function runExport(ctx, sessionId, turnId, signal) {
|
|
90
|
-
const found = await ctx.store.tx(async (t) => {
|
|
91
|
-
const s = await t.get("sessions", sessionId);
|
|
92
|
-
// Only a turn that completed, and only an agent's (a flow agent's sandbox is its agents').
|
|
93
|
-
if (!s || s.status !== "completed" || s.lastTurnId !== turnId || isWorkflowManifest(s.manifest))
|
|
94
|
-
return undefined;
|
|
95
|
-
const workspace = sandboxWorkspaceOf(s);
|
|
96
|
-
const prior = (await t.listArtifacts({ sessionId })).find((row) => row.kind === "folder" && row.name === OUTPUTS_ARTIFACT_NAME);
|
|
97
|
-
const latest = prior ? await t.artifactVersion(prior.id, prior.latestVersion) : undefined;
|
|
98
|
-
return {
|
|
99
|
-
session: {
|
|
100
|
-
sessionId,
|
|
101
|
-
turnId,
|
|
102
|
-
ownerId: workspace.ownerId,
|
|
103
|
-
...(workspace.environmentId === undefined ? {} : { environmentId: workspace.environmentId }),
|
|
104
|
-
manifest: s.manifest,
|
|
105
|
-
},
|
|
106
|
-
limits: await readArtifactLimits(t),
|
|
107
|
-
priorId: prior?.id,
|
|
108
|
-
latestSha: latest?.sha256,
|
|
109
|
-
epoch: await contentEpoch(t),
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
if (!found)
|
|
113
|
-
return;
|
|
114
|
-
const { session, limits } = found;
|
|
115
|
-
const listing = await ctx.workspaces.list(session, OUTPUTS_DIR, {
|
|
116
|
-
maxEntries: EXPORT_MAX_FILES,
|
|
117
|
-
signal,
|
|
118
|
-
});
|
|
119
|
-
if (!listing || listing.entries.length === 0)
|
|
120
|
-
return;
|
|
121
|
-
if (listing.truncated)
|
|
122
|
-
throw new Skip("too_many_files", `${OUTPUTS_DIR} holds more than ${EXPORT_MAX_FILES} files`, EXPORT_MAX_FILES);
|
|
123
|
-
const large = listing.entries.find((entry) => entry.size > limits.fileBytes);
|
|
124
|
-
if (large)
|
|
125
|
-
throw tooLargeFile(large.path, large.size, limits.fileBytes);
|
|
126
|
-
const maxBytes = Math.min(EXPORT_MAX_BYTES, limits.totalBytes);
|
|
127
|
-
const listed = listing.entries.reduce((sum, entry) => sum + entry.size, 0);
|
|
128
|
-
if (listed > maxBytes)
|
|
129
|
-
throw tooLarge(listed, maxBytes);
|
|
130
|
-
// The files this export stored: removed again if it commits nothing, unless another version
|
|
131
|
-
// names them by then.
|
|
132
|
-
const put = [];
|
|
133
|
-
try {
|
|
134
|
-
await storeAndCommit(ctx, found, listing.entries, maxBytes, put, signal);
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
if (put.length > 0)
|
|
138
|
-
await ctx.store
|
|
139
|
-
.tx(async (t) => {
|
|
140
|
-
await t.lockArtifactQuota();
|
|
141
|
-
await collectContent(ctx.blobs, t, put);
|
|
142
|
-
})
|
|
143
|
-
.catch(() => undefined);
|
|
144
|
-
throw error;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async function storeAndCommit(ctx, found, listed, maxBytes, put, signal) {
|
|
148
|
-
const { session, limits } = found;
|
|
149
|
-
const { sessionId, turnId } = session;
|
|
150
|
-
const entries = [];
|
|
151
|
-
const stored = new Set();
|
|
152
|
-
let read = 0;
|
|
153
|
-
for (const entry of listed) {
|
|
154
|
-
let bytes;
|
|
155
|
-
try {
|
|
156
|
-
bytes = await ctx.workspaces.readBytes(session, posix.join(OUTPUTS_DIR, entry.path), {
|
|
157
|
-
maxBytes: limits.fileBytes,
|
|
158
|
-
signal,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
if (error instanceof SandboxFileTooLargeError)
|
|
163
|
-
throw tooLargeFile(entry.path, error.size, limits.fileBytes);
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
// Gone since the listing: the folder is what is there now.
|
|
167
|
-
if (bytes === undefined)
|
|
168
|
-
continue;
|
|
169
|
-
read += bytes.length;
|
|
170
|
-
if (read > maxBytes)
|
|
171
|
-
throw tooLarge(read, maxBytes);
|
|
172
|
-
const sha256 = sha256Hex(bytes);
|
|
173
|
-
const contentType = contentTypeFor(posix.basename(entry.path));
|
|
174
|
-
if (!stored.has(sha256)) {
|
|
175
|
-
stored.add(sha256);
|
|
176
|
-
const key = contentKey(sha256);
|
|
177
|
-
if (!(await ctx.blobs.head(key))) {
|
|
178
|
-
put.push(sha256);
|
|
179
|
-
await ctx.blobs.put(key, bytes, { contentType, signal });
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
entries.push({ path: entry.path, size: bytes.length, sha256, contentType });
|
|
183
|
-
}
|
|
184
|
-
// Every file vanished, or nothing changed since the last export: no new version.
|
|
185
|
-
if (entries.length === 0)
|
|
186
|
-
return;
|
|
187
|
-
if (found.latestSha !== undefined && sha256Hex(encodeManifest(entries)) === found.latestSha)
|
|
188
|
-
return;
|
|
189
|
-
await commitFolderVersion(ctx, {
|
|
190
|
-
sessionId,
|
|
191
|
-
turnId,
|
|
192
|
-
...(found.priorId === undefined ? {} : { artifactId: found.priorId }),
|
|
193
|
-
name: OUTPUTS_ARTIFACT_NAME,
|
|
194
|
-
entries,
|
|
195
|
-
epoch: found.epoch,
|
|
196
|
-
totalBytes: limits.totalBytes,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
function tooLargeFile(path, size, limit) {
|
|
200
|
-
return new Skip("file_too_large", `${OUTPUTS_DIR}/${path} is ${size} bytes; a file may hold at most ${limit}`, limit, path);
|
|
201
|
-
}
|
|
202
|
-
function tooLarge(bytes, limit) {
|
|
203
|
-
return new Skip("too_large", `${OUTPUTS_DIR} holds ${bytes} bytes; an export stores at most ${limit}`, limit);
|
|
204
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Media types of file artifacts: what an upload may declare, what a name's extension implies,
|
|
3
|
-
* and which model input a file is (`modelInputOf`, protocol 12): text, an image, a PDF, an Office
|
|
4
|
-
* document, audio or video. A model reads a file only when its inputs include that one.
|
|
5
|
-
*/
|
|
6
|
-
import type { ModelInput } from "@nylorun/core/contracts";
|
|
7
|
-
/** Images the model reads as images. */
|
|
8
|
-
export declare const MODEL_IMAGE_TYPES: readonly string[];
|
|
9
|
-
/** The largest image or text file a model call carries inline. */
|
|
10
|
-
export declare const MODEL_FILE_MAX_BYTES: number;
|
|
11
|
-
/** The type part of a `Content-Type` value, lowercased, without parameters. */
|
|
12
|
-
export declare function essence(contentType: string): string;
|
|
13
|
-
/** A declared `Content-Type` as stored, or undefined when it is not a media type. */
|
|
14
|
-
export declare function normalizeContentType(value: string | undefined): string | undefined;
|
|
15
|
-
/** The media type a file name implies, or `application/octet-stream`. */
|
|
16
|
-
export declare function contentTypeFor(name: string): string;
|
|
17
|
-
export declare function isModelImage(contentType: string): boolean;
|
|
18
|
-
/** Files the model reads as text: `text/*`, JSON, XML, YAML and their `+json`/`+xml` kin. */
|
|
19
|
-
export declare function isText(contentType: string): boolean;
|
|
20
|
-
/** The usual extension of a media type, for a name the Runtime gives a file; undefined if none. */
|
|
21
|
-
export declare function extensionFor(contentType: string): string | undefined;
|
|
22
|
-
/** The model input a file of `contentType` is, or undefined for a type no model reads. */
|
|
23
|
-
export declare function modelInputOf(contentType: string): ModelInput | undefined;
|
|
24
|
-
/** An input, in a sentence: "PDFs", "Office documents"; "files of this type" for none. */
|
|
25
|
-
export declare function inputLabel(input: ModelInput | undefined): string;
|
|
26
|
-
/**
|
|
27
|
-
* Why a model that reads `inputs` cannot read a file of `contentType`, or undefined when it can.
|
|
28
|
-
* An image must also be PNG, JPEG, GIF or WebP.
|
|
29
|
-
*/
|
|
30
|
-
export declare function unreadableReason(contentType: string, inputs: readonly ModelInput[]): string | undefined;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_CONTENT_TYPE } from "../blob/index.js";
|
|
2
|
-
const MEDIA_TYPE = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
|
|
3
|
-
const BY_EXTENSION = {
|
|
4
|
-
png: "image/png",
|
|
5
|
-
jpg: "image/jpeg",
|
|
6
|
-
jpeg: "image/jpeg",
|
|
7
|
-
gif: "image/gif",
|
|
8
|
-
webp: "image/webp",
|
|
9
|
-
svg: "image/svg+xml",
|
|
10
|
-
pdf: "application/pdf",
|
|
11
|
-
doc: "application/msword",
|
|
12
|
-
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
13
|
-
xls: "application/vnd.ms-excel",
|
|
14
|
-
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
15
|
-
ppt: "application/vnd.ms-powerpoint",
|
|
16
|
-
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
17
|
-
txt: "text/plain",
|
|
18
|
-
md: "text/markdown",
|
|
19
|
-
csv: "text/csv",
|
|
20
|
-
html: "text/html",
|
|
21
|
-
htm: "text/html",
|
|
22
|
-
css: "text/css",
|
|
23
|
-
js: "text/javascript",
|
|
24
|
-
mjs: "text/javascript",
|
|
25
|
-
ts: "text/x-typescript",
|
|
26
|
-
py: "text/x-python",
|
|
27
|
-
sh: "text/x-sh",
|
|
28
|
-
json: "application/json",
|
|
29
|
-
xml: "application/xml",
|
|
30
|
-
yaml: "application/yaml",
|
|
31
|
-
yml: "application/yaml",
|
|
32
|
-
zip: "application/zip",
|
|
33
|
-
tar: "application/x-tar",
|
|
34
|
-
gz: "application/gzip",
|
|
35
|
-
mp3: "audio/mpeg",
|
|
36
|
-
wav: "audio/wav",
|
|
37
|
-
mp4: "video/mp4",
|
|
38
|
-
};
|
|
39
|
-
/** Images the model reads as images. */
|
|
40
|
-
export const MODEL_IMAGE_TYPES = Object.freeze([
|
|
41
|
-
"image/png",
|
|
42
|
-
"image/jpeg",
|
|
43
|
-
"image/webp",
|
|
44
|
-
"image/gif",
|
|
45
|
-
]);
|
|
46
|
-
/** The largest image or text file a model call carries inline. */
|
|
47
|
-
export const MODEL_FILE_MAX_BYTES = 8 * 1024 * 1024;
|
|
48
|
-
/** The type part of a `Content-Type` value, lowercased, without parameters. */
|
|
49
|
-
export function essence(contentType) {
|
|
50
|
-
return contentType.split(";")[0].trim().toLowerCase();
|
|
51
|
-
}
|
|
52
|
-
/** A declared `Content-Type` as stored, or undefined when it is not a media type. */
|
|
53
|
-
export function normalizeContentType(value) {
|
|
54
|
-
if (value === undefined)
|
|
55
|
-
return undefined;
|
|
56
|
-
const trimmed = value.trim();
|
|
57
|
-
if (trimmed === "" || trimmed.length > 255)
|
|
58
|
-
return undefined;
|
|
59
|
-
return MEDIA_TYPE.test(essence(trimmed)) ? trimmed : undefined;
|
|
60
|
-
}
|
|
61
|
-
/** The media type a file name implies, or `application/octet-stream`. */
|
|
62
|
-
export function contentTypeFor(name) {
|
|
63
|
-
const dot = name.lastIndexOf(".");
|
|
64
|
-
if (dot < 0)
|
|
65
|
-
return DEFAULT_CONTENT_TYPE;
|
|
66
|
-
return BY_EXTENSION[name.slice(dot + 1).toLowerCase()] ?? DEFAULT_CONTENT_TYPE;
|
|
67
|
-
}
|
|
68
|
-
export function isModelImage(contentType) {
|
|
69
|
-
return MODEL_IMAGE_TYPES.includes(essence(contentType));
|
|
70
|
-
}
|
|
71
|
-
/** Files the model reads as text: `text/*`, JSON, XML, YAML and their `+json`/`+xml` kin. */
|
|
72
|
-
export function isText(contentType) {
|
|
73
|
-
const type = essence(contentType);
|
|
74
|
-
return (type.startsWith("text/") ||
|
|
75
|
-
type === "application/json" ||
|
|
76
|
-
type === "application/xml" ||
|
|
77
|
-
type === "application/yaml" ||
|
|
78
|
-
type === "application/x-yaml" ||
|
|
79
|
-
type === "application/javascript" ||
|
|
80
|
-
type.endsWith("+json") ||
|
|
81
|
-
type.endsWith("+xml"));
|
|
82
|
-
}
|
|
83
|
-
/** The usual extension of a media type, for a name the Runtime gives a file; undefined if none. */
|
|
84
|
-
export function extensionFor(contentType) {
|
|
85
|
-
const type = essence(contentType);
|
|
86
|
-
return Object.keys(BY_EXTENSION).find((extension) => BY_EXTENSION[extension] === type);
|
|
87
|
-
}
|
|
88
|
-
/** Word, Excel and PowerPoint documents, old and Open XML. */
|
|
89
|
-
function isOffice(type) {
|
|
90
|
-
return (type === "application/msword" ||
|
|
91
|
-
type === "application/vnd.ms-excel" ||
|
|
92
|
-
type === "application/vnd.ms-powerpoint" ||
|
|
93
|
-
type.startsWith("application/vnd.openxmlformats-officedocument."));
|
|
94
|
-
}
|
|
95
|
-
/** The model input a file of `contentType` is, or undefined for a type no model reads. */
|
|
96
|
-
export function modelInputOf(contentType) {
|
|
97
|
-
const type = essence(contentType);
|
|
98
|
-
if (isText(type))
|
|
99
|
-
return "text";
|
|
100
|
-
if (type.startsWith("image/"))
|
|
101
|
-
return "image";
|
|
102
|
-
if (type === "application/pdf")
|
|
103
|
-
return "pdf";
|
|
104
|
-
if (isOffice(type))
|
|
105
|
-
return "office";
|
|
106
|
-
if (type.startsWith("audio/"))
|
|
107
|
-
return "audio";
|
|
108
|
-
if (type.startsWith("video/"))
|
|
109
|
-
return "video";
|
|
110
|
-
return undefined;
|
|
111
|
-
}
|
|
112
|
-
const LABELS = {
|
|
113
|
-
text: "text files",
|
|
114
|
-
image: "images",
|
|
115
|
-
pdf: "PDFs",
|
|
116
|
-
office: "Office documents",
|
|
117
|
-
audio: "audio",
|
|
118
|
-
video: "video",
|
|
119
|
-
};
|
|
120
|
-
/** An input, in a sentence: "PDFs", "Office documents"; "files of this type" for none. */
|
|
121
|
-
export function inputLabel(input) {
|
|
122
|
-
return input === undefined ? "files of this type" : LABELS[input];
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Why a model that reads `inputs` cannot read a file of `contentType`, or undefined when it can.
|
|
126
|
-
* An image must also be PNG, JPEG, GIF or WebP.
|
|
127
|
-
*/
|
|
128
|
-
export function unreadableReason(contentType, inputs) {
|
|
129
|
-
const input = modelInputOf(contentType);
|
|
130
|
-
if (input === undefined)
|
|
131
|
-
return `no model reads ${essence(contentType)} files`;
|
|
132
|
-
if (!inputs.includes(input))
|
|
133
|
-
return `this model does not read ${LABELS[input]}`;
|
|
134
|
-
if (input === "image" && !isModelImage(contentType))
|
|
135
|
-
return "the model reads PNG, JPEG, GIF and WebP images only";
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `WorkspaceReader` seam (F8.2): how core reads a session's sandbox workspace at turn end, to
|
|
3
|
-
* export its outputs as a folder artifact. Two calls, a listing and one file's bytes, both the
|
|
4
|
-
* claim of whoever holds the workspace: the Runtime stores what it is given without observing it.
|
|
5
|
-
*
|
|
6
|
-
* One implementation, `sandboxWorkspaceReader`, over the workspace feature (F6.2,
|
|
7
|
-
* `harness-api/workspace.ts`): the in-process `SandboxManager`, or the harness that serves the
|
|
8
|
-
* Tenant's workspaces (`workspace.read`). F6.2 reimplements it over the Harness API's `workspace.read`, and F7.2's pod
|
|
9
|
-
* sandboxes are read through the same seam, so the export never knows which kind of sandbox it
|
|
10
|
-
* reads.
|
|
11
|
-
*/
|
|
12
|
-
import type { AgentManifest } from "@nylorun/core/define";
|
|
13
|
-
import { type SandboxManager } from "../sandbox/manager.js";
|
|
14
|
-
/** What the reader needs of a workspace: a `SandboxManager`, or the workspace feature. */
|
|
15
|
-
export type WorkspaceFiles = Pick<SandboxManager, "listFiles" | "readBytes">;
|
|
16
|
-
/** Whose workspace to read: the session whose turn ended, resolved to the workspace it uses. */
|
|
17
|
-
export interface WorkspaceSession {
|
|
18
|
-
/** The session whose turn ended. */
|
|
19
|
-
readonly sessionId: string;
|
|
20
|
-
/** The turn that ended. */
|
|
21
|
-
readonly turnId: string | null;
|
|
22
|
-
/** The session that owns the workspace (one hop through `sandboxOwnerId`). */
|
|
23
|
-
readonly ownerId: string;
|
|
24
|
-
/** The sandbox resource the workspace belongs to, when the owner is attached to one. */
|
|
25
|
-
readonly environmentId?: string;
|
|
26
|
-
/** The session's pinned manifest: its `runtime` block carries the sandbox's spec. */
|
|
27
|
-
readonly manifest: AgentManifest;
|
|
28
|
-
}
|
|
29
|
-
/** One regular file under the listed directory. */
|
|
30
|
-
export interface WorkspaceEntry {
|
|
31
|
-
/** Relative to the directory listed, `/`-separated, never `.` or `..` segments. */
|
|
32
|
-
readonly path: string;
|
|
33
|
-
readonly size: number;
|
|
34
|
-
}
|
|
35
|
-
export interface WorkspaceListing {
|
|
36
|
-
/** The files in path order, symbolic links left out. */
|
|
37
|
-
readonly entries: readonly WorkspaceEntry[];
|
|
38
|
-
/** More than `maxEntries` files: `entries` holds `maxEntries + 1` of them. */
|
|
39
|
-
readonly truncated: boolean;
|
|
40
|
-
}
|
|
41
|
-
export interface WorkspaceReader {
|
|
42
|
-
/**
|
|
43
|
-
* The regular files under `dir` (absolute, e.g. `/workspace/outputs`), recursively. Undefined
|
|
44
|
-
* when the session has no sandbox, its sandbox was never created, or `dir` is not a directory.
|
|
45
|
-
*/
|
|
46
|
-
list(session: WorkspaceSession, dir: string, options: {
|
|
47
|
-
readonly maxEntries: number;
|
|
48
|
-
readonly signal: AbortSignal;
|
|
49
|
-
}): Promise<WorkspaceListing | undefined>;
|
|
50
|
-
/**
|
|
51
|
-
* One file's bytes (absolute path), or undefined when it is gone. A file larger than
|
|
52
|
-
* `maxBytes` throws `SandboxFileTooLargeError`; a sandbox that fails throws.
|
|
53
|
-
*/
|
|
54
|
-
readBytes(session: WorkspaceSession, path: string, options: {
|
|
55
|
-
readonly maxBytes: number;
|
|
56
|
-
readonly signal: AbortSignal;
|
|
57
|
-
}): Promise<Uint8Array | undefined>;
|
|
58
|
-
}
|
|
59
|
-
/** The reader over the workspaces, in each sandbox's queue of tool calls. */
|
|
60
|
-
export declare function sandboxWorkspaceReader(sandbox: WorkspaceFiles): WorkspaceReader;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { sandboxToolSpec } from "../sandbox/manager.js";
|
|
2
|
-
import { SandboxFileTooLargeError } from "../sandbox/types.js";
|
|
3
|
-
/** The reader over the workspaces, in each sandbox's queue of tool calls. */
|
|
4
|
-
export function sandboxWorkspaceReader(sandbox) {
|
|
5
|
-
const target = (session) => {
|
|
6
|
-
const spec = sandboxToolSpec(session.manifest, "read");
|
|
7
|
-
if (!spec)
|
|
8
|
-
return undefined;
|
|
9
|
-
return {
|
|
10
|
-
spec,
|
|
11
|
-
ref: {
|
|
12
|
-
id: session.ownerId,
|
|
13
|
-
activeTurnId: session.turnId,
|
|
14
|
-
manifest: session.manifest,
|
|
15
|
-
...(session.environmentId === undefined ? {} : { environmentId: session.environmentId }),
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
async list(session, dir, options) {
|
|
21
|
-
const found = target(session);
|
|
22
|
-
if (!found)
|
|
23
|
-
return undefined;
|
|
24
|
-
const listed = await sandbox.listFiles(found.ref, found.spec, dir, options.maxEntries, options.signal);
|
|
25
|
-
if (listed.kind === "missing")
|
|
26
|
-
return undefined;
|
|
27
|
-
if (listed.kind === "failed")
|
|
28
|
-
throw new Error(listed.message);
|
|
29
|
-
return listed.listing;
|
|
30
|
-
},
|
|
31
|
-
async readBytes(session, path, options) {
|
|
32
|
-
const found = target(session);
|
|
33
|
-
if (!found)
|
|
34
|
-
return undefined;
|
|
35
|
-
const read = await sandbox.readBytes(found.ref, found.spec, path, options.maxBytes, options.signal);
|
|
36
|
-
if (read.kind === "read")
|
|
37
|
-
return read.bytes;
|
|
38
|
-
if (read.kind === "missing")
|
|
39
|
-
return undefined;
|
|
40
|
-
if (read.code === "artifact.too_large")
|
|
41
|
-
throw new SandboxFileTooLargeError(path, options.maxBytes + 1, options.maxBytes);
|
|
42
|
-
throw new Error(read.message);
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
package/dist/media.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image checks for code that handles image bytes itself (a service behind an HTTP tool):
|
|
3
|
-
* the supported types, the size cap and a signature check. Storing images is the job of file
|
|
4
|
-
* artifacts (`artifacts/`, protocol 6), which replaced `MediaStore`.
|
|
5
|
-
*/
|
|
6
|
-
export declare const IMAGE_MEDIA_TYPES: readonly string[];
|
|
7
|
-
export declare const MAX_IMAGE_BYTES: number;
|
|
8
|
-
export declare function decodeImageBase64(mediaType: string, base64: string): Uint8Array;
|
|
9
|
-
/** Verify the asserted media type and lightweight file signature for a supported image. */
|
|
10
|
-
export declare function validateImageBytes(mediaType: string, bytes: Uint8Array): void;
|
package/dist/media.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image checks for code that handles image bytes itself (a service behind an HTTP tool):
|
|
3
|
-
* the supported types, the size cap and a signature check. Storing images is the job of file
|
|
4
|
-
* artifacts (`artifacts/`, protocol 6), which replaced `MediaStore`.
|
|
5
|
-
*/
|
|
6
|
-
export const IMAGE_MEDIA_TYPES = Object.freeze([
|
|
7
|
-
"image/jpeg",
|
|
8
|
-
"image/png",
|
|
9
|
-
"image/webp",
|
|
10
|
-
]);
|
|
11
|
-
export const MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
12
|
-
export function decodeImageBase64(mediaType, base64) {
|
|
13
|
-
if (!IMAGE_MEDIA_TYPES.includes(mediaType))
|
|
14
|
-
throw new Error("Only JPEG, PNG, and WebP images are supported.");
|
|
15
|
-
if (typeof base64 !== "string" || base64 === "")
|
|
16
|
-
throw new Error("Image data is required.");
|
|
17
|
-
if (base64.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(base64))
|
|
18
|
-
throw new Error("Image data must be canonical base64.");
|
|
19
|
-
const decoded = atob(base64);
|
|
20
|
-
const bytes = Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
21
|
-
if (btoa(decoded) !== base64)
|
|
22
|
-
throw new Error("Image data must be canonical base64.");
|
|
23
|
-
validateImageBytes(mediaType, bytes);
|
|
24
|
-
return bytes;
|
|
25
|
-
}
|
|
26
|
-
/** Verify the asserted media type and lightweight file signature for a supported image. */
|
|
27
|
-
export function validateImageBytes(mediaType, bytes) {
|
|
28
|
-
if (!IMAGE_MEDIA_TYPES.includes(mediaType))
|
|
29
|
-
throw new Error("Only JPEG, PNG, and WebP images are supported.");
|
|
30
|
-
if (bytes.byteLength === 0 || bytes.byteLength > MAX_IMAGE_BYTES)
|
|
31
|
-
throw new Error("Images must be no larger than 8 MiB.");
|
|
32
|
-
const signature = mediaType === "image/jpeg"
|
|
33
|
-
? bytes.byteLength >= 3 &&
|
|
34
|
-
bytes[0] === 0xff &&
|
|
35
|
-
bytes[1] === 0xd8 &&
|
|
36
|
-
bytes[2] === 0xff
|
|
37
|
-
: mediaType === "image/png"
|
|
38
|
-
? bytes.byteLength >= 8 &&
|
|
39
|
-
bytes[0] === 0x89 &&
|
|
40
|
-
bytes[1] === 0x50 &&
|
|
41
|
-
bytes[2] === 0x4e &&
|
|
42
|
-
bytes[3] === 0x47 &&
|
|
43
|
-
bytes[4] === 0x0d &&
|
|
44
|
-
bytes[5] === 0x0a &&
|
|
45
|
-
bytes[6] === 0x1a &&
|
|
46
|
-
bytes[7] === 0x0a
|
|
47
|
-
: bytes.byteLength >= 12 &&
|
|
48
|
-
bytes[0] === 0x52 &&
|
|
49
|
-
bytes[1] === 0x49 &&
|
|
50
|
-
bytes[2] === 0x46 &&
|
|
51
|
-
bytes[3] === 0x46 &&
|
|
52
|
-
bytes[8] === 0x57 &&
|
|
53
|
-
bytes[9] === 0x45 &&
|
|
54
|
-
bytes[10] === 0x42 &&
|
|
55
|
-
bytes[11] === 0x50;
|
|
56
|
-
if (!signature)
|
|
57
|
-
throw new Error(`Image bytes do not match ${mediaType}.`);
|
|
58
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type AgentManifest, type SandboxManifest } from "@nylorun/core/define";
|
|
2
|
-
/**
|
|
3
|
-
* The session's sandbox spec when `toolName` is one of the sandbox tools the Runtime pinned on
|
|
4
|
-
* `manifest` (its `runtime` block); undefined for any other tool. A tool's name alone says whose
|
|
5
|
-
* it is: an author tool may not take a Runtime tool's name.
|
|
6
|
-
*/
|
|
7
|
-
export declare function sandboxToolSpec(manifest: AgentManifest | undefined, toolName: string | undefined): SandboxManifest | undefined;
|
|
8
|
-
/** True when `toolName` is a tool the Runtime pinned on `manifest` (its `runtime.tools`). */
|
|
9
|
-
export declare function isPinnedRuntimeTool(manifest: AgentManifest | undefined, toolName: string | undefined): boolean;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { isSandboxToolName, runtimeToolOf, } from "@nylorun/core/define";
|
|
2
|
-
/**
|
|
3
|
-
* The session's sandbox spec when `toolName` is one of the sandbox tools the Runtime pinned on
|
|
4
|
-
* `manifest` (its `runtime` block); undefined for any other tool. A tool's name alone says whose
|
|
5
|
-
* it is: an author tool may not take a Runtime tool's name.
|
|
6
|
-
*/
|
|
7
|
-
export function sandboxToolSpec(manifest, toolName) {
|
|
8
|
-
if (!toolName || !isSandboxToolName(toolName))
|
|
9
|
-
return undefined;
|
|
10
|
-
const sandbox = manifest?.runtime?.sandbox;
|
|
11
|
-
return sandbox !== undefined && runtimeToolOf(manifest, toolName) !== undefined ? sandbox : undefined;
|
|
12
|
-
}
|
|
13
|
-
/** True when `toolName` is a tool the Runtime pinned on `manifest` (its `runtime.tools`). */
|
|
14
|
-
export function isPinnedRuntimeTool(manifest, toolName) {
|
|
15
|
-
return runtimeToolOf(manifest, toolName) !== undefined;
|
|
16
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type AgentManifest, type SandboxManifest } from "@nylorun/core/define";
|
|
2
|
-
export type SandboxedManifest = {
|
|
3
|
-
readonly ok: true;
|
|
4
|
-
readonly manifest: AgentManifest;
|
|
5
|
-
readonly manifestHash: string;
|
|
6
|
-
} | {
|
|
7
|
-
readonly ok: false;
|
|
8
|
-
readonly message: string;
|
|
9
|
-
};
|
|
10
|
-
/** The agent's pinned manifest with the session's sandbox, or why it cannot take one. */
|
|
11
|
-
export declare function withSandbox(manifest: AgentManifest, spec: SandboxManifest): SandboxedManifest;
|
|
12
|
-
/**
|
|
13
|
-
* The pinned manifest of an agent session without a sandbox: `read_artifact` for each agent of
|
|
14
|
-
* the tree (R2b C11, protocol 12), and the skill tools for each with skills.
|
|
15
|
-
*/
|
|
16
|
-
export declare function withRuntimeTools(manifest: AgentManifest): SandboxedManifest;
|