@aexhq/sdk 0.40.15 → 0.41.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 +25 -25
- package/dist/_contracts/api-key.d.ts +6 -4
- package/dist/_contracts/api-key.js +20 -7
- package/dist/_contracts/asset-upload-helper.d.ts +2 -2
- package/dist/_contracts/asset-upload-helper.js +2 -2
- package/dist/_contracts/connection-ticket.d.ts +5 -5
- package/dist/_contracts/connection-ticket.js +7 -7
- package/dist/_contracts/error-codes.d.ts +1 -1
- package/dist/_contracts/error-codes.js +4 -4
- package/dist/_contracts/error-factory.js +1 -1
- package/dist/_contracts/event-envelope.d.ts +41 -41
- package/dist/_contracts/event-envelope.js +49 -49
- package/dist/_contracts/event-stream-client.d.ts +9 -9
- package/dist/_contracts/event-stream-client.js +5 -5
- package/dist/_contracts/event-view.d.ts +6 -6
- package/dist/_contracts/event-view.js +12 -12
- package/dist/_contracts/http.js +1 -1
- package/dist/_contracts/index.d.ts +10 -10
- package/dist/_contracts/index.js +9 -9
- package/dist/_contracts/models.d.ts +17 -17
- package/dist/_contracts/models.js +14 -14
- package/dist/_contracts/operations.d.ts +79 -82
- package/dist/_contracts/operations.js +233 -243
- package/dist/_contracts/provider-support.d.ts +2 -2
- package/dist/_contracts/runner-event.d.ts +8 -8
- package/dist/_contracts/runner-event.js +9 -9
- package/dist/_contracts/runtime-manifest.d.ts +12 -12
- package/dist/_contracts/runtime-manifest.js +1 -1
- package/dist/_contracts/runtime-sizes.d.ts +12 -12
- package/dist/_contracts/runtime-sizes.js +18 -18
- package/dist/_contracts/runtime-types.d.ts +117 -117
- package/dist/_contracts/runtime-types.js +2 -2
- package/dist/_contracts/sdk-errors.d.ts +3 -3
- package/dist/_contracts/sdk-errors.js +4 -4
- package/dist/_contracts/sdk-secrets.js +10 -10
- package/dist/_contracts/session-artifacts.d.ts +151 -0
- package/dist/_contracts/session-artifacts.js +234 -0
- package/dist/_contracts/{run-config.d.ts → session-config.d.ts} +29 -29
- package/dist/_contracts/{run-config.js → session-config.js} +37 -37
- package/dist/_contracts/session-cost.d.ts +140 -0
- package/dist/_contracts/{run-cost.js → session-cost.js} +94 -94
- package/dist/_contracts/{run-custody.d.ts → session-custody.d.ts} +16 -16
- package/dist/_contracts/{run-custody.js → session-custody.js} +42 -42
- package/dist/_contracts/session-record.d.ts +112 -0
- package/dist/_contracts/{run-record.js → session-record.js} +23 -23
- package/dist/_contracts/session-retention.d.ts +209 -0
- package/dist/_contracts/{run-retention.js → session-retention.js} +116 -116
- package/dist/_contracts/{run-unit.d.ts → session-unit.d.ts} +38 -38
- package/dist/_contracts/{run-unit.js → session-unit.js} +33 -33
- package/dist/_contracts/side-effect-audit.d.ts +14 -14
- package/dist/_contracts/side-effect-audit.js +38 -38
- package/dist/_contracts/sse.d.ts +1 -1
- package/dist/_contracts/sse.js +0 -0
- package/dist/_contracts/stable.d.ts +1 -1
- package/dist/_contracts/stable.js +1 -1
- package/dist/_contracts/status.d.ts +18 -18
- package/dist/_contracts/status.js +19 -19
- package/dist/_contracts/submission.d.ts +101 -99
- package/dist/_contracts/submission.js +96 -96
- package/dist/_contracts/{run-trace.d.ts → turn-trace.d.ts} +1 -1
- package/dist/_contracts/webhook-verify.d.ts +1 -1
- package/dist/_contracts/webhook-verify.js +1 -1
- package/dist/agents-md.d.ts +4 -4
- package/dist/agents-md.js +5 -5
- package/dist/agents-md.js.map +1 -1
- package/dist/bundle.d.ts +1 -1
- package/dist/bundle.js +1 -1
- package/dist/bundle.js.map +1 -1
- package/dist/cli.mjs +580 -623
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +141 -155
- package/dist/client.js +236 -245
- package/dist/client.js.map +1 -1
- package/dist/fetch-archive.js +3 -3
- package/dist/file.d.ts +7 -7
- package/dist/file.js +8 -8
- package/dist/file.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +1 -1
- package/dist/retry.d.ts +1 -1
- package/dist/secret.d.ts +6 -6
- package/dist/secret.js +6 -6
- package/dist/secret.js.map +1 -1
- package/dist/skill.d.ts +2 -2
- package/dist/skill.js +3 -3
- package/dist/skill.js.map +1 -1
- package/dist/tool.js +2 -2
- package/dist/tool.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/authentication.md +11 -11
- package/docs/billing.md +3 -3
- package/docs/cleanup.md +5 -5
- package/docs/concepts/agent-tools.md +5 -5
- package/docs/concepts/composition.md +3 -3
- package/docs/concepts/providers-and-runtimes.md +3 -3
- package/docs/concepts/{runs.md → sessions.md} +8 -8
- package/docs/concepts/subagents.md +16 -16
- package/docs/credentials.md +7 -7
- package/docs/defaults.md +14 -14
- package/docs/errors.md +10 -10
- package/docs/events.md +26 -26
- package/docs/files.md +267 -0
- package/docs/limits-and-quotas.md +35 -35
- package/docs/limits.md +6 -6
- package/docs/mcp.md +3 -3
- package/docs/networking.md +24 -24
- package/docs/provider-runtime-capabilities.md +1 -1
- package/docs/public-surface.json +6 -6
- package/docs/quickstart.md +13 -13
- package/docs/release.md +5 -5
- package/docs/retries.md +3 -3
- package/docs/secrets.md +16 -16
- package/docs/session-config.md +58 -0
- package/docs/{run-record.md → session-record.md} +16 -16
- package/docs/skills.md +9 -9
- package/docs/vision-skills.md +7 -7
- package/docs/webhooks.md +11 -11
- package/examples/feature-tour.ts +17 -17
- package/examples/spike-settle-latency.ts +11 -11
- package/package.json +1 -1
- package/dist/_contracts/run-artifacts.d.ts +0 -49
- package/dist/_contracts/run-artifacts.js +0 -102
- package/dist/_contracts/run-cost.d.ts +0 -140
- package/dist/_contracts/run-record.d.ts +0 -116
- package/dist/_contracts/run-retention.d.ts +0 -209
- package/docs/outputs.md +0 -267
- package/docs/run-config.md +0 -58
- /package/dist/_contracts/{run-trace.js → turn-trace.js} +0 -0
package/docs/files.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Files
|
|
6
|
+
|
|
7
|
+
Every session produces durable metadata (status, events, cleanup state) and a files namespace backed by the latest complete checkpoint of the session workspace. By default, managed sessions expose regular workspace files from that checkpoint, EXCLUDING the inputs the platform itself materialized for you (your mounted `files`/`skills`) — those are excluded by IDENTITY (their exact destination paths and skill-dir prefixes), not by any before/after timing comparison. There is no default or official capture directory. Use `fileCapture.allowedDirs` only when you want to narrow the visible file roots, and `fileCapture.deniedDirs` to subtract noise. `session.download()` returns the public session record — metadata, typed events, and captured file bytes — as a zip; the per-namespace verbs (`session.files().download()` / `session.events().download()` / `session.downloadMetadata()`) return one slice each.
|
|
8
|
+
|
|
9
|
+
The file verbs below hang off the session's `files()` accessor
|
|
10
|
+
(`session.files().list()`, `.read()`, `.download()`, …). Reach a handle from a
|
|
11
|
+
live session (`openSession` / `run`) or reopen one later with
|
|
12
|
+
`aex.openSession(sessionId)`; the client also exposes cross-session reads under
|
|
13
|
+
`aex.sessions.*`.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Models } from "@aexhq/sdk";
|
|
19
|
+
|
|
20
|
+
const session = await aex.openSession({
|
|
21
|
+
model: Models.CLAUDE_HAIKU_4_5,
|
|
22
|
+
apiKeys: { anthropic: apiKey }
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await session.send("Produce a report and save it as a file.").done();
|
|
26
|
+
await session.wait();
|
|
27
|
+
await session.download({ to: "./session.zip" });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx aex download <session-id> --out ./session.zip --api-key …
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## The three namespaces
|
|
35
|
+
|
|
36
|
+
A session's downloadable content is organised into three logical namespaces, each with a matching verb. Every zip is assembled **client-side** from the public read endpoints (session record + `session.events().list()` + `session.files().list()` + per-file `/download`) — there is no server-side archive route.
|
|
37
|
+
|
|
38
|
+
| Namespace | What it holds | Verb | CLI |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `files` | The session's real deliverables. | `session.files().download()` | `download <id> --only files` |
|
|
41
|
+
| `events` | Typed event-channel records (`events.jsonl`) plus a namespace manifest. | `session.events().download()` | `download <id> --only events` |
|
|
42
|
+
| `metadata` | The session record (`session.json`) plus a namespace manifest. | `session.downloadMetadata()` | `download <id> --only metadata` |
|
|
43
|
+
|
|
44
|
+
Platform diagnostics are stored outside the public archive under `sessions/<sessionId>/internal/logs/` for internal/admin access only. They are not exposed by the SDK download helpers or the public CLI.
|
|
45
|
+
|
|
46
|
+
## What `session.download()` returns
|
|
47
|
+
|
|
48
|
+
`session.download()` is the **whole-session** verb — it bundles the public namespaces as top-level folders. It is distinct from `session.files().download(selector)`, which fetches a single file. Layout:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
metadata/session.json # session record (status, sessionId, timestamps, snapshot)
|
|
52
|
+
metadata/submission.json # public-safe submission snapshot, when available
|
|
53
|
+
metadata/cost.json # public cost telemetry, when available
|
|
54
|
+
events/events.jsonl # typed event-channel records, ordered
|
|
55
|
+
files/<name> # one file per deliverable
|
|
56
|
+
manifest.json # SessionRecordManifestV1
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`manifest.json` is the versioned `SessionRecordManifestV1` described in [Session record](session-record.md). It carries:
|
|
60
|
+
|
|
61
|
+
| Field | Meaning |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `schemaVersion` / `sessionRecordSchemaVersion` | Manifest and session-record contract versions. |
|
|
64
|
+
| `sessionId` | The session the zip was assembled for. |
|
|
65
|
+
| `namespaces[]` / `files[]` | Namespace inventory and per-file presence state. Optional submission/cost files are marked `present` only when the client assembled actual entries; custody remains `pending` until its writer/read path exists. |
|
|
66
|
+
| `sessionFiles[]` | `{ id, filename, sizeBytes?, contentType? }` — one row per file successfully written under `files/`. |
|
|
67
|
+
| `errors[]` | `{ namespace, id, filename, message }` — per-artifact byte fetches that failed during assembly. Best-effort: a failure records an entry here and is skipped from the tree rather than aborting the whole zip. |
|
|
68
|
+
|
|
69
|
+
The single-namespace verbs return the same per-file bytes at the zip root (e.g. `session.files().download()` -> `report.txt` + `manifest.json`; `session.events().download()` -> `events.jsonl` + `manifest.json`; `session.downloadMetadata()` -> `session.json` + `manifest.json`).
|
|
70
|
+
|
|
71
|
+
## Downloading one file
|
|
72
|
+
|
|
73
|
+
`session.files().download(selector)` returns a `Uint8Array`. Omit the selector to download the whole files namespace as a zip; pass a file from `session.files().list()`, an `{ id }`, or a path selector against the listed `SessionFile.filename` values to download one file:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const allFiles = await session.files().download();
|
|
77
|
+
await session.files().download(undefined, { to: "./files.zip" });
|
|
78
|
+
|
|
79
|
+
const report = await session.files().download({ path: "reports/report.txt" });
|
|
80
|
+
console.log(new TextDecoder().decode(report));
|
|
81
|
+
|
|
82
|
+
const looseReport = await session.files().download({ path: "report.txt", match: "suffix" });
|
|
83
|
+
console.log(looseReport.byteLength);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Reading one file as text
|
|
87
|
+
|
|
88
|
+
`session.files().read(selector, options?)` reads ONE file as byte-capped, decoded UTF-8 text. It streams the file and stops at `options.maxBytes` (default 50 KB, ceiling 10 MB), so a large deliverable never fully buffers — this is the read built for handing a session file to an LLM tool. Select the file by `{ path }` (suffix-matchable) or `{ id }`. To read from a session id without a live handle, use `aex.sessions.files(sessionId).read(selector, options?)` — the same accessor, addressed by id.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const { text, truncated, totalBytes } = await session.files().read(
|
|
92
|
+
{ path: "report.md", match: "suffix" },
|
|
93
|
+
{ maxBytes: 50_000, grep: "error" }
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (truncated) {
|
|
97
|
+
// text is a prefix of a larger file — narrow with `grep` or a tighter `path`.
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
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 `file` is the matched `SessionFile` record, and `totalBytes` is the file's full size when the server reports it.
|
|
102
|
+
|
|
103
|
+
## Finding files
|
|
104
|
+
|
|
105
|
+
`session.files().list(query?)` can filter the captured file list client-side. Use `session.files().find(query)` when you want discovery to be explicit, or `session.files().findOne(query)` when exactly one file is expected:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const images = await session.files().find({ type: "image" });
|
|
109
|
+
const jsonReports = await session.files().list({
|
|
110
|
+
dir: "reports",
|
|
111
|
+
extension: ".json"
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const report = await session.files().findOne({
|
|
115
|
+
filename: "summary.json",
|
|
116
|
+
contentType: "application/json"
|
|
117
|
+
});
|
|
118
|
+
if (report) {
|
|
119
|
+
const bytes = await session.files().download(report);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Query fields compose with AND semantics:
|
|
124
|
+
|
|
125
|
+
| Field | Match |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `path` | Exact normalized file path. Leading `/` and `files/` are ignored. |
|
|
128
|
+
| `filename` | Basename match, as a string or `RegExp`. |
|
|
129
|
+
| `dir` / `recursive` | Directory prefix. `recursive` defaults to `true`; set `false` for direct children only. |
|
|
130
|
+
| `extension` | Case-insensitive extension, with or without a leading dot. |
|
|
131
|
+
| `contentType` | Exact content type or a prefix wildcard such as `image/*`. |
|
|
132
|
+
| `type` | High-level type: `text`, `json`, `image`, `audio`, `video`, `pdf`, `archive`, `binary`, or `unknown`. |
|
|
133
|
+
|
|
134
|
+
`session.files().findOne(query)` returns `null` when nothing matches and throws `SessionStateError` when the query matches more than one file.
|
|
135
|
+
|
|
136
|
+
## Searching files
|
|
137
|
+
|
|
138
|
+
Search is metadata-only (reference hits — filename / extension / content type — no bytes). `filename` accepts a `string` (case-insensitive substring) or a `RegExp`. A content-shaped query (`content`/`text`/…) throws a typed "content search unsupported" rather than silently returning zero hits.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// One session's files:
|
|
142
|
+
const hits = await session.files().search({ filename: /report/i });
|
|
143
|
+
|
|
144
|
+
// Across every session in the workspace (or scope with sessionIds):
|
|
145
|
+
const all = await aex.files.search({ extension: "md", sessionIds: ["session-a", "session-b"] });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Each hit is `{ sessionId, fileId, filename?, sizeBytes?, contentType? }`; read the bytes with `session.files().read(...)` / `.download(...)`.
|
|
149
|
+
|
|
150
|
+
## CLI
|
|
151
|
+
|
|
152
|
+
The `aex files` verb is a thin pass-through over the same SDK accessor, so every per-file operation has a subcommand (`npx aex` on a local install):
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx aex files <session-id> # list captured files (NDJSON)
|
|
156
|
+
npx aex files read <session-id> <path> # read one file as capped text (JSON)
|
|
157
|
+
npx aex files download <session-id> <path> --out f # download one file's raw bytes
|
|
158
|
+
npx aex files link <session-id> <path> # mint a temporary download URL (JSON)
|
|
159
|
+
npx aex files find <session-id> --name S --ext E --type T
|
|
160
|
+
npx aex files search --query S --ext E --session-id ID # cross-session metadata search
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`aex files search` (no session id) is the cross-session search (`aex.files.search`); the whole-namespace zip stays `aex download <session-id>`.
|
|
164
|
+
|
|
165
|
+
## Temporary file links
|
|
166
|
+
|
|
167
|
+
Use `session.files().link(selectorOrQuery, options?)` when another process, browser, media tag, or downloader needs a direct artifact URL instead of bytes buffered through the SDK.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const link = await session.files().link(
|
|
171
|
+
{ path: "reports/summary.json" },
|
|
172
|
+
{ expiresIn: "15m" }
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
console.log(link.url, link.expiresAt);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Selectors can be a file id, a `SessionFile` object, a path selector, or a `SessionFileQuery`. `expiresIn` accepts seconds or `"15m"`, `"1h"`, or `"1d"`; the default is `"1h"`.
|
|
179
|
+
|
|
180
|
+
The returned URL is a reusable bearer URL until it expires. Anyone who has it can read that artifact during the TTL. aex does not promise one-time use or early revocation for these direct artifact URLs.
|
|
181
|
+
|
|
182
|
+
For large files, `session.files().fetch()` mints the same temporary URL and returns the `Response` from fetching it directly, without adding the SDK API key to that second request:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const response = await session.files().fetch({ type: "video", filename: /clip\.mp4$/ });
|
|
186
|
+
const stream = response.body;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Lifecycle behaviour
|
|
190
|
+
|
|
191
|
+
`session.download()` works at any session state — it reads whatever the public endpoints currently expose, so the zip reflects the session as of the call:
|
|
192
|
+
|
|
193
|
+
| SessionRecord state | Behaviour |
|
|
194
|
+
| --- | --- |
|
|
195
|
+
| `queued` / `claiming` / `provisioning` | `metadata/session.json` reflects the early state; `events/` and `files/` are typically empty. |
|
|
196
|
+
| `provider_running`, mid-session / `capturing_files` / `cleaning_up` | Whatever events + files have been captured so far. Call again after the session parks for the complete set. |
|
|
197
|
+
| `idle` / `suspended` (parked between turns) | The complete archive for every turn sent so far; a later turn appends to it. |
|
|
198
|
+
| `succeeded` / `failed` / `timed_out` / `cancelled` | The complete typed event archive + all captured files. |
|
|
199
|
+
|
|
200
|
+
## `fileCapture.allowedDirs` — override capture roots
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
aex.openSession({
|
|
204
|
+
/* ... */
|
|
205
|
+
fileCapture: {
|
|
206
|
+
allowedDirs: ["/workspace/reports", "/workspace/state"]
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
When omitted, aex exposes the session workspace files from the latest complete checkpoint, subject to mandatory platform excludes. When supplied, `fileCapture.allowedDirs` is a whitelist that replaces that default with the listed roots. In other words, explicit `fileCapture.allowedDirs` narrows file capture; it does not add paths on top of `/`.
|
|
212
|
+
|
|
213
|
+
Validation:
|
|
214
|
+
|
|
215
|
+
- absolute UNIX paths only (`/...`),
|
|
216
|
+
- no `..` segments, no NUL bytes,
|
|
217
|
+
- maximum 32 entries,
|
|
218
|
+
- maximum 512 bytes per entry.
|
|
219
|
+
|
|
220
|
+
Runtime notes:
|
|
221
|
+
|
|
222
|
+
- The managed runtime exposes regular files from the latest complete checkpoint under the capture roots, EXCLUDING the inputs the platform itself materialized (your mounted `files`/`skills`) by IDENTITY — their exact destination paths and skill-dir prefixes are threaded into the capture filter, so an untouched mounted input is never re-emitted as a captured file regardless of path policy or timing.
|
|
223
|
+
- If you pass an explicit root that does not exist by terminal time, that root contributes no files.
|
|
224
|
+
|
|
225
|
+
## `fileCapture.deniedDirs` — subtract noise
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
aex.openSession({
|
|
229
|
+
/* ... */
|
|
230
|
+
fileCapture: {
|
|
231
|
+
deniedDirs: ["node_modules", "/var/cache", "*.tmp"]
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`fileCapture.deniedDirs` is subtracted from the capture roots. Entries may be an absolute subtree (`/var/cache`), a bare path segment (`node_modules`), or a `*.ext` extension match. Denied entries beat allowed roots. Platform-mandatory excludes, including pseudo-filesystems and secret/platform paths, always apply and cannot be re-included.
|
|
237
|
+
|
|
238
|
+
Mechanism (no platform-magical paths — this is honest):
|
|
239
|
+
|
|
240
|
+
1. The hosted platform materializes the workspace (your mounted `files`/`skills`) and records the exact destination paths + skill-dir prefixes it wrote.
|
|
241
|
+
2. The agent sessions normally. There is no extra model turn and no synthetic sync instruction.
|
|
242
|
+
3. At a complete checkpoint, the runner scans the capture roots and drops any file whose path is a materialized INPUT (exact path or under a materialized skill dir) — inputs are excluded by WHO PUT THEM THERE (the platform), not by timing.
|
|
243
|
+
4. The runner uploads the remaining regular files to durable session file storage. Diagnostic log paths are routed to internal diagnostics under `sessions/<sessionId>/internal/logs/`; other paths are routed to `files`.
|
|
244
|
+
|
|
245
|
+
Cost: file capture does not add a model turn. The runner pays a filesystem scan and upload cost near the end of the session.
|
|
246
|
+
|
|
247
|
+
Capture notes:
|
|
248
|
+
|
|
249
|
+
- Files over a configured per-file size cap are skipped.
|
|
250
|
+
- Once total file or byte caps are reached, remaining changed files are dropped from upload.
|
|
251
|
+
- Files that vanish between scan and upload are skipped.
|
|
252
|
+
- Upload failures are recorded in runner diagnostics. The zip's `manifest.errors[]` only records byte fetches that failed while assembling the download archive.
|
|
253
|
+
|
|
254
|
+
## Sessions without explicit `fileCapture.allowedDirs`
|
|
255
|
+
|
|
256
|
+
Metadata still gets the full treatment. aex exposes every regular workspace file from the latest complete checkpoint outside mandatory platform excludes. A session that produces no files still returns a whole-session zip with `metadata/session.json`, `events/events.jsonl`, and `manifest.json` (manifest `files: []`).
|
|
257
|
+
|
|
258
|
+
## Mid-session download semantics
|
|
259
|
+
|
|
260
|
+
Mid-session calls are **best-effort and side-effect-free**: they expose whatever files from a complete checkpoint have already been uploaded. Files written by the agent are normally uploaded after a complete checkpoint, once the runner scans the capture roots. If you need the full file set, wait for the session to park and call `session.download()` again.
|
|
261
|
+
|
|
262
|
+
## Safety
|
|
263
|
+
|
|
264
|
+
- Filenames are sanitized for cross-platform safety; collisions are disambiguated with a short id suffix before the extension.
|
|
265
|
+
- Downloads stay within the requested local directory.
|
|
266
|
+
- The archive endpoint is workspace-scoped (`files:read` scope) and rate-limited (`AEX_RATE_LIMIT_SESSION_ARCHIVE_PER_MINUTE`, default 30/min/workspace).
|
|
267
|
+
- `manifest.json` never contains file bytes — only ids, paths, sizes, content types.
|
|
@@ -4,7 +4,7 @@ title: Limits & quotas
|
|
|
4
4
|
|
|
5
5
|
# Limits & quotas
|
|
6
6
|
|
|
7
|
-
These are the hard ceilings and caps that bound a
|
|
7
|
+
These are the hard ceilings and caps that bound a session, a workspace, and a single
|
|
8
8
|
request. Every value is mirrored from a single source-of-truth constant in the
|
|
9
9
|
platform's limits module; this page is hand-maintained against those constants.
|
|
10
10
|
If a value here ever disagrees with the constant, the constant wins.
|
|
@@ -19,21 +19,21 @@ Each row is tagged with its **source**:
|
|
|
19
19
|
- **aex policy** — an aex platform ceiling, the same for every workspace.
|
|
20
20
|
- **Workspace default** — a per-workspace value with a configurable override.
|
|
21
21
|
|
|
22
|
-
And whether you can **raise** it: per-
|
|
22
|
+
And whether you can **raise** it: per-session option, per-plan, or no.
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## SessionRecord scope
|
|
25
25
|
|
|
26
26
|
| Limit | Value | Source | Raisable? | Constant |
|
|
27
27
|
| --- | --- | --- | --- | --- |
|
|
28
|
-
| Maximum
|
|
29
|
-
| Minimum
|
|
30
|
-
| Per-call exec timeout (default) | 30 minutes | aex policy | Per-call via the tool call's `timeoutMs` | `
|
|
31
|
-
| MCP connect timeout (default) | 30 seconds | aex policy | Per-port via `connectTimeoutMs` | `
|
|
32
|
-
| MCP call timeout (default) | 30 minutes | aex policy | Per-port via `callTimeoutMs` | `
|
|
28
|
+
| Maximum session timeout | 8 hours (also the default when `timeout` is omitted) | aex policy | Per plan (billing-driven) | `SESSION_MAX_TIMEOUT_MS` |
|
|
29
|
+
| Minimum session timeout | 1 minute | aex policy | No (floor) | `SESSION_MIN_TIMEOUT_MS` |
|
|
30
|
+
| Per-call exec timeout (default) | 30 minutes | aex policy | Per-call via the tool call's `timeoutMs` | `SESSION_DEFAULT_EXEC_TIMEOUT_MS` |
|
|
31
|
+
| MCP connect timeout (default) | 30 seconds | aex policy | Per-port via `connectTimeoutMs` | `SESSION_DEFAULT_MCP_CONNECT_TIMEOUT_MS` |
|
|
32
|
+
| MCP call timeout (default) | 30 minutes | aex policy | Per-port via `callTimeoutMs` | `SESSION_DEFAULT_MCP_CALL_TIMEOUT_MS` |
|
|
33
33
|
| Per-session spend cap | None by default; when set, the session is stopped once its spend would exceed the cap | aex policy | Per-session via `overrides.maxSpendUsd` (a positive USD amount) | — |
|
|
34
|
-
| Max agent iterations (turns) per
|
|
34
|
+
| Max agent iterations (turns) per session | 20 by default; hard ceiling 200 | aex policy | Per-session via `overrides.maxTurns` (a positive integer, clamped to the ceiling) | `SESSION_DEFAULT_MAX_TURNS` / `SESSION_MAX_TURNS_CEILING` |
|
|
35
35
|
|
|
36
|
-
###
|
|
36
|
+
### SessionFile capture (per session)
|
|
37
37
|
|
|
38
38
|
Files stream from the container to object storage one at a time. When a cap is
|
|
39
39
|
reached, remaining files are **dropped and counted in the summary**, never
|
|
@@ -41,22 +41,22 @@ silently lost.
|
|
|
41
41
|
|
|
42
42
|
| Limit | Value | Source | Raisable? | Constant |
|
|
43
43
|
| --- | --- | --- | --- | --- |
|
|
44
|
-
| Capture wall-clock budget | 1 hour | aex policy | No (hard ceiling) | `
|
|
45
|
-
| Max files captured | 50,000 | aex policy | No (hard ceiling) | `
|
|
46
|
-
| Max bytes per captured file | 500 GB (decimal) | aex policy | No (hard ceiling) | `
|
|
47
|
-
| Max total captured bytes | 500 GB (decimal) | aex policy | No (hard ceiling) | `
|
|
44
|
+
| Capture wall-clock budget | 1 hour | aex policy | No (hard ceiling) | `SESSION_CAPTURE_DEFAULT_TIMEOUT_MS` |
|
|
45
|
+
| Max files captured | 50,000 | aex policy | No (hard ceiling) | `SESSION_CAPTURE_MAX_FILES` |
|
|
46
|
+
| Max bytes per captured file | 500 GB (decimal) | aex policy | No (hard ceiling) | `SESSION_CAPTURE_MAX_FILE_BYTES` |
|
|
47
|
+
| Max total captured bytes | 500 GB (decimal) | aex policy | No (hard ceiling) | `SESSION_CAPTURE_MAX_TOTAL_BYTES` |
|
|
48
48
|
|
|
49
|
-
### Tool output caps (per
|
|
49
|
+
### Tool output caps (per session)
|
|
50
50
|
|
|
51
51
|
| Limit | Value | Source | Raisable? | Constant |
|
|
52
52
|
| --- | --- | --- | --- | --- |
|
|
53
53
|
| `web_fetch` returned body | 500 KB (UTF-8) | aex policy | Per-call via `max_bytes` | `REQUEST_WEB_FETCH_DEFAULT_MAX_BYTES` |
|
|
54
|
-
| `bash_output` per-read body | 1 MB (UTF-8) | aex policy | No (hard ceiling) | `
|
|
55
|
-
| `grep` max file size (larger files skipped — use `bash grep`) | 25 MB | aex policy | No (hard ceiling) | `
|
|
56
|
-
| `head`/`tail` max file size (larger files rejected — use `bash head`/`tail`) | 100 MB | aex policy | No (hard ceiling) | `
|
|
57
|
-
| `grep`/`glob` files visited per recursive walk (then truncates with a notice) | 100,000 | aex policy | No (hard ceiling) | `
|
|
54
|
+
| `bash_output` per-read body | 1 MB (UTF-8) | aex policy | No (hard ceiling) | `SESSION_BASH_BG_OUTPUT_MAX_BYTES` |
|
|
55
|
+
| `grep` max file size (larger files skipped — use `bash grep`) | 25 MB | aex policy | No (hard ceiling) | `SESSION_TOOL_GREP_MAX_FILE_BYTES` |
|
|
56
|
+
| `head`/`tail` max file size (larger files rejected — use `bash head`/`tail`) | 100 MB | aex policy | No (hard ceiling) | `SESSION_TOOL_HEAD_TAIL_MAX_FILE_BYTES` |
|
|
57
|
+
| `grep`/`glob` files visited per recursive walk (then truncates with a notice) | 100,000 | aex policy | No (hard ceiling) | `SESSION_TOOL_WALK_MAX_FILES` |
|
|
58
58
|
|
|
59
|
-
### Subagents (per
|
|
59
|
+
### Subagents (per session lineage)
|
|
60
60
|
|
|
61
61
|
Subagent lineage bounds are enforced server-side but are intentionally not
|
|
62
62
|
advertised as fixed public ceilings because capacity can change. The runtime
|
|
@@ -65,19 +65,19 @@ by the platform at spawn time.
|
|
|
65
65
|
|
|
66
66
|
| Limit | Value | Source | Raisable? | Constant |
|
|
67
67
|
| --- | --- | --- | --- | --- |
|
|
68
|
-
| Max subagent depth (`subagent` tool) | High recursive subagent depth, bounded server-side. | aex policy | No public per-
|
|
69
|
-
| Concurrent child
|
|
68
|
+
| Max subagent depth (`subagent` tool) | High recursive subagent depth, bounded server-side. | aex policy | No public per-session override (subagents session in-process) | `SESSION_MAX_PUBLIC_SUBAGENT_DEPTH` |
|
|
69
|
+
| Concurrent child sessions per lineage root | Designed to scale across hundreds, even thousands, of live child agents, bounded server-side. | aex policy | No public per-session override (subagents session in-process) | `SESSION_DEFAULT_MAX_CONCURRENT_CHILD_SESSIONS` |
|
|
70
70
|
|
|
71
|
-
### Retention (per
|
|
71
|
+
### Retention (per session)
|
|
72
72
|
|
|
73
73
|
| Limit | Value | Source | Raisable? | Constant |
|
|
74
74
|
| --- | --- | --- | --- | --- |
|
|
75
|
-
| Per-
|
|
76
|
-
| Per-
|
|
75
|
+
| Per-session metadata record TTL | 24 hours | aex policy | No (hard ceiling) | `SESSION_KV_RECORD_TTL_SECONDS` |
|
|
76
|
+
| Per-session secret-envelope TTL | 24 hours | aex policy | No (hard ceiling) | `SESSION_KV_SECRET_TTL_SECONDS` |
|
|
77
77
|
|
|
78
78
|
## Sandbox (managed runtime)
|
|
79
79
|
|
|
80
|
-
Each
|
|
80
|
+
Each session executes in an ephemeral Linux container sized by the `runtime` preset.
|
|
81
81
|
A few behaviours are worth knowing before you rely on the filesystem or RAM:
|
|
82
82
|
|
|
83
83
|
- **Only `/workspace` persists across turns** of the same session. Everything
|
|
@@ -95,7 +95,7 @@ A few behaviours are worth knowing before you rely on the filesystem or RAM:
|
|
|
95
95
|
[`packages/contracts/src/runtime-sizes.ts`](https://github.com/aexhq/aex/blob/main/packages/contracts/src/runtime-sizes.ts);
|
|
96
96
|
see [Defaults](defaults.md).
|
|
97
97
|
- **The agent loop is bounded** by `maxTurns` (default 20, ceiling 200) — a
|
|
98
|
-
documented, per-
|
|
98
|
+
documented, per-session-overridable limit (see the SessionRecord scope table above and
|
|
99
99
|
`overrides.maxTurns`), not a silent cutoff.
|
|
100
100
|
|
|
101
101
|
## Workspace scope
|
|
@@ -103,7 +103,7 @@ A few behaviours are worth knowing before you rely on the filesystem or RAM:
|
|
|
103
103
|
| Limit | Value | Source | Raisable? | Constant |
|
|
104
104
|
| --- | --- | --- | --- | --- |
|
|
105
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` |
|
|
106
|
-
| Max concurrent
|
|
106
|
+
| Max concurrent sessions per workspace | Plan-based: **5** live (non-terminal) root sessions 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.maxConcurrentSessions`. | Workspace default (per plan) | Per-plan (upgrade) or per-workspace override (contact support), clamped to the 200 ceiling | `PLANS[planKey].maxConcurrentSessions` / `WORKSPACE_MAX_CONCURRENT_SESSIONS_CEILING` |
|
|
107
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` |
|
|
108
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` |
|
|
109
109
|
| Skill bundle max decompressed size (sum of uncompressed file sizes) | 50 MB | aex policy | No (hard ceiling) | `SKILL_BUNDLE_LIMITS.maxDecompressedBytes` |
|
|
@@ -114,8 +114,8 @@ A few behaviours are worth knowing before you rely on the filesystem or RAM:
|
|
|
114
114
|
|
|
115
115
|
### Rate limits (per workspace, per minute)
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
minute** per workspace by default (`0` = disabled). Past it, `POST /
|
|
117
|
+
SessionRecord submission has its own platform-enforced velocity cap: **120 submits per
|
|
118
|
+
minute** per workspace by default (`0` = disabled). Past it, `POST /sessions` fails
|
|
119
119
|
with `429 workspace_submit_rate_exceeded` (see [Errors](errors.md)). It is
|
|
120
120
|
overridable per-plane via `AEX_WORKSPACE_SUBMIT_RATE_PER_MIN` or per-workspace
|
|
121
121
|
via support.
|
|
@@ -125,16 +125,16 @@ per-plane via the matching `AEX_RATE_LIMIT_<ACTION>_PER_MINUTE` env var.
|
|
|
125
125
|
|
|
126
126
|
| Action | Default per minute | Source | Constant |
|
|
127
127
|
| --- | --- | --- | --- |
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
| Signed
|
|
128
|
+
| SessionRecord cancel | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
129
|
+
| SessionRecord delete | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
130
|
+
| Signed file link | 120 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
131
131
|
| API key create | 10 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
132
132
|
| API key delete | 30 | Workspace default | `WORKSPACE_RATE_LIMIT_DEFAULTS` |
|
|
133
133
|
|
|
134
134
|
### Introspecting your effective caps
|
|
135
135
|
|
|
136
136
|
`aex.whoami()` (CLI: `aex whoami`) returns a `limits` object carrying the
|
|
137
|
-
workspace's *effective* values for the caps above — `
|
|
137
|
+
workspace's *effective* values for the caps above — `maxConcurrentSessions`,
|
|
138
138
|
`submitRatePerMinute`, `spendCapUsd`, plus the live `monthSpendUsd`,
|
|
139
139
|
`balanceUsd`, `balanceGraceFloorUsd`, and `paymentMethodStatus` — resolved by
|
|
140
140
|
the same code the admission gates use, so you can anticipate a `429`/`402`
|
|
@@ -145,5 +145,5 @@ before submitting. See [Authentication](authentication.md) and
|
|
|
145
145
|
|
|
146
146
|
| Limit | Value | Source | Raisable? | Constant |
|
|
147
147
|
| --- | --- | --- | --- | --- |
|
|
148
|
-
| Signed
|
|
148
|
+
| Signed file URL TTL | 300 seconds | aex policy | Per-call via `expiresSeconds` | `REQUEST_PRESIGN_URL_DEFAULT_TTL_SECONDS` |
|
|
149
149
|
| Event-stream connection ticket TTL | 60 seconds | aex policy | Per-mint via `ttlMs` | `REQUEST_TICKET_DEFAULT_TTL_MS` |
|
package/docs/limits.md
CHANGED
|
@@ -4,8 +4,8 @@ title: Limits
|
|
|
4
4
|
|
|
5
5
|
# Limits
|
|
6
6
|
|
|
7
|
-
aex
|
|
8
|
-
sessions, sends turns, streams events, captures
|
|
7
|
+
aex sessions autonomous agents on the hosted managed runtime. The SDK opens durable
|
|
8
|
+
sessions, sends turns, streams events, captures files, and exposes auth-gated
|
|
9
9
|
reads and downloads.
|
|
10
10
|
|
|
11
11
|
For what the product supports, see [Features](https://aex.dev/docs/features/).
|
|
@@ -16,7 +16,7 @@ For the current provider/model set, see the generated
|
|
|
16
16
|
|
|
17
17
|
| Area | Default |
|
|
18
18
|
| --- | --- |
|
|
19
|
-
| Workspace storage | 500 GB per workspace for captured
|
|
19
|
+
| Workspace storage | 500 GB per workspace for captured files and workspace artifacts. aex-maintainer admin workspaces may be unlimited for internal dogfooding; this is not a customer entitlement. |
|
|
20
20
|
|
|
21
21
|
## Product Boundaries
|
|
22
22
|
|
|
@@ -26,9 +26,9 @@ For the current provider/model set, see the generated
|
|
|
26
26
|
| Provider policy | Provider retention, training exclusion, HIPAA/BAA, data residency, abuse policy, and pricing belong to the selected provider account, endpoint, and contract. |
|
|
27
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). |
|
|
28
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. |
|
|
29
|
-
|
|
|
30
|
-
| Human review |
|
|
31
|
-
| Sessions | The durable product primitive is the session/
|
|
29
|
+
| Files | Captured files, events, and metadata are stored under the session record and downloaded through auth-gated routes. SessionFile content is customer content. |
|
|
30
|
+
| Human review | Sessions execute after submission. Cancellation is available, but aex does not pause a session for platform-mediated approval or interactive clarification. |
|
|
31
|
+
| Sessions | The durable product primitive is the session/session 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. |
|
|
32
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. |
|
|
33
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. |
|
|
34
34
|
|
package/docs/mcp.md
CHANGED
|
@@ -8,7 +8,7 @@ MCP support is remote HTTPS/SSE only. Stdio MCP servers are rejected because aex
|
|
|
8
8
|
|
|
9
9
|
Rules:
|
|
10
10
|
|
|
11
|
-
- MCP servers are declared in the
|
|
11
|
+
- MCP servers are declared in the session request config (`mcpServers`).
|
|
12
12
|
- Runtime HITL is disabled.
|
|
13
13
|
- Tool policy must be configured before session start.
|
|
14
14
|
- Enabled MCP tools use `always_allow` provider permissions.
|
|
@@ -35,11 +35,11 @@ putting the whole response in model context:
|
|
|
35
35
|
2. Keep any upstream HTTPS credentials in `environment.secrets`.
|
|
36
36
|
3. Have the CLI write the full payload to the session filesystem. By default,
|
|
37
37
|
files it creates or modifies are captured automatically; pass
|
|
38
|
-
`
|
|
38
|
+
`fileCapture.allowedDirs` only when you want to narrow capture to specific roots.
|
|
39
39
|
Return only a small handle (path, item count, summary) to the model.
|
|
40
40
|
|
|
41
41
|
The agent sees the handle in context; the bytes ride out through
|
|
42
|
-
`download()` as
|
|
42
|
+
`download()` as normal captured files.
|
|
43
43
|
|
|
44
44
|
If you genuinely want everything in context (small responses, code
|
|
45
45
|
search, etc.), use MCP. If the payload would blow your context budget,
|
package/docs/networking.md
CHANGED
|
@@ -4,12 +4,12 @@ title: Networking
|
|
|
4
4
|
|
|
5
5
|
# Networking
|
|
6
6
|
|
|
7
|
-
A
|
|
7
|
+
A session executes your agent's code in a sandbox with **no direct route to the
|
|
8
8
|
internet**. Outbound traffic is governed by **two layers**:
|
|
9
9
|
|
|
10
|
-
1. **The per-
|
|
11
|
-
runtime *inside the
|
|
12
|
-
HTTP client uses (see below) and can only *narrow* what the
|
|
10
|
+
1. **The per-session policy (`environment.networking`)** — enforced by the agent
|
|
11
|
+
runtime *inside the session*. It applies to the standard proxy path every normal
|
|
12
|
+
HTTP client uses (see below) and can only *narrow* what the session may reach.
|
|
13
13
|
2. **The platform ceiling** — a fixed, platform-managed egress boundary every
|
|
14
14
|
connection ultimately traverses. It allows the hosts aex itself manages
|
|
15
15
|
(model providers, built-in tool endpoints, package registries, and related
|
|
@@ -20,19 +20,19 @@ internet**. Outbound traffic is governed by **two layers**:
|
|
|
20
20
|
plane a subprocess that deliberately bypasses the proxy with a **raw socket**
|
|
21
21
|
can still reach the on-link task-metadata IP (`169.254.170.2`), which exposes
|
|
22
22
|
non-secret task identity (AWS account id via the task ARN, cluster/image ref)
|
|
23
|
-
— but **never IAM credentials** (the
|
|
23
|
+
— but **never IAM credentials** (the session's task role is unset, so the metadata
|
|
24
24
|
credential endpoint serves nothing) and never another tenant's data.
|
|
25
25
|
|
|
26
|
-
Honest boundary statement: the per-
|
|
27
|
-
|
|
26
|
+
Honest boundary statement: the per-session `allowedHosts` policy is enforced by the
|
|
27
|
+
session's own runtime on the standard proxy path — it is **not** yet enforced at
|
|
28
28
|
the platform proxy layer. A subprocess that deliberately bypasses the standard
|
|
29
29
|
proxy environment (a raw socket / raw CONNECT) is bounded by the **platform
|
|
30
|
-
ceiling** rather than by the per-
|
|
30
|
+
ceiling** rather than by the per-session list. Per-session enforcement at the platform
|
|
31
31
|
proxy layer is planned; until it ships, treat `allowedHosts` as a strong
|
|
32
32
|
default-path control and an auditable statement of intent, not a hard isolation
|
|
33
|
-
boundary against adversarial code inside the
|
|
33
|
+
boundary against adversarial code inside the session.
|
|
34
34
|
|
|
35
|
-
**Default posture.** A
|
|
35
|
+
**Default posture.** A session that does not set `environment.networking` sessions in
|
|
36
36
|
`open` mode: its own code may reach anything within the platform ceiling with
|
|
37
37
|
no allowlist required. Use `environment.networking` to *narrow* that surface
|
|
38
38
|
when you want a tighter, auditable egress posture. Code cannot widen the
|
|
@@ -43,7 +43,7 @@ ceiling from inside the container.
|
|
|
43
43
|
These reach the network over managed platform paths and are **not** subject to
|
|
44
44
|
`environment.networking`, so you never list their hosts:
|
|
45
45
|
|
|
46
|
-
- The model / provider call for the
|
|
46
|
+
- The model / provider call for the session (and its subagents).
|
|
47
47
|
- The built-in `web_search` and `web_fetch` tools. They run over a managed,
|
|
48
48
|
SSRF-guarded server-side path, which is why they can reach arbitrary public
|
|
49
49
|
URLs even though your own code is bounded by the ceiling.
|
|
@@ -57,12 +57,12 @@ These reach the network over managed platform paths and are **not** subject to
|
|
|
57
57
|
code makes — a `curl` in the `bash` tool, a `requests`/`urllib` call in Python,
|
|
58
58
|
a `fetch` in `code_execution`, or a third-party SDK.
|
|
59
59
|
|
|
60
|
-
## Restrict a
|
|
60
|
+
## Restrict a session to an allowlist
|
|
61
61
|
|
|
62
62
|
Set `mode: "limited"` and list exactly the hosts your code is allowed to reach.
|
|
63
|
-
The
|
|
63
|
+
The session's runtime enforces the list on the standard proxy path: a connection to
|
|
64
64
|
a host that is neither on the list nor one of the always-allowed paths above is
|
|
65
|
-
refused before it leaves the
|
|
65
|
+
refused before it leaves the session. Package-registry hosts implied by
|
|
66
66
|
`environment.packages` are appended automatically so installs keep working.
|
|
67
67
|
|
|
68
68
|
Note that `allowedHosts` narrows *within* the platform ceiling — listing a host
|
|
@@ -76,7 +76,7 @@ import { Aex, Models, Providers } from "@aexhq/sdk";
|
|
|
76
76
|
|
|
77
77
|
const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
|
|
78
78
|
|
|
79
|
-
await aex.
|
|
79
|
+
await aex.start({
|
|
80
80
|
provider: Providers.ANTHROPIC,
|
|
81
81
|
model: Models.CLAUDE_HAIKU_4_5,
|
|
82
82
|
message: "Fetch the public status page and summarize it.",
|
|
@@ -100,15 +100,15 @@ visible at the same call site as the code that needs it.
|
|
|
100
100
|
|
|
101
101
|
## Open mode
|
|
102
102
|
|
|
103
|
-
`open` is the default: a
|
|
103
|
+
`open` is the default: a session that omits `environment.networking` already sessions in
|
|
104
104
|
open mode. Set `mode: "open"` explicitly when you want to be unambiguous. Open
|
|
105
|
-
mode applies no per-
|
|
105
|
+
mode applies no per-session allowlist — the session's own code may reach anything the
|
|
106
106
|
platform ceiling allows, still subject to the SSRF deny-list. Prefer `limited`
|
|
107
|
-
whenever you can name the hosts — it gives the
|
|
107
|
+
whenever you can name the hosts — it gives the session a stable, auditable,
|
|
108
108
|
least-privilege egress surface (it is the tighter posture, not the default).
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
|
-
await aex.
|
|
111
|
+
await aex.start({
|
|
112
112
|
model: Models.CLAUDE_HAIKU_4_5,
|
|
113
113
|
message: "Research the topic across the open web.",
|
|
114
114
|
environment: { networking: { mode: "open" } },
|
|
@@ -139,12 +139,12 @@ your client succeeds without extra setup.
|
|
|
139
139
|
|
|
140
140
|
## Limitations and gotchas
|
|
141
141
|
|
|
142
|
-
- **The per-
|
|
142
|
+
- **The per-session policy is enforced by the session's runtime, not at the platform
|
|
143
143
|
proxy.** Clients that honor the standard proxy environment (almost all HTTP
|
|
144
144
|
tooling) are held to the `allowedHosts` list. A subprocess that deliberately
|
|
145
145
|
ignores the proxy environment and opens a raw connection is **not** held to
|
|
146
|
-
the per-
|
|
147
|
-
provider/tool/registry host set) and the SSRF deny-list instead. Per-
|
|
146
|
+
the per-session list — it is bounded by the platform ceiling (the aex-managed
|
|
147
|
+
provider/tool/registry host set) and the SSRF deny-list instead. Per-session
|
|
148
148
|
enforcement at the platform proxy layer is planned.
|
|
149
149
|
- **A client that hard-bypasses the standard environment may fail to connect.**
|
|
150
150
|
A client that ignores the proxy environment, pins or replaces its certificate
|
|
@@ -159,5 +159,5 @@ your client succeeds without extra setup.
|
|
|
159
159
|
|
|
160
160
|
For credentialed HTTP calls, pass the credential as an `environment.secrets`
|
|
161
161
|
entry and let your code use its normal HTTP client. For remote tool servers, see
|
|
162
|
-
[MCP](mcp.md). For the full set of
|
|
163
|
-
[
|
|
162
|
+
[MCP](mcp.md). For the full set of session-config fields, see
|
|
163
|
+
[SessionRecord configuration](session-config.md).
|
|
@@ -40,7 +40,7 @@ All new submissions run on the managed runtime. Public support is expressed as s
|
|
|
40
40
|
|
|
41
41
|
## Skills
|
|
42
42
|
|
|
43
|
-
Skills are supplied through the top-level `skills` option. Build one with `Skill.fromDir`, `Skill.fromUrl`, `Skill.fromFiles`, `Skill.fromContent`, or `Skill.fromBytes`; each normalizes to a named workspace skill that the platform snapshots into durable
|
|
43
|
+
Skills are supplied through the top-level `skills` option. Build one with `Skill.fromDir`, `Skill.fromUrl`, `Skill.fromFiles`, `Skill.fromContent`, or `Skill.fromBytes`; each normalizes to a named workspace skill that the platform snapshots into durable session asset storage.
|
|
44
44
|
|
|
45
45
|
Notes:
|
|
46
46
|
|