@aexhq/sdk 0.16.0 → 0.17.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.
@@ -20,6 +20,7 @@
20
20
  * detail response stays bounded). The archive zip carries the bytes.
21
21
  */
22
22
  import { parseMcpServerRef, parseSkillRef } from "./run-config.js";
23
+ import { RunModels, parseRunModel } from "./models.js";
23
24
  import { PLATFORM_PACKAGE_ECOSYSTEMS } from "./submission.js";
24
25
  // ---------------------------------------------------------------------------
25
26
  // Submission parser
@@ -29,10 +30,10 @@ import { PLATFORM_PACKAGE_ECOSYSTEMS } from "./submission.js";
29
30
  * submission. Never throws on minor unknown keys so we can
30
31
  * forward-compat with worker-side enrichment.
31
32
  *
32
- * Returns a typed shape even for malformed snapshots the worst case
33
- * is `{kind: "submission", submission: {model: "", ...}}` with empty
34
- * defaults — because the dashboard must still render *something* for a
35
- * buggy historical row rather than 500ing the whole detail page.
33
+ * Returns a typed shape even for malformed snapshots by falling back to the
34
+ * default public model and empty collection defaults, because the dashboard
35
+ * must still render *something* for a buggy historical row rather than 500ing
36
+ * the whole detail page.
36
37
  */
37
38
  export function parseRunUnitSubmission(input) {
38
39
  if (!input || typeof input !== "object" || Array.isArray(input)) {
@@ -52,7 +53,7 @@ function parseFlatProjection(value) {
52
53
  const allowedDirs = toOptionalStringArray(outputsRaw.allowedDirs);
53
54
  const deniedDirs = toOptionalStringArray(outputsRaw.deniedDirs);
54
55
  const submission = {
55
- model: typeof submissionRaw.model === "string" ? submissionRaw.model : "",
56
+ model: coerceRunUnitModel(submissionRaw.model),
56
57
  ...(typeof submissionRaw.system === "string" ? { system: submissionRaw.system } : {}),
57
58
  prompt: toStringArray(submissionRaw.prompt),
58
59
  skills: toSkillRefArray(submissionRaw.skills),
@@ -87,7 +88,7 @@ function fallbackFlat() {
87
88
  return {
88
89
  kind: "submission",
89
90
  submission: {
90
- model: "",
91
+ model: RunModels.CLAUDE_HAIKU_4_5,
91
92
  prompt: [],
92
93
  skills: [],
93
94
  agentsMd: [],
@@ -104,6 +105,16 @@ function fallbackFlat() {
104
105
  function isRecord(value) {
105
106
  return typeof value === "object" && value !== null && !Array.isArray(value);
106
107
  }
108
+ function coerceRunUnitModel(value) {
109
+ if (typeof value !== "string")
110
+ return RunModels.CLAUDE_HAIKU_4_5;
111
+ try {
112
+ return parseRunModel(value, "run unit submission.model");
113
+ }
114
+ catch {
115
+ return RunModels.CLAUDE_HAIKU_4_5;
116
+ }
117
+ }
107
118
  function isJsonRecord(value) {
108
119
  return isRecord(value);
109
120
  }
@@ -2,6 +2,8 @@ import { PROXY_ENDPOINT_DEFAULTS, type ProxyAuthShape, type ProxyMethod, type Pr
2
2
  export { PROXY_ENDPOINT_DEFAULTS };
3
3
  import type { AgentsMdRef, FileRef, McpServerRef, SkillRef } from "./run-config.js";
4
4
  import { type RuntimeSize } from "./runtime-sizes.js";
5
+ import { type PlatformPostHook, type PlatformPostHookInput } from "./post-hook.js";
6
+ import { type RunModel } from "./models.js";
5
7
  import { type RuntimeSecurityProfileName } from "./runtime-security-profile.js";
6
8
  import { type CredentialMode, type ManagedKeyPolicyV1 } from "./managed-key.js";
7
9
  export type JsonPrimitive = string | number | boolean | null;
@@ -226,7 +228,7 @@ export declare function optionalPositiveInt(input: unknown, field: string): numb
226
228
  * into `run_skill_snapshots`), provider refs pass through unchanged.
227
229
  */
228
230
  export interface PlatformSubmission {
229
- readonly model: string;
231
+ readonly model: RunModel;
230
232
  readonly system?: string;
231
233
  readonly prompt: readonly string[];
232
234
  readonly skills: readonly SkillRef[];
@@ -244,23 +246,22 @@ export interface PlatformSubmission {
244
246
  readonly outputs?: PlatformOutputCaptureConfig;
245
247
  /**
246
248
  * Optional override for the managed-runtime builtin extensions enabled
247
- * inside the runner container. Each entry is the bare extension name
248
- * accepted by the selected runtime. The platform
249
- * default is `["developer"]` which gives the agent shell + write +
250
- * edit + tree tools (bash, grep via shell, file read via shell or
251
- * editor, file edit). To opt in to more tools (e.g. web search via
252
- * the `computercontroller` extension), pass the full list. To opt
253
- * out of all builtins (pure-MCP setup), pass an empty array.
249
+ * inside the runner container. Each entry is one of the closed
250
+ * {@link BUILTINS} set (prefer the {@link Builtins} symbol
251
+ * const). The platform default is `["developer"]` which gives the agent
252
+ * shell + write + edit + tree tools (bash, grep via shell, file read via
253
+ * shell or editor, file edit). To opt in to more tools (e.g. web fetch via
254
+ * the `computercontroller` extension), pass the full list. To opt out of
255
+ * all builtins (pure-MCP setup), pass an empty array.
254
256
  *
255
257
  * Validation:
256
- * - Each entry matches /^[a-z][a-z0-9_-]{0,63}$/ (managed-runtime
257
- * builtin naming convention).
258
+ * - Each entry must be a member of {@link BUILTINS}.
258
259
  * - Max 16 entries.
259
260
  * - Deduplicated.
260
261
  *
261
262
  * The dispatcher accepts and persists it for snapshot fidelity.
262
263
  */
263
- readonly builtins?: readonly string[];
264
+ readonly builtins?: readonly Builtin[];
264
265
  /**
265
266
  * Assistant-output granularity. `buffered` (the default) emits one event per
266
267
  * assistant message; `stream` emits the agent's per-token text deltas as they
@@ -333,6 +334,13 @@ export interface PlatformRunSubmissionRequest {
333
334
  * terminal wait window and self-kill deadline.
334
335
  */
335
336
  readonly timeoutMs?: number;
337
+ /**
338
+ * Optional post-agent-run verifier. Parsed from the public `postHook`
339
+ * duration/string shape into fixed runner budgets. The runner executes it
340
+ * after a successful agent process and sends failures back through the model
341
+ * for repair until this budget is exhausted.
342
+ */
343
+ readonly postHook?: PlatformPostHook;
336
344
  }
337
345
  /**
338
346
  * Wire shape posted by the SDK and CLI. `workspaceId` is **omitted by
@@ -347,7 +355,7 @@ export interface PlatformRunSubmissionRequest {
347
355
  * {@link DEFAULT_RUN_PROVIDER} (`anthropic`). The parser fills it in
348
356
  * before the value enters the run snapshot.
349
357
  */
350
- export type PlatformRunSubmissionInput = Omit<PlatformRunSubmissionRequest, "workspaceId" | "credentialMode" | "provider" | "runtime" | "timeoutMs"> & {
358
+ export type PlatformRunSubmissionInput = Omit<PlatformRunSubmissionRequest, "workspaceId" | "credentialMode" | "provider" | "runtime" | "timeoutMs" | "postHook"> & {
351
359
  readonly workspaceId?: string;
352
360
  readonly credentialMode?: CredentialMode;
353
361
  readonly provider?: RunProvider;
@@ -363,6 +371,7 @@ export type PlatformRunSubmissionInput = Omit<PlatformRunSubmissionRequest, "wor
363
371
  * {@link PlatformRunSubmissionRequest.timeoutMs}. Absent ⇒ 1h default.
364
372
  */
365
373
  readonly timeout?: string;
374
+ readonly postHook?: PlatformPostHookInput;
366
375
  };
367
376
  export interface ParseRunSubmissionOptions {
368
377
  readonly managedKeyPolicy?: ManagedKeyPolicyV1;
@@ -386,6 +395,31 @@ export declare function parseSubmission(input: unknown): PlatformSubmission;
386
395
  export declare const OUTPUT_MODES: readonly ["buffered", "stream"];
387
396
  export type OutputMode = (typeof OUTPUT_MODES)[number];
388
397
  export declare const DEFAULT_OUTPUT_MODE: OutputMode;
398
+ /**
399
+ * Managed-runtime builtin extensions — the closed set the managed runtime
400
+ * accepts. Closed so an invalid name is a compile error via {@link Builtins},
401
+ * not a silent runtime no-op. `developer` is the platform default when
402
+ * `builtins` is omitted; pass an empty array to disable all builtins
403
+ * (pure-MCP setup).
404
+ */
405
+ export declare const BUILTINS: readonly ["developer", "computercontroller", "memory", "autovisualiser", "tutorial"];
406
+ export type Builtin = (typeof BUILTINS)[number];
407
+ /**
408
+ * Symbol-style accessors for the closed builtin set, e.g.
409
+ * `Builtins.COMPUTER_CONTROLLER`.
410
+ */
411
+ export declare const Builtins: {
412
+ /** Shell (bash + UNIX tools incl. grep), write, edit, tree. The default. */
413
+ readonly DEVELOPER: "developer";
414
+ /** Web fetch/scrape, scripting, general computer-control tools. */
415
+ readonly COMPUTER_CONTROLLER: "computercontroller";
416
+ /** Cross-session preference memory. */
417
+ readonly MEMORY: "memory";
418
+ /** Inline data-visualisation rendering. */
419
+ readonly AUTOVISUALISER: "autovisualiser";
420
+ /** Interactive guided tutorials. */
421
+ readonly TUTORIAL: "tutorial";
422
+ };
389
423
  /**
390
424
  * Codes emitted when a submission contains features the active runtime cannot
391
425
  * serve. Code values are stable so dashboard / SDK error rendering can branch
@@ -6,6 +6,8 @@ import { authShapeHeaderName, PROXY_ALLOWED_METHODS, PROXY_ENDPOINT_DEFAULTS, PR
6
6
  export { PROXY_ENDPOINT_DEFAULTS };
7
7
  import { parseAssetRefFields, parseMcpServerRef, parseSkillRef } from "./run-config.js";
8
8
  import { parseRunTimeout, parseRuntimeSize } from "./runtime-sizes.js";
9
+ import { parsePostHook } from "./post-hook.js";
10
+ import { assertRunModelMatchesProvider, parseRunModel } from "./models.js";
9
11
  import { parseRuntimeSecurityProfile } from "./runtime-security-profile.js";
10
12
  import { assertManagedKeyAdmissionAllowed, parseCredentialMode } from "./managed-key.js";
11
13
  /**
@@ -782,6 +784,7 @@ export function parseRunSubmissionRequest(input, options = {}) {
782
784
  "submission",
783
785
  "runtimeSize",
784
786
  "timeout",
787
+ "postHook",
785
788
  "proxyEndpoints",
786
789
  SECRETS_KEY
787
790
  ]);
@@ -815,11 +818,13 @@ export function parseRunSubmissionRequest(input, options = {}) {
815
818
  }
816
819
  const runtimeSize = parseRuntimeSize(value.runtimeSize);
817
820
  const timeoutMs = parseRunTimeout(value.timeout);
821
+ const postHook = parsePostHook(value.postHook, "submission.postHook");
818
822
  const proxyEndpoints = parseProxyEndpoints(value.proxyEndpoints);
819
823
  const secrets = parseInlineSecrets(value.secrets);
820
824
  enforceCredentialSecretPolicy(credentialMode, secrets);
821
825
  crossValidateProxyEndpointsAndAuth(proxyEndpoints, secrets.proxyEndpointAuth);
822
826
  const submission = parseSubmission(value.submission);
827
+ assertRunModelMatchesProvider(provider, submission.model);
823
828
  // mcpServers names must agree across the submission half and the
824
829
  // secrets half — every secrets.mcpServers[i].name MUST resolve to a
825
830
  // submission.mcpServers entry (no orphan secrets) AND the URL must
@@ -861,6 +866,7 @@ export function parseRunSubmissionRequest(input, options = {}) {
861
866
  submission,
862
867
  ...(runtimeSize ? { runtimeSize } : {}),
863
868
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
869
+ ...(postHook !== undefined ? { postHook } : {}),
864
870
  ...(proxyEndpoints ? { proxyEndpoints } : {}),
865
871
  secrets
866
872
  };
@@ -927,7 +933,7 @@ export function parseSubmission(input) {
927
933
  throw new Error(`submission.${key} is not an allowed field; permitted: ${[...allowed].join(", ")}`);
928
934
  }
929
935
  }
930
- const model = requireString(value.model, "submission.model");
936
+ const model = parseRunModel(value.model, "submission.model");
931
937
  const system = optionalString(value.system, "submission.system");
932
938
  const prompt = parsePrompt(value.prompt);
933
939
  const skills = parseSkills(value.skills);
@@ -985,7 +991,36 @@ function parseOutputMode(input) {
985
991
  }
986
992
  return input;
987
993
  }
988
- const BUILTIN_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
994
+ /**
995
+ * Managed-runtime builtin extensions — the closed set the managed runtime
996
+ * accepts. Closed so an invalid name is a compile error via {@link Builtins},
997
+ * not a silent runtime no-op. `developer` is the platform default when
998
+ * `builtins` is omitted; pass an empty array to disable all builtins
999
+ * (pure-MCP setup).
1000
+ */
1001
+ export const BUILTINS = [
1002
+ "developer",
1003
+ "computercontroller",
1004
+ "memory",
1005
+ "autovisualiser",
1006
+ "tutorial"
1007
+ ];
1008
+ /**
1009
+ * Symbol-style accessors for the closed builtin set, e.g.
1010
+ * `Builtins.COMPUTER_CONTROLLER`.
1011
+ */
1012
+ export const Builtins = {
1013
+ /** Shell (bash + UNIX tools incl. grep), write, edit, tree. The default. */
1014
+ DEVELOPER: "developer",
1015
+ /** Web fetch/scrape, scripting, general computer-control tools. */
1016
+ COMPUTER_CONTROLLER: "computercontroller",
1017
+ /** Cross-session preference memory. */
1018
+ MEMORY: "memory",
1019
+ /** Inline data-visualisation rendering. */
1020
+ AUTOVISUALISER: "autovisualiser",
1021
+ /** Interactive guided tutorials. */
1022
+ TUTORIAL: "tutorial"
1023
+ };
989
1024
  const MAX_BUILTINS = 16;
990
1025
  function parseBuiltins(input) {
991
1026
  if (input === undefined || input === null)
@@ -1003,8 +1038,9 @@ function parseBuiltins(input) {
1003
1038
  if (typeof v !== "string") {
1004
1039
  throw new Error(`submission.builtins[${i}] must be a string`);
1005
1040
  }
1006
- if (!BUILTIN_NAME_PATTERN.test(v)) {
1007
- throw new Error(`submission.builtins[${i}] (${JSON.stringify(v)}) is not a valid managed-runtime builtin name; expected /^[a-z][a-z0-9_-]{0,63}$/`);
1041
+ if (!BUILTINS.includes(v)) {
1042
+ throw new Error(`submission.builtins[${i}] (${JSON.stringify(v)}) is not a managed-runtime builtin; ` +
1043
+ `expected one of: ${BUILTINS.join(", ")}`);
1008
1044
  }
1009
1045
  if (seen.has(v))
1010
1046
  continue; // dedupe silently
package/dist/cli.mjs CHANGED
@@ -119,6 +119,94 @@ var PROVIDER_PUBLIC_SUPPORT = {
119
119
  }
120
120
  };
121
121
 
122
+ // ../contracts/dist/models.js
123
+ var RUN_MODELS = [
124
+ "claude-haiku-4-5",
125
+ "claude-3-5-haiku-latest",
126
+ "claude-3-5-sonnet-latest",
127
+ "deepseek-v4-flash",
128
+ "deepseek-v4-pro",
129
+ "deepseek-chat",
130
+ "deepseek-reasoner",
131
+ "gpt-4.1",
132
+ "gpt-4o-mini",
133
+ "gemini-2.0-flash",
134
+ "gemini-2.5-flash",
135
+ "mistral-large-latest",
136
+ "mistral-small-latest"
137
+ ];
138
+ var Models = {
139
+ /** Claude Haiku 4.5 — Anthropic. */
140
+ CLAUDE_HAIKU_4_5: "claude-haiku-4-5",
141
+ /** Claude 3.5 Haiku (latest) — Anthropic. */
142
+ CLAUDE_3_5_HAIKU_LATEST: "claude-3-5-haiku-latest",
143
+ /** Claude 3.5 Sonnet (latest) — Anthropic. */
144
+ CLAUDE_3_5_SONNET_LATEST: "claude-3-5-sonnet-latest",
145
+ /** DeepSeek V4 Flash — DeepSeek (non-thinking, fast). */
146
+ DEEPSEEK_V4_FLASH: "deepseek-v4-flash",
147
+ /** DeepSeek V4 Pro — DeepSeek (reasoning-heavy). */
148
+ DEEPSEEK_V4_PRO: "deepseek-v4-pro",
149
+ /**
150
+ * @deprecated Legacy alias DeepSeek routes to `deepseek-v4-flash` (non-thinking).
151
+ * DeepSeek removes this name on 2026-07-24 15:59 UTC — migrate to
152
+ * {@link Models.DEEPSEEK_V4_FLASH}.
153
+ */
154
+ DEEPSEEK_CHAT: "deepseek-chat",
155
+ /**
156
+ * @deprecated Legacy alias DeepSeek routes to `deepseek-v4-flash` (thinking).
157
+ * DeepSeek removes this name on 2026-07-24 15:59 UTC — migrate to
158
+ * {@link Models.DEEPSEEK_V4_FLASH} (or {@link Models.DEEPSEEK_V4_PRO} for
159
+ * heavier reasoning).
160
+ */
161
+ DEEPSEEK_REASONER: "deepseek-reasoner",
162
+ /** GPT-4.1 — OpenAI. */
163
+ GPT_4_1: "gpt-4.1",
164
+ /** GPT-4o mini — OpenAI. */
165
+ GPT_4O_MINI: "gpt-4o-mini",
166
+ /** Gemini 2.0 Flash — Gemini. */
167
+ GEMINI_2_0_FLASH: "gemini-2.0-flash",
168
+ /** Gemini 2.5 Flash — Gemini. */
169
+ GEMINI_2_5_FLASH: "gemini-2.5-flash",
170
+ /** Mistral Large (latest) — Mistral. */
171
+ MISTRAL_LARGE_LATEST: "mistral-large-latest",
172
+ /** Mistral Small (latest) — Mistral. */
173
+ MISTRAL_SMALL_LATEST: "mistral-small-latest"
174
+ };
175
+ var RUN_MODELS_BY_PROVIDER = {
176
+ anthropic: [
177
+ Models.CLAUDE_HAIKU_4_5,
178
+ Models.CLAUDE_3_5_HAIKU_LATEST,
179
+ Models.CLAUDE_3_5_SONNET_LATEST
180
+ ],
181
+ deepseek: [
182
+ Models.DEEPSEEK_V4_FLASH,
183
+ Models.DEEPSEEK_V4_PRO,
184
+ Models.DEEPSEEK_CHAT,
185
+ Models.DEEPSEEK_REASONER
186
+ ],
187
+ openai: [Models.GPT_4_1, Models.GPT_4O_MINI],
188
+ gemini: [Models.GEMINI_2_0_FLASH, Models.GEMINI_2_5_FLASH],
189
+ mistral: [Models.MISTRAL_LARGE_LATEST, Models.MISTRAL_SMALL_LATEST]
190
+ };
191
+ var PROVIDER_BY_MODEL = (() => {
192
+ const map = {};
193
+ for (const [provider, models] of Object.entries(RUN_MODELS_BY_PROVIDER)) {
194
+ for (const model of models) {
195
+ map[model] = provider;
196
+ }
197
+ }
198
+ return map;
199
+ })();
200
+ function isRunModel(input) {
201
+ return typeof input === "string" && RUN_MODELS.includes(input);
202
+ }
203
+ function parseRunModel(input, field = "submission.model") {
204
+ if (!isRunModel(input)) {
205
+ throw new Error(`${field} must be one of: ${RUN_MODELS.join(", ")}`);
206
+ }
207
+ return input;
208
+ }
209
+
122
210
  // ../contracts/dist/status.js
123
211
  var TERMINAL_RUN_STATUSES = [
124
212
  "succeeded",
@@ -131,15 +219,113 @@ var TERMINAL_RUN_STATUSES = [
131
219
  ];
132
220
  var terminalRunStatuses = new Set(TERMINAL_RUN_STATUSES);
133
221
 
222
+ // ../contracts/dist/runtime-sizes.js
223
+ var RUNTIME_SIZE_PRESETS = {
224
+ "shared-1x-256mb": { cpus: 1, memoryMb: 256 },
225
+ "shared-1x-512mb": { cpus: 1, memoryMb: 512 },
226
+ "shared-1x-1gb": { cpus: 1, memoryMb: 1024 },
227
+ "shared-1x-2gb": { cpus: 1, memoryMb: 2048 },
228
+ "shared-2x-512mb": { cpus: 2, memoryMb: 512 },
229
+ "shared-2x-1gb": { cpus: 2, memoryMb: 1024 },
230
+ "shared-2x-2gb": { cpus: 2, memoryMb: 2048 },
231
+ "shared-2x-4gb": { cpus: 2, memoryMb: 4096 },
232
+ "shared-4x-1gb": { cpus: 4, memoryMb: 1024 },
233
+ "shared-4x-2gb": { cpus: 4, memoryMb: 2048 },
234
+ "shared-4x-4gb": { cpus: 4, memoryMb: 4096 },
235
+ "shared-4x-8gb": { cpus: 4, memoryMb: 8192 },
236
+ "shared-8x-2gb": { cpus: 8, memoryMb: 2048 },
237
+ "shared-8x-4gb": { cpus: 8, memoryMb: 4096 },
238
+ "shared-8x-8gb": { cpus: 8, memoryMb: 8192 },
239
+ "shared-8x-16gb": { cpus: 8, memoryMb: 16384 }
240
+ };
241
+ var RUNTIME_SIZES = Object.keys(RUNTIME_SIZE_PRESETS);
242
+ var DEFAULT_RUN_TIMEOUT_MS = 60 * 60 * 1e3;
243
+ var MAX_RUN_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
244
+ var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
245
+ var DURATION_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/;
246
+ function parseDurationToMs(input) {
247
+ const match = DURATION_PATTERN.exec(input.trim());
248
+ if (!match) {
249
+ throw new Error(`invalid duration ${JSON.stringify(input)} (expected e.g. "1h", "90m", "30s", "500ms", or a bare ms integer)`);
250
+ }
251
+ const value = Number(match[1]);
252
+ if (!Number.isFinite(value) || value < 0) {
253
+ throw new Error(`invalid duration ${JSON.stringify(input)} (must be a non-negative number)`);
254
+ }
255
+ const unit = match[2] ?? "ms";
256
+ const factor = unit === "h" ? 36e5 : unit === "m" ? 6e4 : unit === "s" ? 1e3 : 1;
257
+ return Math.round(value * factor);
258
+ }
259
+ var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
260
+ var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
261
+
262
+ // ../contracts/dist/post-hook.js
263
+ var DEFAULT_POST_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
264
+ var DEFAULT_POST_HOOK_MAX_TURNS = 10;
265
+ function parsePostHook(input, path = "postHook") {
266
+ if (input === void 0 || input === null) {
267
+ return void 0;
268
+ }
269
+ const value = requirePostHookRecord(input, path);
270
+ const allowed = /* @__PURE__ */ new Set(["command", "timeout", "maxTurns", "maxChars"]);
271
+ for (const key of Object.keys(value)) {
272
+ if (!allowed.has(key)) {
273
+ throw new Error(`${path}.${key} is not an allowed field; permitted: command, timeout, maxTurns, maxChars`);
274
+ }
275
+ }
276
+ if (typeof value.command !== "string") {
277
+ throw new Error(`${path}.command must be a string`);
278
+ }
279
+ if (value.command.trim().length === 0) {
280
+ return void 0;
281
+ }
282
+ const timeoutMs = parsePostHookTimeout(value.timeout, `${path}.timeout`);
283
+ const maxTurns = parseNonNegativeInteger(value.maxTurns, `${path}.maxTurns`, DEFAULT_POST_HOOK_MAX_TURNS);
284
+ const maxChars = value.maxChars === null ? null : parseNonNegativeInteger(value.maxChars, `${path}.maxChars`, null);
285
+ return {
286
+ command: value.command,
287
+ timeoutMs,
288
+ maxTurns,
289
+ maxChars
290
+ };
291
+ }
292
+ function parsePostHookTimeout(input, path) {
293
+ if (input === void 0) {
294
+ return DEFAULT_POST_HOOK_TIMEOUT_MS;
295
+ }
296
+ if (typeof input !== "string") {
297
+ throw new Error(`${path} must be a duration string (e.g. "5m", "30s"); got ${JSON.stringify(input)}`);
298
+ }
299
+ const ms = parseDurationToMs(input);
300
+ if (ms <= 0) {
301
+ throw new Error(`${path} must be greater than 0ms; got ${ms}ms`);
302
+ }
303
+ return ms;
304
+ }
305
+ function parseNonNegativeInteger(input, path, defaultValue) {
306
+ if (input === void 0) {
307
+ return defaultValue;
308
+ }
309
+ if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0) {
310
+ throw new Error(`${path} must be a non-negative integer`);
311
+ }
312
+ return input;
313
+ }
314
+ function requirePostHookRecord(input, path) {
315
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
316
+ throw new Error(`${path} must be an object`);
317
+ }
318
+ return input;
319
+ }
320
+
134
321
  // ../contracts/dist/run-config.js
135
322
  var SKILL_BUNDLE_LIMITS = {
136
323
  /** Compressed (.zip) ceiling. */
137
324
  maxCompressedBytes: 10 * 1024 * 1024,
138
325
  /**
139
- * Hard ceiling for the direct-to-storage (presigned PUT) upload path, where
140
- * bytes never transit the hosted API so its memory/request-payload limits no
141
- * longer cap the bundle. Kept well under the object store's 5 GiB single-PUT
142
- * limit; objects above this would need S3 multipart, which is out of scope.
326
+ * Hard ceiling for the direct-to-storage upload path. Bytes never transit the
327
+ * hosted API, so its memory/request-payload limits do not cap accepted
328
+ * bundles; objects above this product cap are rejected before upload.
143
329
  */
144
330
  maxBytes: 2 * 1024 * 1024 * 1024,
145
331
  /** Sum of uncompressed file sizes. */
@@ -446,6 +632,7 @@ function parseRunRequestConfig(input) {
446
632
  "environment",
447
633
  "runtimeSize",
448
634
  "timeout",
635
+ "postHook",
449
636
  "proxyEndpoints",
450
637
  "metadata"
451
638
  ]);
@@ -454,10 +641,7 @@ function parseRunRequestConfig(input) {
454
641
  throw new Error(`run request config contains unexpected field: ${key}`);
455
642
  }
456
643
  }
457
- const model = record.model;
458
- if (typeof model !== "string" || model.length === 0) {
459
- throw new Error("run request config model must be a non-empty string");
460
- }
644
+ const model = parseRunModel(record.model, "run request config model");
461
645
  const system = record.system;
462
646
  if (system !== void 0 && typeof system !== "string") {
463
647
  throw new Error("run request config system, when provided, must be a string");
@@ -465,6 +649,7 @@ function parseRunRequestConfig(input) {
465
649
  const prompt = parseRunRequestConfigPrompt(record.prompt);
466
650
  const skills = parseRunRequestConfigSkills(record.skills);
467
651
  const mcpServers = parseRunRequestConfigMcpServers(record.mcpServers);
652
+ const postHook = parsePostHook(record.postHook, "run request config postHook");
468
653
  return {
469
654
  model,
470
655
  ...system !== void 0 ? { system } : {},
@@ -478,6 +663,7 @@ function parseRunRequestConfig(input) {
478
663
  ...record.environment !== void 0 ? { environment: record.environment } : {},
479
664
  ...record.runtimeSize !== void 0 ? { runtimeSize: record.runtimeSize } : {},
480
665
  ...record.timeout !== void 0 ? { timeout: record.timeout } : {},
666
+ ...postHook !== void 0 ? { postHook: record.postHook } : {},
481
667
  ...record.proxyEndpoints !== void 0 ? { proxyEndpoints: record.proxyEndpoints } : {},
482
668
  ...record.metadata !== void 0 ? { metadata: record.metadata } : {}
483
669
  };
@@ -532,32 +718,6 @@ function parseRunRequestConfigMcpServers(value) {
532
718
  });
533
719
  }
534
720
 
535
- // ../contracts/dist/runtime-sizes.js
536
- var RUNTIME_SIZE_PRESETS = {
537
- "shared-1x-256mb": { cpus: 1, memoryMb: 256 },
538
- "shared-1x-512mb": { cpus: 1, memoryMb: 512 },
539
- "shared-1x-1gb": { cpus: 1, memoryMb: 1024 },
540
- "shared-1x-2gb": { cpus: 1, memoryMb: 2048 },
541
- "shared-2x-512mb": { cpus: 2, memoryMb: 512 },
542
- "shared-2x-1gb": { cpus: 2, memoryMb: 1024 },
543
- "shared-2x-2gb": { cpus: 2, memoryMb: 2048 },
544
- "shared-2x-4gb": { cpus: 2, memoryMb: 4096 },
545
- "shared-4x-1gb": { cpus: 4, memoryMb: 1024 },
546
- "shared-4x-2gb": { cpus: 4, memoryMb: 2048 },
547
- "shared-4x-4gb": { cpus: 4, memoryMb: 4096 },
548
- "shared-4x-8gb": { cpus: 4, memoryMb: 8192 },
549
- "shared-8x-2gb": { cpus: 8, memoryMb: 2048 },
550
- "shared-8x-4gb": { cpus: 8, memoryMb: 4096 },
551
- "shared-8x-8gb": { cpus: 8, memoryMb: 8192 },
552
- "shared-8x-16gb": { cpus: 8, memoryMb: 16384 }
553
- };
554
- var RUNTIME_SIZES = Object.keys(RUNTIME_SIZE_PRESETS);
555
- var DEFAULT_RUN_TIMEOUT_MS = 60 * 60 * 1e3;
556
- var MAX_RUN_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
557
- var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
558
- var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
559
- var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
560
-
561
721
  // ../contracts/dist/runtime-security-profile.js
562
722
  var RUNTIME_SECURITY_PROFILE_CONFIG = Object.freeze({
563
723
  strict: Object.freeze({
@@ -3052,6 +3212,11 @@ async function runRunCmd(io2, argv) {
3052
3212
  io2.stderr("--model is required when --config is not provided\n");
3053
3213
  return USAGE_ERR;
3054
3214
  }
3215
+ if (!RUN_MODELS.includes(modelFlag.value)) {
3216
+ io2.stderr(`--model must be one of: ${RUN_MODELS.join(", ")} (got: ${modelFlag.value})
3217
+ `);
3218
+ return USAGE_ERR;
3219
+ }
3055
3220
  if (promptFlags.values.length === 0) {
3056
3221
  io2.stderr("--prompt is required (repeatable)\n");
3057
3222
  return USAGE_ERR;
@@ -3180,6 +3345,7 @@ async function runRunCmd(io2, argv) {
3180
3345
  secrets,
3181
3346
  ...runtimeSizeFlag.value ? { runtimeSize: runtimeSizeFlag.value } : runConfig.runtimeSize ? { runtimeSize: runConfig.runtimeSize } : {},
3182
3347
  ...runTimeoutFlag.value ? { timeout: runTimeoutFlag.value } : runConfig.timeout ? { timeout: runConfig.timeout } : {},
3348
+ ...runConfig.postHook ? { postHook: runConfig.postHook } : {},
3183
3349
  ...proxyEndpoints.length > 0 ? { proxyEndpoints } : {}
3184
3350
  };
3185
3351
  const http = makeHttpClient(io2, common.flags);
@@ -4074,7 +4240,7 @@ Protocol version: ${manifest.protocolVersion}
4074
4240
  io2.stdout(" aex wait <run-id> [--timeout 8m] [--interval 2s] --api-token T\n");
4075
4241
  io2.stdout(" aex events <run-id> [--follow] [--timeout 8m] --api-token T\n");
4076
4242
  io2.stdout(" aex outputs <run-id> --api-token T\n");
4077
- io2.stdout(" aex download <run-id> [--out path] --api-token T\n");
4243
+ io2.stdout(" aex download <run-id> [--only outputs|events|metadata] [--out path] --api-token T\n");
4078
4244
  io2.stdout(" aex cancel <run-id> --api-token T\n");
4079
4245
  io2.stdout(" aex delete <run-id> --api-token T\n");
4080
4246
  io2.stdout(" aex delete-asset <assetId|hash> --api-token T\n");
@@ -1 +1 @@
1
- 8fc0f76c60d859e98d5f41fc2d6f2195fbb10b1ece6de8275ab3d3f27518c202 cli.mjs
1
+ 8bfa8cd36b90d39d27799f8919e6d19de682c6e6fe5153bd1116bc80a228c7cc cli.mjs
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HttpClient, type AexEvent, type AgentsMdRecord, type CredentialMode, type DebugSink, type FetchLike, type FileRecord, type Output, type OutputMode, type PlatformEnvironmentInput, type PlatformSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type Run, type RunEvent, type RunProvider, type RunUnit, type RuntimeSize, type RuntimeKind, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_contracts/index.js";
1
+ import { HttpClient, type AexEvent, type AgentsMdRecord, type CredentialMode, type DebugSink, type FetchLike, type FileRecord, type Output, type OutputMode, type PlatformEnvironmentInput, type PlatformSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type PlatformPostHookInput, type Run, type RunModel, type RunEvent, type RunProvider, type RunUnit, type Builtin, type RuntimeSize, type RuntimeKind, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_contracts/index.js";
2
2
  import { AgentsMd } from "./agents-md.js";
3
3
  import { type UploadedAsset } from "./asset-upload.js";
4
4
  import { File } from "./file.js";
@@ -54,10 +54,11 @@ export interface SubmitRunOptions {
54
54
  */
55
55
  readonly credentialMode?: CredentialMode;
56
56
  /**
57
- * Provider selector. Optionaldefaults to
58
- * {@link DEFAULT_RUN_PROVIDER} (`"anthropic"`). Selects which upstream
59
- * model route the managed provider-proxy uses; the BYOK key for it is
60
- * supplied as `secrets.apiKey`.
57
+ * Provider selector. Normally OMITTED the provider is a pure function of
58
+ * `model` and is derived automatically, so the model alone determines which
59
+ * upstream the managed provider-proxy routes to (the BYOK key for it is
60
+ * supplied as `secrets.apiKey`). Pass this only to be explicit; if supplied
61
+ * it MUST match the model's provider or `submitRun` throws.
61
62
  */
62
63
  readonly provider?: RunProvider;
63
64
  /**
@@ -66,7 +67,12 @@ export interface SubmitRunOptions {
66
67
  * is no longer accepted.
67
68
  */
68
69
  readonly runtime?: RuntimeKind;
69
- readonly model: string;
70
+ /**
71
+ * Closed public model id. Prefer the {@link Models} symbol const, e.g.
72
+ * `Models.CLAUDE_HAIKU_4_5`. The model fully determines the upstream
73
+ * `provider`, so you never pass `provider` alongside it.
74
+ */
75
+ readonly model: RunModel;
70
76
  readonly system?: string;
71
77
  readonly prompt: string | readonly string[];
72
78
  readonly skills?: readonly Skill[];
@@ -86,6 +92,12 @@ export interface SubmitRunOptions {
86
92
  * [1m, 6h]; omit for the 1h default. Applies to both runtimes.
87
93
  */
88
94
  readonly timeout?: string;
95
+ /**
96
+ * Command to run after the agent process exits successfully. A non-zero exit
97
+ * or timeout is sent back to the model as a repair prompt until `maxTurns`
98
+ * is exhausted. Empty commands are treated as omitted.
99
+ */
100
+ readonly postHook?: PlatformPostHookInput;
89
101
  readonly proxyEndpoints?: readonly ProxyEndpoint[];
90
102
  /**
91
103
  * Output capture policy for the run's output files.
@@ -105,20 +117,23 @@ export interface SubmitRunOptions {
105
117
  };
106
118
  /**
107
119
  * Override the managed runtime builtin extensions enabled inside the runner.
120
+ * Each entry is one of the closed {@link Builtin} set — prefer the
121
+ * {@link Builtins} symbol const so a typo is a compile error.
108
122
  *
109
- * - Omitted (default): the runner enables `["developer"]` which gives
110
- * the agent `shell`, `write`, `edit`, and `tree` tools (bash, grep
111
- * via shell, file read via shell or editor, file edit).
123
+ * - Omitted (default): the runner enables `[Builtins.DEVELOPER]`
124
+ * which gives the agent `shell`, `write`, `edit`, and `tree` tools (bash,
125
+ * grep via shell, file read via shell or editor, file edit).
112
126
  * - Empty array: the agent runs with zero builtin extensions —
113
127
  * useful for pure-MCP setups where every tool comes from a
114
128
  * submitted `mcpServers` entry.
115
- * - Custom list: e.g. `["developer", "computercontroller"]` to add
116
- * web search alongside the default shell/edit toolkit.
129
+ * - Custom list: e.g.
130
+ * `[Builtins.DEVELOPER, Builtins.COMPUTER_CONTROLLER]` to add
131
+ * web fetch/scrape alongside the default shell/edit toolkit.
117
132
  *
118
- * Validation: each entry matches `/^[a-z][a-z0-9_-]{0,63}$/`, max 16
133
+ * Validation: each entry must be a member of {@link Builtins}, max 16
119
134
  * entries, deduplicated server-side.
120
135
  */
121
- readonly builtins?: readonly string[];
136
+ readonly builtins?: readonly Builtin[];
122
137
  /**
123
138
  * Assistant-output granularity. `"buffered"` (default) delivers one event per
124
139
  * assistant message; `"stream"` delivers per-token text deltas for live