@aexhq/sdk 0.40.6 → 0.40.7

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/docs/billing.md CHANGED
@@ -1,118 +1,118 @@
1
- ---
2
- title: Billing & webhook signing secret
3
- ---
4
-
5
- # Billing & webhook signing secret
6
-
7
- Workspace-level billing, subscription, and webhook verification calls are
8
- token-scoped like every other client call — the workspace is derived
9
- server-side from the API key.
10
-
11
- The billing reads — `aex.billing()`, `aex.billingLedger()`, and the CLI
12
- `aex billing` (and its `ledger` sub-verb) — require the **`billing:read`**
13
- scope; a token without it fails with `403 insufficient_scope` (see
14
- [Errors](errors.md)). This is why the [Quickstart](quickstart.md) mints
15
- `billing:read` alongside `runs:read` / `runs:write` / `outputs:read`.
16
-
17
- ## Read the billing summary
18
-
19
- `aex.billing()` returns the workspace's prepaid balance, current-month spend,
20
- and the spend cap enforced on new runs, plus plan fields:
21
-
22
- ```ts
23
- import { Aex } from "@aexhq/sdk";
24
-
25
- const aex = new Aex(process.env.AEX_API_KEY!);
26
- const billing = await aex.billing();
27
- console.log(billing.balanceUsd, billing.monthSpendUsd, billing.spendCapUsd);
28
- ```
29
-
30
- The returned `BillingSummary` is additive-tolerant: fields a newer deployment
31
- reports that this SDK version does not know yet pass through on the object
32
- instead of being rejected.
33
-
34
- CLI equivalent:
35
-
36
- ```bash
37
- aex billing # human-readable balance / month spend / spend cap
38
- aex billing --json # the raw wire body for scripting
39
- ```
40
-
41
- ## Manage the subscription
42
-
43
- `aex.billingCheckout({ planKey })` creates a hosted checkout session for a paid
44
- plan. Open the returned URL in a browser; the workspace plan changes after
45
- checkout completes and the hosted API confirms the subscription.
46
-
47
- ```ts
48
- const { url } = await aex.billingCheckout({
49
- planKey: "pro",
50
- idempotencyKey: crypto.randomUUID()
51
- });
52
- console.log(url);
53
- ```
54
-
55
- `aex.billingPortal()` creates a hosted billing portal session for the workspace:
56
-
57
- ```ts
58
- const { url } = await aex.billingPortal({ returnUrl: "https://aex.dev/billing" });
59
- console.log(url);
60
- ```
61
-
62
- CLI equivalents:
63
-
64
- ```bash
65
- aex billing upgrade pro
66
- aex billing portal
67
- ```
68
-
69
- ## Read the credit ledger
70
-
71
- `aex.billingLedger({ limit })` returns recent credit-ledger rows, newest first —
72
- allowance grants, adjustments, and run charges with signed `amountUsd` (credits
73
- positive, charges negative). `limit` is clamped server-side to [1, 100] (default
74
- 25); the read is not cursor-paged.
75
-
76
- ```ts
77
- const { entries } = await aex.billingLedger({ limit: 50 });
78
- for (const entry of entries) {
79
- console.log(entry.createdAt, entry.entryType, entry.amountUsd);
80
- }
81
- ```
82
-
83
- CLI equivalent:
84
-
85
- ```bash
86
- aex billing ledger --limit 50 # JSON rows, newest first
87
- ```
88
-
89
- ## Reveal the webhook signing secret
90
-
91
- Run webhooks are signed Standard-Webhooks style with a per-workspace secret.
92
- `aex.webhookSigningSecret()` reveals it (creating one on first use) as the
93
- `whsec_<base64>` string that `verifyAexWebhook` takes as `secret`:
94
-
95
- ```ts
96
- import { Aex, verifyAexWebhook } from "@aexhq/sdk";
97
-
98
- const aex = new Aex(process.env.AEX_API_KEY!);
99
- const { whsec } = await aex.webhookSigningSecret();
100
-
101
- // In your webhook receiver:
102
- const verified = await verifyAexWebhook({
103
- rawBody, // the exact request body bytes as a string
104
- headers, // the inbound request headers
105
- secret: whsec
106
- });
107
- ```
108
-
109
- Repeat calls return the SAME value — the hosted API does not rotate the
110
- signing secret. Treat the reveal as sensitive: store it in your secret manager
111
- and never log it.
112
-
113
- CLI equivalent (prints the bare `whsec_...` string, pipeable into a secret
114
- store; the reveal never goes to stderr or debug traces):
115
-
116
- ```bash
117
- aex webhooks secret
118
- ```
1
+ ---
2
+ title: Billing & webhook signing secret
3
+ ---
4
+
5
+ # Billing & webhook signing secret
6
+
7
+ Workspace-level billing, subscription, and webhook verification calls are
8
+ token-scoped like every other client call — the workspace is derived
9
+ server-side from the API key.
10
+
11
+ The billing reads — `aex.billing()`, `aex.billingLedger()`, and the CLI
12
+ `aex billing` (and its `ledger` sub-verb) — require the **`billing:read`**
13
+ scope; a token without it fails with `403 insufficient_scope` (see
14
+ [Errors](errors.md)). This is why the [Quickstart](quickstart.md) mints
15
+ `billing:read` alongside `runs:read` / `runs:write` / `outputs:read`.
16
+
17
+ ## Read the billing summary
18
+
19
+ `aex.billing()` returns the workspace's prepaid balance, current-month spend,
20
+ and the spend cap enforced on new runs, plus plan fields:
21
+
22
+ ```ts
23
+ import { Aex } from "@aexhq/sdk";
24
+
25
+ const aex = new Aex(process.env.AEX_API_KEY!);
26
+ const billing = await aex.billing();
27
+ console.log(billing.balanceUsd, billing.monthSpendUsd, billing.spendCapUsd);
28
+ ```
29
+
30
+ The returned `BillingSummary` is additive-tolerant: fields a newer deployment
31
+ reports that this SDK version does not know yet pass through on the object
32
+ instead of being rejected.
33
+
34
+ CLI equivalent:
35
+
36
+ ```bash
37
+ aex billing # human-readable balance / month spend / spend cap
38
+ aex billing --json # the raw wire body for scripting
39
+ ```
40
+
41
+ ## Manage the subscription
42
+
43
+ `aex.billingCheckout({ planKey })` creates a hosted checkout session for a paid
44
+ plan. Open the returned URL in a browser; the workspace plan changes after
45
+ checkout completes and the hosted API confirms the subscription.
46
+
47
+ ```ts
48
+ const { url } = await aex.billingCheckout({
49
+ planKey: "pro",
50
+ idempotencyKey: crypto.randomUUID()
51
+ });
52
+ console.log(url);
53
+ ```
54
+
55
+ `aex.billingPortal()` creates a hosted billing portal session for the workspace:
56
+
57
+ ```ts
58
+ const { url } = await aex.billingPortal({ returnUrl: "https://aex.dev/billing" });
59
+ console.log(url);
60
+ ```
61
+
62
+ CLI equivalents:
63
+
64
+ ```bash
65
+ aex billing upgrade pro
66
+ aex billing portal
67
+ ```
68
+
69
+ ## Read the credit ledger
70
+
71
+ `aex.billingLedger({ limit })` returns recent credit-ledger rows, newest first —
72
+ allowance grants, adjustments, and run charges with signed `amountUsd` (credits
73
+ positive, charges negative). `limit` is clamped server-side to [1, 100] (default
74
+ 25); the read is not cursor-paged.
75
+
76
+ ```ts
77
+ const { entries } = await aex.billingLedger({ limit: 50 });
78
+ for (const entry of entries) {
79
+ console.log(entry.createdAt, entry.entryType, entry.amountUsd);
80
+ }
81
+ ```
82
+
83
+ CLI equivalent:
84
+
85
+ ```bash
86
+ aex billing ledger --limit 50 # JSON rows, newest first
87
+ ```
88
+
89
+ ## Reveal the webhook signing secret
90
+
91
+ Run webhooks are signed Standard-Webhooks style with a per-workspace secret.
92
+ `aex.webhookSigningSecret()` reveals it (creating one on first use) as the
93
+ `whsec_<base64>` string that `verifyAexWebhook` takes as `secret`:
94
+
95
+ ```ts
96
+ import { Aex, verifyAexWebhook } from "@aexhq/sdk";
97
+
98
+ const aex = new Aex(process.env.AEX_API_KEY!);
99
+ const { whsec } = await aex.webhookSigningSecret();
100
+
101
+ // In your webhook receiver:
102
+ const verified = await verifyAexWebhook({
103
+ rawBody, // the exact request body bytes as a string
104
+ headers, // the inbound request headers
105
+ secret: whsec
106
+ });
107
+ ```
108
+
109
+ Repeat calls return the SAME value — the hosted API does not rotate the
110
+ signing secret. Treat the reveal as sensitive: store it in your secret manager
111
+ and never log it.
112
+
113
+ CLI equivalent (prints the bare `whsec_...` string, pipeable into a secret
114
+ store; the reveal never goes to stderr or debug traces):
115
+
116
+ ```bash
117
+ aex webhooks secret
118
+ ```
@@ -1,66 +1,66 @@
1
- ---
2
- title: Agent tools
3
- description: The default builtin tools available inside managed runs.
4
- icon: TerminalSquare
5
- ---
6
-
7
- Managed runs inject the complete builtin tool set into the agent by default:
8
-
9
- - `bash`, `code_execution` — run shell commands / model-written snippets
10
- - `read_file`, `write_file`, `edit_file` — file read/create/patch
11
- - `grep`, `glob` — search file contents and paths
12
- - `head`, `tail` — read bounded file slices
13
- - `ls`, `stat`, `wc` — list a directory, inspect path metadata, count lines/words/bytes
14
- - `web_fetch`, `web_search` — fetch a URL / managed web search
15
- - `todo_write` — maintain a todo list
16
- - `subagent`, `subagent_result` — delegate to and read back from child runs (see [Subagents](subagents.md))
17
- - `bash_output`, `bash_kill` — manage background bash jobs
18
- - `wait`, `git` — bounded idle-yield and first-class git
19
-
20
- ## Toggling builtins
21
-
22
- Set `includeBuiltinTools: false` to inject NO builtins — useful for a pure-MCP
23
- or pure-custom run where every tool comes from `mcpServers` or `tools`.
24
-
25
- `includeBuiltinTools` defaults to `true`.
26
-
27
- ## Cherry-picking builtins
28
-
29
- The `tools` list accepts both custom tool bundles and BUILTIN tool references
30
- (bare name strings, preferably `BuiltinTools.<name>`). Use builtin references
31
- to pick a narrow subset alongside `includeBuiltinTools: false`.
32
-
33
- The final tool list is ordered: resolved builtin tools, then custom tools, then
34
- MCP tools.
35
-
36
- ## Custom tools
37
-
38
- Attach your own tool in `tools` as a bundle built with `Tool.fromFiles(...)` (an
39
- explicit `{ entry, files }`) or `Tool.fromPath(dir)` (a directory with a
40
- `tool.json` at its root). The bundle's **entry must be a JS module** — a
41
- `.js`/`.mjs`/`.cjs` file present in the bundle that **default-exports a function
42
- or an object with an `execute` method**. This is validated at authoring time, so
43
- a non-JS entry (e.g. a `run.sh`) is rejected right where you build the tool, not
44
- opaquely mid-run when the runtime's module loader would fail to import it.
45
-
46
- Networking is open by default within the platform's managed egress ceiling and
47
- a fixed SSRF deny-list. `web_fetch` and `web_search` reach the network over a
48
- managed, SSRF-guarded path that is **not** governed by `environment.networking`,
49
- so their hosts never need to be listed in a `limited` allowlist. Setting
50
- `environment.networking.mode` to `limited` restricts only the agent's own
51
- arbitrary egress (e.g. a `curl` in `bash`); the built-in web tools keep working.
52
- See [Networking](../networking.md) for the full two-layer model.
53
-
54
- ## Disable builtins
55
-
56
- ```ts
57
- import { Models } from "@aexhq/sdk";
58
-
59
- await aex.run({
60
- model: Models.CLAUDE_HAIKU_4_5,
61
- message: "Use only the declared MCP tools.",
62
- mcpServers,
63
- includeBuiltinTools: false,
64
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
65
- });
66
- ```
1
+ ---
2
+ title: Agent tools
3
+ description: The default builtin tools available inside managed runs.
4
+ icon: TerminalSquare
5
+ ---
6
+
7
+ Managed runs inject the complete builtin tool set into the agent by default:
8
+
9
+ - `bash`, `code_execution` — run shell commands / model-written snippets
10
+ - `read_file`, `write_file`, `edit_file` — file read/create/patch
11
+ - `grep`, `glob` — search file contents and paths
12
+ - `head`, `tail` — read bounded file slices
13
+ - `ls`, `stat`, `wc` — list a directory, inspect path metadata, count lines/words/bytes
14
+ - `web_fetch`, `web_search` — fetch a URL / managed web search
15
+ - `todo_write` — maintain a todo list
16
+ - `subagent`, `subagent_result` — delegate to and read back from child runs (see [Subagents](subagents.md))
17
+ - `bash_output`, `bash_kill` — manage background bash jobs
18
+ - `wait`, `git` — bounded idle-yield and first-class git
19
+
20
+ ## Toggling builtins
21
+
22
+ Set `includeBuiltinTools: false` to inject NO builtins — useful for a pure-MCP
23
+ or pure-custom run where every tool comes from `mcpServers` or `tools`.
24
+
25
+ `includeBuiltinTools` defaults to `true`.
26
+
27
+ ## Cherry-picking builtins
28
+
29
+ The `tools` list accepts both custom tool bundles and BUILTIN tool references
30
+ (bare name strings, preferably `BuiltinTools.<name>`). Use builtin references
31
+ to pick a narrow subset alongside `includeBuiltinTools: false`.
32
+
33
+ The final tool list is ordered: resolved builtin tools, then custom tools, then
34
+ MCP tools.
35
+
36
+ ## Custom tools
37
+
38
+ Attach your own tool in `tools` as a bundle built with `Tool.fromFiles(...)` (an
39
+ explicit `{ entry, files }`) or `Tool.fromPath(dir)` (a directory with a
40
+ `tool.json` at its root). The bundle's **entry must be a JS module** — a
41
+ `.js`/`.mjs`/`.cjs` file present in the bundle that **default-exports a function
42
+ or an object with an `execute` method**. This is validated at authoring time, so
43
+ a non-JS entry (e.g. a `run.sh`) is rejected right where you build the tool, not
44
+ opaquely mid-run when the runtime's module loader would fail to import it.
45
+
46
+ Networking is open by default within the platform's managed egress ceiling and
47
+ a fixed SSRF deny-list. `web_fetch` and `web_search` reach the network over a
48
+ managed, SSRF-guarded path that is **not** governed by `environment.networking`,
49
+ so their hosts never need to be listed in a `limited` allowlist. Setting
50
+ `environment.networking.mode` to `limited` restricts only the agent's own
51
+ arbitrary egress (e.g. a `curl` in `bash`); the built-in web tools keep working.
52
+ See [Networking](../networking.md) for the full two-layer model.
53
+
54
+ ## Disable builtins
55
+
56
+ ```ts
57
+ import { Models } from "@aexhq/sdk";
58
+
59
+ await aex.run({
60
+ model: Models.CLAUDE_HAIKU_4_5,
61
+ message: "Use only the declared MCP tools.",
62
+ mcpServers,
63
+ includeBuiltinTools: false,
64
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
65
+ });
66
+ ```
@@ -1,40 +1,40 @@
1
- ---
2
- title: Composition
3
- description: The primitives used to assemble each run.
4
- icon: Blocks
5
- ---
6
-
7
- aex composes an agent from explicit per-session inputs. The SDK materializes local
8
- bytes before the session lands and the platform mounts them into the managed
9
- runtime before the first agent turn.
10
-
11
- | Need | Primitive |
12
- | --- | --- |
1
+ ---
2
+ title: Composition
3
+ description: The primitives used to assemble each run.
4
+ icon: Blocks
5
+ ---
6
+
7
+ aex composes an agent from explicit per-session inputs. The SDK materializes local
8
+ bytes before the session lands and the platform mounts them into the managed
9
+ runtime before the first agent turn.
10
+
11
+ | Need | Primitive |
12
+ | --- | --- |
13
13
  | Instructional skill bundles | `Skill.fromDir`, `Skill.fromUrl`, `Skill.fromFiles` |
14
- | Agent instructions | `AgentsMd.fromPath`, `AgentsMd.fromContent` |
15
- | Reference files and folders | `File.fromPath`, `File.fromBytes` |
16
- | Remote tools | `McpServer.remote`, `McpServer.fromId` |
17
- | Non-secret runtime settings | `environment.variables`, `environment.packages`, `environment.networking` |
18
- | Runtime secrets for your code | `Secret.value`, `Secret.ref`, `environment.secrets` |
19
-
20
- ```ts
14
+ | Agent instructions | `AgentsMd.fromPath`, `AgentsMd.fromContent` |
15
+ | Reference files and folders | `File.fromPath`, `File.fromBytes` |
16
+ | Remote tools | `McpServer.remote`, `McpServer.fromId` |
17
+ | Non-secret runtime settings | `environment.variables`, `environment.packages`, `environment.networking` |
18
+ | Runtime secrets for your code | `Secret.value`, `Secret.ref`, `environment.secrets` |
19
+
20
+ ```ts
21
21
  import { AgentsMd, File, McpServer, Models, Secret, Skill } from "@aexhq/sdk";
22
-
23
- await aex.run({
24
- model: Models.CLAUDE_HAIKU_4_5,
25
- message: "Use the attached docs and tools to produce a report.",
26
- agentsMd: [await AgentsMd.fromContent("Follow the repo conventions.", { name: "repo-rules" })],
27
- files: [await File.fromPath("./input")],
22
+
23
+ await aex.run({
24
+ model: Models.CLAUDE_HAIKU_4_5,
25
+ message: "Use the attached docs and tools to produce a report.",
26
+ agentsMd: [await AgentsMd.fromContent("Follow the repo conventions.", { name: "repo-rules" })],
27
+ files: [await File.fromPath("./input")],
28
28
  skills: [await Skill.fromDir("./skills/report-writer", { name: "report-writer" })],
29
- mcpServers: [McpServer.remote({ name: "github", url: "https://example.com/mcp" })],
30
- environment: {
31
- secrets: { INTERNAL_API_TOKEN: Secret.value(process.env.INTERNAL_API_TOKEN!) },
32
- networking: { mode: "limited", allowedHosts: ["api.example.com"] }
33
- },
34
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
35
- });
36
- ```
37
-
38
- Secrets stay out of reusable configs. Provider keys go in the top-level `apiKeys`
39
- map; reusable or per-run values for your own code go in `environment.secrets`.
40
- Your code then makes normal HTTP calls with the standard client for that service.
29
+ mcpServers: [McpServer.remote({ name: "github", url: "https://example.com/mcp" })],
30
+ environment: {
31
+ secrets: { INTERNAL_API_TOKEN: Secret.value(process.env.INTERNAL_API_TOKEN!) },
32
+ networking: { mode: "limited", allowedHosts: ["api.example.com"] }
33
+ },
34
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
35
+ });
36
+ ```
37
+
38
+ Secrets stay out of reusable configs. Provider keys go in the top-level `apiKeys`
39
+ map; reusable or per-run values for your own code go in `environment.secrets`.
40
+ Your code then makes normal HTTP calls with the standard client for that service.
@@ -1,54 +1,54 @@
1
- ---
2
- title: Providers & runtimes
3
- description: How provider selection maps to managed runtime execution.
4
- icon: Network
5
- ---
6
-
7
- aex exposes one submission shape across supported providers:
8
-
9
- | Provider | Selector |
10
- | --- | --- |
11
- | Anthropic | `Providers.ANTHROPIC` |
12
- | DeepSeek | `Providers.DEEPSEEK` |
13
- | OpenAI | `Providers.OPENAI` |
14
- | Gemini | `Providers.GEMINI` |
15
- | Mistral | `Providers.MISTRAL` |
16
- | OpenRouter | `Providers.OPENROUTER` |
17
- | Doubao | `Providers.DOUBAO` |
18
- | Doubao China | `Providers.DOUBAO_CN` |
19
-
20
- All submissions run on the managed runtime. The optional `runtime` option picks
21
- a managed machine-size preset — use `Sizes.*` in TypeScript (e.g.
22
- `runtime: Sizes.SHARED_0_25X_1GB`) or `--runtime-size` in the CLI. Omit it for
23
- the default size; there is no alternative runtime backend to select.
24
-
25
- ## Selection
26
-
27
- ### TypeScript
28
-
29
- ```ts
30
- import { Models, Providers } from "@aexhq/sdk";
31
-
32
- await aex.run({
33
- provider: Providers.OPENAI,
34
- model: Models.GPT_4_1,
35
- message: "Summarise the attached files.",
36
- apiKeys: { openai: process.env.OPENAI_API_KEY! }
37
- });
38
- ```
39
-
40
- ### CLI
41
-
42
- ```bash
43
- aex run \
44
- --api-key "$AEX_API_KEY" \
45
- --provider openai \
46
- --openai-api-key "$OPENAI_API_KEY" \
47
- --model gpt-4.1 \
48
- --prompt "Summarise the attached files." \
49
- --follow
50
- ```
51
-
52
- Events, outputs, cleanup, and downloads use the same SDK and CLI surface for
53
- every provider. For the exact supported model list, use the generated
54
- [provider/runtime capability matrix](../provider-runtime-capabilities.md).
1
+ ---
2
+ title: Providers & runtimes
3
+ description: How provider selection maps to managed runtime execution.
4
+ icon: Network
5
+ ---
6
+
7
+ aex exposes one submission shape across supported providers:
8
+
9
+ | Provider | Selector |
10
+ | --- | --- |
11
+ | Anthropic | `Providers.ANTHROPIC` |
12
+ | DeepSeek | `Providers.DEEPSEEK` |
13
+ | OpenAI | `Providers.OPENAI` |
14
+ | Gemini | `Providers.GEMINI` |
15
+ | Mistral | `Providers.MISTRAL` |
16
+ | OpenRouter | `Providers.OPENROUTER` |
17
+ | Doubao | `Providers.DOUBAO` |
18
+ | Doubao China | `Providers.DOUBAO_CN` |
19
+
20
+ All submissions run on the managed runtime. The optional `runtime` option picks
21
+ a managed machine-size preset — use `Sizes.*` in TypeScript (e.g.
22
+ `runtime: Sizes.SHARED_0_25X_1GB`) or `--runtime-size` in the CLI. Omit it for
23
+ the default size; there is no alternative runtime backend to select.
24
+
25
+ ## Selection
26
+
27
+ ### TypeScript
28
+
29
+ ```ts
30
+ import { Models, Providers } from "@aexhq/sdk";
31
+
32
+ await aex.run({
33
+ provider: Providers.OPENAI,
34
+ model: Models.GPT_4_1,
35
+ message: "Summarise the attached files.",
36
+ apiKeys: { openai: process.env.OPENAI_API_KEY! }
37
+ });
38
+ ```
39
+
40
+ ### CLI
41
+
42
+ ```bash
43
+ aex run \
44
+ --api-key "$AEX_API_KEY" \
45
+ --provider openai \
46
+ --openai-api-key "$OPENAI_API_KEY" \
47
+ --model gpt-4.1 \
48
+ --prompt "Summarise the attached files." \
49
+ --follow
50
+ ```
51
+
52
+ Events, outputs, cleanup, and downloads use the same SDK and CLI surface for
53
+ every provider. For the exact supported model list, use the generated
54
+ [provider/runtime capability matrix](../provider-runtime-capabilities.md).
@@ -1,49 +1,49 @@
1
- ---
2
- title: Runs
3
- description: The durable unit aex submits, observes, and archives.
4
- icon: Play
5
- ---
6
-
7
- A run is a durable, resumable **session**: the model, system message, composition
8
- primitives, output policy, and per-provider keys you open it with, plus every
9
- turn you send to it. aex snapshots the non-secret inputs, holds secrets for the
10
- session lifecycle, dispatches each turn through the managed runtime, and records
11
- status, typed events, and outputs. Sessions are the low-level API; `run()` is the
12
- one-shot convenience wrapper over them.
13
-
14
- ```ts
15
- import { Aex, Models } from "@aexhq/sdk";
16
-
17
- const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
18
-
19
- const session = await aex.openSession({
20
- provider: "anthropic",
21
- model: Models.CLAUDE_HAIKU_4_5,
22
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
23
- });
24
-
25
- const turn = session.send("Write the report and save it as a file.");
26
- for await (const event of turn) {
27
- console.log(event.type);
28
- }
29
- await turn.done();
30
-
31
- await session.wait();
32
- await session.download({ to: "./session.zip" });
33
- ```
34
-
35
- The same durable record backs SDK and CLI reads. From the handle use `refresh`,
36
- `unit`, `wait`, and `download` for lifecycle, plus the grouped read accessors —
37
- `messages()`, `events()`, and `outputs()` (each with `list()`/`last()`/`first()`,
38
- and `events().stream()` / `events().streamEnvelopes()` / `outputs().read(...)` for
39
- streaming and byte-capped reads) — to inspect the session live or after it parks;
40
- from the client, `aex.sessions.list()` / `aex.sessions.get(id)` read across the
41
- workspace (CLI mirrors: `aex sessions` and `aex runs` list the workspace's
42
- sessions/runs newest-first).
43
-
44
- Use `idempotencyKey` when retrying `openSession` or `send` from your own
45
- workflow. aex hashes the normalized non-secret submission, so a retry with the
46
- same key and same body returns the existing session while a mismatched body fails
47
- with an idempotency conflict.
48
-
49
- aex selects product placement server-side. There is no region selector.
1
+ ---
2
+ title: Runs
3
+ description: The durable unit aex submits, observes, and archives.
4
+ icon: Play
5
+ ---
6
+
7
+ A run is a durable, resumable **session**: the model, system message, composition
8
+ primitives, output policy, and per-provider keys you open it with, plus every
9
+ turn you send to it. aex snapshots the non-secret inputs, holds secrets for the
10
+ session lifecycle, dispatches each turn through the managed runtime, and records
11
+ status, typed events, and outputs. Sessions are the low-level API; `run()` is the
12
+ one-shot convenience wrapper over them.
13
+
14
+ ```ts
15
+ import { Aex, Models } from "@aexhq/sdk";
16
+
17
+ const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
18
+
19
+ const session = await aex.openSession({
20
+ provider: "anthropic",
21
+ model: Models.CLAUDE_HAIKU_4_5,
22
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
23
+ });
24
+
25
+ const turn = session.send("Write the report and save it as a file.");
26
+ for await (const event of turn) {
27
+ console.log(event.type);
28
+ }
29
+ await turn.done();
30
+
31
+ await session.wait();
32
+ await session.download({ to: "./session.zip" });
33
+ ```
34
+
35
+ The same durable record backs SDK and CLI reads. From the handle use `refresh`,
36
+ `unit`, `wait`, and `download` for lifecycle, plus the grouped read accessors —
37
+ `messages()`, `events()`, and `outputs()` (each with `list()`/`last()`/`first()`,
38
+ and `events().stream()` / `events().streamEnvelopes()` / `outputs().read(...)` for
39
+ streaming and byte-capped reads) — to inspect the session live or after it parks;
40
+ from the client, `aex.sessions.list()` / `aex.sessions.get(id)` read across the
41
+ workspace (CLI mirrors: `aex sessions` and `aex runs` list the workspace's
42
+ sessions/runs newest-first).
43
+
44
+ Use `idempotencyKey` when retrying `openSession` or `send` from your own
45
+ workflow. aex hashes the normalized non-secret submission, so a retry with the
46
+ same key and same body returns the existing session while a mismatched body fails
47
+ with an idempotency conflict.
48
+
49
+ aex selects product placement server-side. There is no region selector.