@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,812 +0,0 @@
|
|
|
1
|
-
import { SKILL_NAME_PATTERN, SKILL_RESERVED_NAMES, TOOL_NAME_PATTERN, assertValidMountPath, normaliseSkillBundlePath, parseMcpServerRef } from "./session-config.js";
|
|
2
|
-
import { parseSessionTimeout } from "./runtime-sizes.js";
|
|
3
|
-
import { assertPinnedWorkspaceResource, assertWorkspaceFileResourceName, assertWorkspaceInstructionResourceName } from "./workspace-resources.js";
|
|
4
|
-
import { withContractParseError } from "./contract-parse-error.js";
|
|
5
|
-
import { parseWire } from "./schemas/wire.js";
|
|
6
|
-
import { SessionSubmissionRequestSchema } from "./schemas/submission-request.js";
|
|
7
|
-
import { ApprovalGateSchema, ResponseFormatSchema, SubmissionSchema } from "./schemas/submission-body.js";
|
|
8
|
-
import { SessionWebhookSchema } from "./schemas/session-webhook.js";
|
|
9
|
-
import { SessionLimitsSchema, normalizeSessionLimits } from "./schemas/session-limits.js";
|
|
10
|
-
import { SessionMachineSchema, normalizeSessionMachine } from "./schemas/session-machine.js";
|
|
11
|
-
import { normalizeAllowedHosts, normalizePlatformPackage } from "./schemas/submission-environment.js";
|
|
12
|
-
import { InlineSecretsSchema, normalizeEnvSecrets } from "./schemas/submission-secrets.js";
|
|
13
|
-
import { isRecord, isStringLiteral } from "./value-guards.js";
|
|
14
|
-
// Bounds, patterns and the ecosystem list live in a leaf module so the schemas
|
|
15
|
-
// can read them without importing this file back. Re-exported here so the
|
|
16
|
-
// published surface is unchanged.
|
|
17
|
-
export { AEX_RESERVED_ENV_PREFIX, ENV_VARS_MAX_ENTRIES, ENV_VARS_MAX_VALUE_BYTES, ENV_VARS_MAX_TOTAL_BYTES, PLATFORM_PACKAGE_ECOSYSTEMS } from "./submission-limits.js";
|
|
18
|
-
// Also imported locally: a re-export does not bind the names in this module.
|
|
19
|
-
import { PLATFORM_PACKAGE_ECOSYSTEMS } from "./submission-limits.js";
|
|
20
|
-
const APT_PACKAGE_NAME_PATTERN = /^[a-z0-9][a-z0-9+.-]+(?::[a-z0-9][a-z0-9-]*)?$/;
|
|
21
|
-
const APT_EXACT_VERSION_PATTERN = /^[0-9][0-9A-Za-z.+:~-]*$/;
|
|
22
|
-
const NPM_PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
23
|
-
const NPM_EXACT_VERSION_PATTERN = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
24
|
-
const PIP_PACKAGE_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
25
|
-
const PIP_EXACT_VERSION_PATTERN = /^[0-9](?:[0-9A-Za-z.!+_-]*[0-9A-Za-z])?$/;
|
|
26
|
-
function assertPlatformPackage(pkg, path) {
|
|
27
|
-
const invalidName = () => new Error(`${path}.name must be a valid ${pkg.ecosystem} registry package name`);
|
|
28
|
-
const invalidVersion = () => new Error(`${path}.version must be an exact ${pkg.ecosystem} version`);
|
|
29
|
-
switch (pkg.ecosystem) {
|
|
30
|
-
case "apt":
|
|
31
|
-
if (!APT_PACKAGE_NAME_PATTERN.test(pkg.name))
|
|
32
|
-
throw invalidName();
|
|
33
|
-
if (pkg.version !== undefined && !APT_EXACT_VERSION_PATTERN.test(pkg.version))
|
|
34
|
-
throw invalidVersion();
|
|
35
|
-
return;
|
|
36
|
-
case "npm":
|
|
37
|
-
if (!NPM_PACKAGE_NAME_PATTERN.test(pkg.name))
|
|
38
|
-
throw invalidName();
|
|
39
|
-
if (pkg.version !== undefined && !NPM_EXACT_VERSION_PATTERN.test(pkg.version))
|
|
40
|
-
throw invalidVersion();
|
|
41
|
-
return;
|
|
42
|
-
case "pip":
|
|
43
|
-
if (!PIP_PACKAGE_NAME_PATTERN.test(pkg.name))
|
|
44
|
-
throw invalidName();
|
|
45
|
-
if (pkg.version !== undefined && !PIP_EXACT_VERSION_PATTERN.test(pkg.version))
|
|
46
|
-
throw invalidVersion();
|
|
47
|
-
return;
|
|
48
|
-
default:
|
|
49
|
-
throw new Error(`${path}.ecosystem must be one of: ${PLATFORM_PACKAGE_ECOSYSTEMS.join(", ")}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Render a parsed {@link PlatformPackage} as the version-embedded install
|
|
54
|
-
* string used by runtime materialization. The join differs per manager:
|
|
55
|
-
* - pip → `name==version`
|
|
56
|
-
* - npm → `name@version`
|
|
57
|
-
* - apt → `name=version`
|
|
58
|
-
* With no `version`, just the bare `name`. Pure; used by the managed runner
|
|
59
|
-
* package installer.
|
|
60
|
-
*/
|
|
61
|
-
export function packageInstallString(pkg) {
|
|
62
|
-
assertPlatformPackage(pkg, "package");
|
|
63
|
-
if (pkg.version === undefined) {
|
|
64
|
-
return pkg.name;
|
|
65
|
-
}
|
|
66
|
-
switch (pkg.ecosystem) {
|
|
67
|
-
case "pip":
|
|
68
|
-
return `${pkg.name}==${pkg.version}`;
|
|
69
|
-
case "npm":
|
|
70
|
-
return `${pkg.name}@${pkg.version}`;
|
|
71
|
-
case "apt":
|
|
72
|
-
return `${pkg.name}=${pkg.version}`;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export const SECRETS_KEY = "secrets";
|
|
76
|
-
/**
|
|
77
|
-
* The `secretEnv` grammars, declared in the leaf bounds module so
|
|
78
|
-
* `schemas/submission-body.ts` can enforce them without importing this file
|
|
79
|
-
* back, and re-exported here so the published surface is unchanged.
|
|
80
|
-
*/
|
|
81
|
-
export { SECRET_ENV_NAME_PATTERN, SECRET_HANDLE_PATTERN } from "./submission-limits.js";
|
|
82
|
-
export const deniedSecretFields = new Set([
|
|
83
|
-
"providerApiKey",
|
|
84
|
-
"anthropicApiKey",
|
|
85
|
-
"apiKey",
|
|
86
|
-
"apiKeys",
|
|
87
|
-
"accessToken",
|
|
88
|
-
"refreshToken",
|
|
89
|
-
"password",
|
|
90
|
-
"mcpCredentials",
|
|
91
|
-
"credentials"
|
|
92
|
-
]);
|
|
93
|
-
/**
|
|
94
|
-
* Resolve a validated `submission.environment` into the runtime snapshot.
|
|
95
|
-
*
|
|
96
|
-
* The schema owns shape, the allow-list and the bounds; the normalisers own the
|
|
97
|
-
* transforms (ecosystem-prefix splitting, host case folding) and the
|
|
98
|
-
* collapse-to-`undefined` rules. Keeping those apart is what lets the same
|
|
99
|
-
* schema generate the OpenAPI document — see D4/L1.
|
|
100
|
-
*/
|
|
101
|
-
function normalizeEnvironment(parsed) {
|
|
102
|
-
if (parsed === undefined) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
const networking = normalizeNetworking(parsed.networking);
|
|
106
|
-
const packages = normalizePackages(parsed.packages);
|
|
107
|
-
const envVars = normalizeEnvVars(parsed.envVars);
|
|
108
|
-
if (!networking && !packages && !envVars) {
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
...(networking ? { networking } : {}),
|
|
113
|
-
...(packages ? { packages } : {}),
|
|
114
|
-
...(envVars ? { envVars } : {})
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/** An empty map is treated as not supplied, so it never lands on the snapshot. */
|
|
118
|
-
function normalizeEnvVars(envVars) {
|
|
119
|
-
if (envVars === undefined || Object.keys(envVars).length === 0) {
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
return Object.freeze({ ...envVars });
|
|
123
|
-
}
|
|
124
|
-
function normalizeNetworking(networking) {
|
|
125
|
-
if (networking?.mode === undefined) {
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
const allowedHosts = networking.allowedHosts;
|
|
129
|
-
return allowedHosts
|
|
130
|
-
? { mode: networking.mode, allowedHosts: normalizeAllowedHosts(allowedHosts) }
|
|
131
|
-
: { mode: networking.mode };
|
|
132
|
-
}
|
|
133
|
-
function normalizePackages(packages) {
|
|
134
|
-
if (packages === undefined) {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
return packages.map((entry, index) => {
|
|
138
|
-
const path = `submission.environment.packages[${index}]`;
|
|
139
|
-
const normalized = normalizePlatformPackage(entry, path);
|
|
140
|
-
assertPlatformPackage(normalized, path);
|
|
141
|
-
return normalized;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Cross-check `submission.secretEnv` declarations against `secrets.envSecrets`
|
|
146
|
-
* values:
|
|
147
|
-
*
|
|
148
|
-
* - `{ ephemeral: true }` MUST have a matching `secrets.envSecrets` value.
|
|
149
|
-
* - `{ ref }` MUST NOT supply a value (the value lives in the workspace store).
|
|
150
|
-
* - every `secrets.envSecrets` value MUST have a matching `{ ephemeral: true }`
|
|
151
|
-
* declaration (no orphan values that would never be injected).
|
|
152
|
-
*/
|
|
153
|
-
export function crossValidateSecretEnvAndValues(secretEnv, envSecrets) {
|
|
154
|
-
const declarations = secretEnv ?? {};
|
|
155
|
-
const values = envSecrets ?? {};
|
|
156
|
-
for (const [envName, entry] of Object.entries(declarations)) {
|
|
157
|
-
const hasValue = Object.prototype.hasOwnProperty.call(values, envName);
|
|
158
|
-
if ("ref" in entry) {
|
|
159
|
-
if (hasValue) {
|
|
160
|
-
throw new Error(`submission.secretEnv[${envName}] is a workspace ref and must not supply a value in secrets.envSecrets[${envName}]; the value resolves server-side`);
|
|
161
|
-
}
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (!hasValue) {
|
|
165
|
-
throw new Error(`submission.secretEnv[${envName}] is ephemeral but has no matching secrets.envSecrets[${envName}] value`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
for (const envName of Object.keys(values)) {
|
|
169
|
-
const entry = declarations[envName];
|
|
170
|
-
if (!entry || !("ephemeral" in entry)) {
|
|
171
|
-
throw new Error(`secrets.envSecrets[${envName}] has no matching submission.secretEnv[${envName}] ephemeral declaration`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
export function parseInlineSecrets(input) {
|
|
176
|
-
return withContractParseError("parseInlineSecrets", () => {
|
|
177
|
-
// Absent/null secrets collapse to an empty bundle. Under managed gateway keys
|
|
178
|
-
// a run needs no provider key, so an empty bundle is always admissible.
|
|
179
|
-
if (input === undefined || input === null)
|
|
180
|
-
return {};
|
|
181
|
-
return normalizeInlineSecrets(parseWire(InlineSecretsSchema, input));
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Collapse a validated secrets bundle onto {@link PlatformInlineSecrets}.
|
|
186
|
-
*
|
|
187
|
-
* Split from {@link parseInlineSecrets} so the request envelope — which
|
|
188
|
-
* validates the bundle as part of its own single parse — resolves it without
|
|
189
|
-
* parsing it a second time.
|
|
190
|
-
*/
|
|
191
|
-
function normalizeInlineSecrets(parsed) {
|
|
192
|
-
if (parsed === undefined || parsed === null) {
|
|
193
|
-
return {};
|
|
194
|
-
}
|
|
195
|
-
const mcpServers = parsed.mcpServers;
|
|
196
|
-
const envSecrets = normalizeEnvSecrets(parsed.envSecrets);
|
|
197
|
-
// Spread only the present halves: `PlatformInlineSecrets` promises each key
|
|
198
|
-
// is absent or a value, never present-and-undefined.
|
|
199
|
-
return {
|
|
200
|
-
...(mcpServers ? { mcpServers } : {}),
|
|
201
|
-
...(envSecrets ? { envSecrets } : {})
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
export function assertNoSecretBearingFields(input, path) {
|
|
205
|
-
if (Array.isArray(input)) {
|
|
206
|
-
input.forEach((item, index) => assertNoSecretBearingFields(item, [...path, String(index)]));
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
if (!isRecord(input)) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
for (const [key, value] of Object.entries(input)) {
|
|
213
|
-
if (deniedSecretFields.has(key)) {
|
|
214
|
-
throw new Error(`Secret-bearing field is not allowed in platform submission: ${[...path, key].join(".")}`);
|
|
215
|
-
}
|
|
216
|
-
assertNoSecretBearingFields(value, [...path, key]);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
export function requireRecord(input, field) {
|
|
220
|
-
if (!isRecord(input)) {
|
|
221
|
-
throw new Error(`${field} must be an object`);
|
|
222
|
-
}
|
|
223
|
-
return input;
|
|
224
|
-
}
|
|
225
|
-
export function requireString(input, field) {
|
|
226
|
-
if (typeof input !== "string" || input.length === 0) {
|
|
227
|
-
throw new Error(`${field} must be a non-empty string`);
|
|
228
|
-
}
|
|
229
|
-
return input;
|
|
230
|
-
}
|
|
231
|
-
export function optionalString(input, field) {
|
|
232
|
-
if (input === undefined) {
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
return requireString(input, field);
|
|
236
|
-
}
|
|
237
|
-
export function optionalEnum(input, field, allowed) {
|
|
238
|
-
if (input === undefined) {
|
|
239
|
-
return undefined;
|
|
240
|
-
}
|
|
241
|
-
if (!isStringLiteral(input, allowed)) {
|
|
242
|
-
throw new Error(`${field} must be one of: ${allowed.join(", ")}`);
|
|
243
|
-
}
|
|
244
|
-
return input;
|
|
245
|
-
}
|
|
246
|
-
export function optionalPositiveInt(input, field) {
|
|
247
|
-
if (input === undefined) {
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
if (typeof input !== "number" || !Number.isSafeInteger(input) || input <= 0) {
|
|
251
|
-
throw new Error(`${field} must be a positive safe integer`);
|
|
252
|
-
}
|
|
253
|
-
return input;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* A finite positive NUMBER (fractional allowed — e.g. a USD amount like `2.5`), or
|
|
257
|
-
* undefined when absent. Rejects non-numbers, NaN/Infinity, and `<= 0`.
|
|
258
|
-
*/
|
|
259
|
-
export function optionalPositiveNumber(input, field) {
|
|
260
|
-
if (input === undefined) {
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
if (typeof input !== "number" || !Number.isFinite(input) || input <= 0) {
|
|
264
|
-
throw new Error(`${field} must be a positive finite number`);
|
|
265
|
-
}
|
|
266
|
-
return input;
|
|
267
|
-
}
|
|
268
|
-
export function parseSessionSubmissionRequest(input) {
|
|
269
|
-
return withContractParseError("parseSessionSubmissionRequest", () => {
|
|
270
|
-
const value = parseWire(SessionSubmissionRequestSchema, input);
|
|
271
|
-
// Defence in depth: scan every non-secrets field for credential-named
|
|
272
|
-
// keys. The `secrets` key is
|
|
273
|
-
// the only allow-listed home for credential material.
|
|
274
|
-
for (const [key, fieldValue] of Object.entries(value)) {
|
|
275
|
-
if (key === SECRETS_KEY) {
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
if (deniedSecretFields.has(key)) {
|
|
279
|
-
throw new Error(`Secret-bearing field is not allowed in platform submission: ${key}`);
|
|
280
|
-
}
|
|
281
|
-
assertNoSecretBearingFields(fieldValue, [key]);
|
|
282
|
-
}
|
|
283
|
-
// The envelope schema has already validated every dial; what is left is the
|
|
284
|
-
// decode (`timeout` -> ms) and the collapse-to-`undefined` rules.
|
|
285
|
-
const runtimeSize = value.runtimeSize;
|
|
286
|
-
const runtimeKind = value.runtimeKind;
|
|
287
|
-
const timeoutMs = parseSessionTimeout(value.timeout);
|
|
288
|
-
const webhook = value.webhook;
|
|
289
|
-
const limits = value.limits === undefined ? undefined : normalizeSessionLimits(value.limits);
|
|
290
|
-
const machine = value.machine === undefined ? undefined : normalizeSessionMachine(value.machine);
|
|
291
|
-
const secrets = normalizeInlineSecrets(value.secrets);
|
|
292
|
-
// Branded as `parseSubmission` because that is the parser a caller would
|
|
293
|
-
// recognise as having rejected the brief, even though the shape was validated
|
|
294
|
-
// one level up as part of the envelope.
|
|
295
|
-
const submission = withContractParseError("parseSubmission", () => normalizeSubmission(value.submission));
|
|
296
|
-
crossValidateSecretEnvAndValues(submission.secretEnv, secrets.envSecrets);
|
|
297
|
-
// mcpServers names must agree across the submission half and the
|
|
298
|
-
// secrets half — every secrets.mcpServers[i].name MUST resolve to a
|
|
299
|
-
// submission.mcpServers entry (no orphan secrets) AND the URL must
|
|
300
|
-
// match exactly. The reverse is allowed (an MCP server with no auth
|
|
301
|
-
// headers is a valid public-MCP mode).
|
|
302
|
-
if (secrets.mcpServers !== undefined) {
|
|
303
|
-
const declared = new Map(submission.mcpServers.map((m) => [m.name, m.url]));
|
|
304
|
-
for (const secret of secrets.mcpServers) {
|
|
305
|
-
const declaredUrl = declared.get(secret.name);
|
|
306
|
-
if (declaredUrl === undefined) {
|
|
307
|
-
throw new Error(`secrets.mcpServers[name=${secret.name}] has no matching submission.mcpServers entry`);
|
|
308
|
-
}
|
|
309
|
-
if (declaredUrl !== secret.url) {
|
|
310
|
-
throw new Error(`secrets.mcpServers[name=${secret.name}].url must equal submission.mcpServers[name=${secret.name}].url ` +
|
|
311
|
-
`(got submission=${declaredUrl}, secrets=${secret.url})`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
return {
|
|
316
|
-
workspaceId: value.workspaceId,
|
|
317
|
-
idempotencyKey: value.idempotencyKey,
|
|
318
|
-
submission,
|
|
319
|
-
...(runtimeSize ? { runtimeSize } : {}),
|
|
320
|
-
...(runtimeKind ? { runtimeKind } : {}),
|
|
321
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
322
|
-
...(webhook !== undefined ? { webhook } : {}),
|
|
323
|
-
...(limits !== undefined ? { limits } : {}),
|
|
324
|
-
...(machine !== undefined ? { machine } : {}),
|
|
325
|
-
secrets
|
|
326
|
-
};
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Parse + SSRF-shape-validate the optional per-session `webhook`. The URL must be
|
|
331
|
-
* https with no userinfo (a `user:pass@host` URL is rejected — credentials must
|
|
332
|
-
* not ride in a callback URL). Unknown subfields are rejected so the strict
|
|
333
|
-
* top-level allow-list extends to the nested object. Returns `undefined` when
|
|
334
|
-
* absent. Delivery-time re-resolution + IP-deny checks live server-side; this
|
|
335
|
-
* is the submit-time shape gate.
|
|
336
|
-
*/
|
|
337
|
-
export function parseSessionWebhook(input) {
|
|
338
|
-
return withContractParseError("parseSessionWebhook", () => {
|
|
339
|
-
if (input === undefined) {
|
|
340
|
-
return undefined;
|
|
341
|
-
}
|
|
342
|
-
return parseWire(SessionWebhookSchema, input);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Parse the optional per-session `limits` override. Mirrors {@link parseSessionWebhook}:
|
|
347
|
-
* absent ⇒ `undefined`; a non-object or any unknown subfield is rejected so the
|
|
348
|
-
* strict top-level allow-list extends to the nested object. Each present field
|
|
349
|
-
* is validated as a positive safe integer via {@link optionalPositiveInt}.
|
|
350
|
-
*
|
|
351
|
-
* This is a SHAPE/positivity gate only — it does NOT clamp to the workspace or
|
|
352
|
-
* platform ceilings (that precedence lives in the resolver, `resolveSessionLimits`).
|
|
353
|
-
* Only the present fields are returned; an all-absent override (e.g. `{}`)
|
|
354
|
-
* collapses to `undefined` so it carries no signal onto the request.
|
|
355
|
-
*/
|
|
356
|
-
export function parseSessionLimits(input) {
|
|
357
|
-
return withContractParseError("parseSessionLimits", () => {
|
|
358
|
-
if (input === undefined) {
|
|
359
|
-
return undefined;
|
|
360
|
-
}
|
|
361
|
-
return normalizeSessionLimits(parseWire(SessionLimitsSchema, input));
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Boot-session budget fragment. The public submit surface names a session's spend
|
|
366
|
-
* cap `limits.maxSpendUsd`; the frozen boot session config the managed runtime
|
|
367
|
-
* folds the loop against names the SAME USD value `budgetUsd` — the field the
|
|
368
|
-
* session planner reads to enforce/terminate a session that would out-spend its cap.
|
|
369
|
-
* This is the single source of truth for that wire→boot name mapping so the two
|
|
370
|
-
* layers can never drift.
|
|
371
|
-
*
|
|
372
|
-
* Returns a fragment safe to spread into `sessionConfig.limits`: `{ budgetUsd }`
|
|
373
|
-
* when a cap is set, `{}` when none is (an absent cap stays absent — the session is
|
|
374
|
-
* unbounded per-session, subject only to the session timeout + the per-workspace cap).
|
|
375
|
-
* Pure: same input ⇒ same output.
|
|
376
|
-
*/
|
|
377
|
-
export function sessionBudgetLimits(limits) {
|
|
378
|
-
if (limits?.maxSpendUsd === undefined) {
|
|
379
|
-
return {};
|
|
380
|
-
}
|
|
381
|
-
return { budgetUsd: limits.maxSpendUsd };
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Parse the optional per-session `machine` capacity intent. Mirrors
|
|
385
|
-
* {@link parseSessionWebhook}: absent ⇒ `undefined`; a non-object or any unknown
|
|
386
|
-
* subfield is rejected so the strict top-level allow-list extends to the nested
|
|
387
|
-
* object. `spot` must be a boolean when present. A no-signal object (e.g.
|
|
388
|
-
* `machine: {}`) collapses to `undefined` so it never lands an empty object on
|
|
389
|
-
* the request. An explicit `spot` (true or false) is preserved verbatim. Only
|
|
390
|
-
* shape is validated here — capacity selection is a runtime concern.
|
|
391
|
-
*/
|
|
392
|
-
export function parseSessionMachine(input) {
|
|
393
|
-
return withContractParseError("parseSessionMachine", () => {
|
|
394
|
-
if (input === undefined) {
|
|
395
|
-
return undefined;
|
|
396
|
-
}
|
|
397
|
-
return normalizeSessionMachine(parseWire(SessionMachineSchema, input));
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
export function parseSubmission(input) {
|
|
401
|
-
return withContractParseError("parseSubmission", () => normalizeSubmission(parseWire(SubmissionSchema, input)));
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Resolve a validated brief onto {@link PlatformSubmission}.
|
|
405
|
-
*
|
|
406
|
-
* Everything here is a transform or a lookup the schema deliberately does not
|
|
407
|
-
* own: the prompt's single-string form widened to a list, ecosystem prefixes
|
|
408
|
-
* split, resource name grammars asserted, no-signal objects collapsed to
|
|
409
|
-
* `undefined`, builtin tool names resolved into canonical order. Key order in
|
|
410
|
-
* the returned object is pinned by
|
|
411
|
-
* `test/allowed-keys-parser-golden.test.ts`.
|
|
412
|
-
*/
|
|
413
|
-
function normalizeSubmission(value) {
|
|
414
|
-
const system = value.system;
|
|
415
|
-
const prompt = normalizePrompt(value.prompt);
|
|
416
|
-
const assets = normalizeSubmissionAssets(value.assets);
|
|
417
|
-
const mcpServers = parseMcpServers(value.mcpServers);
|
|
418
|
-
const secretEnv = normalizeSecretEnv(value.secretEnv);
|
|
419
|
-
const environment = normalizeEnvironment(value.environment);
|
|
420
|
-
const securityProfile = value.securityProfile ?? undefined;
|
|
421
|
-
const metadata = value.metadata;
|
|
422
|
-
const fileCapture = normalizeFileCapture(value.fileCapture);
|
|
423
|
-
const builtinTools = resolveBuiltinToolsSelection(value.builtinTools);
|
|
424
|
-
const outputMode = value.outputMode ?? undefined;
|
|
425
|
-
const responseFormat = normalizeResponseFormat(value.responseFormat);
|
|
426
|
-
const approvalGate = normalizeApprovalGate(value.approvalGate);
|
|
427
|
-
const platform = normalizePlatformInjection(value.platform);
|
|
428
|
-
return {
|
|
429
|
-
model: value.model,
|
|
430
|
-
...(system ? { system } : {}),
|
|
431
|
-
prompt,
|
|
432
|
-
assets,
|
|
433
|
-
mcpServers,
|
|
434
|
-
...(secretEnv ? { secretEnv } : {}),
|
|
435
|
-
...(environment ? { environment } : {}),
|
|
436
|
-
...(securityProfile ? { securityProfile } : {}),
|
|
437
|
-
...(metadata ? { metadata } : {}),
|
|
438
|
-
...(fileCapture ? { fileCapture } : {}),
|
|
439
|
-
builtinTools,
|
|
440
|
-
...(outputMode !== undefined ? { outputMode } : {}),
|
|
441
|
-
...(responseFormat !== undefined ? { responseFormat } : {}),
|
|
442
|
-
...(approvalGate !== undefined ? { approvalGate } : {}),
|
|
443
|
-
...(platform ? { platform } : {})
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
function normalizeSubmissionAssets(value) {
|
|
447
|
-
return {
|
|
448
|
-
files: projectWorkspaceResources(value.files, "files", (raw, base, path) => {
|
|
449
|
-
assertWorkspaceFileResourceName(raw.name, `${path}.name`);
|
|
450
|
-
assertValidMountPath(raw.mountPath, `${path}.mountPath`);
|
|
451
|
-
return { ...base, kind: "file", name: raw.name, mountPath: raw.mountPath };
|
|
452
|
-
}),
|
|
453
|
-
skills: projectWorkspaceResources(value.skills, "skills", (raw, base, path) => {
|
|
454
|
-
assertValidSkillName(raw.name, `${path}.name`);
|
|
455
|
-
return { ...base, kind: "skill", name: raw.name, description: raw.description };
|
|
456
|
-
}),
|
|
457
|
-
tools: projectWorkspaceResources(value.tools, "tools", (raw, base, path) => {
|
|
458
|
-
if (!TOOL_NAME_PATTERN.test(raw.name) || raw.name.includes("__")) {
|
|
459
|
-
throw new Error(`${path}.name must be a non-reserved tool name matching ${TOOL_NAME_PATTERN.source}`);
|
|
460
|
-
}
|
|
461
|
-
return {
|
|
462
|
-
...base,
|
|
463
|
-
kind: "tool",
|
|
464
|
-
name: raw.name,
|
|
465
|
-
description: raw.description,
|
|
466
|
-
input_schema: raw.input_schema,
|
|
467
|
-
entry: normaliseSkillBundlePath(raw.entry)
|
|
468
|
-
};
|
|
469
|
-
}),
|
|
470
|
-
instructions: projectWorkspaceResources(value.instructions, "instructions", (raw, base, path) => {
|
|
471
|
-
assertWorkspaceInstructionResourceName(raw.name, `${path}.name`);
|
|
472
|
-
return { ...base, kind: "instruction", name: raw.name };
|
|
473
|
-
})
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Project one validated list onto its parsed refs.
|
|
478
|
-
*
|
|
479
|
-
* What survives here rather than moving into the element schema is what a
|
|
480
|
-
* schema cannot state: the name grammars (asserts the SDK builders call
|
|
481
|
-
* directly, two of them declared in `session-config.ts`, which imports this
|
|
482
|
-
* file), the `entry` bundle-path normalisation, the `assetId`/`contentHash`
|
|
483
|
-
* agreement, and per-list duplicate detection — which needs every element at
|
|
484
|
-
* once and reports the LATER element, not the list.
|
|
485
|
-
*/
|
|
486
|
-
function projectWorkspaceResources(entries, field, project) {
|
|
487
|
-
if (entries === undefined)
|
|
488
|
-
return [];
|
|
489
|
-
const seen = new Set();
|
|
490
|
-
return entries.map((raw, index) => {
|
|
491
|
-
const path = `submission.assets.${field}[${index}]`;
|
|
492
|
-
const base = {
|
|
493
|
-
resourceId: raw.resourceId,
|
|
494
|
-
version: raw.version,
|
|
495
|
-
assetId: raw.assetId,
|
|
496
|
-
contentHash: raw.contentHash
|
|
497
|
-
};
|
|
498
|
-
const result = project(raw, base, path);
|
|
499
|
-
assertPinnedWorkspaceResource(result, path);
|
|
500
|
-
const identity = `${result.resourceId}:${result.version}`;
|
|
501
|
-
if (seen.has(identity))
|
|
502
|
-
throw new Error(`${path} duplicates resource version ${identity}`);
|
|
503
|
-
seen.add(identity);
|
|
504
|
-
return result;
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
/** An empty declaration set carries no signal, so it is dropped rather than landed empty. */
|
|
508
|
-
function normalizeSecretEnv(secretEnv) {
|
|
509
|
-
if (secretEnv === undefined || secretEnv === null)
|
|
510
|
-
return undefined;
|
|
511
|
-
const entries = Object.entries(secretEnv);
|
|
512
|
-
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
513
|
-
}
|
|
514
|
-
function normalizePlatformInjection(value) {
|
|
515
|
-
if (value === undefined || value === null || value.systemPrompt === undefined)
|
|
516
|
-
return undefined;
|
|
517
|
-
return { systemPrompt: value.systemPrompt };
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Assistant-output granularity values and the structured-output kinds, declared
|
|
521
|
-
* with the schemas that enforce them and re-exported here so the published
|
|
522
|
-
* surface is unchanged — see the sibling note on the `secretEnv` grammars.
|
|
523
|
-
*/
|
|
524
|
-
export { OUTPUT_MODES, RESPONSE_FORMAT_KINDS } from "./schemas/submission-body.js";
|
|
525
|
-
export const DEFAULT_OUTPUT_MODE = "buffered";
|
|
526
|
-
/**
|
|
527
|
-
* Parse the optional `submission.responseFormat`. Absent ⇒ undefined; a bad
|
|
528
|
-
* `kind` or unknown subfield is rejected (fail-fast). `json_schema` requires a
|
|
529
|
-
* JSON-object `schema`.
|
|
530
|
-
*/
|
|
531
|
-
export function parseResponseFormat(input) {
|
|
532
|
-
return withContractParseError("parseResponseFormat", () => {
|
|
533
|
-
if (input === undefined || input === null)
|
|
534
|
-
return undefined;
|
|
535
|
-
return normalizeResponseFormat(parseWire(ResponseFormatSchema, input));
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
/** Drop absent optional members so an omitted field never lands as `undefined`. */
|
|
539
|
-
function normalizeResponseFormat(value) {
|
|
540
|
-
if (value === undefined || value === null)
|
|
541
|
-
return undefined;
|
|
542
|
-
if (value.kind === "text") {
|
|
543
|
-
return { kind: "text" };
|
|
544
|
-
}
|
|
545
|
-
return {
|
|
546
|
-
kind: "json_schema",
|
|
547
|
-
schema: value.schema,
|
|
548
|
-
...(value.strict !== undefined ? { strict: value.strict } : {}),
|
|
549
|
-
...(value.name !== undefined ? { name: value.name } : {})
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Parse the optional `submission.approvalGate`. Absent / empty tool list ⇒
|
|
554
|
-
* undefined (no gate). Tool names are deduped; the strict allow-list mirrors the
|
|
555
|
-
* sibling parsers.
|
|
556
|
-
*/
|
|
557
|
-
export function parseApprovalGate(input) {
|
|
558
|
-
return withContractParseError("parseApprovalGate", () => {
|
|
559
|
-
if (input === undefined || input === null)
|
|
560
|
-
return undefined;
|
|
561
|
-
return normalizeApprovalGate(parseWire(ApprovalGateSchema, input));
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
/** Dedupe in first-seen order; an empty gate carries no signal and is dropped. */
|
|
565
|
-
function normalizeApprovalGate(value) {
|
|
566
|
-
if (value === undefined || value === null)
|
|
567
|
-
return undefined;
|
|
568
|
-
const seen = new Set();
|
|
569
|
-
const tools = [];
|
|
570
|
-
for (const entry of value.tools) {
|
|
571
|
-
if (!seen.has(entry)) {
|
|
572
|
-
seen.add(entry);
|
|
573
|
-
tools.push(entry);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return tools.length === 0 ? undefined : { tools };
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* The CLOSED set of builtin tool NAMES the managed runtime can inject — one per
|
|
580
|
-
* machine tool the hands implement. This list is the single source of truth for
|
|
581
|
-
* validating builtin tool references; the platform's `HANDS_TOOLS` (the execute
|
|
582
|
-
* vocabulary) is pinned EQUAL to it at module load (`platform-runtime-agent`
|
|
583
|
-
* `assertNamesMatch`), so a rename on either side fails loudly rather than
|
|
584
|
-
* silently shipping a name the executors do not speak.
|
|
585
|
-
*
|
|
586
|
-
* Order mirrors `HANDS_TOOLS`. A builtin tool reference (a bare string in
|
|
587
|
-
* `submission.tools`) must be a member of this set.
|
|
588
|
-
*/
|
|
589
|
-
export const BUILTIN_TOOL_NAMES = [
|
|
590
|
-
"bash",
|
|
591
|
-
"read_file",
|
|
592
|
-
"write_file",
|
|
593
|
-
"edit_file",
|
|
594
|
-
"grep",
|
|
595
|
-
"glob",
|
|
596
|
-
"head",
|
|
597
|
-
"tail",
|
|
598
|
-
"todo_write",
|
|
599
|
-
"subagent",
|
|
600
|
-
"subagent_result",
|
|
601
|
-
"web_fetch",
|
|
602
|
-
"web_search",
|
|
603
|
-
"bash_output",
|
|
604
|
-
"bash_kill",
|
|
605
|
-
"code_execution",
|
|
606
|
-
"wait",
|
|
607
|
-
"git",
|
|
608
|
-
"ls",
|
|
609
|
-
"stat",
|
|
610
|
-
"wc"
|
|
611
|
-
];
|
|
612
|
-
/**
|
|
613
|
-
* Typo-safe accessors for the closed builtin tool set: each key maps to the
|
|
614
|
-
* real tool NAME string. Reference a builtin in `submission.tools` via
|
|
615
|
-
* `BuiltinTools.web_search` rather than the bare string so a rename is a
|
|
616
|
-
* compile error, not a runtime 400.
|
|
617
|
-
*
|
|
618
|
-
* Keys are the real tool names; a unit test asserts `Object.values(BuiltinTools)`
|
|
619
|
-
* deep-equals `BUILTIN_TOOL_NAMES` so the two can never drift.
|
|
620
|
-
*/
|
|
621
|
-
export const BuiltinTools = {
|
|
622
|
-
bash: "bash",
|
|
623
|
-
read_file: "read_file",
|
|
624
|
-
write_file: "write_file",
|
|
625
|
-
edit_file: "edit_file",
|
|
626
|
-
grep: "grep",
|
|
627
|
-
glob: "glob",
|
|
628
|
-
head: "head",
|
|
629
|
-
tail: "tail",
|
|
630
|
-
todo_write: "todo_write",
|
|
631
|
-
subagent: "subagent",
|
|
632
|
-
subagent_result: "subagent_result",
|
|
633
|
-
web_fetch: "web_fetch",
|
|
634
|
-
web_search: "web_search",
|
|
635
|
-
bash_output: "bash_output",
|
|
636
|
-
bash_kill: "bash_kill",
|
|
637
|
-
code_execution: "code_execution",
|
|
638
|
-
wait: "wait",
|
|
639
|
-
git: "git",
|
|
640
|
-
ls: "ls",
|
|
641
|
-
stat: "stat",
|
|
642
|
-
wc: "wc"
|
|
643
|
-
};
|
|
644
|
-
/**
|
|
645
|
-
* The complete builtin set selected by `builtinTools: "default"`.
|
|
646
|
-
*/
|
|
647
|
-
export const DEFAULT_BUILTIN_TOOLS = BUILTIN_TOOL_NAMES;
|
|
648
|
-
// ---------------------------------------------------------------------------
|
|
649
|
-
// The default `skills` meta-tool
|
|
650
|
-
// ---------------------------------------------------------------------------
|
|
651
|
-
/**
|
|
652
|
-
* Fixed name of the single skills meta-tool. Deliberately NOT a member of
|
|
653
|
-
* {@link BUILTIN_TOOL_NAMES} (that closed set is the customer-cherry-pickable
|
|
654
|
-
* toggle surface, pinned equal to `HANDS_TOOLS`) — the skills tool is IMPLIED by
|
|
655
|
-
* a session having ≥1 skill, not chosen, and is injected platform-side. Kept in
|
|
656
|
-
* lockstep with {@link SKILL_RESERVED_NAMES} so it can never be shadowed by a
|
|
657
|
-
* custom tool or skill of the same name.
|
|
658
|
-
*/
|
|
659
|
-
export const SKILLS_TOOL_NAME = "skills";
|
|
660
|
-
/**
|
|
661
|
-
* The single default `skills` meta-tool (list/load) the platform injects when a
|
|
662
|
-
* session references ≥1 workspace skill. Shared by the platform tool composer and
|
|
663
|
-
* kept adjacent to the reserved-name guard so the model-visible contract and the
|
|
664
|
-
* name reservation stay in one place. It replaces the former N per-skill no-arg
|
|
665
|
-
* load-tools with one arg-taking dispatcher.
|
|
666
|
-
*/
|
|
667
|
-
export const SKILLS_TOOL_DEFINITION = {
|
|
668
|
-
name: "skills",
|
|
669
|
-
description: "List and load the workspace SKILLS available to this session. Call with {action:'list'} to see each skill's " +
|
|
670
|
-
"name + description (cheap; do this first). Call with {action:'load', name:'<skill>'} to read that skill's " +
|
|
671
|
-
"full SKILL.md instructions into context before doing work the skill governs. A skill's supporting files are " +
|
|
672
|
-
"already on disk under /workspace/skills/<name>/ — load pulls the instructions; read_file/bash read the rest.",
|
|
673
|
-
input_schema: {
|
|
674
|
-
type: "object",
|
|
675
|
-
properties: {
|
|
676
|
-
action: { type: "string", enum: ["list", "load"], description: "'list' all skills, or 'load' one by name." },
|
|
677
|
-
name: { type: "string", description: "Skill name to load (required when action='load')." }
|
|
678
|
-
},
|
|
679
|
-
required: ["action"],
|
|
680
|
-
additionalProperties: false
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
export function resolveBuiltinToolNames(selection = "default") {
|
|
684
|
-
if (selection === "default")
|
|
685
|
-
return DEFAULT_BUILTIN_TOOLS;
|
|
686
|
-
if (selection === "none")
|
|
687
|
-
return [];
|
|
688
|
-
const enabled = new Set();
|
|
689
|
-
for (const ref of selection) {
|
|
690
|
-
if (!BUILTIN_TOOL_NAMES.includes(ref)) {
|
|
691
|
-
throw new Error(`${JSON.stringify(ref)} is not a builtin tool; expected one of: ${BUILTIN_TOOL_NAMES.join(", ")}`);
|
|
692
|
-
}
|
|
693
|
-
enabled.add(ref);
|
|
694
|
-
}
|
|
695
|
-
return BUILTIN_TOOL_NAMES.filter((name) => enabled.has(name));
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Resolve a validated selection into canonical order.
|
|
699
|
-
*
|
|
700
|
-
* Membership in {@link BUILTIN_TOOL_NAMES} and the resulting order are
|
|
701
|
-
* `resolveBuiltinToolNames`' — a lookup plus a re-order, which is a transform,
|
|
702
|
-
* and against a list the schema cannot import without a cycle.
|
|
703
|
-
*/
|
|
704
|
-
function resolveBuiltinToolsSelection(input) {
|
|
705
|
-
if (input === undefined || input === null || input === "default")
|
|
706
|
-
return "default";
|
|
707
|
-
if (input === "none")
|
|
708
|
-
return "none";
|
|
709
|
-
return resolveBuiltinToolNames(input.map((value, index) => {
|
|
710
|
-
if (typeof value !== "string") {
|
|
711
|
-
throw new Error(`submission.builtinTools[${index}] must be a builtin tool name`);
|
|
712
|
-
}
|
|
713
|
-
return value;
|
|
714
|
-
}));
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Ceiling the platform will spend capturing files. A CLAMP, not a rejection —
|
|
718
|
-
* an over-large request is honoured at the maximum rather than refused — so it
|
|
719
|
-
* belongs here and not on the schema, which describes what is accepted.
|
|
720
|
-
*/
|
|
721
|
-
const MAX_FILE_CAPTURE_TIMEOUT_MS = 6 * 60 * 60 * 1000;
|
|
722
|
-
function normalizeFileCapture(value) {
|
|
723
|
-
if (value === undefined || value === null) {
|
|
724
|
-
return undefined;
|
|
725
|
-
}
|
|
726
|
-
const allowedDirs = normalizeCaptureDirs(value.allowedDirs);
|
|
727
|
-
const deniedDirs = normalizeCaptureDirs(value.deniedDirs);
|
|
728
|
-
const captureTimeoutMs = value.captureTimeoutMs === undefined
|
|
729
|
-
? undefined
|
|
730
|
-
: Math.min(value.captureTimeoutMs, MAX_FILE_CAPTURE_TIMEOUT_MS);
|
|
731
|
-
const { maxFileBytes, maxTotalBytes, maxFiles } = value;
|
|
732
|
-
if (!allowedDirs && !deniedDirs && captureTimeoutMs === undefined && maxFileBytes === undefined && maxTotalBytes === undefined && maxFiles === undefined) {
|
|
733
|
-
return undefined;
|
|
734
|
-
}
|
|
735
|
-
return {
|
|
736
|
-
...(allowedDirs ? { allowedDirs } : {}),
|
|
737
|
-
...(deniedDirs ? { deniedDirs } : {}),
|
|
738
|
-
...(captureTimeoutMs !== undefined ? { captureTimeoutMs } : {}),
|
|
739
|
-
...(maxFileBytes !== undefined ? { maxFileBytes } : {}),
|
|
740
|
-
...(maxTotalBytes !== undefined ? { maxTotalBytes } : {}),
|
|
741
|
-
...(maxFiles !== undefined ? { maxFiles } : {})
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Canonicalise a capture list and drop repeats.
|
|
746
|
-
*
|
|
747
|
-
* An empty array is treated as omission so the idempotency hash matches the
|
|
748
|
-
* "no list supplied" case. Only absolute entries are canonicalised: a denied
|
|
749
|
-
* pattern like `node_modules` or `*.tmp` is not a path and is kept verbatim.
|
|
750
|
-
*/
|
|
751
|
-
function normalizeCaptureDirs(entries) {
|
|
752
|
-
if (entries === undefined || entries.length === 0) {
|
|
753
|
-
return undefined;
|
|
754
|
-
}
|
|
755
|
-
const seen = new Set();
|
|
756
|
-
const normalised = [];
|
|
757
|
-
for (const entry of entries) {
|
|
758
|
-
const canonical = entry.startsWith("/") ? canonicalCapturePath(entry) : entry;
|
|
759
|
-
if (seen.has(canonical)) {
|
|
760
|
-
continue;
|
|
761
|
-
}
|
|
762
|
-
seen.add(canonical);
|
|
763
|
-
normalised.push(canonical);
|
|
764
|
-
}
|
|
765
|
-
return normalised;
|
|
766
|
-
}
|
|
767
|
-
/** Collapse repeated separators and drop a trailing slash; `/` stays `/`. */
|
|
768
|
-
function canonicalCapturePath(entry) {
|
|
769
|
-
const collapsed = entry
|
|
770
|
-
.split("/")
|
|
771
|
-
.filter((segment, index) => segment.length > 0 || index === 0)
|
|
772
|
-
.join("/");
|
|
773
|
-
const stripped = collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
774
|
-
return stripped.length === 0 ? "/" : stripped;
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Widen the single-string form to the one-element list the platform carries
|
|
778
|
-
* everywhere downstream. The emptiness and whitespace rules are the schema's.
|
|
779
|
-
*/
|
|
780
|
-
function normalizePrompt(input) {
|
|
781
|
-
return typeof input === "string" ? [input] : input;
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* Shared skill-name gate for {@link parseSkills}
|
|
785
|
-
* (and mirrored SDK-side in `Skill`): pattern + `__` MCP separator + reserved
|
|
786
|
-
* names (`skills`, `skill`).
|
|
787
|
-
*/
|
|
788
|
-
function assertValidSkillName(name, field) {
|
|
789
|
-
if (!SKILL_NAME_PATTERN.test(name)) {
|
|
790
|
-
throw new Error(`${field} must match ${SKILL_NAME_PATTERN.source}`);
|
|
791
|
-
}
|
|
792
|
-
if (name.includes("__")) {
|
|
793
|
-
throw new Error(`${field} must not contain "__"; that separator is reserved for MCP tools`);
|
|
794
|
-
}
|
|
795
|
-
if (SKILL_RESERVED_NAMES.has(name)) {
|
|
796
|
-
throw new Error(`${field} must not be a reserved skills name (${[...SKILL_RESERVED_NAMES].join(", ")})`);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
function parseMcpServers(input) {
|
|
800
|
-
if (input === undefined) {
|
|
801
|
-
return [];
|
|
802
|
-
}
|
|
803
|
-
const seen = new Set();
|
|
804
|
-
return input.map((item, index) => {
|
|
805
|
-
const ref = parseMcpServerRef(item, `submission.mcpServers[${index}]`);
|
|
806
|
-
if (seen.has(ref.name)) {
|
|
807
|
-
throw new Error(`submission.mcpServers duplicate name: ${ref.name}`);
|
|
808
|
-
}
|
|
809
|
-
seen.add(ref.name);
|
|
810
|
-
return ref;
|
|
811
|
-
});
|
|
812
|
-
}
|