@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.
Files changed (53) hide show
  1. package/README.md +159 -159
  2. package/dist/_contracts/asset-upload-helper.d.ts +38 -0
  3. package/dist/_contracts/asset-upload-helper.js +111 -0
  4. package/dist/_contracts/internal.d.ts +5 -9
  5. package/dist/_contracts/internal.js +5 -79
  6. package/dist/_contracts/operations.d.ts +9 -2
  7. package/dist/_contracts/operations.js +141 -16
  8. package/dist/_contracts/retry-core.d.ts +27 -0
  9. package/dist/_contracts/retry-core.js +75 -0
  10. package/dist/_contracts/runtime-types.d.ts +6 -0
  11. package/dist/_contracts/stable.d.ts +10 -5
  12. package/dist/_contracts/stable.js +10 -5
  13. package/dist/asset-upload.d.ts +2 -1
  14. package/dist/asset-upload.js +28 -61
  15. package/dist/asset-upload.js.map +1 -1
  16. package/dist/cli.mjs +297 -80
  17. package/dist/cli.mjs.sha256 +1 -1
  18. package/dist/client.d.ts +5 -0
  19. package/dist/client.js +8 -13
  20. package/dist/client.js.map +1 -1
  21. package/dist/retry.js +13 -41
  22. package/dist/retry.js.map +1 -1
  23. package/dist/version.d.ts +1 -1
  24. package/dist/version.js +1 -1
  25. package/docs/authentication.md +125 -125
  26. package/docs/billing.md +118 -118
  27. package/docs/concepts/agent-tools.md +66 -66
  28. package/docs/concepts/composition.md +37 -37
  29. package/docs/concepts/providers-and-runtimes.md +54 -54
  30. package/docs/concepts/runs.md +49 -49
  31. package/docs/concepts/subagents.md +88 -88
  32. package/docs/credentials.md +125 -125
  33. package/docs/defaults.md +64 -64
  34. package/docs/errors.md +196 -196
  35. package/docs/events.md +243 -243
  36. package/docs/limits-and-quotas.md +144 -144
  37. package/docs/limits.md +50 -50
  38. package/docs/mcp.md +44 -44
  39. package/docs/networking.md +163 -163
  40. package/docs/outputs.md +267 -267
  41. package/docs/public-surface.json +77 -77
  42. package/docs/quickstart.md +119 -119
  43. package/docs/release.md +38 -38
  44. package/docs/retries.md +129 -129
  45. package/docs/run-config.md +58 -58
  46. package/docs/run-record.md +55 -55
  47. package/docs/secrets.md +125 -125
  48. package/docs/testing.md +35 -35
  49. package/docs/vision-skills.md +91 -91
  50. package/docs/webhooks.md +127 -127
  51. package/examples/feature-tour.ts +282 -282
  52. package/examples/spike-settle-latency.ts +125 -125
  53. package/package.json +1 -1
package/docs/secrets.md CHANGED
@@ -1,129 +1,129 @@
1
- ---
2
- title: Secrets
3
- ---
4
-
5
- # Secrets
6
-
7
- aex supports BYOK provider keys, per-run credentials, and reusable workspace
8
- secrets. Secret values are excluded from the idempotency fingerprint and do not
9
- belong in run config.
10
-
11
- Runnable examples need both `AEX_API_KEY` for aex and the matching BYOK
12
- provider key, such as `ANTHROPIC_API_KEY` for Claude.
13
-
14
- ## Use A Provider Key For One Run
15
-
16
- ### TypeScript
17
-
18
- ```ts
19
- import { Aex, Models } from "@aexhq/sdk";
20
-
21
- const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
22
-
23
- await aex.run({
24
- model: Models.CLAUDE_HAIKU_4_5,
25
- message: "Write a short report and save it as a file.",
26
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
27
- });
28
- ```
29
-
30
- ### CLI
31
-
32
- ```bash
33
- aex run \
34
- --api-key "$AEX_API_KEY" \
35
- --anthropic-api-key "$ANTHROPIC_API_KEY" \
36
- --model claude-haiku-4-5 \
37
- --prompt "Write a short report and save it as a file."
38
- ```
39
-
40
- ## Upload An Env Secret
41
-
42
- Use `Secret.value(...).upload(...)` when you start with an ephemeral value and
43
- want to persist it as a named workspace secret for later runs.
44
-
45
- ```ts
46
- import { Aex, Models, Providers, Secret } from "@aexhq/sdk";
47
-
48
- const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
49
-
50
- const githubToken = await Secret.value(process.env.GITHUB_TOKEN!).upload(aex, {
51
- name: "github-token"
52
- });
53
-
54
- await aex.run({
55
- provider: Providers.ANTHROPIC,
56
- model: Models.CLAUDE_HAIKU_4_5,
57
- message: "Inspect the repository issues.",
58
- environment: { secrets: { GITHUB_TOKEN: githubToken } },
59
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
60
- });
61
- ```
62
-
63
- ## Set Or Rotate A Workspace Secret
64
-
65
- Use `client.secrets.set(...)` to create a named secret directly. Use
66
- `client.secrets.rotate(...)` to replace its value while keeping the same name.
67
-
68
- ```ts
69
- await aex.secrets.set({
70
- name: "serper-api-key",
71
- value: process.env.SERPER_API_KEY!
72
- });
73
-
74
- await aex.secrets.rotate({
75
- name: "serper-api-key",
76
- value: process.env.SERPER_API_KEY_NEXT!
77
- });
78
- ```
79
-
80
- ## Retrieve Secret Metadata
81
-
82
- `list` and `get` return metadata only. They never return the secret value.
83
-
84
- ```ts
85
- const secrets = await aex.secrets.list();
86
- const metadata = await aex.secrets.get("serper-api-key");
87
- ```
88
-
1
+ ---
2
+ title: Secrets
3
+ ---
4
+
5
+ # Secrets
6
+
7
+ aex supports BYOK provider keys, per-run credentials, and reusable workspace
8
+ secrets. Secret values are excluded from the idempotency fingerprint and do not
9
+ belong in run config.
10
+
11
+ Runnable examples need both `AEX_API_KEY` for aex and the matching BYOK
12
+ provider key, such as `ANTHROPIC_API_KEY` for Claude.
13
+
14
+ ## Use A Provider Key For One Run
15
+
16
+ ### TypeScript
17
+
18
+ ```ts
19
+ import { Aex, Models } from "@aexhq/sdk";
20
+
21
+ const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
22
+
23
+ await aex.run({
24
+ model: Models.CLAUDE_HAIKU_4_5,
25
+ message: "Write a short report and save it as a file.",
26
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
27
+ });
28
+ ```
29
+
30
+ ### CLI
31
+
32
+ ```bash
33
+ aex run \
34
+ --api-key "$AEX_API_KEY" \
35
+ --anthropic-api-key "$ANTHROPIC_API_KEY" \
36
+ --model claude-haiku-4-5 \
37
+ --prompt "Write a short report and save it as a file."
38
+ ```
39
+
40
+ ## Upload An Env Secret
41
+
42
+ Use `Secret.value(...).upload(...)` when you start with an ephemeral value and
43
+ want to persist it as a named workspace secret for later runs.
44
+
45
+ ```ts
46
+ import { Aex, Models, Providers, Secret } from "@aexhq/sdk";
47
+
48
+ const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
49
+
50
+ const githubToken = await Secret.value(process.env.GITHUB_TOKEN!).upload(aex, {
51
+ name: "github-token"
52
+ });
53
+
54
+ await aex.run({
55
+ provider: Providers.ANTHROPIC,
56
+ model: Models.CLAUDE_HAIKU_4_5,
57
+ message: "Inspect the repository issues.",
58
+ environment: { secrets: { GITHUB_TOKEN: githubToken } },
59
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
60
+ });
61
+ ```
62
+
63
+ ## Set Or Rotate A Workspace Secret
64
+
65
+ Use `client.secrets.set(...)` to create a named secret directly. Use
66
+ `client.secrets.rotate(...)` to replace its value while keeping the same name.
67
+
68
+ ```ts
69
+ await aex.secrets.set({
70
+ name: "serper-api-key",
71
+ value: process.env.SERPER_API_KEY!
72
+ });
73
+
74
+ await aex.secrets.rotate({
75
+ name: "serper-api-key",
76
+ value: process.env.SERPER_API_KEY_NEXT!
77
+ });
78
+ ```
79
+
80
+ ## Retrieve Secret Metadata
81
+
82
+ `list` and `get` return metadata only. They never return the secret value.
83
+
84
+ ```ts
85
+ const secrets = await aex.secrets.list();
86
+ const metadata = await aex.secrets.get("serper-api-key");
87
+ ```
88
+
89
89
  ## Inject A Workspace Secret Into A Run
90
90
 
91
91
  Reference workspace secrets with `Secret.ref(name)`. The value resolves
92
92
  server-side and is injected as the named environment variable.
93
-
94
- ```ts
95
- import { Models, Providers, Secret } from "@aexhq/sdk";
96
-
97
- await aex.run({
98
- provider: Providers.ANTHROPIC,
99
- model: Models.CLAUDE_HAIKU_4_5,
100
- message: "Use SERPER_API_KEY for web search.",
101
- environment: {
102
- secrets: { SERPER_API_KEY: Secret.ref("serper-api-key") }
103
- },
104
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
105
- });
106
- ```
107
-
108
- ## Delete A Workspace Secret
109
-
110
- ```ts
111
- await aex.secrets.delete("serper-api-key");
112
- ```
113
-
114
- The CLI supports per-run provider and MCP credentials. Workspace secret
115
- administration is exposed through the SDK.
116
-
117
- ## Redaction Scope And Output Files
118
-
119
- Registered secret values are redacted from the run's **event stream** (both tool
120
- output and model-authored surfaces) — a value you inject via `environment.secrets`
121
- is masked regardless of its shape. Two surfaces are intentionally *not* scrubbed:
122
-
123
- - **Captured output files** (`outputs().download()` / `read()` / the `aex download`
124
- zip) are returned **verbatim**. They are your run's own artifacts, so the platform
125
- does not rewrite their bytes — if the agent writes a secret into a deliverable file,
126
- that file contains it. Treat downloaded outputs as unredacted.
127
- - An **unregistered** secret (a credential the run produces itself and never declared
128
- via `environment.secrets`) can only be masked heuristically by shape; register the
129
- values you care about so they are masked by value.
93
+
94
+ ```ts
95
+ import { Models, Providers, Secret } from "@aexhq/sdk";
96
+
97
+ await aex.run({
98
+ provider: Providers.ANTHROPIC,
99
+ model: Models.CLAUDE_HAIKU_4_5,
100
+ message: "Use SERPER_API_KEY for web search.",
101
+ environment: {
102
+ secrets: { SERPER_API_KEY: Secret.ref("serper-api-key") }
103
+ },
104
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
105
+ });
106
+ ```
107
+
108
+ ## Delete A Workspace Secret
109
+
110
+ ```ts
111
+ await aex.secrets.delete("serper-api-key");
112
+ ```
113
+
114
+ The CLI supports per-run provider and MCP credentials. Workspace secret
115
+ administration is exposed through the SDK.
116
+
117
+ ## Redaction Scope And Output Files
118
+
119
+ Registered secret values are redacted from the run's **event stream** (both tool
120
+ output and model-authored surfaces) — a value you inject via `environment.secrets`
121
+ is masked regardless of its shape. Two surfaces are intentionally *not* scrubbed:
122
+
123
+ - **Captured output files** (`outputs().download()` / `read()` / the `aex download`
124
+ zip) are returned **verbatim**. They are your run's own artifacts, so the platform
125
+ does not rewrite their bytes — if the agent writes a secret into a deliverable file,
126
+ that file contains it. Treat downloaded outputs as unredacted.
127
+ - An **unregistered** secret (a credential the run produces itself and never declared
128
+ via `environment.secrets`) can only be masked heuristically by shape; register the
129
+ values you care about so they are masked by value.
package/docs/testing.md CHANGED
@@ -1,35 +1,35 @@
1
- ---
2
- title: Testing
3
- ---
4
-
5
- # Testing
6
-
7
- aex uses test-first development.
8
- For repository changes, default the first meaningful test to blackbox behavior
9
- from requirements, public contracts, docs, logs, or user-visible evidence before
10
- reading or changing implementation.
11
-
12
- Workspace-wide commands:
13
-
14
- ```text
15
- bun run lint # typecheck/build prerequisites + lint
16
- bun run test # unit, all packages, deterministic
17
- bun run test:user:offline # clean Bun install of packed/published SDK, no live API
18
- bun run test:user # live hosted API user tests
19
- bun run test:user:heavy # explicit heavy live canary
20
- bun run docs:build # generated docs + Next build
21
- bun run pack:sdk # SDK pack dry-run + public boundary check
22
- ```
23
-
24
- Unit tests are deterministic and may use fakes. Offline user tests install the
25
- packed or published SDK into clean Bun temp projects and do not need provider
26
- credentials.
27
-
28
- When neither `AEX_USER_TEST_TARBALL` nor `AEX_USER_TEST_VERSION` is set, the
29
- user-test fixture packs the current workspace SDK into a tempdir and installs
30
- that artifact. CI or release validation can pin an explicit artifact with
31
- exactly one of `AEX_USER_TEST_TARBALL` or `AEX_USER_TEST_VERSION`.
32
-
33
- Live user tests run against a hosted aex API and fail loudly when required env
34
- is missing: `AEX_API_URL`, `AEX_API_KEY`, `ANTHROPIC_API_KEY`, and
35
- `DEEPSEEK_API_KEY`.
1
+ ---
2
+ title: Testing
3
+ ---
4
+
5
+ # Testing
6
+
7
+ aex uses test-first development.
8
+ For repository changes, default the first meaningful test to blackbox behavior
9
+ from requirements, public contracts, docs, logs, or user-visible evidence before
10
+ reading or changing implementation.
11
+
12
+ Workspace-wide commands:
13
+
14
+ ```text
15
+ bun run lint # typecheck/build prerequisites + lint
16
+ bun run test # unit, all packages, deterministic
17
+ bun run test:user:offline # clean Bun install of packed/published SDK, no live API
18
+ bun run test:user # live hosted API user tests
19
+ bun run test:user:heavy # explicit heavy live canary
20
+ bun run docs:build # generated docs + Next build
21
+ bun run pack:sdk # SDK pack dry-run + public boundary check
22
+ ```
23
+
24
+ Unit tests are deterministic and may use fakes. Offline user tests install the
25
+ packed or published SDK into clean Bun temp projects and do not need provider
26
+ credentials.
27
+
28
+ When neither `AEX_USER_TEST_TARBALL` nor `AEX_USER_TEST_VERSION` is set, the
29
+ user-test fixture packs the current workspace SDK into a tempdir and installs
30
+ that artifact. CI or release validation can pin an explicit artifact with
31
+ exactly one of `AEX_USER_TEST_TARBALL` or `AEX_USER_TEST_VERSION`.
32
+
33
+ Live user tests run against a hosted aex API and fail loudly when required env
34
+ is missing: `AEX_API_URL`, `AEX_API_KEY`, `ANTHROPIC_API_KEY`, and
35
+ `DEEPSEEK_API_KEY`.
@@ -1,94 +1,94 @@
1
- ---
2
- title: Call a vision API from a skill
3
- ---
4
-
5
- # Call a vision API from a skill
6
-
7
- aex has no built-in vision tool. The agent's `provider` / `model` selects the
8
- reasoning model for the run; if a skill needs image understanding mid-run, ship a
9
- skill that calls the vision provider with normal HTTP and pass that provider key
10
- as a runtime secret.
11
-
12
- The runnable example lives at [`examples/vision-skill/`](../../../examples/vision-skill).
13
- It captions a frame with ByteDance Doubao Seed Vision (Ark) and returns a
14
- per-noun "does the frame depict X?" verdict.
15
-
16
- ## Submit the run
17
-
18
- ```ts
1
+ ---
2
+ title: Call a vision API from a skill
3
+ ---
4
+
5
+ # Call a vision API from a skill
6
+
7
+ aex has no built-in vision tool. The agent's `provider` / `model` selects the
8
+ reasoning model for the run; if a skill needs image understanding mid-run, ship a
9
+ skill that calls the vision provider with normal HTTP and pass that provider key
10
+ as a runtime secret.
11
+
12
+ The runnable example lives at [`examples/vision-skill/`](../../../examples/vision-skill).
13
+ It captions a frame with ByteDance Doubao Seed Vision (Ark) and returns a
14
+ per-noun "does the frame depict X?" verdict.
15
+
16
+ ## Submit the run
17
+
18
+ ```ts
19
19
  import { Aex, Models, Secret, Skill } from "@aexhq/sdk";
20
-
21
- const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
22
-
23
- const result = await aex.run({
24
- model: Models.CLAUDE_HAIKU_4_5,
25
- message: "Read skills/frame-vision-gate/SKILL.md, then caption and verify the frame.",
20
+
21
+ const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
22
+
23
+ const result = await aex.run({
24
+ model: Models.CLAUDE_HAIKU_4_5,
25
+ message: "Read skills/frame-vision-gate/SKILL.md, then caption and verify the frame.",
26
26
  skills: [await Skill.fromDir("./vision-skill", { name: "frame-vision-gate" })],
27
- environment: {
28
- secrets: {
29
- DOUBAO_API_KEY: Secret.value(process.env.DOUBAO_API_KEY!)
30
- },
31
- networking: {
32
- mode: "limited",
33
- allowedHosts: ["ark.ap-southeast.bytepluses.com"]
34
- }
35
- },
36
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
37
- });
38
-
39
- console.log(result.runId, result.text);
40
- ```
41
-
27
+ environment: {
28
+ secrets: {
29
+ DOUBAO_API_KEY: Secret.value(process.env.DOUBAO_API_KEY!)
30
+ },
31
+ networking: {
32
+ mode: "limited",
33
+ allowedHosts: ["ark.ap-southeast.bytepluses.com"]
34
+ }
35
+ },
36
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
37
+ });
38
+
39
+ console.log(result.runId, result.text);
40
+ ```
41
+
42
42
  `Skill.fromDir("./vision-skill", ...)` is resolved relative to the process
43
- CWD. Run the script from the directory that contains `vision-skill/` (in this
44
- repo, `examples/`).
45
-
46
- ## Call the provider from the skill
47
-
48
- Inside the run, the skill reads `DOUBAO_API_KEY` and makes an
49
- OpenAI-compatible chat-completions request with Python's standard HTTP client.
50
- The image is base64-inlined as a data URL in the request body:
51
-
52
- ```python
53
- import base64, json, os, urllib.request
54
-
55
- b64 = base64.b64encode(open("/workspace/files/frame.jpg", "rb").read()).decode()
56
- request_body = {
57
- "model": "doubao-seed-1-6-vision-250815",
58
- "temperature": 0,
59
- "response_format": {"type": "json_object"},
60
- "messages": [
61
- {"role": "system", "content": "Describe only what the pixels show."},
62
- {"role": "user", "content": [
63
- {"type": "text", "text": "Does this frame depict an owlbear? Answer as JSON."},
64
- {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
65
- ]}
66
- ]
67
- }
68
-
69
- req = urllib.request.Request(
70
- "https://ark.ap-southeast.bytepluses.com/api/v3/chat/completions",
71
- data=json.dumps(request_body).encode("utf-8"),
72
- headers={
73
- "Authorization": f"Bearer {os.environ['DOUBAO_API_KEY']}",
74
- "Content-Type": "application/json"
75
- },
76
- method="POST",
77
- )
78
- ```
79
-
80
- The same pattern works for OpenAI, Gemini's OpenAI-compatible endpoint, or any
81
- other HTTPS model API. Put the key in `environment.secrets`, allow-list the host
82
- when using limited networking, and use the provider's normal SDK or HTTP API.
83
-
84
- ## Payload size
85
-
86
- Base64 images are larger than their source files. Scale frames before captioning
87
- when possible, for example:
88
-
89
- ```bash
90
- ffmpeg -i source.mp4 -vf fps=1,scale=960:-1 frame_%03d.jpg
91
- ```
92
-
93
- This keeps upload size and model cost bounded without losing the signal most
94
- vision models need.
43
+ CWD. Run the script from the directory that contains `vision-skill/` (in this
44
+ repo, `examples/`).
45
+
46
+ ## Call the provider from the skill
47
+
48
+ Inside the run, the skill reads `DOUBAO_API_KEY` and makes an
49
+ OpenAI-compatible chat-completions request with Python's standard HTTP client.
50
+ The image is base64-inlined as a data URL in the request body:
51
+
52
+ ```python
53
+ import base64, json, os, urllib.request
54
+
55
+ b64 = base64.b64encode(open("/workspace/files/frame.jpg", "rb").read()).decode()
56
+ request_body = {
57
+ "model": "doubao-seed-1-6-vision-250815",
58
+ "temperature": 0,
59
+ "response_format": {"type": "json_object"},
60
+ "messages": [
61
+ {"role": "system", "content": "Describe only what the pixels show."},
62
+ {"role": "user", "content": [
63
+ {"type": "text", "text": "Does this frame depict an owlbear? Answer as JSON."},
64
+ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
65
+ ]}
66
+ ]
67
+ }
68
+
69
+ req = urllib.request.Request(
70
+ "https://ark.ap-southeast.bytepluses.com/api/v3/chat/completions",
71
+ data=json.dumps(request_body).encode("utf-8"),
72
+ headers={
73
+ "Authorization": f"Bearer {os.environ['DOUBAO_API_KEY']}",
74
+ "Content-Type": "application/json"
75
+ },
76
+ method="POST",
77
+ )
78
+ ```
79
+
80
+ The same pattern works for OpenAI, Gemini's OpenAI-compatible endpoint, or any
81
+ other HTTPS model API. Put the key in `environment.secrets`, allow-list the host
82
+ when using limited networking, and use the provider's normal SDK or HTTP API.
83
+
84
+ ## Payload size
85
+
86
+ Base64 images are larger than their source files. Scale frames before captioning
87
+ when possible, for example:
88
+
89
+ ```bash
90
+ ffmpeg -i source.mp4 -vf fps=1,scale=960:-1 frame_%03d.jpg
91
+ ```
92
+
93
+ This keeps upload size and model cost bounded without losing the signal most
94
+ vision models need.