@aexhq/sdk 0.40.6 → 0.40.8
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 +159 -159
- package/dist/_contracts/asset-upload-helper.d.ts +38 -0
- package/dist/_contracts/asset-upload-helper.js +111 -0
- package/dist/_contracts/internal.d.ts +5 -9
- package/dist/_contracts/internal.js +5 -79
- package/dist/_contracts/operations.d.ts +9 -2
- package/dist/_contracts/operations.js +141 -16
- package/dist/_contracts/retry-core.d.ts +27 -0
- package/dist/_contracts/retry-core.js +75 -0
- package/dist/_contracts/runtime-types.d.ts +6 -0
- package/dist/_contracts/stable.d.ts +10 -5
- package/dist/_contracts/stable.js +10 -5
- package/dist/asset-upload.d.ts +2 -1
- package/dist/asset-upload.js +28 -61
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +297 -80
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.js +8 -13
- package/dist/client.js.map +1 -1
- package/dist/retry.js +13 -41
- package/dist/retry.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +125 -125
- package/docs/billing.md +118 -118
- package/docs/concepts/agent-tools.md +66 -66
- package/docs/concepts/composition.md +37 -37
- package/docs/concepts/providers-and-runtimes.md +54 -54
- package/docs/concepts/runs.md +49 -49
- package/docs/concepts/subagents.md +88 -88
- package/docs/credentials.md +125 -125
- package/docs/defaults.md +64 -64
- package/docs/errors.md +196 -196
- package/docs/events.md +243 -243
- package/docs/limits-and-quotas.md +144 -144
- package/docs/limits.md +50 -50
- package/docs/mcp.md +44 -44
- package/docs/networking.md +163 -163
- package/docs/outputs.md +267 -267
- package/docs/public-surface.json +77 -77
- package/docs/quickstart.md +119 -119
- package/docs/release.md +38 -38
- package/docs/retries.md +129 -129
- package/docs/run-config.md +58 -58
- package/docs/run-record.md +55 -55
- package/docs/secrets.md +125 -125
- package/docs/testing.md +35 -35
- package/docs/vision-skills.md +91 -91
- package/docs/webhooks.md +127 -127
- package/examples/feature-tour.ts +282 -282
- package/examples/spike-settle-latency.ts +125 -125
- package/package.json +1 -1
package/docs/retries.md
CHANGED
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Retries and throttling
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Retries and throttling
|
|
6
|
-
|
|
7
|
-
The SDK ships with built-in transport resilience. Every request it makes to the
|
|
8
|
-
aex API is automatically retried on **transient** failures with bounded
|
|
9
|
-
exponential backoff and jitter, honoring the server's `Retry-After` header. You
|
|
10
|
-
get this by default — no wrapper code — and it is safe to leave on because the
|
|
11
|
-
billable submits carry a stable idempotency key, so a retry never creates a
|
|
12
|
-
duplicate run.
|
|
13
|
-
|
|
14
|
-
## What gets retried
|
|
15
|
-
|
|
16
|
-
Retried automatically:
|
|
17
|
-
|
|
18
|
-
- HTTP `429` (rate limited)
|
|
19
|
-
- HTTP `500`, `502`, `503`, `504` (server hiccups)
|
|
20
|
-
- HTTP `529` (upstream provider overloaded)
|
|
21
|
-
- Network errors (connection reset, DNS failure, timeout)
|
|
22
|
-
|
|
23
|
-
Never retried — these fail fast so you see the real problem immediately:
|
|
24
|
-
|
|
25
|
-
- `400` / `422` (bad request), `401` / `403` (auth), `404` (not found),
|
|
26
|
-
`409` (conflict), and every other non-transient `4xx`.
|
|
27
|
-
- A request you aborted yourself (via an `AbortSignal`).
|
|
28
|
-
|
|
29
|
-
## Tuning or disabling
|
|
30
|
-
|
|
31
|
-
Pass a `retry` option when you construct the client:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { Aex } from "@aexhq/sdk";
|
|
35
|
-
|
|
36
|
-
const aex = new Aex({
|
|
37
|
-
apiKey: process.env.AEX_API_KEY!,
|
|
38
|
-
retry: {
|
|
39
|
-
maxAttempts: 4, // total tries incl. the first (default 4)
|
|
40
|
-
initialDelayMs: 500, // base backoff, doubles per retry (default 500)
|
|
41
|
-
maxDelayMs: 20_000, // cap on any single wait (default 20s)
|
|
42
|
-
maxElapsedMs: 120_000 // overall wall-clock budget (default 2m)
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Turn it off entirely with `retry: false`, or make a single attempt with
|
|
48
|
-
`retry: { maxAttempts: 1 }`.
|
|
49
|
-
|
|
50
|
-
## Idempotent by construction
|
|
51
|
-
|
|
52
|
-
Retries — whether the built-in transport retry or your own re-invocation of
|
|
53
|
-
`run(...)` — never double-bill. The one-shot `run(...)` and `sessions.run(...)`
|
|
54
|
-
derive the turn's idempotency key from the session-create key, so re-invoking
|
|
55
|
-
either with the same `idempotencyKey` de-duplicates **both** the session create
|
|
56
|
-
and the billable turn server-side:
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// A retried call with the same idempotencyKey resolves to the same run,
|
|
60
|
-
// not a second billable one.
|
|
61
|
-
const result = await aex.run({
|
|
62
|
-
model: "claude-haiku-4-5",
|
|
63
|
-
message: "Write a short report and save it as a file.",
|
|
64
|
-
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! },
|
|
65
|
-
idempotencyKey: "report-2026-07-01"
|
|
66
|
-
});
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Replaying a throttled turn
|
|
70
|
-
|
|
71
|
-
When a turn on a live session is interrupted by a throttle, replay the last
|
|
72
|
-
message with `session.replayLast()`. It reuses the previous message's idempotency
|
|
73
|
-
key by default, so if the original turn actually landed it de-duplicates instead
|
|
74
|
-
of billing twice:
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
const session = await aex.openSession({
|
|
78
|
-
model: "claude-haiku-4-5",
|
|
79
|
-
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
await session.send("Summarize the attached dataset.").done();
|
|
84
|
-
} catch (err) {
|
|
85
|
-
const { isRateLimited } = await import("@aexhq/sdk");
|
|
86
|
-
if (isRateLimited(err)) {
|
|
87
|
-
// Wait out the throttle, then replay the same message.
|
|
88
|
-
await new Promise((r) => setTimeout(r, err.retryAfterMs ?? 2_000));
|
|
89
|
-
await session.replayLast().done();
|
|
90
|
-
} else {
|
|
91
|
-
throw err;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Pass a fresh key (`session.replayLast({ idempotencyKey: "..." })`) when you
|
|
97
|
-
deliberately want a brand-new turn instead of a de-duplicated replay.
|
|
98
|
-
|
|
99
|
-
## The throttle error
|
|
100
|
-
|
|
101
|
-
When retries are exhausted on a rate-limit / overloaded status, the SDK throws an
|
|
102
|
-
`AexRateLimitError`. It extends `AexApiError`, so existing `catch` sites keep
|
|
103
|
-
working, and it carries structured, non-leaky detail:
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
import { isRateLimited } from "@aexhq/sdk";
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
await aex.run({ /* … */ });
|
|
110
|
-
} catch (err) {
|
|
111
|
-
if (isRateLimited(err)) {
|
|
112
|
-
err.status; // 429 | 503 | 529
|
|
113
|
-
err.attempts; // how many tries were made
|
|
114
|
-
err.retryAfterMs; // suggested wait, when the server supplied one
|
|
115
|
-
err.source; // "api" (aex plane) or "provider" (upstream model)
|
|
116
|
-
err.providerFault; // upstream fault detail, when the model provider throttled
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
The `message` is a fixed summary (e.g. `aex API rate limit reached (HTTP 429)
|
|
122
|
-
after 4 attempts; retry after ~2s`) — it never echoes the raw response body,
|
|
123
|
-
which stays available, redacted, on `err.body`.
|
|
124
|
-
|
|
125
|
-
When the throttle originated at the upstream model provider (rather than the aex
|
|
126
|
-
API plane), `err.source` is `"provider"` and `err.providerFault` describes it:
|
|
127
|
-
its `kind` (`rate_limit` / `overloaded` / `quota_exceeded` / `provider_error`),
|
|
128
|
-
the upstream `status`, and a suggested `retryAfterMs`. Use `parseProviderFault`
|
|
129
|
-
to read the same shape off a raw fault value yourself.
|
|
1
|
+
---
|
|
2
|
+
title: Retries and throttling
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Retries and throttling
|
|
6
|
+
|
|
7
|
+
The SDK ships with built-in transport resilience. Every request it makes to the
|
|
8
|
+
aex API is automatically retried on **transient** failures with bounded
|
|
9
|
+
exponential backoff and jitter, honoring the server's `Retry-After` header. You
|
|
10
|
+
get this by default — no wrapper code — and it is safe to leave on because the
|
|
11
|
+
billable submits carry a stable idempotency key, so a retry never creates a
|
|
12
|
+
duplicate run.
|
|
13
|
+
|
|
14
|
+
## What gets retried
|
|
15
|
+
|
|
16
|
+
Retried automatically:
|
|
17
|
+
|
|
18
|
+
- HTTP `429` (rate limited)
|
|
19
|
+
- HTTP `500`, `502`, `503`, `504` (server hiccups)
|
|
20
|
+
- HTTP `529` (upstream provider overloaded)
|
|
21
|
+
- Network errors (connection reset, DNS failure, timeout)
|
|
22
|
+
|
|
23
|
+
Never retried — these fail fast so you see the real problem immediately:
|
|
24
|
+
|
|
25
|
+
- `400` / `422` (bad request), `401` / `403` (auth), `404` (not found),
|
|
26
|
+
`409` (conflict), and every other non-transient `4xx`.
|
|
27
|
+
- A request you aborted yourself (via an `AbortSignal`).
|
|
28
|
+
|
|
29
|
+
## Tuning or disabling
|
|
30
|
+
|
|
31
|
+
Pass a `retry` option when you construct the client:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Aex } from "@aexhq/sdk";
|
|
35
|
+
|
|
36
|
+
const aex = new Aex({
|
|
37
|
+
apiKey: process.env.AEX_API_KEY!,
|
|
38
|
+
retry: {
|
|
39
|
+
maxAttempts: 4, // total tries incl. the first (default 4)
|
|
40
|
+
initialDelayMs: 500, // base backoff, doubles per retry (default 500)
|
|
41
|
+
maxDelayMs: 20_000, // cap on any single wait (default 20s)
|
|
42
|
+
maxElapsedMs: 120_000 // overall wall-clock budget (default 2m)
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Turn it off entirely with `retry: false`, or make a single attempt with
|
|
48
|
+
`retry: { maxAttempts: 1 }`.
|
|
49
|
+
|
|
50
|
+
## Idempotent by construction
|
|
51
|
+
|
|
52
|
+
Retries — whether the built-in transport retry or your own re-invocation of
|
|
53
|
+
`run(...)` — never double-bill. The one-shot `run(...)` and `sessions.run(...)`
|
|
54
|
+
derive the turn's idempotency key from the session-create key, so re-invoking
|
|
55
|
+
either with the same `idempotencyKey` de-duplicates **both** the session create
|
|
56
|
+
and the billable turn server-side:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// A retried call with the same idempotencyKey resolves to the same run,
|
|
60
|
+
// not a second billable one.
|
|
61
|
+
const result = await aex.run({
|
|
62
|
+
model: "claude-haiku-4-5",
|
|
63
|
+
message: "Write a short report and save it as a file.",
|
|
64
|
+
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! },
|
|
65
|
+
idempotencyKey: "report-2026-07-01"
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Replaying a throttled turn
|
|
70
|
+
|
|
71
|
+
When a turn on a live session is interrupted by a throttle, replay the last
|
|
72
|
+
message with `session.replayLast()`. It reuses the previous message's idempotency
|
|
73
|
+
key by default, so if the original turn actually landed it de-duplicates instead
|
|
74
|
+
of billing twice:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const session = await aex.openSession({
|
|
78
|
+
model: "claude-haiku-4-5",
|
|
79
|
+
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await session.send("Summarize the attached dataset.").done();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
const { isRateLimited } = await import("@aexhq/sdk");
|
|
86
|
+
if (isRateLimited(err)) {
|
|
87
|
+
// Wait out the throttle, then replay the same message.
|
|
88
|
+
await new Promise((r) => setTimeout(r, err.retryAfterMs ?? 2_000));
|
|
89
|
+
await session.replayLast().done();
|
|
90
|
+
} else {
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Pass a fresh key (`session.replayLast({ idempotencyKey: "..." })`) when you
|
|
97
|
+
deliberately want a brand-new turn instead of a de-duplicated replay.
|
|
98
|
+
|
|
99
|
+
## The throttle error
|
|
100
|
+
|
|
101
|
+
When retries are exhausted on a rate-limit / overloaded status, the SDK throws an
|
|
102
|
+
`AexRateLimitError`. It extends `AexApiError`, so existing `catch` sites keep
|
|
103
|
+
working, and it carries structured, non-leaky detail:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { isRateLimited } from "@aexhq/sdk";
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await aex.run({ /* … */ });
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (isRateLimited(err)) {
|
|
112
|
+
err.status; // 429 | 503 | 529
|
|
113
|
+
err.attempts; // how many tries were made
|
|
114
|
+
err.retryAfterMs; // suggested wait, when the server supplied one
|
|
115
|
+
err.source; // "api" (aex plane) or "provider" (upstream model)
|
|
116
|
+
err.providerFault; // upstream fault detail, when the model provider throttled
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The `message` is a fixed summary (e.g. `aex API rate limit reached (HTTP 429)
|
|
122
|
+
after 4 attempts; retry after ~2s`) — it never echoes the raw response body,
|
|
123
|
+
which stays available, redacted, on `err.body`.
|
|
124
|
+
|
|
125
|
+
When the throttle originated at the upstream model provider (rather than the aex
|
|
126
|
+
API plane), `err.source` is `"provider"` and `err.providerFault` describes it:
|
|
127
|
+
its `kind` (`rate_limit` / `overloaded` / `quota_exceeded` / `provider_error`),
|
|
128
|
+
the upstream `status`, and a suggested `retryAfterMs`. Use `parseProviderFault`
|
|
129
|
+
to read the same shape off a raw fault value yourself.
|
package/docs/run-config.md
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Run configuration
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Run configuration
|
|
6
|
-
|
|
7
|
-
A run config is the credential-free subset of the session options (`openSession` / `run`) that you can keep in code or load from a JSON file. It is not a platform object, saved definition, DSL, trigger, or persistent agent profile. aex only stores the immutable session record created when the session lands.
|
|
8
|
-
|
|
9
|
-
Allowed fields:
|
|
10
|
-
|
|
11
|
-
- `model` - required.
|
|
12
|
-
- `system` - optional system message.
|
|
13
|
-
- `mcpServers` - array of `McpServerRef`; headers are split into the vaulted secrets channel server-side.
|
|
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
|
-
- `runtime` - optional managed-runtime preset. Prefer `Sizes` in TypeScript.
|
|
16
|
-
- `metadata` - non-secret structured metadata.
|
|
17
|
-
- `overrides` - `{ idleTtl?, timeout?, maxSpendUsd?, maxTurns? }`. `timeout` is an optional session deadline (e.g. `"30m"`, `"2h"`); `maxSpendUsd` stops the session once its spend would exceed the cap; `maxTurns` caps the agent's iteration count for the turn (a positive integer — omit to accept the platform default of 20, clamped to the ceiling). See [Limits & quotas](limits-and-quotas.md).
|
|
18
|
-
|
|
19
|
-
`message` (the one-shot `run` input), `agentsMd`, `files`, `outputs`, `tools`, `skills`, `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 `skills` entries built with `Skill.fromDir(...)`, `Skill.fromUrl(...)`, or the other `Skill.from*` factories, 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`, `maxTurns`), and the session surface exposes its spend and iteration dials via `overrides.maxSpendUsd` and `overrides.maxTurns`; the subagent depth/breadth dials are not settable per-session today and take the platform defaults.
|
|
20
|
-
|
|
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.
|
|
26
|
-
|
|
27
|
-
## Reuse in code
|
|
28
|
-
|
|
29
|
-
Use an ordinary function when you want reusable typed parameters. aex does not store or execute this function; it only receives the parameters you pass.
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
import { Models } from "@aexhq/sdk";
|
|
33
|
-
|
|
34
|
-
function summarise(topic: string) {
|
|
35
|
-
return {
|
|
36
|
-
model: Models.CLAUDE_HAIKU_4_5,
|
|
37
|
-
system: "You are a concise automation agent.",
|
|
38
|
-
message: `Write a short answer about ${topic}.`
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
await aex.run({
|
|
43
|
-
...summarise("agent-first SDK design"),
|
|
44
|
-
apiKeys: { anthropic: apiKey }
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## CLI
|
|
49
|
-
|
|
50
|
-
The `aex run` host subcommand accepts the same run config either as a JSON file:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
aex run --config ./run.json \
|
|
54
|
-
--api-key "$AEX_API_KEY" \
|
|
55
|
-
--anthropic-api-key "$ANTHROPIC_API_KEY"
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--runtime-size`, `--run-timeout`, `--metadata`). The two modes are mutually exclusive. Composition inputs attach with repeatable flags that map onto the SDK's `Skill`/`Tool`/`AgentsMd`/`File` factories: `--skill @bundle` (workspace skill), `--tool @tool.js` (custom tool module), `--agents-md @AGENTS.md`, and `--file @path` (mount a file into `/workspace`).
|
|
1
|
+
---
|
|
2
|
+
title: Run configuration
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Run configuration
|
|
6
|
+
|
|
7
|
+
A run config is the credential-free subset of the session options (`openSession` / `run`) that you can keep in code or load from a JSON file. It is not a platform object, saved definition, DSL, trigger, or persistent agent profile. aex only stores the immutable session record created when the session lands.
|
|
8
|
+
|
|
9
|
+
Allowed fields:
|
|
10
|
+
|
|
11
|
+
- `model` - required.
|
|
12
|
+
- `system` - optional system message.
|
|
13
|
+
- `mcpServers` - array of `McpServerRef`; headers are split into the vaulted secrets channel server-side.
|
|
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
|
+
- `runtime` - optional managed-runtime preset. Prefer `Sizes` in TypeScript.
|
|
16
|
+
- `metadata` - non-secret structured metadata.
|
|
17
|
+
- `overrides` - `{ idleTtl?, timeout?, maxSpendUsd?, maxTurns? }`. `timeout` is an optional session deadline (e.g. `"30m"`, `"2h"`); `maxSpendUsd` stops the session once its spend would exceed the cap; `maxTurns` caps the agent's iteration count for the turn (a positive integer — omit to accept the platform default of 20, clamped to the ceiling). See [Limits & quotas](limits-and-quotas.md).
|
|
18
|
+
|
|
19
|
+
`message` (the one-shot `run` input), `agentsMd`, `files`, `outputs`, `tools`, `skills`, `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 `skills` entries built with `Skill.fromDir(...)`, `Skill.fromUrl(...)`, or the other `Skill.from*` factories, 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`, `maxTurns`), and the session surface exposes its spend and iteration dials via `overrides.maxSpendUsd` and `overrides.maxTurns`; the subagent depth/breadth dials are not settable per-session today and take the platform defaults.
|
|
20
|
+
|
|
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.
|
|
26
|
+
|
|
27
|
+
## Reuse in code
|
|
28
|
+
|
|
29
|
+
Use an ordinary function when you want reusable typed parameters. aex does not store or execute this function; it only receives the parameters you pass.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Models } from "@aexhq/sdk";
|
|
33
|
+
|
|
34
|
+
function summarise(topic: string) {
|
|
35
|
+
return {
|
|
36
|
+
model: Models.CLAUDE_HAIKU_4_5,
|
|
37
|
+
system: "You are a concise automation agent.",
|
|
38
|
+
message: `Write a short answer about ${topic}.`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await aex.run({
|
|
43
|
+
...summarise("agent-first SDK design"),
|
|
44
|
+
apiKeys: { anthropic: apiKey }
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## CLI
|
|
49
|
+
|
|
50
|
+
The `aex run` host subcommand accepts the same run config either as a JSON file:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
aex run --config ./run.json \
|
|
54
|
+
--api-key "$AEX_API_KEY" \
|
|
55
|
+
--anthropic-api-key "$ANTHROPIC_API_KEY"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--runtime-size`, `--run-timeout`, `--metadata`). The two modes are mutually exclusive. Composition inputs attach with repeatable flags that map onto the SDK's `Skill`/`Tool`/`AgentsMd`/`File` factories: `--skill @bundle` (workspace skill), `--tool @tool.js` (custom tool module), `--agents-md @AGENTS.md`, and `--file @path` (mount a file into `/workspace`).
|
package/docs/run-record.md
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Run record
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Run record
|
|
6
|
-
|
|
7
|
-
The run record is the durable product primitive for one session. It is the public-safe bundle of status metadata, the non-secret submission snapshot when available, typed events, captured outputs, and manifest entries for custody and cost telemetry.
|
|
8
|
-
|
|
9
|
-
## Listing sessions
|
|
10
|
-
|
|
11
|
-
`aex.sessions.list(query?)` enumerates the sessions in this workspace, most-recent first, one page at a time. The workspace is derived server-side from the API key, so this only ever returns your own sessions. It is the workspace-wide discovery entry point: combine it with `aex.sessions.outputs(id).list()` / `.read(...)` (see [Outputs](outputs.md)) to reach any session's deliverables.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
let cursor: string | undefined;
|
|
15
|
-
do {
|
|
16
|
-
const page = await aex.sessions.list({ status: "idle", limit: 25, cursor });
|
|
17
|
-
for (const session of page.sessions) {
|
|
18
|
-
console.log(session.id, session.status, session.createdAt, session.costUsd);
|
|
19
|
-
}
|
|
20
|
-
cursor = page.nextCursor;
|
|
21
|
-
} while (cursor);
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
`query` fields are all optional: `status` (single session status, e.g. `"idle"`), `since` (ISO-8601 lower bound on `createdAt`), `limit` (defaults to 25, clamped to `[1, 100]`), and `cursor` (the opaque keyset cursor from a prior page's `nextCursor` — absent on the last page). Each page row is a public-safe `SessionSummary` (`id`, `status`, `createdAt`, `updatedAt`, and `costUsd` once settled); it deliberately omits the submission snapshot (model / system / env). Use `aex.sessions.get(id)` for status / timing / cost on one session, or `session.unit()` on a handle for the full self-contained record including the parsed submission.
|
|
25
|
-
|
|
26
|
-
## Downloading a run record
|
|
27
|
-
|
|
28
|
-
`session.download()` and `aex download <session-id>` return a zip with this layout:
|
|
29
|
-
|
|
30
|
-
```text
|
|
31
|
-
manifest.json
|
|
32
|
-
metadata/run.json
|
|
33
|
-
metadata/submission.json # when a public-safe submission snapshot is returned by the read API
|
|
34
|
-
metadata/cost.json # when public cost telemetry is returned by the read API
|
|
35
|
-
events/events.jsonl
|
|
36
|
-
outputs/<captured deliverable files>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
`manifest.json` is versioned as `RunRecordManifestV1`:
|
|
40
|
-
|
|
41
|
-
| Field | Meaning |
|
|
42
|
-
| --- | --- |
|
|
43
|
-
| `schemaVersion` | `aex.run-record.manifest.v1`. |
|
|
44
|
-
| `runRecordSchemaVersion` | `aex.run-record.v1`. |
|
|
45
|
-
| `runId` | The run the archive was assembled for. |
|
|
46
|
-
| `namespaces[]` | The documented top-level namespaces: `metadata`, `events`, `outputs`. |
|
|
47
|
-
| `files[]` | Inventory of expected and present files with `namespace`, `path`, `role`, and `status`. |
|
|
48
|
-
| `outputs[]` | Compatibility alias for present captured output artifacts. |
|
|
49
|
-
| `errors[]` | Per-artifact byte fetch failures during archive assembly. |
|
|
50
|
-
|
|
51
|
-
Current v1 downloads always include `metadata/run.json` and `events/events.jsonl`. `events/events.jsonl` contains typed event-channel records only; internal diagnostics and full internal streams are not mixed into that file.
|
|
52
|
-
|
|
53
|
-
`metadata/submission.json` is present only when the run read shape includes a public-safe submission snapshot. `metadata/cost.json` is present only when the run read shape includes public `costTelemetry`; otherwise cost stays `pending`. `metadata/custody.json` remains `pending` until the custody manifest writer and public read surface land. `events/manifest.json` remains `unavailable` in this client-side slice because there is no public coordinator-manifest download route.
|
|
54
|
-
|
|
55
|
-
The record boundary is public-safe. It must not contain provider API keys, runner bearers, workspace tokens, signed URLs, raw provider response bodies, object-store keys, Vault ids, raw query strings, secret-shaped values, or internal diagnostic files. Credentials are supplied per run and vaulted separately for the run lifetime.
|
|
1
|
+
---
|
|
2
|
+
title: Run record
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Run record
|
|
6
|
+
|
|
7
|
+
The run record is the durable product primitive for one session. It is the public-safe bundle of status metadata, the non-secret submission snapshot when available, typed events, captured outputs, and manifest entries for custody and cost telemetry.
|
|
8
|
+
|
|
9
|
+
## Listing sessions
|
|
10
|
+
|
|
11
|
+
`aex.sessions.list(query?)` enumerates the sessions in this workspace, most-recent first, one page at a time. The workspace is derived server-side from the API key, so this only ever returns your own sessions. It is the workspace-wide discovery entry point: combine it with `aex.sessions.outputs(id).list()` / `.read(...)` (see [Outputs](outputs.md)) to reach any session's deliverables.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
let cursor: string | undefined;
|
|
15
|
+
do {
|
|
16
|
+
const page = await aex.sessions.list({ status: "idle", limit: 25, cursor });
|
|
17
|
+
for (const session of page.sessions) {
|
|
18
|
+
console.log(session.id, session.status, session.createdAt, session.costUsd);
|
|
19
|
+
}
|
|
20
|
+
cursor = page.nextCursor;
|
|
21
|
+
} while (cursor);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`query` fields are all optional: `status` (single session status, e.g. `"idle"`), `since` (ISO-8601 lower bound on `createdAt`), `limit` (defaults to 25, clamped to `[1, 100]`), and `cursor` (the opaque keyset cursor from a prior page's `nextCursor` — absent on the last page). Each page row is a public-safe `SessionSummary` (`id`, `status`, `createdAt`, `updatedAt`, and `costUsd` once settled); it deliberately omits the submission snapshot (model / system / env). Use `aex.sessions.get(id)` for status / timing / cost on one session, or `session.unit()` on a handle for the full self-contained record including the parsed submission.
|
|
25
|
+
|
|
26
|
+
## Downloading a run record
|
|
27
|
+
|
|
28
|
+
`session.download()` and `aex download <session-id>` return a zip with this layout:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
manifest.json
|
|
32
|
+
metadata/run.json
|
|
33
|
+
metadata/submission.json # when a public-safe submission snapshot is returned by the read API
|
|
34
|
+
metadata/cost.json # when public cost telemetry is returned by the read API
|
|
35
|
+
events/events.jsonl
|
|
36
|
+
outputs/<captured deliverable files>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`manifest.json` is versioned as `RunRecordManifestV1`:
|
|
40
|
+
|
|
41
|
+
| Field | Meaning |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `schemaVersion` | `aex.run-record.manifest.v1`. |
|
|
44
|
+
| `runRecordSchemaVersion` | `aex.run-record.v1`. |
|
|
45
|
+
| `runId` | The run the archive was assembled for. |
|
|
46
|
+
| `namespaces[]` | The documented top-level namespaces: `metadata`, `events`, `outputs`. |
|
|
47
|
+
| `files[]` | Inventory of expected and present files with `namespace`, `path`, `role`, and `status`. |
|
|
48
|
+
| `outputs[]` | Compatibility alias for present captured output artifacts. |
|
|
49
|
+
| `errors[]` | Per-artifact byte fetch failures during archive assembly. |
|
|
50
|
+
|
|
51
|
+
Current v1 downloads always include `metadata/run.json` and `events/events.jsonl`. `events/events.jsonl` contains typed event-channel records only; internal diagnostics and full internal streams are not mixed into that file.
|
|
52
|
+
|
|
53
|
+
`metadata/submission.json` is present only when the run read shape includes a public-safe submission snapshot. `metadata/cost.json` is present only when the run read shape includes public `costTelemetry`; otherwise cost stays `pending`. `metadata/custody.json` remains `pending` until the custody manifest writer and public read surface land. `events/manifest.json` remains `unavailable` in this client-side slice because there is no public coordinator-manifest download route.
|
|
54
|
+
|
|
55
|
+
The record boundary is public-safe. It must not contain provider API keys, runner bearers, workspace tokens, signed URLs, raw provider response bodies, object-store keys, Vault ids, raw query strings, secret-shaped values, or internal diagnostic files. Credentials are supplied per run and vaulted separately for the run lifetime.
|