@aexhq/sdk 0.46.4-canary → 0.50.0
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/LICENSE +201 -0
- package/NOTICE +38 -0
- package/README.md +23 -31
- package/dist/client/aex.d.ts +33 -0
- package/dist/client/aex.js +98 -0
- package/dist/client/aex.js.map +1 -0
- package/dist/client/credentials.d.ts +25 -0
- package/dist/client/credentials.js +97 -0
- package/dist/client/credentials.js.map +1 -0
- package/dist/client/routing.d.ts +7 -0
- package/dist/client/routing.js +29 -0
- package/dist/client/routing.js.map +1 -0
- package/dist/downloads/download.d.ts +25 -0
- package/dist/downloads/download.js +53 -0
- package/dist/downloads/download.js.map +1 -0
- package/dist/generated/errors.d.ts +12 -0
- package/dist/generated/errors.js +81 -0
- package/dist/generated/errors.js.map +1 -0
- package/dist/generated/resources.d.ts +730 -0
- package/dist/generated/resources.js +606 -0
- package/dist/generated/resources.js.map +1 -0
- package/dist/generated/routes.d.ts +42 -0
- package/dist/generated/routes.js +2101 -0
- package/dist/generated/routes.js.map +1 -0
- package/dist/index.d.ts +20 -50
- package/dist/index.js +11 -62
- package/dist/index.js.map +1 -1
- package/dist/observations/stream.d.ts +1 -0
- package/dist/observations/stream.js +18 -0
- package/dist/observations/stream.js.map +1 -0
- package/dist/transport/errors.d.ts +60 -0
- package/dist/transport/errors.js +107 -0
- package/dist/transport/errors.js.map +1 -0
- package/dist/transport/pagination.d.ts +8 -0
- package/dist/transport/pagination.js +34 -0
- package/dist/transport/pagination.js.map +1 -0
- package/dist/transport/retry.d.ts +21 -0
- package/dist/transport/retry.js +37 -0
- package/dist/transport/retry.js.map +1 -0
- package/dist/transport/transport.d.ts +25 -0
- package/dist/transport/transport.js +28 -0
- package/dist/transport/transport.js.map +1 -0
- package/package.json +63 -30
- package/dist/_contracts/account-operations.d.ts +0 -101
- package/dist/_contracts/account-operations.js +0 -242
- package/dist/_contracts/account-types.d.ts +0 -461
- package/dist/_contracts/account-types.js +0 -1
- package/dist/_contracts/api-key.d.ts +0 -61
- package/dist/_contracts/api-key.js +0 -101
- package/dist/_contracts/api-routes.d.ts +0 -20
- package/dist/_contracts/api-routes.js +0 -109
- package/dist/_contracts/archive-limits.d.ts +0 -3
- package/dist/_contracts/archive-limits.js +0 -23
- package/dist/_contracts/asset-authoring.d.ts +0 -22
- package/dist/_contracts/asset-authoring.js +0 -106
- package/dist/_contracts/asset-bundle.d.ts +0 -64
- package/dist/_contracts/asset-bundle.js +0 -263
- package/dist/_contracts/asset-upload-helper.d.ts +0 -31
- package/dist/_contracts/asset-upload-helper.js +0 -84
- package/dist/_contracts/billing-admission.d.ts +0 -29
- package/dist/_contracts/billing-admission.js +0 -28
- package/dist/_contracts/bundle-manifest.d.ts +0 -89
- package/dist/_contracts/bundle-manifest.js +0 -158
- package/dist/_contracts/canonical-sha256.d.ts +0 -8
- package/dist/_contracts/canonical-sha256.js +0 -8
- package/dist/_contracts/connection-ticket.d.ts +0 -22
- package/dist/_contracts/connection-ticket.js +0 -54
- package/dist/_contracts/continuation-event.d.ts +0 -31
- package/dist/_contracts/continuation-event.js +0 -6
- package/dist/_contracts/contract-parse-error.d.ts +0 -12
- package/dist/_contracts/contract-parse-error.js +0 -51
- package/dist/_contracts/error-codes.d.ts +0 -26
- package/dist/_contracts/error-codes.js +0 -116
- package/dist/_contracts/error-factory.d.ts +0 -32
- package/dist/_contracts/error-factory.js +0 -174
- package/dist/_contracts/event-envelope.d.ts +0 -471
- package/dist/_contracts/event-envelope.js +0 -501
- package/dist/_contracts/event-stream-client.d.ts +0 -122
- package/dist/_contracts/event-stream-client.js +0 -445
- package/dist/_contracts/event-view.d.ts +0 -44
- package/dist/_contracts/event-view.js +0 -69
- package/dist/_contracts/failure-class.d.ts +0 -29
- package/dist/_contracts/failure-class.js +0 -73
- package/dist/_contracts/http.d.ts +0 -135
- package/dist/_contracts/http.js +0 -434
- package/dist/_contracts/ids.d.ts +0 -66
- package/dist/_contracts/ids.js +0 -119
- package/dist/_contracts/index.d.ts +0 -42
- package/dist/_contracts/index.js +0 -52
- package/dist/_contracts/internal.d.ts +0 -55
- package/dist/_contracts/internal.js +0 -113
- package/dist/_contracts/models.d.ts +0 -30
- package/dist/_contracts/models.js +0 -28
- package/dist/_contracts/operation-core.d.ts +0 -36
- package/dist/_contracts/operation-core.js +0 -70
- package/dist/_contracts/operations.d.ts +0 -218
- package/dist/_contracts/operations.js +0 -1496
- package/dist/_contracts/otlp-projection.d.ts +0 -78
- package/dist/_contracts/otlp-projection.js +0 -171
- package/dist/_contracts/post-hook.d.ts +0 -31
- package/dist/_contracts/post-hook.js +0 -61
- package/dist/_contracts/provider-fault.d.ts +0 -34
- package/dist/_contracts/provider-fault.js +0 -68
- package/dist/_contracts/retry-core.d.ts +0 -29
- package/dist/_contracts/retry-core.js +0 -79
- package/dist/_contracts/runner-event.d.ts +0 -117
- package/dist/_contracts/runner-event.js +0 -172
- package/dist/_contracts/runtime-kind.d.ts +0 -60
- package/dist/_contracts/runtime-kind.js +0 -70
- package/dist/_contracts/runtime-manifest.d.ts +0 -121
- package/dist/_contracts/runtime-manifest.js +0 -83
- package/dist/_contracts/runtime-security-profile.d.ts +0 -26
- package/dist/_contracts/runtime-security-profile.js +0 -73
- package/dist/_contracts/runtime-sizes.d.ts +0 -104
- package/dist/_contracts/runtime-sizes.js +0 -111
- package/dist/_contracts/runtime-types.d.ts +0 -618
- package/dist/_contracts/runtime-types.js +0 -58
- package/dist/_contracts/schemas/asset-bundle.d.ts +0 -70
- package/dist/_contracts/schemas/asset-bundle.js +0 -107
- package/dist/_contracts/schemas/asset-ref.d.ts +0 -61
- package/dist/_contracts/schemas/asset-ref.js +0 -118
- package/dist/_contracts/schemas/bundle-manifest.d.ts +0 -66
- package/dist/_contracts/schemas/bundle-manifest.js +0 -77
- package/dist/_contracts/schemas/index.d.ts +0 -32
- package/dist/_contracts/schemas/index.js +0 -30
- package/dist/_contracts/schemas/mcp-server.d.ts +0 -99
- package/dist/_contracts/schemas/mcp-server.js +0 -209
- package/dist/_contracts/schemas/models.d.ts +0 -29
- package/dist/_contracts/schemas/models.js +0 -51
- package/dist/_contracts/schemas/numeric.d.ts +0 -18
- package/dist/_contracts/schemas/numeric.js +0 -28
- package/dist/_contracts/schemas/post-hook.d.ts +0 -45
- package/dist/_contracts/schemas/post-hook.js +0 -68
- package/dist/_contracts/schemas/response-assets.d.ts +0 -75
- package/dist/_contracts/schemas/response-assets.js +0 -81
- package/dist/_contracts/schemas/response-billing.d.ts +0 -208
- package/dist/_contracts/schemas/response-billing.js +0 -139
- package/dist/_contracts/schemas/response-common.d.ts +0 -132
- package/dist/_contracts/schemas/response-common.js +0 -162
- package/dist/_contracts/schemas/response-identity.d.ts +0 -648
- package/dist/_contracts/schemas/response-identity.js +0 -131
- package/dist/_contracts/schemas/response-mcp-servers.d.ts +0 -51
- package/dist/_contracts/schemas/response-mcp-servers.js +0 -32
- package/dist/_contracts/schemas/response-secrets.d.ts +0 -50
- package/dist/_contracts/schemas/response-secrets.js +0 -32
- package/dist/_contracts/schemas/response-sessions-internal.d.ts +0 -200
- package/dist/_contracts/schemas/response-sessions-internal.js +0 -142
- package/dist/_contracts/schemas/response-sessions.d.ts +0 -1598
- package/dist/_contracts/schemas/response-sessions.js +0 -377
- package/dist/_contracts/schemas/response-webhooks.d.ts +0 -76
- package/dist/_contracts/schemas/response-webhooks.js +0 -42
- package/dist/_contracts/schemas/response-workspace.d.ts +0 -225
- package/dist/_contracts/schemas/response-workspace.js +0 -99
- package/dist/_contracts/schemas/runtime-kind.d.ts +0 -31
- package/dist/_contracts/schemas/runtime-kind.js +0 -29
- package/dist/_contracts/schemas/runtime-security-profile.d.ts +0 -28
- package/dist/_contracts/schemas/runtime-security-profile.js +0 -26
- package/dist/_contracts/schemas/runtime-sizes.d.ts +0 -70
- package/dist/_contracts/schemas/runtime-sizes.js +0 -127
- package/dist/_contracts/schemas/session-limits.d.ts +0 -34
- package/dist/_contracts/schemas/session-limits.js +0 -39
- package/dist/_contracts/schemas/session-machine.d.ts +0 -23
- package/dist/_contracts/schemas/session-machine.js +0 -24
- package/dist/_contracts/schemas/session-request-config.d.ts +0 -58
- package/dist/_contracts/schemas/session-request-config.js +0 -134
- package/dist/_contracts/schemas/session-webhook.d.ts +0 -11
- package/dist/_contracts/schemas/session-webhook.js +0 -38
- package/dist/_contracts/schemas/side-effect-audit.d.ts +0 -98
- package/dist/_contracts/schemas/side-effect-audit.js +0 -102
- package/dist/_contracts/schemas/submission-assets.d.ts +0 -117
- package/dist/_contracts/schemas/submission-assets.js +0 -147
- package/dist/_contracts/schemas/submission-body.d.ts +0 -251
- package/dist/_contracts/schemas/submission-body.js +0 -378
- package/dist/_contracts/schemas/submission-environment.d.ts +0 -79
- package/dist/_contracts/schemas/submission-environment.js +0 -179
- package/dist/_contracts/schemas/submission-request.d.ts +0 -158
- package/dist/_contracts/schemas/submission-request.js +0 -49
- package/dist/_contracts/schemas/submission-secrets.d.ts +0 -47
- package/dist/_contracts/schemas/submission-secrets.js +0 -108
- package/dist/_contracts/schemas/wire.d.ts +0 -118
- package/dist/_contracts/schemas/wire.js +0 -171
- package/dist/_contracts/schemas/workspace-resources.d.ts +0 -50
- package/dist/_contracts/schemas/workspace-resources.js +0 -87
- package/dist/_contracts/sdk-errors.d.ts +0 -212
- package/dist/_contracts/sdk-errors.js +0 -313
- package/dist/_contracts/sdk-secrets.d.ts +0 -67
- package/dist/_contracts/sdk-secrets.js +0 -427
- package/dist/_contracts/session-archive.d.ts +0 -16
- package/dist/_contracts/session-archive.js +0 -92
- package/dist/_contracts/session-artifacts.d.ts +0 -189
- package/dist/_contracts/session-artifacts.js +0 -264
- package/dist/_contracts/session-config.d.ts +0 -373
- package/dist/_contracts/session-config.js +0 -562
- package/dist/_contracts/session-cost-types.d.ts +0 -211
- package/dist/_contracts/session-cost-types.js +0 -69
- package/dist/_contracts/session-cost.d.ts +0 -8
- package/dist/_contracts/session-cost.js +0 -582
- package/dist/_contracts/session-custody.d.ts +0 -165
- package/dist/_contracts/session-custody.js +0 -345
- package/dist/_contracts/session-file-query.d.ts +0 -14
- package/dist/_contracts/session-file-query.js +0 -178
- package/dist/_contracts/session-record.d.ts +0 -112
- package/dist/_contracts/session-record.js +0 -165
- package/dist/_contracts/session-retention.d.ts +0 -201
- package/dist/_contracts/session-retention.js +0 -450
- package/dist/_contracts/side-effect-audit.d.ts +0 -126
- package/dist/_contracts/side-effect-audit.js +0 -520
- package/dist/_contracts/sse.d.ts +0 -74
- package/dist/_contracts/sse.js +0 -227
- package/dist/_contracts/stable.d.ts +0 -45
- package/dist/_contracts/stable.js +0 -62
- package/dist/_contracts/status.d.ts +0 -25
- package/dist/_contracts/status.js +0 -57
- package/dist/_contracts/submission-limits.d.ts +0 -61
- package/dist/_contracts/submission-limits.js +0 -60
- package/dist/_contracts/submission.d.ts +0 -547
- package/dist/_contracts/submission.js +0 -812
- package/dist/_contracts/suggest.d.ts +0 -15
- package/dist/_contracts/suggest.js +0 -53
- package/dist/_contracts/testing/response-bindings.d.ts +0 -45
- package/dist/_contracts/testing/response-bindings.js +0 -256
- package/dist/_contracts/testing/wire-conformance-entry.d.ts +0 -10
- package/dist/_contracts/testing/wire-conformance-entry.js +0 -8
- package/dist/_contracts/testing/wire-conformance.d.ts +0 -169
- package/dist/_contracts/testing/wire-conformance.js +0 -276
- package/dist/_contracts/turn-trace.d.ts +0 -28
- package/dist/_contracts/turn-trace.js +0 -1
- package/dist/_contracts/unknown-field-error.d.ts +0 -13
- package/dist/_contracts/unknown-field-error.js +0 -21
- package/dist/_contracts/value-guards.d.ts +0 -20
- package/dist/_contracts/value-guards.js +0 -34
- package/dist/_contracts/webhook-verify.d.ts +0 -34
- package/dist/_contracts/webhook-verify.js +0 -93
- package/dist/_contracts/wire-observer.d.ts +0 -49
- package/dist/_contracts/wire-observer.js +0 -34
- package/dist/_contracts/workflow-status.d.ts +0 -7
- package/dist/_contracts/workflow-status.js +0 -43
- package/dist/_contracts/workspace-resources.d.ts +0 -98
- package/dist/_contracts/workspace-resources.js +0 -39
- package/dist/archive-limits.d.ts +0 -1
- package/dist/archive-limits.js +0 -2
- package/dist/archive-limits.js.map +0 -1
- package/dist/asset-upload.d.ts +0 -47
- package/dist/asset-upload.js +0 -269
- package/dist/asset-upload.js.map +0 -1
- package/dist/bundle.d.ts +0 -9
- package/dist/bundle.js +0 -20
- package/dist/bundle.js.map +0 -1
- package/dist/canonical-zip.d.ts +0 -68
- package/dist/canonical-zip.js +0 -355
- package/dist/canonical-zip.js.map +0 -1
- package/dist/cli.mjs +0 -12048
- package/dist/cli.mjs.sha256 +0 -1
- package/dist/client-types.d.ts +0 -192
- package/dist/client-types.js +0 -2
- package/dist/client-types.js.map +0 -1
- package/dist/client.d.ts +0 -464
- package/dist/client.js +0 -1207
- package/dist/client.js.map +0 -1
- package/dist/event-projection.d.ts +0 -22
- package/dist/event-projection.js +0 -380
- package/dist/event-projection.js.map +0 -1
- package/dist/fetch-archive.d.ts +0 -16
- package/dist/fetch-archive.js +0 -252
- package/dist/fetch-archive.js.map +0 -1
- package/dist/file.d.ts +0 -96
- package/dist/file.js +0 -272
- package/dist/file.js.map +0 -1
- package/dist/instructions.d.ts +0 -20
- package/dist/instructions.js +0 -40
- package/dist/instructions.js.map +0 -1
- package/dist/legacy-session-provider-fault.d.ts +0 -7
- package/dist/legacy-session-provider-fault.js +0 -38
- package/dist/legacy-session-provider-fault.js.map +0 -1
- package/dist/mcp-server.d.ts +0 -84
- package/dist/mcp-server.js +0 -117
- package/dist/mcp-server.js.map +0 -1
- package/dist/node-fs.d.ts +0 -29
- package/dist/node-fs.js +0 -19
- package/dist/node-fs.js.map +0 -1
- package/dist/node-walk.d.ts +0 -69
- package/dist/node-walk.js +0 -151
- package/dist/node-walk.js.map +0 -1
- package/dist/path-basename.d.ts +0 -5
- package/dist/path-basename.js +0 -9
- package/dist/path-basename.js.map +0 -1
- package/dist/retry.d.ts +0 -70
- package/dist/retry.js +0 -155
- package/dist/retry.js.map +0 -1
- package/dist/secret.d.ts +0 -65
- package/dist/secret.js +0 -110
- package/dist/secret.js.map +0 -1
- package/dist/session-validate.d.ts +0 -100
- package/dist/session-validate.js +0 -303
- package/dist/session-validate.js.map +0 -1
- package/dist/skill.d.ts +0 -99
- package/dist/skill.js +0 -169
- package/dist/skill.js.map +0 -1
- package/dist/submission-wire.d.ts +0 -13
- package/dist/submission-wire.js +0 -69
- package/dist/submission-wire.js.map +0 -1
- package/dist/tool.d.ts +0 -41
- package/dist/tool.js +0 -76
- package/dist/tool.js.map +0 -1
- package/dist/version.d.ts +0 -9
- package/dist/version.js +0 -10
- package/dist/version.js.map +0 -1
- package/docs/authentication.md +0 -125
- package/docs/billing.md +0 -164
- package/docs/cleanup.md +0 -27
- package/docs/concepts/agent-tools.md +0 -47
- package/docs/concepts/composition.md +0 -60
- package/docs/concepts/providers-and-runtimes.md +0 -121
- package/docs/concepts/sessions.md +0 -51
- package/docs/concepts/subagents.md +0 -35
- package/docs/credentials.md +0 -116
- package/docs/defaults.md +0 -51
- package/docs/errors.md +0 -258
- package/docs/events.md +0 -143
- package/docs/files.md +0 -130
- package/docs/limits-and-quotas.md +0 -114
- package/docs/limits.md +0 -51
- package/docs/mcp.md +0 -47
- package/docs/networking.md +0 -114
- package/docs/provider-runtime-capabilities.md +0 -32
- package/docs/public-surface.json +0 -73
- package/docs/quickstart.md +0 -135
- package/docs/release.md +0 -44
- package/docs/retries.md +0 -108
- package/docs/secrets.md +0 -141
- package/docs/session-config.md +0 -51
- package/docs/session-record.md +0 -58
- package/docs/skills.md +0 -65
- package/docs/telemetry.md +0 -66
- package/docs/testing.md +0 -35
- package/docs/vision-skills.md +0 -94
- package/docs/webhooks.md +0 -143
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schemas for the submission BRIEF — `request.submission` — and for the inline
|
|
3
|
-
* policy objects that hang directly off it: platform injection, file capture,
|
|
4
|
-
* response format and the HITL approval gate.
|
|
5
|
-
*
|
|
6
|
-
* Declaration order is the permitted-key order reported on an unknown field, and
|
|
7
|
-
* `test/allowed-keys-parser-golden.test.ts` pins every sentence below
|
|
8
|
-
* byte-for-byte — including the three different wordings, which are historical
|
|
9
|
-
* and deliberately not normalised toward each other.
|
|
10
|
-
*
|
|
11
|
-
* **What stays in `submission.ts`** (D4/L1): every transform and every
|
|
12
|
-
* cross-field rule. Splitting `"pip:pandas"`, decoding a duration, collapsing an
|
|
13
|
-
* empty object to `undefined`, canonicalising a capture path, deduplicating a
|
|
14
|
-
* tool list, reconciling `secretEnv` against `secrets.envSecrets` — none of
|
|
15
|
-
* those are shapes, and a `.transform()` here would make the response half of
|
|
16
|
-
* the generated spec ungenerable. Rule sets whose ORDER decides which message
|
|
17
|
-
* fires (`prompt`, `secretEnv`, the capture-path ladders) are single
|
|
18
|
-
* `z.check()`s for the reason `submission-environment.ts` documents: composed
|
|
19
|
-
* schemas report in Zod's order, not the ladder's.
|
|
20
|
-
*/
|
|
21
|
-
import * as z from "zod/mini";
|
|
22
|
-
import { isJsonValue, isRecord } from "../value-guards.js";
|
|
23
|
-
import { SECRET_ENV_NAME_PATTERN, SECRET_HANDLE_PATTERN } from "../submission-limits.js";
|
|
24
|
-
import { modelSlugSchema } from "./models.js";
|
|
25
|
-
import { RuntimeSecurityProfileSchema } from "./runtime-security-profile.js";
|
|
26
|
-
import { SubmissionAssetsSchema } from "./submission-assets.js";
|
|
27
|
-
import { EnvironmentSchema } from "./submission-environment.js";
|
|
28
|
-
import { lastSegment, nonEmptyString, wireObject } from "./wire.js";
|
|
29
|
-
const SUBMISSION = "submission";
|
|
30
|
-
const PROMPT = `${SUBMISSION}.prompt`;
|
|
31
|
-
const MCP_SERVERS = `${SUBMISSION}.mcpServers`;
|
|
32
|
-
const SECRET_ENV = `${SUBMISSION}.secretEnv`;
|
|
33
|
-
const METADATA = `${SUBMISSION}.metadata`;
|
|
34
|
-
const BUILTIN_TOOLS = `${SUBMISSION}.builtinTools`;
|
|
35
|
-
const PLATFORM = `${SUBMISSION}.platform`;
|
|
36
|
-
const FILE_CAPTURE = `${SUBMISSION}.fileCapture`;
|
|
37
|
-
const RESPONSE_FORMAT = `${SUBMISSION}.responseFormat`;
|
|
38
|
-
const APPROVAL_GATE = `${SUBMISSION}.approvalGate`;
|
|
39
|
-
/** Assistant-output granularity values. Buffered is the platform default. */
|
|
40
|
-
export const OUTPUT_MODES = ["buffered", "stream"];
|
|
41
|
-
/** Response-format kinds: free-form `text` (default) or provider-native `json_schema`. */
|
|
42
|
-
export const RESPONSE_FORMAT_KINDS = ["text", "json_schema"];
|
|
43
|
-
/**
|
|
44
|
-
* Maximum number of file capture entries accepted per list.
|
|
45
|
-
*
|
|
46
|
-
* 32 is enough room for the typical "one or two capture roots" pattern plus a
|
|
47
|
-
* generous margin for legitimate multi-root use cases (per-tool file directory +
|
|
48
|
-
* scratch state + logs, repeated across a few subdirectories), without inviting
|
|
49
|
-
* abuse of the synthetic-turn path the platform capture path drives at session
|
|
50
|
-
* terminal.
|
|
51
|
-
*/
|
|
52
|
-
export const MAX_FILE_CAPTURE_DIRS = 32;
|
|
53
|
-
/**
|
|
54
|
-
* Maximum byte length of a single file capture entry (after UTF-8 encoding).
|
|
55
|
-
* 512 bytes comfortably covers `/very/long/nested/path` style entries without
|
|
56
|
-
* letting a misuse smuggle large blobs through the field.
|
|
57
|
-
*/
|
|
58
|
-
export const MAX_FILE_CAPTURE_DIR_BYTES = 512;
|
|
59
|
-
function rejector(payload) {
|
|
60
|
-
return (message) => {
|
|
61
|
-
payload.issues.push({ code: "custom", message, input: payload.value });
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// prompt
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
/**
|
|
68
|
-
* The prompt as a single string or an ordered list of parts.
|
|
69
|
-
*
|
|
70
|
-
* The union admits ANY array rather than an array of strings, and the per-entry
|
|
71
|
-
* rules ride the ladder below, because a union reports its own failure — the
|
|
72
|
-
* caller would learn "must be a string or an array of strings" for
|
|
73
|
-
* `["ok", 7]` instead of which entry is wrong. The ladder is the parser's own
|
|
74
|
-
* order: length, then each entry, then the all-whitespace check that only makes
|
|
75
|
-
* sense once every entry is a string.
|
|
76
|
-
*/
|
|
77
|
-
export const PromptSchema = z
|
|
78
|
-
.union([z.string(), z.array(z.unknown())], {
|
|
79
|
-
error: `${PROMPT} must be a string or an array of strings`
|
|
80
|
-
})
|
|
81
|
-
.check(z.check((payload) => {
|
|
82
|
-
const reject = rejector(payload);
|
|
83
|
-
const value = payload.value;
|
|
84
|
-
if (typeof value === "string") {
|
|
85
|
-
if (value.length === 0) {
|
|
86
|
-
reject(`${PROMPT} must be non-empty`);
|
|
87
|
-
}
|
|
88
|
-
else if (value.trim().length === 0) {
|
|
89
|
-
reject(`${PROMPT} must contain non-whitespace text`);
|
|
90
|
-
}
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const parts = value;
|
|
94
|
-
if (parts.length === 0) {
|
|
95
|
-
reject(`${PROMPT} array must be non-empty`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
for (const [index, part] of parts.entries()) {
|
|
99
|
-
if (typeof part !== "string" || part.length === 0) {
|
|
100
|
-
reject(`${PROMPT}[${index}] must be a non-empty string`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (parts.every((part) => part.trim().length === 0)) {
|
|
105
|
-
reject(`${PROMPT} must contain non-whitespace text`);
|
|
106
|
-
}
|
|
107
|
-
}))
|
|
108
|
-
.register(z.globalRegistry, {
|
|
109
|
-
id: "SubmissionPrompt",
|
|
110
|
-
description: "The run brief: one string, or an ordered list of non-empty parts. At least one part must " +
|
|
111
|
-
"carry non-whitespace text. A single string is normalised to a one-element list."
|
|
112
|
-
});
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// mcpServers
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
/**
|
|
117
|
-
* The non-secret half of the MCP server declarations.
|
|
118
|
-
*
|
|
119
|
-
* Deliberately an array of unvalidated entries: the per-entry rules
|
|
120
|
-
* (`mcpServerRefSchema`) need the SSRF host deny-list and the transport parser,
|
|
121
|
-
* which `schemas/mcp-server.ts` takes as an injected `McpWirePolicy` because
|
|
122
|
-
* they must stay inside `session-config.ts` for the cross-repo parity check —
|
|
123
|
-
* and `session-config.ts` imports `submission.ts`, so this module cannot reach
|
|
124
|
-
* them without a cycle. `parseMcpServers` applies them one entry at a time.
|
|
125
|
-
*/
|
|
126
|
-
export const McpServerRefsSchema = z
|
|
127
|
-
.array(z.unknown(), { error: `${MCP_SERVERS} must be an array of {name, url} objects` })
|
|
128
|
-
.register(z.globalRegistry, {
|
|
129
|
-
id: "SubmissionMcpServers",
|
|
130
|
-
description: "Remote MCP servers, each `{ name, url, transport? }`. Names must be unique and every URL " +
|
|
131
|
-
"must clear the SSRF host deny-list — enforced in packages/contracts/src/session-config.ts."
|
|
132
|
-
});
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
// secretEnv
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
/**
|
|
137
|
-
* Value-free env-var secret bindings, keyed by env name.
|
|
138
|
-
*
|
|
139
|
-
* One ordered `z.check()` rather than a record of a union, for the reason
|
|
140
|
-
* `EnvSecretsSchema` documents: each rung has its own message and the key rule
|
|
141
|
-
* must beat the value rule, which a composed schema cannot promise.
|
|
142
|
-
*/
|
|
143
|
-
export const SecretEnvSchema = z
|
|
144
|
-
.record(z.string(), z.unknown(), { error: `${SECRET_ENV} must be an object` })
|
|
145
|
-
.check(z.check((payload) => {
|
|
146
|
-
const reject = rejector(payload);
|
|
147
|
-
for (const [envName, entry] of Object.entries(payload.value)) {
|
|
148
|
-
if (!SECRET_ENV_NAME_PATTERN.test(envName)) {
|
|
149
|
-
reject(`${SECRET_ENV} key "${envName}" must be a valid env var name matching ${SECRET_ENV_NAME_PATTERN.source}`);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const path = `${SECRET_ENV}.${envName}`;
|
|
153
|
-
if (!isRecord(entry)) {
|
|
154
|
-
reject(`${path} must be an object`);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const keys = Object.keys(entry);
|
|
158
|
-
if (keys.length !== 1 || (!("ref" in entry) && !("ephemeral" in entry))) {
|
|
159
|
-
reject(`${path} must be exactly one of { ref } or { ephemeral: true }`);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if ("ref" in entry) {
|
|
163
|
-
const handle = entry.ref;
|
|
164
|
-
if (typeof handle !== "string" || handle.length === 0) {
|
|
165
|
-
reject(`${path}.ref must be a non-empty string`);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (!SECRET_HANDLE_PATTERN.test(handle)) {
|
|
169
|
-
reject(`${path}.ref handle must match ${SECRET_HANDLE_PATTERN.source}`);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else if (entry.ephemeral !== true) {
|
|
174
|
-
reject(`${path}.ephemeral must be the literal true`);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}))
|
|
179
|
-
.register(z.globalRegistry, {
|
|
180
|
-
id: "SubmissionSecretEnv",
|
|
181
|
-
description: "Env-var secret bindings keyed by env name. Each value is exactly one of `{ ref }` (a " +
|
|
182
|
-
`workspace secret handle matching ${SECRET_HANDLE_PATTERN.source}) or ` +
|
|
183
|
-
"`{ ephemeral: true }` (the value rides in `secrets.envSecrets`). Enforced in " +
|
|
184
|
-
"packages/contracts/src/schemas/submission-body.ts."
|
|
185
|
-
});
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
// metadata
|
|
188
|
-
// ---------------------------------------------------------------------------
|
|
189
|
-
/** Caller-owned metadata: any JSON-serializable value per key. */
|
|
190
|
-
export const MetadataSchema = z.record(z.string(), z.unknown().check(z.refine(isJsonValue, {
|
|
191
|
-
error: (issue) => `${METADATA}.${lastSegment(issue.path)} must be JSON-serializable`,
|
|
192
|
-
abort: true
|
|
193
|
-
})), { error: `${METADATA} must be an object` });
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
// builtinTools
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
/**
|
|
198
|
-
* The builtin-capability selection.
|
|
199
|
-
*
|
|
200
|
-
* The array branch admits any entry: the closed name set lives on
|
|
201
|
-
* `submission.ts` (`BUILTIN_TOOL_NAMES`, pinned equal to the platform's
|
|
202
|
-
* `HANDS_TOOLS`), which imports this module, and `resolveBuiltinToolNames` both
|
|
203
|
-
* validates membership and re-orders the result — a transform either way.
|
|
204
|
-
*/
|
|
205
|
-
export const BuiltinToolsSchema = z.union([z.literal("default"), z.literal("none"), z.array(z.unknown())], { error: `${BUILTIN_TOOLS} must be 'default', 'none', or an array of builtin tool names` });
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
// outputMode
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
/** Assistant-output granularity. */
|
|
210
|
-
export const OutputModeSchema = z.enum(OUTPUT_MODES, {
|
|
211
|
-
error: `${SUBMISSION}.outputMode must be one of ${OUTPUT_MODES.join(", ")}`
|
|
212
|
-
});
|
|
213
|
-
// ---------------------------------------------------------------------------
|
|
214
|
-
// fileCapture
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
function utf8Bytes(value) {
|
|
217
|
-
return new TextEncoder().encode(value).length;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* The shared entry rules for a capture list, in the order the parser applied
|
|
221
|
-
* them: the list bound first, then each entry top to bottom. One ladder rather
|
|
222
|
-
* than a composed array schema because the bound must outrank an entry
|
|
223
|
-
* complaint, and every rung words itself differently.
|
|
224
|
-
*/
|
|
225
|
-
function captureDirs(field, emptyEntry, notArray) {
|
|
226
|
-
const path = `${FILE_CAPTURE}.${field}`;
|
|
227
|
-
return z.array(z.unknown(), { error: notArray }).check(z.check((payload) => {
|
|
228
|
-
const reject = rejector(payload);
|
|
229
|
-
const entries = payload.value;
|
|
230
|
-
if (entries.length > MAX_FILE_CAPTURE_DIRS) {
|
|
231
|
-
reject(`${path} has ${entries.length} entries; max is ${MAX_FILE_CAPTURE_DIRS}`);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
for (const [index, entry] of entries.entries()) {
|
|
235
|
-
const at = `${path}[${index}]`;
|
|
236
|
-
if (typeof entry !== "string") {
|
|
237
|
-
reject(`${at} must be a string`);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (entry.length === 0) {
|
|
241
|
-
reject(`${at} ${emptyEntry}`);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const bytes = utf8Bytes(entry);
|
|
245
|
-
if (bytes > MAX_FILE_CAPTURE_DIR_BYTES) {
|
|
246
|
-
reject(`${at} exceeds ${MAX_FILE_CAPTURE_DIR_BYTES} bytes (got ${bytes})`);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
if (field === "allowedDirs" && !entry.startsWith("/")) {
|
|
250
|
-
reject(`${at} must be an absolute UNIX path (start with '/')`);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (entry.includes("\0")) {
|
|
254
|
-
reject(`${at} must not contain NUL bytes`);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
if (entry.includes("\n") || entry.includes("\r")) {
|
|
258
|
-
reject(`${at} must not contain newline characters`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (entry.split("/").includes("..")) {
|
|
262
|
-
reject(`${at} must not contain '..' segments`);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}));
|
|
267
|
-
}
|
|
268
|
-
/** A positive integer capture bound. Clamping to the platform maximum is the parser's. */
|
|
269
|
-
function captureInteger(field) {
|
|
270
|
-
const message = `${FILE_CAPTURE}.${field} must be a positive integer`;
|
|
271
|
-
return z
|
|
272
|
-
.number({ error: message })
|
|
273
|
-
.check(z.refine((value) => Number.isInteger(value) && value > 0, {
|
|
274
|
-
error: message,
|
|
275
|
-
abort: true
|
|
276
|
-
}));
|
|
277
|
-
}
|
|
278
|
-
/** `submission.fileCapture` — the post-run capture policy. */
|
|
279
|
-
export const FileCaptureSchema = wireObject(FILE_CAPTURE, {
|
|
280
|
-
allowedDirs: z.optional(captureDirs("allowedDirs", "must be a non-empty absolute UNIX path", `${FILE_CAPTURE}.allowedDirs must be an array of absolute UNIX paths`)),
|
|
281
|
-
deniedDirs: z.optional(captureDirs("deniedDirs", "must be a non-empty pattern", `${FILE_CAPTURE}.deniedDirs must be an array of strings`)),
|
|
282
|
-
captureTimeoutMs: z.optional(captureInteger("captureTimeoutMs")),
|
|
283
|
-
maxFileBytes: z.optional(captureInteger("maxFileBytes")),
|
|
284
|
-
maxTotalBytes: z.optional(captureInteger("maxTotalBytes")),
|
|
285
|
-
maxFiles: z.optional(captureInteger("maxFiles"))
|
|
286
|
-
}).register(z.globalRegistry, {
|
|
287
|
-
id: "SubmissionFileCapture",
|
|
288
|
-
description: "Post-run file capture policy. Entries are bounded to " +
|
|
289
|
-
`${MAX_FILE_CAPTURE_DIRS} per list and ${MAX_FILE_CAPTURE_DIR_BYTES} bytes each; allowed ` +
|
|
290
|
-
"roots are absolute UNIX paths and neither list may contain '..' segments. Paths are " +
|
|
291
|
-
"canonicalised and deduplicated after parsing, and `captureTimeoutMs` is clamped to the " +
|
|
292
|
-
"platform maximum. Enforced in packages/contracts/src/schemas/submission-body.ts."
|
|
293
|
-
});
|
|
294
|
-
// ---------------------------------------------------------------------------
|
|
295
|
-
// responseFormat
|
|
296
|
-
// ---------------------------------------------------------------------------
|
|
297
|
-
/**
|
|
298
|
-
* `submission.responseFormat` when `kind` is `"text"` — a free-form response
|
|
299
|
-
* admits no other field, and says so in its own words rather than listing a
|
|
300
|
-
* permitted set of one.
|
|
301
|
-
*/
|
|
302
|
-
export const ResponseFormatTextSchema = wireObject(RESPONSE_FORMAT, { kind: z.literal("text") }, { unknownKey: (path, key) => `${path}.${key} is not allowed when kind is 'text'` });
|
|
303
|
-
const RESPONSE_FORMAT_SCHEMA_FIELD = `${RESPONSE_FORMAT}.schema must be a JSON-serializable object`;
|
|
304
|
-
/** `submission.responseFormat` when `kind` is `"json_schema"`. */
|
|
305
|
-
export const ResponseFormatJsonSchemaSchema = wireObject(RESPONSE_FORMAT, {
|
|
306
|
-
kind: z.literal("json_schema"),
|
|
307
|
-
schema: z
|
|
308
|
-
.record(z.string(), z.unknown(), { error: RESPONSE_FORMAT_SCHEMA_FIELD })
|
|
309
|
-
.check(z.refine(isJsonValue, { error: RESPONSE_FORMAT_SCHEMA_FIELD, abort: true })),
|
|
310
|
-
strict: z.optional(z.boolean({ error: `${RESPONSE_FORMAT}.strict must be a boolean` })),
|
|
311
|
-
name: z.optional(nonEmptyString(`${RESPONSE_FORMAT}.name`))
|
|
312
|
-
});
|
|
313
|
-
/**
|
|
314
|
-
* The structured-output policy, selected by `kind`.
|
|
315
|
-
*
|
|
316
|
-
* A DISCRIMINATED union rather than a plain one: `kind` picks the variant before
|
|
317
|
-
* either member runs, so a bad `kind` outranks an unknown key and each variant
|
|
318
|
-
* words its own rejection — which is exactly the two-stage sequencing the
|
|
319
|
-
* hand-written parser had. A plain `z.union` would report its own failure for
|
|
320
|
-
* both cases and lose the distinction.
|
|
321
|
-
*
|
|
322
|
-
* The union's own message therefore covers only what routing itself can fail on:
|
|
323
|
-
* a non-object, or an object whose `kind` names no variant.
|
|
324
|
-
*/
|
|
325
|
-
export const ResponseFormatSchema = z.discriminatedUnion("kind", [ResponseFormatTextSchema, ResponseFormatJsonSchemaSchema], {
|
|
326
|
-
error: (issue) => isRecord(issue.input)
|
|
327
|
-
? `${RESPONSE_FORMAT}.kind must be one of ${RESPONSE_FORMAT_KINDS.join(", ")}`
|
|
328
|
-
: `${RESPONSE_FORMAT} must be an object`
|
|
329
|
-
});
|
|
330
|
-
// ---------------------------------------------------------------------------
|
|
331
|
-
// approvalGate + platform
|
|
332
|
-
// ---------------------------------------------------------------------------
|
|
333
|
-
const approvalTool = (() => {
|
|
334
|
-
const message = (issue) => `${APPROVAL_GATE}.tools[${lastSegment(issue.path)}] must be a non-empty string`;
|
|
335
|
-
return z.string({ error: message }).check(z.minLength(1, { error: message, abort: true }));
|
|
336
|
-
})();
|
|
337
|
-
/** `submission.approvalGate` — the declarative HITL write-gate. */
|
|
338
|
-
export const ApprovalGateSchema = wireObject(APPROVAL_GATE, {
|
|
339
|
-
tools: z.array(approvalTool, { error: `${APPROVAL_GATE}.tools must be an array of tool names` })
|
|
340
|
-
});
|
|
341
|
-
/** `submission.platform` — platform-injection controls. */
|
|
342
|
-
export const PlatformInjectionSchema = wireObject(PLATFORM, {
|
|
343
|
-
systemPrompt: z.optional(z.enum(["default", "off"], {
|
|
344
|
-
error: `${PLATFORM}.systemPrompt must be "default" or "off"`
|
|
345
|
-
}))
|
|
346
|
-
});
|
|
347
|
-
// ---------------------------------------------------------------------------
|
|
348
|
-
// the brief
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
/**
|
|
351
|
-
* Wire shape of the submission brief.
|
|
352
|
-
*
|
|
353
|
-
* The two diagnostics report under DIFFERENT paths and always have: an unknown
|
|
354
|
-
* key is `submission.<key> …` (the brief's fields are the caller's
|
|
355
|
-
* `submission.*` fields), while a non-object brief is `submission.submission …`
|
|
356
|
-
* (the brief's own position inside the request envelope). Both are pinned.
|
|
357
|
-
*
|
|
358
|
-
* `null` is admitted wherever the parser treated it as "unset" — that
|
|
359
|
-
* back-compat is the parser's, and the schema states it rather than letting a
|
|
360
|
-
* null reach a normaliser that would have to re-check it.
|
|
361
|
-
*/
|
|
362
|
-
export const SubmissionSchema = wireObject(SUBMISSION, {
|
|
363
|
-
model: modelSlugSchema(`${SUBMISSION}.model`),
|
|
364
|
-
system: z.optional(nonEmptyString(`${SUBMISSION}.system`)),
|
|
365
|
-
prompt: PromptSchema,
|
|
366
|
-
assets: SubmissionAssetsSchema,
|
|
367
|
-
mcpServers: z.optional(McpServerRefsSchema),
|
|
368
|
-
secretEnv: z.optional(z.nullable(SecretEnvSchema)),
|
|
369
|
-
environment: z.optional(EnvironmentSchema),
|
|
370
|
-
securityProfile: z.optional(z.nullable(RuntimeSecurityProfileSchema)),
|
|
371
|
-
metadata: z.optional(MetadataSchema),
|
|
372
|
-
fileCapture: z.optional(z.nullable(FileCaptureSchema)),
|
|
373
|
-
builtinTools: z.optional(z.nullable(BuiltinToolsSchema)),
|
|
374
|
-
outputMode: z.optional(z.nullable(OutputModeSchema)),
|
|
375
|
-
responseFormat: z.optional(z.nullable(ResponseFormatSchema)),
|
|
376
|
-
approvalGate: z.optional(z.nullable(ApprovalGateSchema)),
|
|
377
|
-
platform: z.optional(z.nullable(PlatformInjectionSchema))
|
|
378
|
-
}, { notObject: () => `${SUBMISSION}.submission must be an object` });
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schemas for `submission.environment` — the customer-controlled runtime
|
|
3
|
-
* environment: egress policy, OS/language packages, and plain env vars.
|
|
4
|
-
*
|
|
5
|
-
* Two behaviours here are deliberately NOT in the schemas, per D4/L1:
|
|
6
|
-
*
|
|
7
|
-
* - **Ecosystem-prefix splitting** (`"pip:pandas"` -> `{name:"pandas",
|
|
8
|
-
* ecosystem:"pip"}`) is a transform, so it lives in
|
|
9
|
-
* {@link normalizePlatformPackage}. A `.transform()` would make the response
|
|
10
|
-
* half of the generated spec ungenerable.
|
|
11
|
-
* - **Host lower-casing** lives in {@link normalizeAllowedHosts} for the same
|
|
12
|
-
* reason.
|
|
13
|
-
*/
|
|
14
|
-
import * as z from "zod/mini";
|
|
15
|
-
/**
|
|
16
|
-
* `envVars` as a bounded map of shell-portable names to UTF-8 values.
|
|
17
|
-
*
|
|
18
|
-
* The whole rule set is one ordered `z.check()` rather than a composition of
|
|
19
|
-
* record key/value schemas, because the ladder is order-sensitive and every rung
|
|
20
|
-
* has its own message: a key that is both malformed AND non-string must report
|
|
21
|
-
* the malformed key, and the byte budget must fail on the entry that crosses
|
|
22
|
-
* `ENV_VARS_MAX_TOTAL_BYTES` — not on the map as a whole. Composed schemas
|
|
23
|
-
* report in Zod's order, not this one.
|
|
24
|
-
*
|
|
25
|
-
* The cost is that these rules are invisible in the generated OpenAPI document
|
|
26
|
-
* (L2), so the description below is what a spec consumer sees. A weaker-but-true
|
|
27
|
-
* schema beats a precise-but-wrong one.
|
|
28
|
-
*/
|
|
29
|
-
export declare const EnvVarsSchema: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>;
|
|
30
|
-
export declare const AllowedHostsSchema: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
31
|
-
/** Lower-case every host so the deny/allow comparison downstream is case-insensitive. */
|
|
32
|
-
export declare function normalizeAllowedHosts(hosts: readonly string[]): readonly string[];
|
|
33
|
-
export declare const NetworkingSchema: z.ZodMiniObject<{
|
|
34
|
-
mode: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
35
|
-
limited: "limited";
|
|
36
|
-
open: "open";
|
|
37
|
-
}>>;
|
|
38
|
-
allowedHosts: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
|
|
39
|
-
}, z.core.$strict>;
|
|
40
|
-
export declare const PlatformPackageSchema: z.ZodMiniObject<{
|
|
41
|
-
name: z.ZodMiniString<string>;
|
|
42
|
-
version: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
43
|
-
}, z.core.$strict>;
|
|
44
|
-
export declare const PackagesSchema: z.ZodMiniArray<z.ZodMiniObject<{
|
|
45
|
-
name: z.ZodMiniString<string>;
|
|
46
|
-
version: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
47
|
-
}, z.core.$strict>>;
|
|
48
|
-
/**
|
|
49
|
-
* Split the `"<ecosystem>:<name>"` prefix a caller encodes into `name`.
|
|
50
|
-
*
|
|
51
|
-
* An unprefixed name defaults to `apt`. A colon-delimited prefix that is not a
|
|
52
|
-
* known ecosystem is rejected rather than folded into the package name, so a
|
|
53
|
-
* typo'd manager fails closed instead of installing something unexpected.
|
|
54
|
-
*
|
|
55
|
-
* Throws rather than returning a result because it runs inside the parser's
|
|
56
|
-
* existing `withContractParseError` boundary, which brands whatever it throws.
|
|
57
|
-
*/
|
|
58
|
-
export declare function normalizePlatformPackage(pkg: {
|
|
59
|
-
readonly name: string;
|
|
60
|
-
readonly version?: string | undefined;
|
|
61
|
-
}, path: string): {
|
|
62
|
-
readonly name: string;
|
|
63
|
-
readonly version?: string;
|
|
64
|
-
readonly ecosystem: string;
|
|
65
|
-
};
|
|
66
|
-
export declare const EnvironmentSchema: z.ZodMiniObject<{
|
|
67
|
-
networking: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
68
|
-
mode: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
69
|
-
limited: "limited";
|
|
70
|
-
open: "open";
|
|
71
|
-
}>>;
|
|
72
|
-
allowedHosts: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
|
|
73
|
-
}, z.core.$strict>>;
|
|
74
|
-
packages: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
75
|
-
name: z.ZodMiniString<string>;
|
|
76
|
-
version: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
77
|
-
}, z.core.$strict>>>;
|
|
78
|
-
envVars: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>>;
|
|
79
|
-
}, z.core.$strict>;
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schemas for `submission.environment` — the customer-controlled runtime
|
|
3
|
-
* environment: egress policy, OS/language packages, and plain env vars.
|
|
4
|
-
*
|
|
5
|
-
* Two behaviours here are deliberately NOT in the schemas, per D4/L1:
|
|
6
|
-
*
|
|
7
|
-
* - **Ecosystem-prefix splitting** (`"pip:pandas"` -> `{name:"pandas",
|
|
8
|
-
* ecosystem:"pip"}`) is a transform, so it lives in
|
|
9
|
-
* {@link normalizePlatformPackage}. A `.transform()` would make the response
|
|
10
|
-
* half of the generated spec ungenerable.
|
|
11
|
-
* - **Host lower-casing** lives in {@link normalizeAllowedHosts} for the same
|
|
12
|
-
* reason.
|
|
13
|
-
*/
|
|
14
|
-
import * as z from "zod/mini";
|
|
15
|
-
import { ENV_VARS_MAX_ENTRIES, ENV_VARS_MAX_TOTAL_BYTES, ENV_VARS_MAX_VALUE_BYTES, ENV_VAR_KEY_PATTERN, AEX_RESERVED_ENV_PREFIX, PLATFORM_PACKAGE_ECOSYSTEMS } from "../submission-limits.js";
|
|
16
|
-
import { indexedPath, lastSegment, wireObject } from "./wire.js";
|
|
17
|
-
/**
|
|
18
|
-
* Every schema below is mounted under {@link EnvironmentSchema}, which is itself
|
|
19
|
-
* mounted under the submission brief. That matters for paths: Zod reports an
|
|
20
|
-
* issue position relative to the schema the parse STARTED at, so a message
|
|
21
|
-
* rendered by folding the whole reported position is only correct at one mount
|
|
22
|
-
* depth. Every path below is either a fixed string or derived from the last
|
|
23
|
-
* segment(s) Zod reports (`indexedPath`, `lastSegment`), which is what keeps it
|
|
24
|
-
* right however deeply the environment is mounted.
|
|
25
|
-
*/
|
|
26
|
-
const ENVIRONMENT = "submission.environment";
|
|
27
|
-
const NETWORKING = `${ENVIRONMENT}.networking`;
|
|
28
|
-
const PACKAGES = `${ENVIRONMENT}.packages`;
|
|
29
|
-
const ENV_VARS = `${ENVIRONMENT}.envVars`;
|
|
30
|
-
/**
|
|
31
|
-
* `envVars` as a bounded map of shell-portable names to UTF-8 values.
|
|
32
|
-
*
|
|
33
|
-
* The whole rule set is one ordered `z.check()` rather than a composition of
|
|
34
|
-
* record key/value schemas, because the ladder is order-sensitive and every rung
|
|
35
|
-
* has its own message: a key that is both malformed AND non-string must report
|
|
36
|
-
* the malformed key, and the byte budget must fail on the entry that crosses
|
|
37
|
-
* `ENV_VARS_MAX_TOTAL_BYTES` — not on the map as a whole. Composed schemas
|
|
38
|
-
* report in Zod's order, not this one.
|
|
39
|
-
*
|
|
40
|
-
* The cost is that these rules are invisible in the generated OpenAPI document
|
|
41
|
-
* (L2), so the description below is what a spec consumer sees. A weaker-but-true
|
|
42
|
-
* schema beats a precise-but-wrong one.
|
|
43
|
-
*/
|
|
44
|
-
export const EnvVarsSchema = z
|
|
45
|
-
.record(z.string(), z.unknown(), { error: `${ENV_VARS} must be an object` })
|
|
46
|
-
.check(z.check((payload) => {
|
|
47
|
-
const value = payload.value;
|
|
48
|
-
const keys = Object.keys(value);
|
|
49
|
-
const reject = (message) => {
|
|
50
|
-
payload.issues.push({ code: "custom", message, input: payload.value });
|
|
51
|
-
};
|
|
52
|
-
if (keys.length > ENV_VARS_MAX_ENTRIES) {
|
|
53
|
-
reject(`${ENV_VARS} has ${keys.length} entries; maximum is ${ENV_VARS_MAX_ENTRIES}`);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
let totalBytes = 0;
|
|
57
|
-
for (const key of keys) {
|
|
58
|
-
if (!ENV_VAR_KEY_PATTERN.test(key)) {
|
|
59
|
-
reject(`${ENV_VARS}.${key} key must match /^[A-Z_][A-Z0-9_]*$/`);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (key.startsWith(AEX_RESERVED_ENV_PREFIX)) {
|
|
63
|
-
reject(`${ENV_VARS}.${key} uses reserved prefix "${AEX_RESERVED_ENV_PREFIX}" (set by the aex runtime)`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const raw = value[key];
|
|
67
|
-
if (typeof raw !== "string") {
|
|
68
|
-
reject(`${ENV_VARS}.${key} must be a string`);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (raw.includes("\0")) {
|
|
72
|
-
reject(`${ENV_VARS}.${key} must not contain NUL bytes`);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const valueBytes = Buffer.byteLength(raw, "utf8");
|
|
76
|
-
if (valueBytes > ENV_VARS_MAX_VALUE_BYTES) {
|
|
77
|
-
reject(`${ENV_VARS}.${key} value is ${valueBytes} bytes; maximum is ${ENV_VARS_MAX_VALUE_BYTES}`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
totalBytes += Buffer.byteLength(key, "utf8") + valueBytes;
|
|
81
|
-
if (totalBytes > ENV_VARS_MAX_TOTAL_BYTES) {
|
|
82
|
-
reject(`${ENV_VARS} total byte size exceeds maximum ${ENV_VARS_MAX_TOTAL_BYTES}`);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}))
|
|
87
|
-
.register(z.globalRegistry, {
|
|
88
|
-
id: "SubmissionEnvVars",
|
|
89
|
-
description: "Environment variables injected into the session runtime. Keys match /^[A-Z_][A-Z0-9_]*$/ " +
|
|
90
|
-
`and must not use the reserved "${AEX_RESERVED_ENV_PREFIX}" prefix. Bounded to ` +
|
|
91
|
-
`${ENV_VARS_MAX_ENTRIES} entries, ${ENV_VARS_MAX_VALUE_BYTES} bytes per value and ` +
|
|
92
|
-
`${ENV_VARS_MAX_TOTAL_BYTES} bytes in total. Enforced in packages/contracts/src/schemas/submission-environment.ts.`
|
|
93
|
-
});
|
|
94
|
-
/** A single allowed egress host. Case folding is {@link normalizeAllowedHosts}' job. */
|
|
95
|
-
const allowedHost = z.string().check(z.refine((value) => typeof value === "string" && value.length > 0, {
|
|
96
|
-
error: (issue) => `${NETWORKING}.allowedHosts[${lastSegment(issue.path)}] must be a non-empty string`,
|
|
97
|
-
abort: true
|
|
98
|
-
}));
|
|
99
|
-
export const AllowedHostsSchema = z
|
|
100
|
-
.array(allowedHost, {
|
|
101
|
-
error: `${NETWORKING}.allowedHosts must be an array of strings`
|
|
102
|
-
})
|
|
103
|
-
.check(z.check((payload) => {
|
|
104
|
-
const seen = new Set();
|
|
105
|
-
for (const entry of payload.value) {
|
|
106
|
-
const lower = entry.toLowerCase();
|
|
107
|
-
if (seen.has(lower)) {
|
|
108
|
-
payload.issues.push({
|
|
109
|
-
code: "custom",
|
|
110
|
-
message: `${NETWORKING}.allowedHosts duplicate entry: ${entry}`,
|
|
111
|
-
input: payload.value
|
|
112
|
-
});
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
seen.add(lower);
|
|
116
|
-
}
|
|
117
|
-
}));
|
|
118
|
-
/** Lower-case every host so the deny/allow comparison downstream is case-insensitive. */
|
|
119
|
-
export function normalizeAllowedHosts(hosts) {
|
|
120
|
-
return hosts.map((host) => host.toLowerCase());
|
|
121
|
-
}
|
|
122
|
-
export const NetworkingSchema = wireObject(NETWORKING, {
|
|
123
|
-
mode: z.optional(z.enum(["limited", "open"], {
|
|
124
|
-
error: `${NETWORKING}.mode must be one of: limited, open`
|
|
125
|
-
})),
|
|
126
|
-
allowedHosts: z.optional(AllowedHostsSchema)
|
|
127
|
-
}, {
|
|
128
|
-
unknownKey: (path, key) => `${path}.${key} is not an allowed field; permitted: mode, allowedHosts`
|
|
129
|
-
}).check(
|
|
130
|
-
// `mode` is optional on the wire only so that an unknown-key error outranks a
|
|
131
|
-
// missing-mode error, matching the parser's order. Supplying `networking` at
|
|
132
|
-
// all is a commitment to state the egress mode.
|
|
133
|
-
z.refine((value) => value.mode !== undefined, {
|
|
134
|
-
error: `${NETWORKING}.mode is required when networking is provided`,
|
|
135
|
-
abort: true
|
|
136
|
-
}));
|
|
137
|
-
export const PlatformPackageSchema = wireObject(indexedPath(PACKAGES), {
|
|
138
|
-
name: z.string(),
|
|
139
|
-
version: z.optional(z.string())
|
|
140
|
-
}, {
|
|
141
|
-
unknownKey: (path, key) => `${path}.${key} is not an allowed field; permitted: name, version`
|
|
142
|
-
});
|
|
143
|
-
export const PackagesSchema = z.array(PlatformPackageSchema, {
|
|
144
|
-
error: `${PACKAGES} must be an array`
|
|
145
|
-
});
|
|
146
|
-
/**
|
|
147
|
-
* Split the `"<ecosystem>:<name>"` prefix a caller encodes into `name`.
|
|
148
|
-
*
|
|
149
|
-
* An unprefixed name defaults to `apt`. A colon-delimited prefix that is not a
|
|
150
|
-
* known ecosystem is rejected rather than folded into the package name, so a
|
|
151
|
-
* typo'd manager fails closed instead of installing something unexpected.
|
|
152
|
-
*
|
|
153
|
-
* Throws rather than returning a result because it runs inside the parser's
|
|
154
|
-
* existing `withContractParseError` boundary, which brands whatever it throws.
|
|
155
|
-
*/
|
|
156
|
-
export function normalizePlatformPackage(pkg, path) {
|
|
157
|
-
let ecosystem = "apt";
|
|
158
|
-
let name = pkg.name;
|
|
159
|
-
const colon = pkg.name.indexOf(":");
|
|
160
|
-
if (colon > 0) {
|
|
161
|
-
const prefix = pkg.name.slice(0, colon);
|
|
162
|
-
if (!PLATFORM_PACKAGE_ECOSYSTEMS.includes(prefix)) {
|
|
163
|
-
throw new Error(`${path}.name has unknown ecosystem prefix "${prefix}:"; permitted: ${PLATFORM_PACKAGE_ECOSYSTEMS.join(", ")}`);
|
|
164
|
-
}
|
|
165
|
-
ecosystem = prefix;
|
|
166
|
-
name = pkg.name.slice(colon + 1);
|
|
167
|
-
}
|
|
168
|
-
if (name.length === 0) {
|
|
169
|
-
throw new Error(`${path}.name resolves to an empty package after stripping the "${ecosystem}:" ecosystem prefix`);
|
|
170
|
-
}
|
|
171
|
-
return pkg.version !== undefined ? { name, version: pkg.version, ecosystem } : { name, ecosystem };
|
|
172
|
-
}
|
|
173
|
-
export const EnvironmentSchema = wireObject(ENVIRONMENT, {
|
|
174
|
-
networking: z.optional(NetworkingSchema),
|
|
175
|
-
packages: z.optional(PackagesSchema),
|
|
176
|
-
envVars: z.optional(EnvVarsSchema)
|
|
177
|
-
}, {
|
|
178
|
-
unknownKey: (path, key) => `${path}.${key} is not an allowed field; permitted: networking, packages, envVars`
|
|
179
|
-
});
|