@aexhq/sdk 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -16
- package/dist/_contracts/event-envelope.d.ts +22 -1
- package/dist/_contracts/event-envelope.js +26 -2
- package/dist/_contracts/event-stream-client.js +7 -1
- package/dist/_contracts/index.d.ts +3 -4
- package/dist/_contracts/index.js +1 -4
- package/dist/_contracts/operations.d.ts +31 -1
- package/dist/_contracts/operations.js +64 -1
- package/dist/_contracts/run-config.d.ts +2 -4
- package/dist/_contracts/run-config.js +2 -7
- package/dist/_contracts/run-trace.d.ts +0 -86
- package/dist/_contracts/run-trace.js +1 -184
- package/dist/_contracts/run-unit.d.ts +14 -25
- package/dist/_contracts/run-unit.js +56 -2
- package/dist/_contracts/runtime-manifest.d.ts +1 -1
- package/dist/_contracts/runtime-security-profile.d.ts +0 -2
- package/dist/_contracts/runtime-security-profile.js +0 -9
- package/dist/_contracts/runtime-sizes.d.ts +2 -2
- package/dist/_contracts/runtime-sizes.js +5 -5
- package/dist/_contracts/runtime-types.d.ts +123 -4
- package/dist/_contracts/stable.d.ts +1 -1
- package/dist/_contracts/stable.js +1 -1
- package/dist/_contracts/submission.d.ts +8 -76
- package/dist/_contracts/submission.js +5 -472
- package/dist/cli.mjs +574 -511
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +69 -25
- package/dist/client.js +338 -68
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +8 -16
- package/dist/index.js +5 -17
- package/dist/index.js.map +1 -1
- package/dist/secret.d.ts +2 -2
- package/dist/secret.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +92 -0
- package/docs/billing.md +112 -0
- package/docs/concepts/agent-tools.md +4 -4
- package/docs/concepts/composition.md +8 -14
- package/docs/concepts/providers-and-runtimes.md +4 -1
- package/docs/concepts/runs.md +2 -1
- package/docs/concepts/subagents.md +85 -0
- package/docs/credentials.md +78 -96
- package/docs/defaults.md +9 -15
- package/docs/errors.md +132 -0
- package/docs/events.md +44 -32
- package/docs/limits-and-quotas.md +30 -17
- package/docs/limits.md +4 -8
- package/docs/mcp.md +5 -6
- package/docs/networking.md +75 -59
- package/docs/outputs.md +4 -7
- package/docs/public-surface.json +4 -4
- package/docs/quickstart.md +12 -13
- package/docs/run-config.md +7 -4
- package/docs/secrets.md +6 -1
- package/docs/skills.md +3 -3
- package/docs/vision-skills.md +52 -101
- package/docs/webhooks.md +132 -0
- package/examples/feature-tour.ts +4 -21
- package/package.json +1 -1
- package/dist/_contracts/proxy-protocol.d.ts +0 -305
- package/dist/_contracts/proxy-protocol.js +0 -297
- package/dist/_contracts/proxy-validation.d.ts +0 -19
- package/dist/_contracts/proxy-validation.js +0 -51
- package/dist/data-tools.d.ts +0 -82
- package/dist/data-tools.js +0 -251
- package/dist/data-tools.js.map +0 -1
- package/dist/proxy-endpoint.d.ts +0 -131
- package/dist/proxy-endpoint.js +0 -144
- package/dist/proxy-endpoint.js.map +0 -1
- package/examples/chat-corpus.ts +0 -84
package/docs/events.md
CHANGED
|
@@ -53,9 +53,9 @@ for the string:
|
|
|
53
53
|
const lastText = (await session.messages().last())?.text;
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
Prefer `session.messages().list()` or the collected `result.messages` /
|
|
57
|
+
`result.text` fields for assistant text. Low-level event helpers remain exported
|
|
58
|
+
for callers that build custom collectors.
|
|
59
59
|
|
|
60
60
|
The CLI mirrors the same surface:
|
|
61
61
|
|
|
@@ -110,22 +110,32 @@ collected session turn. The returned `runId` is the session id.
|
|
|
110
110
|
|
|
111
111
|
## Terminal events vs. the run record
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
Two families of events can end a turn's stream, and which one you see depends
|
|
114
|
+
on how the turn ends:
|
|
115
|
+
|
|
116
|
+
- **AG-UI terminals** — `RUN_FINISHED` / `RUN_ERROR`. These are *render-complete*
|
|
117
|
+
signals emitted by the agent stream itself. On the managed plane a normal
|
|
118
|
+
session turn usually does **not** emit `RUN_FINISHED`: the session *parks*
|
|
119
|
+
instead (see below). Expect `RUN_ERROR` on stream-level failures, and treat
|
|
120
|
+
`RUN_FINISHED` — when it does appear — as a low-latency "stop the spinner"
|
|
121
|
+
hint, not a read-consistency barrier.
|
|
122
|
+
- **`aex.session.*` park terminals** — `CUSTOM` events named `aex.session.idle`,
|
|
123
|
+
`aex.session.suspended`, or `aex.session.error`. On the managed plane these
|
|
124
|
+
are what actually end a turn: the session parks with the matching status, and
|
|
125
|
+
by the time the park event is broadcast the session record has already
|
|
126
|
+
reached that status. This is the terminal you should expect from a managed
|
|
127
|
+
run's event stream.
|
|
128
|
+
|
|
129
|
+
The SDK's helpers cover both families so you never have to switch on the plane:
|
|
130
|
+
|
|
131
|
+
- `isRunTerminal(event)` — true for the AG-UI `RUN_FINISHED` / `RUN_ERROR` pair.
|
|
132
|
+
- `isRunSettled(event)` — true for the `aex.run.settled` settle barrier **and**
|
|
133
|
+
for any `aex.session.*` park terminal. The managed plane does not broadcast a
|
|
134
|
+
separate `aex.run.settled` barrier — the park event plays that role — so
|
|
135
|
+
`isRunSettled` is the one guard that reliably means "this stream is done and
|
|
136
|
+
the record is authoritative".
|
|
137
|
+
|
|
138
|
+
To read the authoritative status consistently, use one of:
|
|
129
139
|
|
|
130
140
|
```ts
|
|
131
141
|
// Session record path: send a turn, then wait for the session to park.
|
|
@@ -135,18 +145,21 @@ const record = await session.wait(); // the parked session record
|
|
|
135
145
|
```
|
|
136
146
|
|
|
137
147
|
```ts
|
|
138
|
-
// Live events AND a settle-consistent end: the iterator
|
|
139
|
-
//
|
|
148
|
+
// Live events AND a settle-consistent end: the iterator ends on the settle
|
|
149
|
+
// barrier OR the aex.session.* park terminal, whichever the plane emits —
|
|
150
|
+
// so when it ends, the session record is already parked/terminal.
|
|
140
151
|
for await (const event of session.events().streamEnvelopes({ settleConsistent: true })) {
|
|
141
152
|
// render events live…
|
|
142
153
|
}
|
|
143
|
-
const settled = await aex.sessions.get(session.id); //
|
|
154
|
+
const settled = await aex.sessions.get(session.id); // parked/terminal here
|
|
144
155
|
```
|
|
145
156
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`
|
|
157
|
+
`settleConsistent: true` makes the iterator end exactly when `isRunSettled(event)`
|
|
158
|
+
first fires; on a raw stream, apply `isRunSettled(event)` yourself. What it
|
|
159
|
+
guarantees: when the stream ends, a subsequent `aex.sessions.get(id)` reads a
|
|
160
|
+
parked/terminal status and `session.outputs().list()` is complete. Outputs are
|
|
161
|
+
uploaded before the terminal is broadcast, so they are readable the moment the
|
|
162
|
+
stream ends.
|
|
150
163
|
|
|
151
164
|
## Temporary event archive links
|
|
152
165
|
|
|
@@ -162,7 +175,7 @@ const jsonl = await response.text();
|
|
|
162
175
|
|
|
163
176
|
## Event shape
|
|
164
177
|
|
|
165
|
-
Events are typed as the discriminated `RunEvent` union for compatibility and as the versioned coordinator envelope for live consumers. aex records raw runtime/provider payloads **after** secret redaction and structural sanitization, so the bytes you see never contain
|
|
178
|
+
Events are typed as the discriminated `RunEvent` union for compatibility and as the versioned coordinator envelope for live consumers. aex records raw runtime/provider payloads **after** secret redaction and structural sanitization, so the bytes you see never contain provider keys, MCP credentials, or runtime secrets supplied when the session was opened.
|
|
166
179
|
|
|
167
180
|
## Typed helpers
|
|
168
181
|
|
|
@@ -180,8 +193,7 @@ import {
|
|
|
180
193
|
isToolCallResult,
|
|
181
194
|
isCustom,
|
|
182
195
|
isLog,
|
|
183
|
-
isEventChannel
|
|
184
|
-
textOf
|
|
196
|
+
isEventChannel
|
|
185
197
|
} from "@aexhq/sdk";
|
|
186
198
|
```
|
|
187
199
|
|
|
@@ -191,6 +203,6 @@ All guards test the `type` discriminant at runtime. `isTextMessage`,
|
|
|
191
203
|
`event.data` to the fields that event type carries — e.g. inside
|
|
192
204
|
`if (isTextMessage(e))`, `e.data.text` is typed `string`. The lifecycle/channel
|
|
193
205
|
guards (`isRunStarted`, `isRunError`, `isCustom`, `isLog`, …) operate on the
|
|
194
|
-
coordinator envelope and narrow only the discriminant. `
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
coordinator envelope and narrow only the discriminant. Use `result.text` or
|
|
207
|
+
`session.messages.all()` when you need assistant text without inspecting the
|
|
208
|
+
event stream directly.
|
|
@@ -5,10 +5,9 @@ title: Limits & quotas
|
|
|
5
5
|
# Limits & quotas
|
|
6
6
|
|
|
7
7
|
These are the hard ceilings and caps that bound a run, a workspace, and a single
|
|
8
|
-
request. Every value is mirrored from a single source-of-truth constant
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
the constant wins.
|
|
8
|
+
request. Every value is mirrored from a single source-of-truth constant in the
|
|
9
|
+
platform's limits module; this page is hand-maintained against those constants.
|
|
10
|
+
If a value here ever disagrees with the constant, the constant wins.
|
|
12
11
|
|
|
13
12
|
Each row is named by its source-of-truth constant. For the values that apply
|
|
14
13
|
when you omit an option, see
|
|
@@ -26,7 +25,7 @@ And whether you can **raise** it: per-run option, per-plan, or no.
|
|
|
26
25
|
|
|
27
26
|
| Limit | Value | Source | Raisable? | Constant |
|
|
28
27
|
| --- | --- | --- | --- | --- |
|
|
29
|
-
| Maximum run timeout |
|
|
28
|
+
| Maximum run timeout | 8 hours (also the default when `timeout` is omitted) | aex policy | Per plan (billing-driven) | `RUN_MAX_TIMEOUT_MS` |
|
|
30
29
|
| Minimum run timeout | 1 minute | aex policy | No (floor) | `RUN_MIN_TIMEOUT_MS` |
|
|
31
30
|
| Per-call exec timeout (default) | 30 minutes | aex policy | Per-call via the tool call's `timeoutMs` | `RUN_DEFAULT_EXEC_TIMEOUT_MS` |
|
|
32
31
|
| MCP connect timeout (default) | 30 seconds | aex policy | Per-port via `connectTimeoutMs` | `RUN_DEFAULT_MCP_CONNECT_TIMEOUT_MS` |
|
|
@@ -43,8 +42,8 @@ silently lost.
|
|
|
43
42
|
| --- | --- | --- | --- | --- |
|
|
44
43
|
| Capture wall-clock budget | 1 hour | aex policy | No (hard ceiling) | `RUN_CAPTURE_DEFAULT_TIMEOUT_MS` |
|
|
45
44
|
| Max files captured | 50,000 | aex policy | No (hard ceiling) | `RUN_CAPTURE_MAX_FILES` |
|
|
46
|
-
| Max bytes per captured file |
|
|
47
|
-
| Max total captured bytes |
|
|
45
|
+
| Max bytes per captured file | 500 GB (decimal) | aex policy | No (hard ceiling) | `RUN_CAPTURE_MAX_FILE_BYTES` |
|
|
46
|
+
| Max total captured bytes | 500 GB (decimal) | aex policy | No (hard ceiling) | `RUN_CAPTURE_MAX_TOTAL_BYTES` |
|
|
48
47
|
|
|
49
48
|
### Tool output caps (per run)
|
|
50
49
|
|
|
@@ -74,9 +73,11 @@ silently lost.
|
|
|
74
73
|
|
|
75
74
|
| Limit | Value | Source | Raisable? | Constant |
|
|
76
75
|
| --- | --- | --- | --- | --- |
|
|
77
|
-
| Workspace storage cap |
|
|
78
|
-
| Max concurrent runs per workspace |
|
|
79
|
-
|
|
|
76
|
+
| 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` |
|
|
77
|
+
| Max concurrent runs per workspace | **50** live (non-terminal) root runs by default; 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. | Workspace default | Per-workspace override (contact support), clamped to the 200 ceiling | `WORKSPACE_DEFAULT_MAX_CONCURRENT_RUNS` / `WORKSPACE_MAX_CONCURRENT_RUNS_CEILING` |
|
|
78
|
+
| 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` |
|
|
79
|
+
| 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` |
|
|
80
|
+
| Skill bundle max decompressed size (sum of uncompressed file sizes) | 50 MB | aex policy | No (hard ceiling) | `SKILL_BUNDLE_LIMITS.maxDecompressedBytes` |
|
|
80
81
|
| Skill bundle max file entries | 1,000 | Workspace default | Per-workspace (plan/env) | `WORKSPACE_SKILL_BUNDLE_MAX_FILES` |
|
|
81
82
|
| Skill bundle max directory depth (`a/b/c/d` = 4) | 16 | Workspace default | Per-workspace (plan/env) | `WORKSPACE_SKILL_BUNDLE_MAX_DEPTH` |
|
|
82
83
|
| Skill bundle max entry path length | 512 characters | Workspace default | No (hard ceiling) | `WORKSPACE_SKILL_BUNDLE_MAX_PATH_LENGTH` |
|
|
@@ -84,24 +85,36 @@ silently lost.
|
|
|
84
85
|
|
|
85
86
|
### Rate limits (per workspace, per minute)
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
`
|
|
88
|
+
Run submission has its own platform-enforced velocity cap: **120 submits per
|
|
89
|
+
minute** per workspace by default (`0` = disabled). Past it, `POST /runs` fails
|
|
90
|
+
with `429 workspace_submit_rate_exceeded` (see [Errors](errors.md)). It is
|
|
91
|
+
overridable per-plane via `AEX_WORKSPACE_SUBMIT_RATE_PER_MIN` or per-workspace
|
|
92
|
+
via support.
|
|
93
|
+
|
|
94
|
+
The dashboard mutation actions below default as listed; each is overridable
|
|
95
|
+
per-plane via the matching `AEX_RATE_LIMIT_<ACTION>_PER_MINUTE` env var.
|
|
89
96
|
|
|
90
97
|
| Action | Default per minute | Source | Constant |
|
|
91
98
|
| --- | --- | --- | --- |
|
|
92
|
-
| Run submit | 60 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
93
99
|
| Run cancel | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
94
100
|
| Run delete | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
95
101
|
| Signed output link | 120 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
96
102
|
| API token create | 10 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
97
103
|
| API token delete | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
98
104
|
|
|
99
|
-
|
|
105
|
+
### Introspecting your effective caps
|
|
106
|
+
|
|
107
|
+
`aex.whoami()` (CLI: `aex whoami`) returns a `limits` object carrying the
|
|
108
|
+
workspace's *effective* values for the caps above — `maxConcurrentRuns`,
|
|
109
|
+
`submitRatePerMinute`, `spendCapUsd`, plus the live `monthSpendUsd`,
|
|
110
|
+
`balanceUsd`, `balanceGraceFloorUsd`, and `paymentMethodStatus` — resolved by
|
|
111
|
+
the same code the admission gates use, so you can anticipate a `429`/`402`
|
|
112
|
+
before submitting. See [Authentication](authentication.md) and
|
|
113
|
+
[Errors](errors.md).
|
|
114
|
+
|
|
115
|
+
## Request Scope
|
|
100
116
|
|
|
101
117
|
| Limit | Value | Source | Raisable? | Constant |
|
|
102
118
|
| --- | --- | --- | --- | --- |
|
|
103
|
-
| Proxy request body | 10 MiB | aex policy | Per-endpoint via `maxRequestBytes` | `REQUEST_PROXY_DEFAULT_MAX_REQUEST_BYTES` |
|
|
104
|
-
| Proxy response body | `0` = unlimited (streamed unbuffered) | aex policy | Per-endpoint via `maxResponseBytes` | `REQUEST_PROXY_DEFAULT_MAX_RESPONSE_BYTES` |
|
|
105
|
-
| Proxy upstream timeout | 5 minutes | aex policy | Per-endpoint via `timeoutMs` | `REQUEST_PROXY_DEFAULT_TIMEOUT_MS` |
|
|
106
119
|
| Signed output URL TTL | 300 seconds | aex policy | Per-call via `expiresSeconds` | `REQUEST_PRESIGN_URL_DEFAULT_TTL_SECONDS` |
|
|
107
120
|
| Event-stream connection ticket TTL | 60 seconds | aex policy | Per-mint via `ttlMs` | `REQUEST_TICKET_DEFAULT_TTL_MS` |
|
package/docs/limits.md
CHANGED
|
@@ -16,25 +16,21 @@ For the current provider/model set, see the generated
|
|
|
16
16
|
|
|
17
17
|
| Area | Default |
|
|
18
18
|
| --- | --- |
|
|
19
|
-
| Workspace storage |
|
|
20
|
-
| Proxy request body | 10 MiB per proxy endpoint unless the endpoint declares a different `maxRequestBytes`. |
|
|
21
|
-
| Proxy timeout | 5 minutes per proxy endpoint unless the endpoint declares a different `timeoutMs`. |
|
|
22
|
-
| Proxy telemetry | Proxy calls emit report-only usage telemetry for call count, failed calls, request bytes, response bytes when known, and duration. Public proxy pricing is not shipped unless documented later. |
|
|
19
|
+
| Workspace storage | 500 GB per workspace for captured outputs and workspace artifacts. aex-maintainer admin workspaces may be unlimited for internal dogfooding; this is not a customer entitlement. |
|
|
23
20
|
|
|
24
21
|
## Product Boundaries
|
|
25
22
|
|
|
26
23
|
| Area | Boundary |
|
|
27
24
|
| --- | --- |
|
|
28
|
-
| Runtime | New submissions run on the managed runtime.
|
|
25
|
+
| Runtime | New submissions run on the managed runtime. The `runtime` option selects a managed machine-size preset (`Sizes.*`); there is no alternative runtime backend. |
|
|
29
26
|
| Provider policy | Provider retention, training exclusion, HIPAA/BAA, data residency, abuse policy, and pricing belong to the selected provider account, endpoint, and contract. |
|
|
30
|
-
| Secrets | Provider keys, MCP credentials,
|
|
27
|
+
| Secrets | Provider keys, MCP credentials, and env secrets are caller-owned. aex excludes secret values from idempotency and uses the explicit secret surfaces described in [Secrets](secrets.md). |
|
|
31
28
|
| MCP servers | Remote MCP servers are customer-trusted systems. aex validates declarations and routes credentials; it does not make an untrusted MCP server safe. |
|
|
32
|
-
| Proxy endpoints | The proxy enforces declared host/path/method/auth policy for calls routed through it. Upstream side effects and data handling remain with the upstream service and customer. |
|
|
33
29
|
| Outputs | Captured outputs, events, and metadata are stored under the run record and downloaded through auth-gated routes. Output content is customer content. |
|
|
34
30
|
| Human review | Runs execute after submission. Cancellation is available, but aex does not pause a run for platform-mediated approval or interactive clarification. |
|
|
35
31
|
| Sessions | The durable product primitive is the session/run record. Sessions can be resumed by id and auto-suspend after the configured idle window; persistent named agent profiles and saved agent definitions are out of scope. |
|
|
36
32
|
| Deployment | The supported product is the hosted aex service plus the SDK and CLI. Alternate `baseUrl` values are for local, staging, or hosted aex API planes, not a self-host product promise. |
|
|
37
|
-
| Cost | BYOK provider-token charges accrue to the customer's provider account. aex records report-only telemetry for runtime
|
|
33
|
+
| Cost | BYOK provider-token charges accrue to the customer's provider account. aex records report-only telemetry for runtime and storage usage; free trials, billing-grade invoices, and public pricing documents are not shipped unless documented later. |
|
|
38
34
|
|
|
39
35
|
## Provider Policy Links
|
|
40
36
|
|
package/docs/mcp.md
CHANGED
|
@@ -20,19 +20,18 @@ Use allowlists for sensitive servers whenever possible.
|
|
|
20
20
|
## Large-payload responses
|
|
21
21
|
|
|
22
22
|
aex is a session dispatcher, not an MCP runtime. We intentionally do
|
|
23
|
-
**not** interpose on the transport between
|
|
23
|
+
**not** interpose on the transport between the model and an upstream MCP
|
|
24
24
|
server, so we cannot elide MCP responses or write them to the session
|
|
25
25
|
filesystem on the user's behalf. Anything an MCP tool returns lands
|
|
26
26
|
directly in the model's context.
|
|
27
27
|
|
|
28
|
-
For ingestion-style
|
|
29
|
-
catalogue dumps, bulk reads),
|
|
30
|
-
|
|
28
|
+
For ingestion-style MCP servers that return large JSON blobs (search results,
|
|
29
|
+
catalogue dumps, bulk reads), prefer a skill that writes files instead of
|
|
30
|
+
putting the whole response in model context:
|
|
31
31
|
|
|
32
32
|
1. Package the upstream as a skill-tool (`Tools.fromSkillDir` /
|
|
33
33
|
`Tools.fromSkillUrl`) — a CLI binary the agent invokes with its bash tool.
|
|
34
|
-
2.
|
|
35
|
-
(audit, byte caps, budget enforcement).
|
|
34
|
+
2. Keep any upstream HTTPS credentials in `environment.secrets`.
|
|
36
35
|
3. Have the CLI write the full payload to the session filesystem. By default,
|
|
37
36
|
files it creates or modifies are captured automatically; pass
|
|
38
37
|
`outputs.allowedDirs` only when you want to narrow capture to specific roots.
|
package/docs/networking.md
CHANGED
|
@@ -4,45 +4,64 @@ title: Networking
|
|
|
4
4
|
|
|
5
5
|
# Networking
|
|
6
6
|
|
|
7
|
-
A run executes your agent's code in a sandbox
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
A run executes your agent's code in a sandbox with **no direct route to the
|
|
8
|
+
internet**. Outbound traffic is governed by **two layers**:
|
|
9
|
+
|
|
10
|
+
1. **The per-run policy (`environment.networking`)** — enforced by the agent
|
|
11
|
+
runtime *inside the run*. It applies to the standard proxy path every normal
|
|
12
|
+
HTTP client uses (see below) and can only *narrow* what the run may reach.
|
|
13
|
+
2. **The platform ceiling** — a fixed, platform-managed egress boundary every
|
|
14
|
+
connection ultimately traverses. It allows the hosts aex itself manages
|
|
15
|
+
(model providers, built-in tool endpoints, package registries, and related
|
|
16
|
+
well-known development hosts such as `github.com`) and enforces a fixed SSRF
|
|
17
|
+
deny-list: loopback, link-local, cloud-metadata, and other private ranges are
|
|
18
|
+
always blocked, including hostnames that resolve to those ranges.
|
|
19
|
+
|
|
20
|
+
Honest boundary statement: the per-run `allowedHosts` policy is enforced by the
|
|
21
|
+
run's own runtime on the standard proxy path — it is **not** yet enforced at
|
|
22
|
+
the platform proxy layer. A subprocess that deliberately bypasses the standard
|
|
23
|
+
proxy environment (a raw socket / raw CONNECT) is bounded by the **platform
|
|
24
|
+
ceiling** rather than by the per-run list. Per-run enforcement at the platform
|
|
25
|
+
proxy layer is planned; until it ships, treat `allowedHosts` as a strong
|
|
26
|
+
default-path control and an auditable statement of intent, not a hard isolation
|
|
27
|
+
boundary against adversarial code inside the run.
|
|
28
|
+
|
|
29
|
+
**Default posture.** A run that does not set `environment.networking` runs in
|
|
30
|
+
`open` mode: its own code may reach anything within the platform ceiling with
|
|
31
|
+
no allowlist required. Use `environment.networking` to *narrow* that surface
|
|
32
|
+
when you want a tighter, auditable egress posture. Code cannot widen the
|
|
33
|
+
ceiling from inside the container.
|
|
20
34
|
|
|
21
35
|
## Paths that always work
|
|
22
36
|
|
|
23
|
-
These reach the network over managed paths and are **not** subject to
|
|
37
|
+
These reach the network over managed platform paths and are **not** subject to
|
|
24
38
|
`environment.networking`, so you never list their hosts:
|
|
25
39
|
|
|
26
40
|
- The model / provider call for the run (and its subagents).
|
|
27
|
-
- The built-in `web_search` and `web_fetch` tools
|
|
28
|
-
-
|
|
29
|
-
|
|
41
|
+
- The built-in `web_search` and `web_fetch` tools. They run over a managed,
|
|
42
|
+
SSRF-guarded server-side path, which is why they can reach arbitrary public
|
|
43
|
+
URLs even though your own code is bounded by the ceiling.
|
|
44
|
+
- Remote MCP servers you declare in `mcpServers` — MCP traffic rides a managed
|
|
45
|
+
path; see [MCP](mcp.md).
|
|
30
46
|
- The package registries for any `environment.packages` you declare (pip → PyPI,
|
|
31
47
|
apt → the distribution mirrors). Declaring a package implicitly allows the
|
|
32
48
|
registry it installs from.
|
|
33
49
|
|
|
34
|
-
`environment.networking` governs the **other** case:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`code_execution`, or a third-party SDK.
|
|
50
|
+
`environment.networking` governs the **other** case: outbound that your own
|
|
51
|
+
code makes — a `curl` in the `bash` tool, a `requests`/`urllib` call in Python,
|
|
52
|
+
a `fetch` in `code_execution`, or a third-party SDK.
|
|
38
53
|
|
|
39
54
|
## Restrict a run to an allowlist
|
|
40
55
|
|
|
41
56
|
Set `mode: "limited"` and list exactly the hosts your code is allowed to reach.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
The run's runtime enforces the list on the standard proxy path: a connection to
|
|
58
|
+
a host that is neither on the list nor one of the always-allowed paths above is
|
|
59
|
+
refused before it leaves the run. Package-registry hosts implied by
|
|
60
|
+
`environment.packages` are appended automatically so installs keep working.
|
|
61
|
+
|
|
62
|
+
Note that `allowedHosts` narrows *within* the platform ceiling — listing a host
|
|
63
|
+
does not by itself make it reachable if the platform ceiling does not carry it.
|
|
64
|
+
If your run needs a host the ceiling blocks, contact support.
|
|
46
65
|
|
|
47
66
|
### TypeScript
|
|
48
67
|
|
|
@@ -70,26 +89,17 @@ non-default port when you need one (`api.example.com:8443`); a bare host name
|
|
|
70
89
|
covers HTTPS on 443. Matching is exact per host — it is not a wildcard or suffix
|
|
71
90
|
match, so list each host you need.
|
|
72
91
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
import { buildPlatformAllowedHosts } from "@aexhq/sdk";
|
|
78
|
-
|
|
79
|
-
const allowedHosts = buildPlatformAllowedHosts({
|
|
80
|
-
baseUrl: "https://api.aex.dev",
|
|
81
|
-
extraHosts: ["api.example.com"]
|
|
82
|
-
});
|
|
83
|
-
```
|
|
92
|
+
Keep the allowlist in your session options so the submitted network policy is
|
|
93
|
+
visible at the same call site as the code that needs it.
|
|
84
94
|
|
|
85
95
|
## Open mode
|
|
86
96
|
|
|
87
97
|
`open` is the default: a run that omits `environment.networking` already runs in
|
|
88
|
-
open mode. Set `mode: "open"` explicitly when you want to be unambiguous
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
whenever you can name the hosts — it gives the run a stable, auditable,
|
|
92
|
-
privilege egress surface (it is the tighter posture, not the default).
|
|
98
|
+
open mode. Set `mode: "open"` explicitly when you want to be unambiguous. Open
|
|
99
|
+
mode applies no per-run allowlist — the run's own code may reach anything the
|
|
100
|
+
platform ceiling allows, still subject to the SSRF deny-list. Prefer `limited`
|
|
101
|
+
whenever you can name the hosts — it gives the run a stable, auditable,
|
|
102
|
+
least-privilege egress surface (it is the tighter posture, not the default).
|
|
93
103
|
|
|
94
104
|
```ts
|
|
95
105
|
await aex.run({
|
|
@@ -100,6 +110,9 @@ await aex.run({
|
|
|
100
110
|
});
|
|
101
111
|
```
|
|
102
112
|
|
|
113
|
+
(Web research like the example above flows through the managed `web_search` /
|
|
114
|
+
`web_fetch` path, which is not ceiling-bounded.)
|
|
115
|
+
|
|
103
116
|
## Transparent for normal HTTP clients
|
|
104
117
|
|
|
105
118
|
You write ordinary code — there is no per-request proxy configuration and no
|
|
@@ -111,7 +124,7 @@ that honors it — `curl`, Python `requests` / `urllib`, `pip`, `npm`, Node
|
|
|
111
124
|
```bash
|
|
112
125
|
# In the agent's shell, against a limited run that allows api.example.com:
|
|
113
126
|
curl -sS https://api.example.com/v1/status # works
|
|
114
|
-
curl -sS https://other-host.example #
|
|
127
|
+
curl -sS https://other-host.example # refused (not in allowlist)
|
|
115
128
|
```
|
|
116
129
|
|
|
117
130
|
You also do **not** need to install any certificate. The platform manages the
|
|
@@ -120,22 +133,25 @@ your client succeeds without extra setup.
|
|
|
120
133
|
|
|
121
134
|
## Limitations and gotchas
|
|
122
135
|
|
|
123
|
-
- **
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
- **The per-run policy is enforced by the run's runtime, not at the platform
|
|
137
|
+
proxy.** Clients that honor the standard proxy environment (almost all HTTP
|
|
138
|
+
tooling) are held to the `allowedHosts` list. A subprocess that deliberately
|
|
139
|
+
ignores the proxy environment and opens a raw connection is **not** held to
|
|
140
|
+
the per-run list — it is bounded by the platform ceiling (the aex-managed
|
|
141
|
+
provider/tool/registry host set) and the SSRF deny-list instead. Per-run
|
|
142
|
+
enforcement at the platform proxy layer is planned.
|
|
127
143
|
- **A client that hard-bypasses the standard environment may fail to connect.**
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
environment the runtime provides (most libraries do by default), rather than
|
|
134
|
-
overriding it.
|
|
144
|
+
A client that ignores the proxy environment, pins or replaces its certificate
|
|
145
|
+
trust store, or speaks a non-HTTP protocol over a raw socket can hit a wall
|
|
146
|
+
even for a host you allowed. The fix is to let the client use the standard
|
|
147
|
+
proxy and certificate environment the runtime provides (most libraries do by
|
|
148
|
+
default), rather than overriding it.
|
|
135
149
|
- **`allowedHosts` only applies in `limited` mode.** It is ignored in `open`
|
|
136
|
-
mode, where the SSRF deny-list
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
mode, where the platform ceiling and the SSRF deny-list are the gates.
|
|
151
|
+
- **`allowedHosts` cannot exceed the platform ceiling.** It narrows; it never
|
|
152
|
+
widens. A listed host outside the ceiling still fails.
|
|
153
|
+
|
|
154
|
+
For credentialed HTTP calls, pass the credential as an `environment.secrets`
|
|
155
|
+
entry and let your code use its normal HTTP client. For remote tool servers, see
|
|
156
|
+
[MCP](mcp.md). For the full set of run-config fields, see
|
|
157
|
+
[Run configuration](run-config.md).
|
package/docs/outputs.md
CHANGED
|
@@ -100,10 +100,6 @@ if (truncated) {
|
|
|
100
100
|
|
|
101
101
|
Check `truncated` before treating `text` as complete. Pass `options.grep` (a substring or `RegExp`) to keep only matching lines of the capped text. The returned `output` is the matched `Output` record, and `totalBytes` is the file's full size when the server reports it.
|
|
102
102
|
|
|
103
|
-
### Chatting over a workspace's outputs
|
|
104
|
-
|
|
105
|
-
`createDataTools(client)` packages the read surface (`sessions.list` + `sessions.outputs(id).list` + `sessions.outputs(id).read`) as a vendor-neutral LLM tool set (`{ tools, instructions, execute }`) so you can build a search-then-fetch chat over your sessions and their outputs in a few lines on top of the public SDK. The `tools` are plain JSON-Schema definitions (the shape every major LLM tool API accepts); `execute(name, input)` dispatches a tool call against the workspace-scoped client. See the runnable `examples/data-chat/` example.
|
|
106
|
-
|
|
107
103
|
## Finding outputs
|
|
108
104
|
|
|
109
105
|
`session.outputs().list(query?)` can filter the captured output list client-side. Use `session.outputs().find(query)` when you want discovery to be explicit, or `session.outputs().findOne(query)` when exactly one file is expected:
|
|
@@ -167,9 +163,10 @@ const stream = response.body;
|
|
|
167
163
|
|
|
168
164
|
| Run state | Behaviour |
|
|
169
165
|
| --- | --- |
|
|
170
|
-
| `
|
|
171
|
-
| `provider_running`, mid-session / `cleaning_up` | Whatever events + outputs have been captured so far. Call again after
|
|
172
|
-
| `
|
|
166
|
+
| `queued` / `claiming` / `provisioning` | `metadata/run.json` reflects the early state; `events/` and `outputs/` are typically empty. |
|
|
167
|
+
| `provider_running`, mid-session / `capturing_outputs` / `cleaning_up` | Whatever events + outputs have been captured so far. Call again after the session parks for the complete set. |
|
|
168
|
+
| `idle` / `suspended` (parked between turns) | The complete archive for every turn sent so far; a later turn appends to it. |
|
|
169
|
+
| `succeeded` / `failed` / `timed_out` / `cancelled` | The complete typed event archive + all captured outputs. |
|
|
173
170
|
|
|
174
171
|
## `outputs.allowedDirs` — override capture roots
|
|
175
172
|
|
package/docs/public-surface.json
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"brand": "aex",
|
|
3
3
|
"productName": "Agent Executor",
|
|
4
4
|
"oneLine": "aex is an agent execution platform for launching autonomous agents from a simple TypeScript SDK and CLI.",
|
|
5
|
-
"description": "Open durable agent sessions, send turns, stream events, capture outputs, and compose agents with skills, files, MCP,
|
|
5
|
+
"description": "Open durable agent sessions, send turns, stream events, capture outputs, and compose agents with skills, files, MCP, secrets, networking controls, and subagents across the managed runtime.",
|
|
6
6
|
"alpha": {
|
|
7
7
|
"label": "Alpha testing",
|
|
8
8
|
"description": "Access is limited to invited testers while we harden the hosted runtime, dashboard, and SDK workflows."
|
|
9
9
|
},
|
|
10
|
-
"installCommand": "
|
|
10
|
+
"installCommand": "npm i @aexhq/sdk",
|
|
11
11
|
"examples": {
|
|
12
12
|
"typescriptLines": [
|
|
13
13
|
"import { Aex, Models, Sizes } from \"@aexhq/sdk\";",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"slug": "agent-composition",
|
|
62
62
|
"href": "/docs/features/#agent-composition",
|
|
63
63
|
"title": "Agent composition",
|
|
64
|
-
"description": "Skills, files, AGENTS.md, remote MCP servers,
|
|
64
|
+
"description": "Skills, files, AGENTS.md, remote MCP servers, environment variables, packages, secrets, and networking controls."
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"slug": "subagents",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"slug": "typed-control-surface",
|
|
80
80
|
"href": "/docs/features/#typed-control-surface",
|
|
81
81
|
"title": "Typed control surface",
|
|
82
|
-
"description": "Strongly typed SDK inputs, CLI parity, BYOK
|
|
82
|
+
"description": "Strongly typed SDK inputs, CLI parity, BYOK provider keys, workspace secrets, redaction, and output modes."
|
|
83
83
|
}
|
|
84
84
|
]
|
|
85
85
|
}
|
package/docs/quickstart.md
CHANGED
|
@@ -7,16 +7,18 @@ title: Quickstart
|
|
|
7
7
|
## 1. Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
npm i @aexhq/sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
This installs the TypeScript SDK exports and the bundled `aex` CLI.
|
|
14
14
|
|
|
15
15
|
## 2. Set credentials
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
aex is currently in **invite-only beta**: workspaces and API tokens are issued
|
|
18
|
+
by the aex team — contact <support@aex.dev> for beta access. Once you have
|
|
19
|
+
access, create a quickstart SDK token with `runs:read`, `runs:write`, and
|
|
20
|
+
`outputs:read` in the dashboard at <https://aex.dev>. The examples also need
|
|
21
|
+
your BYOK provider key for the model you choose. For the Claude examples below:
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
export AEX_API_TOKEN="<your-aex-token>"
|
|
@@ -28,7 +30,7 @@ export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
|
|
|
28
30
|
```ts
|
|
29
31
|
import { Aex, Models, Sizes } from "@aexhq/sdk";
|
|
30
32
|
|
|
31
|
-
const aex = new Aex(
|
|
33
|
+
const aex = new Aex(process.env.AEX_API_TOKEN!);
|
|
32
34
|
|
|
33
35
|
const session = await aex.openSession({
|
|
34
36
|
model: Models.CLAUDE_HAIKU_4_5,
|
|
@@ -83,11 +85,8 @@ for await (const event of turn) {
|
|
|
83
85
|
}
|
|
84
86
|
await turn.done();
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// assistant message (an AssistantTextEntry; use ?.text for the string).
|
|
89
|
-
const lastText = (await session.messages().last())?.text;
|
|
90
|
-
console.log(lastText);
|
|
88
|
+
const messages = await session.messages().list();
|
|
89
|
+
console.log(messages.at(-1)?.text);
|
|
91
90
|
|
|
92
91
|
// Poll the record until the session parks (idle / suspended / error).
|
|
93
92
|
const record = await session.wait();
|
|
@@ -110,8 +109,8 @@ aex run \
|
|
|
110
109
|
|
|
111
110
|
## Add capabilities
|
|
112
111
|
|
|
113
|
-
- Add files, skills, AGENTS.md, MCP servers,
|
|
114
|
-
-
|
|
115
|
-
-
|
|
112
|
+
- Add files, skills, AGENTS.md, MCP servers, packages, and networking controls with [Composition](concepts/composition.md).
|
|
113
|
+
- Delegate bounded sub-tasks to child runs with [Subagents](concepts/subagents.md).
|
|
114
|
+
- Get notified when a run finishes with [Webhooks](webhooks.md).
|
|
116
115
|
- Narrow output capture or download individual files with [Outputs](outputs.md).
|
|
117
116
|
- Check supported providers and models in the [provider/runtime capability matrix](provider-runtime-capabilities.md).
|
package/docs/run-config.md
CHANGED
|
@@ -13,13 +13,16 @@ Allowed fields:
|
|
|
13
13
|
- `mcpServers` - array of `McpServerRef`; headers are split into the vaulted secrets channel server-side.
|
|
14
14
|
- `environment` - `{ networking?, packages?, variables? }`. Networking is open by default; set `networking.mode` to `limited` only when you want an allowlist. `variables` are merged into the in-container `RUNTIME.env` / `RUNTIME.json` mounts. (Run secrets go in `environment.secrets`, which carries live `Secret` instances and is not part of a shareable config.)
|
|
15
15
|
- `runtime` - optional managed-runtime preset. Prefer `Sizes` in TypeScript.
|
|
16
|
-
- `proxyEndpoints` - array of `ProxyEndpoint` instances; endpoint-level `retry` is allowed here and remains declaration-based.
|
|
17
16
|
- `metadata` - non-secret structured metadata.
|
|
18
17
|
- `overrides` - `{ idleTtl?, timeout?, maxSpendUsd? }`. `timeout` is an optional session deadline (e.g. `"30m"`, `"2h"`); `maxSpendUsd` stops the session once its spend would exceed the cap (see [Limits & quotas](limits-and-quotas.md)).
|
|
19
18
|
|
|
20
|
-
`message` (the one-shot `run` input), `agentsMd`, `files`, `outputs`, `tools`, `includeBuiltinTools`, and `outputMode` are `openSession` / `run` options, not reusable run-config fields. They carry the turn input, bytes, capture behavior, or agent tool/output controls that belong on a concrete call. Skill bundles are `tools` entries built with `Tools.fromSkillDir(...)` / `Tools.fromSkillUrl(...)`, so they too are SDK-code options rather than config fields. Subagents
|
|
19
|
+
`message` (the one-shot `run` input), `agentsMd`, `files`, `outputs`, `tools`, `includeBuiltinTools`, and `outputMode` are `openSession` / `run` options, not reusable run-config fields. They carry the turn input, bytes, capture behavior, or agent tool/output controls that belong on a concrete call. Skill bundles are `tools` entries built with `Tools.fromSkillDir(...)` / `Tools.fromSkillUrl(...)`, so they too are SDK-code options rather than config fields. Subagents are session-internal (the in-run `subagent` tool — see [Subagents](concepts/subagents.md)); there is no `parentRunId` option. The wire contract carries a per-run `limits` object (the exported `RunLimits` type: `maxConcurrentChildRuns`, `maxSubagentDepth`, `maxSpendUsd`), but the session surface exposes only its spend dial — set it with `overrides.maxSpendUsd`; the subagent depth/breadth dials are not settable per-session today and take the platform defaults.
|
|
21
20
|
|
|
22
|
-
Secrets never live in run config. Pass provider keys through the top-level
|
|
21
|
+
Secrets never live in run config. Pass provider keys through the top-level
|
|
22
|
+
`apiKeys` map and runtime secrets through `environment.secrets` in the SDK, or
|
|
23
|
+
the equivalent host-mode flags (`--anthropic-api-key`, `--mcp-auth`) in the CLI.
|
|
24
|
+
See [Secrets](secrets.md) for secret lifecycles and [Credentials](credentials.md)
|
|
25
|
+
for credential handling.
|
|
23
26
|
|
|
24
27
|
## Reuse in code
|
|
25
28
|
|
|
@@ -52,4 +55,4 @@ aex run --config ./run.json \
|
|
|
52
55
|
--anthropic-api-key "$ANTHROPIC_API_KEY"
|
|
53
56
|
```
|
|
54
57
|
|
|
55
|
-
...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--runtime-size`, `--run-timeout`, `--
|
|
58
|
+
...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--runtime-size`, `--run-timeout`, `--metadata`). The two modes are mutually exclusive.
|