@aexhq/sdk 0.40.13 → 0.40.15

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.
@@ -19,9 +19,10 @@ export interface HttpClientOptions {
19
19
  /** When set, every request emits a redacted one-line trace here. */
20
20
  readonly debug?: DebugSink;
21
21
  /**
22
- * Retry transient transport failures for idempotent GET/HEAD requests.
22
+ * Retry transient transport failures for idempotent requests.
23
23
  * Disabled by default; host CLIs enable this so a single dropped API
24
- * connection does not fail read-only commands.
24
+ * connection does not fail read-only commands or billable writes carrying
25
+ * an `Idempotency-Key`.
25
26
  */
26
27
  readonly retryTransientGets?: boolean | TransientGetRetryOptions;
27
28
  }
@@ -55,7 +55,7 @@ export class HttpClient {
55
55
  }
56
56
  }
57
57
  const method = methodOf(init.method);
58
- const retry = retryForMethod(method, this.#retryTransientGets);
58
+ const retry = retryForRequest(method, headers, this.#retryTransientGets);
59
59
  const requestStartedMs = Date.now();
60
60
  for (let attempt = 1; attempt <= retry.maxAttempts; attempt += 1) {
61
61
  const startedMs = Date.now();
@@ -93,7 +93,7 @@ export class HttpClient {
93
93
  ...normalizeHeaders(init.headers)
94
94
  };
95
95
  const method = methodOf(init.method);
96
- const retry = retryForMethod(method, this.#retryTransientGets);
96
+ const retry = retryForRequest(method, headers, this.#retryTransientGets);
97
97
  const requestStartedMs = Date.now();
98
98
  for (let attempt = 1; attempt <= retry.maxAttempts; attempt += 1) {
99
99
  const startedMs = Date.now();
@@ -152,12 +152,23 @@ function resolveTransientGetRetry(retry) {
152
152
  function methodOf(method) {
153
153
  return (method ?? "GET").toUpperCase();
154
154
  }
155
- function retryForMethod(method, retry) {
156
- if (!retry || (method !== "GET" && method !== "HEAD")) {
155
+ function retryForRequest(method, headers, retry) {
156
+ if (!retry || (!isMethodIdempotent(method) && !hasIdempotencyKey(headers))) {
157
157
  return { ...DEFAULT_TRANSIENT_GET_RETRY, maxAttempts: 1 };
158
158
  }
159
159
  return retry;
160
160
  }
161
+ function isMethodIdempotent(method) {
162
+ return method === "GET" || method === "HEAD";
163
+ }
164
+ function hasIdempotencyKey(headers) {
165
+ for (const [name, value] of Object.entries(headers)) {
166
+ if (name.toLowerCase() === "idempotency-key" && value.trim().length > 0) {
167
+ return true;
168
+ }
169
+ }
170
+ return false;
171
+ }
161
172
  function shouldRetryTransientRead(err, retry, attempt) {
162
173
  return attempt < retry.maxAttempts && transientReadErrorCode(err) !== undefined;
163
174
  }
@@ -121,7 +121,7 @@ export declare const READ_OUTPUT_TEXT_MAX_BYTES = 10000000;
121
121
  /** Default `maxBytes` for {@link readOutputText} — a chat-sized preview. */
122
122
  export declare const READ_OUTPUT_TEXT_DEFAULT_BYTES = 50000;
123
123
  /** Default per-attempt timeout while fetching or reading one output body. */
124
- export declare const OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 15000;
124
+ export declare const OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 30000;
125
125
  /** Idempotent output GETs retry once on a transfer timeout. */
126
126
  export declare const OUTPUT_FILE_TRANSFER_ATTEMPTS = 2;
127
127
  export interface OutputTransferOptions {
@@ -382,7 +382,7 @@ export const READ_OUTPUT_TEXT_MAX_BYTES = 10_000_000;
382
382
  /** Default `maxBytes` for {@link readOutputText} — a chat-sized preview. */
383
383
  export const READ_OUTPUT_TEXT_DEFAULT_BYTES = 50_000;
384
384
  /** Default per-attempt timeout while fetching or reading one output body. */
385
- export const OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 15_000;
385
+ export const OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 30_000;
386
386
  /** Idempotent output GETs retry once on a transfer timeout. */
387
387
  export const OUTPUT_FILE_TRANSFER_ATTEMPTS = 2;
388
388
  /**
@@ -434,7 +434,7 @@ export interface ReadOutputTextOptions {
434
434
  readonly maxBytes?: number;
435
435
  /**
436
436
  * Per-attempt timeout for fetching and reading the selected output body.
437
- * Defaults to 15_000ms; idempotent output reads retry once on timeout before
437
+ * Defaults to 30_000ms; idempotent output reads retry once on timeout before
438
438
  * surfacing a structured NETWORK_ERROR.
439
439
  */
440
440
  readonly timeoutMs?: number;
package/dist/cli.mjs CHANGED
@@ -2190,7 +2190,7 @@ var HttpClient = class {
2190
2190
  }
2191
2191
  }
2192
2192
  const method = methodOf(init.method);
2193
- const retry = retryForMethod(method, this.#retryTransientGets);
2193
+ const retry = retryForRequest(method, headers, this.#retryTransientGets);
2194
2194
  const requestStartedMs = Date.now();
2195
2195
  for (let attempt = 1; attempt <= retry.maxAttempts; attempt += 1) {
2196
2196
  const startedMs = Date.now();
@@ -2227,7 +2227,7 @@ var HttpClient = class {
2227
2227
  ...normalizeHeaders(init.headers)
2228
2228
  };
2229
2229
  const method = methodOf(init.method);
2230
- const retry = retryForMethod(method, this.#retryTransientGets);
2230
+ const retry = retryForRequest(method, headers, this.#retryTransientGets);
2231
2231
  const requestStartedMs = Date.now();
2232
2232
  for (let attempt = 1; attempt <= retry.maxAttempts; attempt += 1) {
2233
2233
  const startedMs = Date.now();
@@ -2285,12 +2285,23 @@ function resolveTransientGetRetry(retry) {
2285
2285
  function methodOf(method) {
2286
2286
  return (method ?? "GET").toUpperCase();
2287
2287
  }
2288
- function retryForMethod(method, retry) {
2289
- if (!retry || method !== "GET" && method !== "HEAD") {
2288
+ function retryForRequest(method, headers, retry) {
2289
+ if (!retry || !isMethodIdempotent(method) && !hasIdempotencyKey(headers)) {
2290
2290
  return { ...DEFAULT_TRANSIENT_GET_RETRY, maxAttempts: 1 };
2291
2291
  }
2292
2292
  return retry;
2293
2293
  }
2294
+ function isMethodIdempotent(method) {
2295
+ return method === "GET" || method === "HEAD";
2296
+ }
2297
+ function hasIdempotencyKey(headers) {
2298
+ for (const [name, value] of Object.entries(headers)) {
2299
+ if (name.toLowerCase() === "idempotency-key" && value.trim().length > 0) {
2300
+ return true;
2301
+ }
2302
+ }
2303
+ return false;
2304
+ }
2294
2305
  function shouldRetryTransientRead(err2, retry, attempt) {
2295
2306
  return attempt < retry.maxAttempts && transientReadErrorCode(err2) !== void 0;
2296
2307
  }
@@ -3460,7 +3471,7 @@ async function downloadOutput(http, runId, selector, options) {
3460
3471
  }
3461
3472
  var READ_OUTPUT_TEXT_MAX_BYTES = 1e7;
3462
3473
  var READ_OUTPUT_TEXT_DEFAULT_BYTES = 5e4;
3463
- var OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 15e3;
3474
+ var OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 3e4;
3464
3475
  var OUTPUT_FILE_TRANSFER_ATTEMPTS = 2;
3465
3476
  async function readOutputText(http, runId, selector, options) {
3466
3477
  const maxBytes = Math.max(1, Math.min(options?.maxBytes ?? READ_OUTPUT_TEXT_DEFAULT_BYTES, READ_OUTPUT_TEXT_MAX_BYTES));
@@ -1 +1 @@
1
- e5a409f5a6045f4ac2f366cb55f8a34faac7a3dc0ab048fc99760230305cd5e9 cli.mjs
1
+ 3a1bd5828048c0c5f9c59b3676b2e91e30a45a080c566f95d3f9de2590f28f9c cli.mjs
package/dist/client.d.ts CHANGED
@@ -580,7 +580,7 @@ export interface OutputDownloadOptions {
580
580
  readonly to?: string;
581
581
  /**
582
582
  * Per-attempt timeout for fetching and reading the selected output body.
583
- * Defaults to 15_000ms; idempotent output downloads retry once on timeout.
583
+ * Defaults to 30_000ms; idempotent output downloads retry once on timeout.
584
584
  */
585
585
  readonly timeoutMs?: number;
586
586
  }
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  *
7
7
  * Used by the (future) User-Agent header on outbound SDK requests.
8
8
  */
9
- export declare const SDK_VERSION = "0.40.13";
9
+ export declare const SDK_VERSION = "0.40.15";
package/dist/version.js CHANGED
@@ -6,5 +6,5 @@
6
6
  *
7
7
  * Used by the (future) User-Agent header on outbound SDK requests.
8
8
  */
9
- export const SDK_VERSION = "0.40.13";
9
+ export const SDK_VERSION = "0.40.15";
10
10
  //# sourceMappingURL=version.js.map
@@ -29,17 +29,20 @@ error). See [Credentials](../credentials.md).
29
29
 
30
30
  ## Depth and breadth limits
31
31
 
32
- Delegation is bounded by two server-enforced lineage limits:
32
+ Delegation is bounded by server-enforced lineage budgets. The runtime is
33
+ designed for broad fan-out, across hundreds and even thousands of concurrent
34
+ child agents per root, and for high levels of recursive subagent depth. Exact
35
+ operational limits are platform-managed and may change as the runtime scales.
33
36
 
34
- | Limit | Value | Behavior at the limit |
37
+ | Limit | Runtime posture | Behavior at the limit |
35
38
  | --- | --- | --- |
36
- | Max depth | **5** — the root run is depth 0 and may spawn down to depth 5; a depth-5 run may not spawn further | The spawn is rejected with a `depth_exceeded` tool error (the parent keeps running). |
37
- | Concurrent children per lineage root | **1000** live (non-terminal) descendants by default; hard platform ceiling **4096** | Further spawns are refused until a child settles. |
39
+ | Max recursive subagent depth | Supports high levels of nested delegation, bounded server-side. | A further spawn is rejected with a `depth_exceeded` tool error (the parent keeps running). |
40
+ | Concurrent children per lineage root | Designed to scale across hundreds, even thousands, of live descendants, bounded server-side. | Further spawns are refused until a child settles. |
38
41
 
39
42
  The whole descendant subtree of one root shares a single depth and breadth
40
43
  budget, enforced server-side at every level — a grandchild spawn counts against
41
- the same root budget as a direct child. Values are mirrored in
42
- [Limits & quotas](../limits-and-quotas.md).
44
+ the same root budget as a direct child. These budgets are not public per-session
45
+ dials today.
43
46
 
44
47
  ## Where children run: `in-process` vs `container`
45
48
 
@@ -84,5 +87,5 @@ Every child — in-process or container — is a first-class run record:
84
87
  - Turn delegation off for a run by cherry-picking builtins without `subagent`
85
88
  (see [Agent tools](agent-tools.md)) or setting `includeBuiltinTools: false`.
86
89
  - A per-session spend cap (`overrides.maxSpendUsd`) bounds the parent's spend.
87
- - The depth/breadth limits above are platform defaults and are not settable
90
+ - The depth/breadth budgets above are platform-managed and are not settable
88
91
  per-session today.
package/docs/defaults.md CHANGED
@@ -48,10 +48,13 @@ For the hard ceilings and who can raise them, see
48
48
 
49
49
  ## Subagents
50
50
 
51
+ Subagent breadth and depth are platform-managed budgets rather than fixed public
52
+ numeric entitlements; exact operational values may change as capacity evolves.
53
+
51
54
  | Option | Default | How to override | Source |
52
55
  | --- | --- | --- | --- |
53
- | Concurrent child runs per lineage root | 1000 (live, non-terminal child runs) | Platform default (subagents run in-process; no public per-run override). Hard ceiling 4096. | `RUN_DEFAULT_MAX_CONCURRENT_CHILD_RUNS` |
54
- | Max subagent depth | 5 | Platform default (subagents run in-process; no public per-run override). | `RUN_MAX_PUBLIC_SUBAGENT_DEPTH` |
56
+ | Concurrent child runs per lineage root | Platform-managed; designed to scale across hundreds, even thousands, of live child agents. | No public per-run override; contact support for unusually large workloads. | `RUN_DEFAULT_MAX_CONCURRENT_CHILD_RUNS` |
57
+ | Max subagent depth | Platform-managed; supports high recursive subagent depth. | No public per-run override. | `RUN_MAX_PUBLIC_SUBAGENT_DEPTH` |
55
58
 
56
59
  ## Workspace
57
60
 
@@ -58,10 +58,15 @@ silently lost.
58
58
 
59
59
  ### Subagents (per run lineage)
60
60
 
61
+ Subagent lineage bounds are enforced server-side but are intentionally not
62
+ advertised as fixed public ceilings because capacity can change. The runtime
63
+ posture is high fan-out and high recursive depth; exact admission is governed
64
+ by the platform at spawn time.
65
+
61
66
  | Limit | Value | Source | Raisable? | Constant |
62
67
  | --- | --- | --- | --- | --- |
63
- | Max subagent depth (`subagent` tool) | 5 (a depth-5 lineage may not spawn deeper) | aex policy | No public per-run override (subagents run in-process) | `RUN_MAX_PUBLIC_SUBAGENT_DEPTH` |
64
- | Concurrent child runs per lineage root | 1000 live (non-terminal); hard ceiling 4096 | aex policy | No public per-run override (subagents run in-process) | `RUN_DEFAULT_MAX_CONCURRENT_CHILD_RUNS` |
68
+ | Max subagent depth (`subagent` tool) | High recursive subagent depth, bounded server-side. | aex policy | No public per-run override (subagents run in-process) | `RUN_MAX_PUBLIC_SUBAGENT_DEPTH` |
69
+ | Concurrent child runs per lineage root | Designed to scale across hundreds, even thousands, of live child agents, bounded server-side. | aex policy | No public per-run override (subagents run in-process) | `RUN_DEFAULT_MAX_CONCURRENT_CHILD_RUNS` |
65
70
 
66
71
  ### Retention (per run)
67
72
 
@@ -98,7 +103,7 @@ A few behaviours are worth knowing before you rely on the filesystem or RAM:
98
103
  | Limit | Value | Source | Raisable? | Constant |
99
104
  | --- | --- | --- | --- | --- |
100
105
  | Workspace storage cap | 500 GB (decimal; admins uncapped — not a customer entitlement) | Workspace default | Per-plane via env `AEX_WORKSPACE_STORAGE_CAP_BYTES` | `WORKSPACE_DEFAULT_STORAGE_CAP_BYTES` |
101
- | Max concurrent runs per workspace | Plan-based: **5** live (non-terminal) root runs on the free plan, **50** on Pro, **200** on Team; hard platform ceiling **200**. One more submit past the cap fails with `429 workspace_concurrency_exceeded` (see [Errors](errors.md)). Subagent children are governed separately by the per-lineage caps below. Read your effective cap from `aex.whoami().limits.maxConcurrentRuns`. | Workspace default (per plan) | Per-plan (upgrade) or per-workspace override (contact support), clamped to the 200 ceiling | `PLANS[planKey].maxConcurrentRuns` / `WORKSPACE_MAX_CONCURRENT_RUNS_CEILING` |
106
+ | Max concurrent runs per workspace | Plan-based: **5** live (non-terminal) root runs on the free plan, **50** on Pro, **200** on Team; hard platform ceiling **200**. One more submit past the cap fails with `429 workspace_concurrency_exceeded` (see [Errors](errors.md)). Subagent children are governed separately by the platform-managed per-lineage budgets above. Read your effective cap from `aex.whoami().limits.maxConcurrentRuns`. | Workspace default (per plan) | Per-plan (upgrade) or per-workspace override (contact support), clamped to the 200 ceiling | `PLANS[planKey].maxConcurrentRuns` / `WORKSPACE_MAX_CONCURRENT_RUNS_CEILING` |
102
107
  | Monthly workspace spend cap | **$250** per rolling UTC calendar month by default; `0` = unlimited. A submit past the cap fails with `402 workspace_spend_cap_exceeded` (see [Errors](errors.md)). | Workspace default | Per-workspace override (contact support) | `WORKSPACE_DEFAULT_SPEND_CAP_USD` |
103
108
  | Skill bundle max compressed size (`.zip`) | 10 GiB (enforced at upload by the SDK and re-enforced server-side) | aex policy | No (hard ceiling) | `SKILL_BUNDLE_LIMITS.maxCompressedBytes` |
104
109
  | Skill bundle max decompressed size (sum of uncompressed file sizes) | 50 MB | aex policy | No (hard ceiling) | `SKILL_BUNDLE_LIMITS.maxDecompressedBytes` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexhq/sdk",
3
- "version": "0.40.13",
3
+ "version": "0.40.15",
4
4
  "description": "TypeScript SDK for running autonomous agent sessions across providers (Anthropic, OpenAI, DeepSeek, Gemini, Mistral) behind one interface.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {