@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.
Files changed (337) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +38 -0
  3. package/README.md +23 -31
  4. package/dist/client/aex.d.ts +33 -0
  5. package/dist/client/aex.js +98 -0
  6. package/dist/client/aex.js.map +1 -0
  7. package/dist/client/credentials.d.ts +25 -0
  8. package/dist/client/credentials.js +97 -0
  9. package/dist/client/credentials.js.map +1 -0
  10. package/dist/client/routing.d.ts +7 -0
  11. package/dist/client/routing.js +29 -0
  12. package/dist/client/routing.js.map +1 -0
  13. package/dist/downloads/download.d.ts +25 -0
  14. package/dist/downloads/download.js +53 -0
  15. package/dist/downloads/download.js.map +1 -0
  16. package/dist/generated/errors.d.ts +12 -0
  17. package/dist/generated/errors.js +81 -0
  18. package/dist/generated/errors.js.map +1 -0
  19. package/dist/generated/resources.d.ts +730 -0
  20. package/dist/generated/resources.js +606 -0
  21. package/dist/generated/resources.js.map +1 -0
  22. package/dist/generated/routes.d.ts +42 -0
  23. package/dist/generated/routes.js +2101 -0
  24. package/dist/generated/routes.js.map +1 -0
  25. package/dist/index.d.ts +20 -50
  26. package/dist/index.js +11 -62
  27. package/dist/index.js.map +1 -1
  28. package/dist/observations/stream.d.ts +1 -0
  29. package/dist/observations/stream.js +18 -0
  30. package/dist/observations/stream.js.map +1 -0
  31. package/dist/transport/errors.d.ts +60 -0
  32. package/dist/transport/errors.js +107 -0
  33. package/dist/transport/errors.js.map +1 -0
  34. package/dist/transport/pagination.d.ts +8 -0
  35. package/dist/transport/pagination.js +34 -0
  36. package/dist/transport/pagination.js.map +1 -0
  37. package/dist/transport/retry.d.ts +21 -0
  38. package/dist/transport/retry.js +37 -0
  39. package/dist/transport/retry.js.map +1 -0
  40. package/dist/transport/transport.d.ts +25 -0
  41. package/dist/transport/transport.js +28 -0
  42. package/dist/transport/transport.js.map +1 -0
  43. package/package.json +63 -30
  44. package/dist/_contracts/account-operations.d.ts +0 -101
  45. package/dist/_contracts/account-operations.js +0 -242
  46. package/dist/_contracts/account-types.d.ts +0 -461
  47. package/dist/_contracts/account-types.js +0 -1
  48. package/dist/_contracts/api-key.d.ts +0 -61
  49. package/dist/_contracts/api-key.js +0 -101
  50. package/dist/_contracts/api-routes.d.ts +0 -20
  51. package/dist/_contracts/api-routes.js +0 -109
  52. package/dist/_contracts/archive-limits.d.ts +0 -3
  53. package/dist/_contracts/archive-limits.js +0 -23
  54. package/dist/_contracts/asset-authoring.d.ts +0 -22
  55. package/dist/_contracts/asset-authoring.js +0 -106
  56. package/dist/_contracts/asset-bundle.d.ts +0 -64
  57. package/dist/_contracts/asset-bundle.js +0 -263
  58. package/dist/_contracts/asset-upload-helper.d.ts +0 -31
  59. package/dist/_contracts/asset-upload-helper.js +0 -84
  60. package/dist/_contracts/billing-admission.d.ts +0 -29
  61. package/dist/_contracts/billing-admission.js +0 -28
  62. package/dist/_contracts/bundle-manifest.d.ts +0 -89
  63. package/dist/_contracts/bundle-manifest.js +0 -158
  64. package/dist/_contracts/canonical-sha256.d.ts +0 -8
  65. package/dist/_contracts/canonical-sha256.js +0 -8
  66. package/dist/_contracts/connection-ticket.d.ts +0 -22
  67. package/dist/_contracts/connection-ticket.js +0 -54
  68. package/dist/_contracts/continuation-event.d.ts +0 -31
  69. package/dist/_contracts/continuation-event.js +0 -6
  70. package/dist/_contracts/contract-parse-error.d.ts +0 -12
  71. package/dist/_contracts/contract-parse-error.js +0 -51
  72. package/dist/_contracts/error-codes.d.ts +0 -26
  73. package/dist/_contracts/error-codes.js +0 -116
  74. package/dist/_contracts/error-factory.d.ts +0 -32
  75. package/dist/_contracts/error-factory.js +0 -174
  76. package/dist/_contracts/event-envelope.d.ts +0 -471
  77. package/dist/_contracts/event-envelope.js +0 -501
  78. package/dist/_contracts/event-stream-client.d.ts +0 -122
  79. package/dist/_contracts/event-stream-client.js +0 -445
  80. package/dist/_contracts/event-view.d.ts +0 -44
  81. package/dist/_contracts/event-view.js +0 -69
  82. package/dist/_contracts/failure-class.d.ts +0 -29
  83. package/dist/_contracts/failure-class.js +0 -73
  84. package/dist/_contracts/http.d.ts +0 -135
  85. package/dist/_contracts/http.js +0 -434
  86. package/dist/_contracts/ids.d.ts +0 -66
  87. package/dist/_contracts/ids.js +0 -119
  88. package/dist/_contracts/index.d.ts +0 -42
  89. package/dist/_contracts/index.js +0 -52
  90. package/dist/_contracts/internal.d.ts +0 -55
  91. package/dist/_contracts/internal.js +0 -113
  92. package/dist/_contracts/models.d.ts +0 -30
  93. package/dist/_contracts/models.js +0 -28
  94. package/dist/_contracts/operation-core.d.ts +0 -36
  95. package/dist/_contracts/operation-core.js +0 -70
  96. package/dist/_contracts/operations.d.ts +0 -218
  97. package/dist/_contracts/operations.js +0 -1496
  98. package/dist/_contracts/otlp-projection.d.ts +0 -78
  99. package/dist/_contracts/otlp-projection.js +0 -171
  100. package/dist/_contracts/post-hook.d.ts +0 -31
  101. package/dist/_contracts/post-hook.js +0 -61
  102. package/dist/_contracts/provider-fault.d.ts +0 -34
  103. package/dist/_contracts/provider-fault.js +0 -68
  104. package/dist/_contracts/retry-core.d.ts +0 -29
  105. package/dist/_contracts/retry-core.js +0 -79
  106. package/dist/_contracts/runner-event.d.ts +0 -117
  107. package/dist/_contracts/runner-event.js +0 -172
  108. package/dist/_contracts/runtime-kind.d.ts +0 -60
  109. package/dist/_contracts/runtime-kind.js +0 -70
  110. package/dist/_contracts/runtime-manifest.d.ts +0 -121
  111. package/dist/_contracts/runtime-manifest.js +0 -83
  112. package/dist/_contracts/runtime-security-profile.d.ts +0 -26
  113. package/dist/_contracts/runtime-security-profile.js +0 -73
  114. package/dist/_contracts/runtime-sizes.d.ts +0 -104
  115. package/dist/_contracts/runtime-sizes.js +0 -111
  116. package/dist/_contracts/runtime-types.d.ts +0 -618
  117. package/dist/_contracts/runtime-types.js +0 -58
  118. package/dist/_contracts/schemas/asset-bundle.d.ts +0 -70
  119. package/dist/_contracts/schemas/asset-bundle.js +0 -107
  120. package/dist/_contracts/schemas/asset-ref.d.ts +0 -61
  121. package/dist/_contracts/schemas/asset-ref.js +0 -118
  122. package/dist/_contracts/schemas/bundle-manifest.d.ts +0 -66
  123. package/dist/_contracts/schemas/bundle-manifest.js +0 -77
  124. package/dist/_contracts/schemas/index.d.ts +0 -32
  125. package/dist/_contracts/schemas/index.js +0 -30
  126. package/dist/_contracts/schemas/mcp-server.d.ts +0 -99
  127. package/dist/_contracts/schemas/mcp-server.js +0 -209
  128. package/dist/_contracts/schemas/models.d.ts +0 -29
  129. package/dist/_contracts/schemas/models.js +0 -51
  130. package/dist/_contracts/schemas/numeric.d.ts +0 -18
  131. package/dist/_contracts/schemas/numeric.js +0 -28
  132. package/dist/_contracts/schemas/post-hook.d.ts +0 -45
  133. package/dist/_contracts/schemas/post-hook.js +0 -68
  134. package/dist/_contracts/schemas/response-assets.d.ts +0 -75
  135. package/dist/_contracts/schemas/response-assets.js +0 -81
  136. package/dist/_contracts/schemas/response-billing.d.ts +0 -208
  137. package/dist/_contracts/schemas/response-billing.js +0 -139
  138. package/dist/_contracts/schemas/response-common.d.ts +0 -132
  139. package/dist/_contracts/schemas/response-common.js +0 -162
  140. package/dist/_contracts/schemas/response-identity.d.ts +0 -648
  141. package/dist/_contracts/schemas/response-identity.js +0 -131
  142. package/dist/_contracts/schemas/response-mcp-servers.d.ts +0 -51
  143. package/dist/_contracts/schemas/response-mcp-servers.js +0 -32
  144. package/dist/_contracts/schemas/response-secrets.d.ts +0 -50
  145. package/dist/_contracts/schemas/response-secrets.js +0 -32
  146. package/dist/_contracts/schemas/response-sessions-internal.d.ts +0 -200
  147. package/dist/_contracts/schemas/response-sessions-internal.js +0 -142
  148. package/dist/_contracts/schemas/response-sessions.d.ts +0 -1598
  149. package/dist/_contracts/schemas/response-sessions.js +0 -377
  150. package/dist/_contracts/schemas/response-webhooks.d.ts +0 -76
  151. package/dist/_contracts/schemas/response-webhooks.js +0 -42
  152. package/dist/_contracts/schemas/response-workspace.d.ts +0 -225
  153. package/dist/_contracts/schemas/response-workspace.js +0 -99
  154. package/dist/_contracts/schemas/runtime-kind.d.ts +0 -31
  155. package/dist/_contracts/schemas/runtime-kind.js +0 -29
  156. package/dist/_contracts/schemas/runtime-security-profile.d.ts +0 -28
  157. package/dist/_contracts/schemas/runtime-security-profile.js +0 -26
  158. package/dist/_contracts/schemas/runtime-sizes.d.ts +0 -70
  159. package/dist/_contracts/schemas/runtime-sizes.js +0 -127
  160. package/dist/_contracts/schemas/session-limits.d.ts +0 -34
  161. package/dist/_contracts/schemas/session-limits.js +0 -39
  162. package/dist/_contracts/schemas/session-machine.d.ts +0 -23
  163. package/dist/_contracts/schemas/session-machine.js +0 -24
  164. package/dist/_contracts/schemas/session-request-config.d.ts +0 -58
  165. package/dist/_contracts/schemas/session-request-config.js +0 -134
  166. package/dist/_contracts/schemas/session-webhook.d.ts +0 -11
  167. package/dist/_contracts/schemas/session-webhook.js +0 -38
  168. package/dist/_contracts/schemas/side-effect-audit.d.ts +0 -98
  169. package/dist/_contracts/schemas/side-effect-audit.js +0 -102
  170. package/dist/_contracts/schemas/submission-assets.d.ts +0 -117
  171. package/dist/_contracts/schemas/submission-assets.js +0 -147
  172. package/dist/_contracts/schemas/submission-body.d.ts +0 -251
  173. package/dist/_contracts/schemas/submission-body.js +0 -378
  174. package/dist/_contracts/schemas/submission-environment.d.ts +0 -79
  175. package/dist/_contracts/schemas/submission-environment.js +0 -179
  176. package/dist/_contracts/schemas/submission-request.d.ts +0 -158
  177. package/dist/_contracts/schemas/submission-request.js +0 -49
  178. package/dist/_contracts/schemas/submission-secrets.d.ts +0 -47
  179. package/dist/_contracts/schemas/submission-secrets.js +0 -108
  180. package/dist/_contracts/schemas/wire.d.ts +0 -118
  181. package/dist/_contracts/schemas/wire.js +0 -171
  182. package/dist/_contracts/schemas/workspace-resources.d.ts +0 -50
  183. package/dist/_contracts/schemas/workspace-resources.js +0 -87
  184. package/dist/_contracts/sdk-errors.d.ts +0 -212
  185. package/dist/_contracts/sdk-errors.js +0 -313
  186. package/dist/_contracts/sdk-secrets.d.ts +0 -67
  187. package/dist/_contracts/sdk-secrets.js +0 -427
  188. package/dist/_contracts/session-archive.d.ts +0 -16
  189. package/dist/_contracts/session-archive.js +0 -92
  190. package/dist/_contracts/session-artifacts.d.ts +0 -189
  191. package/dist/_contracts/session-artifacts.js +0 -264
  192. package/dist/_contracts/session-config.d.ts +0 -373
  193. package/dist/_contracts/session-config.js +0 -562
  194. package/dist/_contracts/session-cost-types.d.ts +0 -211
  195. package/dist/_contracts/session-cost-types.js +0 -69
  196. package/dist/_contracts/session-cost.d.ts +0 -8
  197. package/dist/_contracts/session-cost.js +0 -582
  198. package/dist/_contracts/session-custody.d.ts +0 -165
  199. package/dist/_contracts/session-custody.js +0 -345
  200. package/dist/_contracts/session-file-query.d.ts +0 -14
  201. package/dist/_contracts/session-file-query.js +0 -178
  202. package/dist/_contracts/session-record.d.ts +0 -112
  203. package/dist/_contracts/session-record.js +0 -165
  204. package/dist/_contracts/session-retention.d.ts +0 -201
  205. package/dist/_contracts/session-retention.js +0 -450
  206. package/dist/_contracts/side-effect-audit.d.ts +0 -126
  207. package/dist/_contracts/side-effect-audit.js +0 -520
  208. package/dist/_contracts/sse.d.ts +0 -74
  209. package/dist/_contracts/sse.js +0 -227
  210. package/dist/_contracts/stable.d.ts +0 -45
  211. package/dist/_contracts/stable.js +0 -62
  212. package/dist/_contracts/status.d.ts +0 -25
  213. package/dist/_contracts/status.js +0 -57
  214. package/dist/_contracts/submission-limits.d.ts +0 -61
  215. package/dist/_contracts/submission-limits.js +0 -60
  216. package/dist/_contracts/submission.d.ts +0 -547
  217. package/dist/_contracts/submission.js +0 -812
  218. package/dist/_contracts/suggest.d.ts +0 -15
  219. package/dist/_contracts/suggest.js +0 -53
  220. package/dist/_contracts/testing/response-bindings.d.ts +0 -45
  221. package/dist/_contracts/testing/response-bindings.js +0 -256
  222. package/dist/_contracts/testing/wire-conformance-entry.d.ts +0 -10
  223. package/dist/_contracts/testing/wire-conformance-entry.js +0 -8
  224. package/dist/_contracts/testing/wire-conformance.d.ts +0 -169
  225. package/dist/_contracts/testing/wire-conformance.js +0 -276
  226. package/dist/_contracts/turn-trace.d.ts +0 -28
  227. package/dist/_contracts/turn-trace.js +0 -1
  228. package/dist/_contracts/unknown-field-error.d.ts +0 -13
  229. package/dist/_contracts/unknown-field-error.js +0 -21
  230. package/dist/_contracts/value-guards.d.ts +0 -20
  231. package/dist/_contracts/value-guards.js +0 -34
  232. package/dist/_contracts/webhook-verify.d.ts +0 -34
  233. package/dist/_contracts/webhook-verify.js +0 -93
  234. package/dist/_contracts/wire-observer.d.ts +0 -49
  235. package/dist/_contracts/wire-observer.js +0 -34
  236. package/dist/_contracts/workflow-status.d.ts +0 -7
  237. package/dist/_contracts/workflow-status.js +0 -43
  238. package/dist/_contracts/workspace-resources.d.ts +0 -98
  239. package/dist/_contracts/workspace-resources.js +0 -39
  240. package/dist/archive-limits.d.ts +0 -1
  241. package/dist/archive-limits.js +0 -2
  242. package/dist/archive-limits.js.map +0 -1
  243. package/dist/asset-upload.d.ts +0 -47
  244. package/dist/asset-upload.js +0 -269
  245. package/dist/asset-upload.js.map +0 -1
  246. package/dist/bundle.d.ts +0 -9
  247. package/dist/bundle.js +0 -20
  248. package/dist/bundle.js.map +0 -1
  249. package/dist/canonical-zip.d.ts +0 -68
  250. package/dist/canonical-zip.js +0 -355
  251. package/dist/canonical-zip.js.map +0 -1
  252. package/dist/cli.mjs +0 -12048
  253. package/dist/cli.mjs.sha256 +0 -1
  254. package/dist/client-types.d.ts +0 -192
  255. package/dist/client-types.js +0 -2
  256. package/dist/client-types.js.map +0 -1
  257. package/dist/client.d.ts +0 -464
  258. package/dist/client.js +0 -1207
  259. package/dist/client.js.map +0 -1
  260. package/dist/event-projection.d.ts +0 -22
  261. package/dist/event-projection.js +0 -380
  262. package/dist/event-projection.js.map +0 -1
  263. package/dist/fetch-archive.d.ts +0 -16
  264. package/dist/fetch-archive.js +0 -252
  265. package/dist/fetch-archive.js.map +0 -1
  266. package/dist/file.d.ts +0 -96
  267. package/dist/file.js +0 -272
  268. package/dist/file.js.map +0 -1
  269. package/dist/instructions.d.ts +0 -20
  270. package/dist/instructions.js +0 -40
  271. package/dist/instructions.js.map +0 -1
  272. package/dist/legacy-session-provider-fault.d.ts +0 -7
  273. package/dist/legacy-session-provider-fault.js +0 -38
  274. package/dist/legacy-session-provider-fault.js.map +0 -1
  275. package/dist/mcp-server.d.ts +0 -84
  276. package/dist/mcp-server.js +0 -117
  277. package/dist/mcp-server.js.map +0 -1
  278. package/dist/node-fs.d.ts +0 -29
  279. package/dist/node-fs.js +0 -19
  280. package/dist/node-fs.js.map +0 -1
  281. package/dist/node-walk.d.ts +0 -69
  282. package/dist/node-walk.js +0 -151
  283. package/dist/node-walk.js.map +0 -1
  284. package/dist/path-basename.d.ts +0 -5
  285. package/dist/path-basename.js +0 -9
  286. package/dist/path-basename.js.map +0 -1
  287. package/dist/retry.d.ts +0 -70
  288. package/dist/retry.js +0 -155
  289. package/dist/retry.js.map +0 -1
  290. package/dist/secret.d.ts +0 -65
  291. package/dist/secret.js +0 -110
  292. package/dist/secret.js.map +0 -1
  293. package/dist/session-validate.d.ts +0 -100
  294. package/dist/session-validate.js +0 -303
  295. package/dist/session-validate.js.map +0 -1
  296. package/dist/skill.d.ts +0 -99
  297. package/dist/skill.js +0 -169
  298. package/dist/skill.js.map +0 -1
  299. package/dist/submission-wire.d.ts +0 -13
  300. package/dist/submission-wire.js +0 -69
  301. package/dist/submission-wire.js.map +0 -1
  302. package/dist/tool.d.ts +0 -41
  303. package/dist/tool.js +0 -76
  304. package/dist/tool.js.map +0 -1
  305. package/dist/version.d.ts +0 -9
  306. package/dist/version.js +0 -10
  307. package/dist/version.js.map +0 -1
  308. package/docs/authentication.md +0 -125
  309. package/docs/billing.md +0 -164
  310. package/docs/cleanup.md +0 -27
  311. package/docs/concepts/agent-tools.md +0 -47
  312. package/docs/concepts/composition.md +0 -60
  313. package/docs/concepts/providers-and-runtimes.md +0 -121
  314. package/docs/concepts/sessions.md +0 -51
  315. package/docs/concepts/subagents.md +0 -35
  316. package/docs/credentials.md +0 -116
  317. package/docs/defaults.md +0 -51
  318. package/docs/errors.md +0 -258
  319. package/docs/events.md +0 -143
  320. package/docs/files.md +0 -130
  321. package/docs/limits-and-quotas.md +0 -114
  322. package/docs/limits.md +0 -51
  323. package/docs/mcp.md +0 -47
  324. package/docs/networking.md +0 -114
  325. package/docs/provider-runtime-capabilities.md +0 -32
  326. package/docs/public-surface.json +0 -73
  327. package/docs/quickstart.md +0 -135
  328. package/docs/release.md +0 -44
  329. package/docs/retries.md +0 -108
  330. package/docs/secrets.md +0 -141
  331. package/docs/session-config.md +0 -51
  332. package/docs/session-record.md +0 -58
  333. package/docs/skills.md +0 -65
  334. package/docs/telemetry.md +0 -66
  335. package/docs/testing.md +0 -35
  336. package/docs/vision-skills.md +0 -94
  337. 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
- }