@aexhq/sdk 0.24.0 → 0.25.1

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 (64) hide show
  1. package/README.md +36 -125
  2. package/dist/_contracts/index.d.ts +0 -1
  3. package/dist/_contracts/index.js +0 -1
  4. package/dist/_contracts/models.js +1 -2
  5. package/dist/_contracts/operations.d.ts +13 -5
  6. package/dist/_contracts/operations.js +220 -10
  7. package/dist/_contracts/provider-support.d.ts +18 -29
  8. package/dist/_contracts/provider-support.js +3 -22
  9. package/dist/_contracts/proxy-protocol.d.ts +4 -2
  10. package/dist/_contracts/proxy-protocol.js +10 -3
  11. package/dist/_contracts/run-config.d.ts +28 -4
  12. package/dist/_contracts/run-config.js +10 -5
  13. package/dist/_contracts/run-cost.d.ts +3 -11
  14. package/dist/_contracts/run-cost.js +2 -57
  15. package/dist/_contracts/run-custody.d.ts +1 -52
  16. package/dist/_contracts/run-custody.js +3 -87
  17. package/dist/_contracts/run-retention.d.ts +1 -5
  18. package/dist/_contracts/run-retention.js +2 -14
  19. package/dist/_contracts/run-unit.d.ts +2 -2
  20. package/dist/_contracts/run-unit.js +3 -1
  21. package/dist/_contracts/runtime-security-profile.js +1 -1
  22. package/dist/_contracts/runtime-types.d.ts +38 -12
  23. package/dist/_contracts/side-effect-audit.d.ts +4 -5
  24. package/dist/_contracts/side-effect-audit.js +1 -4
  25. package/dist/_contracts/status.d.ts +3 -4
  26. package/dist/_contracts/status.js +3 -8
  27. package/dist/_contracts/submission.d.ts +68 -42
  28. package/dist/_contracts/submission.js +144 -30
  29. package/dist/bundle.d.ts +13 -0
  30. package/dist/bundle.js +51 -0
  31. package/dist/bundle.js.map +1 -1
  32. package/dist/cli.mjs +232 -58
  33. package/dist/cli.mjs.sha256 +1 -1
  34. package/dist/client.d.ts +25 -21
  35. package/dist/client.js +59 -15
  36. package/dist/client.js.map +1 -1
  37. package/dist/index.d.ts +6 -5
  38. package/dist/index.js +4 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/tool.d.ts +41 -0
  41. package/dist/tool.js +138 -0
  42. package/dist/tool.js.map +1 -0
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.js +1 -1
  45. package/docs/concepts/agent-tools.md +53 -0
  46. package/docs/concepts/composition.md +43 -0
  47. package/docs/concepts/providers-and-runtimes.md +53 -0
  48. package/docs/concepts/runs.md +40 -0
  49. package/docs/credentials.md +8 -4
  50. package/docs/events.md +12 -0
  51. package/docs/limits.md +53 -0
  52. package/docs/outputs.md +58 -0
  53. package/docs/provider-runtime-capabilities.md +53 -55
  54. package/docs/public-surface.json +81 -0
  55. package/docs/quickstart.md +28 -105
  56. package/docs/release.md +1 -1
  57. package/docs/run-config.md +2 -2
  58. package/docs/secrets.md +123 -0
  59. package/docs/skills.md +3 -3
  60. package/docs/vision-skills.md +14 -19
  61. package/package.json +2 -2
  62. package/dist/_contracts/managed-key.d.ts +0 -101
  63. package/dist/_contracts/managed-key.js +0 -181
  64. package/docs/product-boundaries.md +0 -57
@@ -1,137 +1,60 @@
1
1
  ---
2
- title: aex quickstart
2
+ title: Quickstart
3
3
  ---
4
4
 
5
5
  # Quickstart
6
6
 
7
- 1. Get an aex SDK API token (`ant_…`).
8
- 2. Create `AgentExecutor` — the workspace is derived server-side from the token.
9
- 3. Submit the run with the agent's brief plus an inline `secrets` bundle. Wait for terminal status. Fetch outputs.
7
+ ## 1. Install
8
+
9
+ ```bash
10
+ npm install @aexhq/sdk
11
+ ```
12
+
13
+ ## 2. Submit a run
10
14
 
11
15
  ```ts
12
- import { AgentExecutor, RunModels } from "@aexhq/sdk";
16
+ import { AgentExecutor, Models, Providers } from "@aexhq/sdk";
13
17
 
14
18
  const aex = new AgentExecutor({
15
- apiToken: process.env.AEX_API_TOKEN!,
16
- // baseUrl defaults to https://api.aex.dev - set it for local or staging planes.
19
+ apiToken: process.env.AEX_API_TOKEN!
17
20
  });
18
21
 
19
22
  const runId = await aex.submit({
20
- model: RunModels.CLAUDE_HAIKU_4_5,
21
- prompt: "Write a short answer about agent-first SDK design.",
23
+ provider: Providers.ANTHROPIC,
24
+ model: Models.CLAUDE_HAIKU_4_5,
25
+ prompt: "Write a short report and save it as a file.",
22
26
  secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
23
27
  });
24
-
25
- const run = await aex.wait(runId);
26
- console.log(run.status);
27
- console.log(await aex.outputs(runId));
28
28
  ```
29
29
 
30
- For reusable, credential-free configs, use an ordinary function:
30
+ ## 3. Stream, wait, and download
31
31
 
32
32
  ```ts
33
- function summarise(topic: string) {
34
- return {
35
- model: RunModels.CLAUDE_HAIKU_4_5,
36
- prompt: `Write a short answer about ${topic}.`
37
- };
33
+ for await (const event of aex.stream(runId)) {
34
+ console.log(event.type);
38
35
  }
39
36
 
40
- const runId = await aex.submit({
41
- ...summarise("agent-first SDK design"),
42
- secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
43
- });
37
+ const run = await aex.wait(runId);
38
+ console.log(run.status);
39
+
40
+ await aex.download(runId, { to: "./run.zip" });
44
41
  ```
45
42
 
46
- Or from the shell:
43
+ The same run from the CLI:
47
44
 
48
45
  ```bash
49
46
  aex run \
50
47
  --api-token "$AEX_API_TOKEN" \
51
48
  --anthropic-api-key "$ANTHROPIC_API_KEY" \
52
49
  --model claude-haiku-4-5 \
53
- --prompt "Write a short answer about agent-first SDK design." \
50
+ --prompt "Write a short report and save it as a file." \
54
51
  --follow
55
52
  ```
56
53
 
57
- For a config-file flow, pass `--config <path>` with a run-config JSON file for a single run request (`{ model, system?, prompt, skills?, mcpServers?, environment?, runtimeSize?, timeout?, postHook?, proxyEndpoints?, metadata? }`). Both surfaces hit the same aex backend and operate on the same durable run records. The JSON `model` value is validated against `RUN_MODELS`.
58
-
59
- ## Runtime controls
60
-
61
- `submit` also accepts per-run controls that are not secrets:
62
-
63
- - `runtimeSize` - a closed managed-runtime preset. Prefer `RuntimeSizes`, e.g. `RuntimeSizes.SHARED_2X_2GB`.
64
- - `timeout` - run deadline as a duration string such as `"30m"` or `"2h"`; bounded server-side.
65
- - `postHook` - optional post-agent verifier, e.g. `{ command: "pnpm test", timeout: "5m", maxTurns: 3, maxChars: 12000 }`. It runs after the agent process exits successfully; failures are sent back to the agent for repair until `maxTurns` is exhausted. Empty `command` is treated as omitted.
66
- - `builtins` - managed-runtime builtin extensions. Omit it to use the default `["developer"]` toolkit. Pass `[]` for a pure-MCP run with no builtins.
67
- - `outputMode` - `"buffered"` by default; pass `"stream"` for per-token assistant text deltas.
68
-
69
- ## Where things go: customer → primitive mapping
70
-
71
- Every kind of thing you want to ship at run time has exactly one right primitive in the SDK. Reach for the right one rather than rolling your own wrapper.
72
-
73
- | What you have | Primitive | What it does |
74
- |---|---|---|
75
- | Non-secret paths or config (`BROLL_STORE`, mode flags) | `environment.envVars` | Mounted as `RUNTIME.env` / `RUNTIME.json`; `__KEY__` substitution in agent-facing markdown; echoed back as `run.runtimeManifest.envVars` |
76
- | Upstream HTTPS API keys (TMDB, Brave, Tavily, …) | `ProxyEndpoint` | Credentials live server-side; aex proxy injects them on outbound calls. The key never enters the container. |
77
- | MCP server credentials | `secrets.mcpServers` | Held in run-scoped custody, attached per run |
78
- | Provider API key | `secrets.apiKey` | Required on every `submit`; held in run-scoped custody. Carries the BYOK key for the selected `provider` |
79
- | Non-secret reference data files or folders (transcripts, persona docs, PDFs) | `File.fromPath('./subtitles.srt')` / `File.fromPath('./customer-folder/')` | Unzipped into the `mountPath` directory (default `/workspace`, the agent's cwd), preserving the real filename + extension — e.g. `/workspace/subtitles.srt`. See [Files: where they land](#files-where-they-land). |
80
- | Executable skill code (a `.pyz` wrapper, scripts, prompts) | `Skill.fromPath('./skills/my-skill/')` | Mounted under `skills/<name>/`; the bundle's `SKILL.md` is composed into the agent's instructions |
81
- | Agent instructions file | `AgentsMd.fromPath('./AGENTS.md')` | Prepended as the first user turn |
82
-
83
- `Skill`, `AgentsMd`, and `File` values are materialized for the run before the first agent turn. `environment.envVars` values surface in runtime metadata and can be referenced by `__KEY__` placeholders in agent-facing markdown.
84
-
85
- ### Files: where they land
86
-
87
- A `File` is uploaded as a content-addressed archive and **unzipped on the runtime into a directory**, preserving the real filename + extension — the agent sees the actual file, never a `.zip` to unpack.
88
-
89
- - **`mountPath` is the directory the archive unzips into.** It must be an absolute container path (`/...`, no `..` traversal). It defaults to **`/workspace`**, which is also the agent's default working directory — so a file handed with no `mountPath` lands directly in the agent's cwd.
90
- - **A single file keeps its real basename.** `File.fromPath('./source-video-subtitles.srt')` lands at `/workspace/source-video-subtitles.srt` (not a slug, not a `.zip`). `File.fromBytes({ name: 'data.csv', bytes })` lands at `/workspace/data.csv` — `name` is the real filename here.
91
- - **A folder lands its entries under the mount directory.** `File.fromPath('./repo/', { mountPath: '/workspace/repo' })` puts each file at `/workspace/repo/<relative-path>`.
92
- - **The resolved mount directory is surfaced back on the run.** Read `run.runtimeManifest.mountedFiles` (an array of `{ name, mountPath }`) to learn where each handed file landed.
93
-
94
- ```ts
95
- const subtitles = await File.fromPath('./source-video-subtitles.srt'); // → /workspace/source-video-subtitles.srt
96
- const dataset = await File.fromPath('./data/', { mountPath: '/workspace/input' }); // → /workspace/input/<files>
97
- const run = await client.submit({ model, prompt, files: [subtitles, dataset], secrets: { apiKey } });
98
- const resolved = (await client.getRun(run.id)).runtimeManifest?.mountedFiles;
99
- // [{ name: 'source-video-subtitles', mountPath: '/workspace' }, { name: 'data', mountPath: '/workspace/input' }]
100
- ```
101
-
102
- ## Safe retries with `idempotencyKey`
103
-
104
- Every `submit` call carries an `idempotencyKey`. When omitted the SDK auto-generates a UUID per call. Supplying your own key makes retries deterministic:
105
-
106
- | Submit shape | Server response |
107
- | --- | --- |
108
- | New `idempotencyKey` | HTTP 202 — returns the new run id. |
109
- | Same key + identical request body hash | HTTP 200 — returns the original run id. The SDK call resolves with that id. |
110
- | Same key + **different** request body hash | HTTP 409 — body `{ error: { message, code: "idempotency_conflict", details: { existingRunId } } }`. The SDK throws an `HttpError` carrying that body. Use `details.existingRunId` to adopt the pre-existing run, or pick a fresh key. |
111
- | Omitted `idempotencyKey` | A new UUID is generated on every call — repeat submissions create new runs. |
112
-
113
- The request hash is computed server-side over the canonical submission JSON (model, prompt, system, environment, skill refs, MCP server descriptors, proxy endpoints, `outputs`, etc.) so reordering JSON keys, adding whitespace, or rotating the inline secret bundle does **not** change the hash. Changing the prompt, model, system, or any other non-secret field does.
114
-
115
- Pattern for safe retries:
116
-
117
- ```ts
118
- const idempotencyKey = crypto.randomUUID();
119
- async function submitWithRetry() {
120
- for (let attempt = 0; attempt < 3; attempt++) {
121
- try {
122
- return await aex.submit({
123
- model: RunModels.CLAUDE_HAIKU_4_5,
124
- prompt: "...",
125
- idempotencyKey,
126
- secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
127
- });
128
- } catch (err) {
129
- if (err instanceof Error && err.message.includes("network")) continue;
130
- throw err;
131
- }
132
- }
133
- throw new Error("submit failed after retries");
134
- }
135
- ```
54
+ ## Add capabilities
136
55
 
137
- The same `idempotencyKey` reused with the same body will deterministically resolve to the same run id regardless of how many times the network drops between attempts. Query, stream, wait, or download the run by that id.
56
+ - Add files, skills, AGENTS.md, MCP servers, proxy endpoints, packages, and networking controls with [Composition](concepts/composition.md).
57
+ - Inspect runtime tools with [Agent tools](concepts/agent-tools.md).
58
+ - Use parent/child run delegation from the [Features](https://aex.dev/docs/features/#subagents) page.
59
+ - Narrow output capture or download individual files with [Outputs](outputs.md).
60
+ - Check supported providers and models in the [provider/runtime capability matrix](provider-runtime-capabilities.md).
package/docs/release.md CHANGED
@@ -29,7 +29,7 @@ publish.
29
29
  publishes `@aexhq/sdk@<version>` under the `canary` tag — `latest` is
30
30
  untouched, so `npm install @aexhq/sdk` users are unaffected.
31
31
  5. Run the platform release against the canary: in **aex-platform**, dispatch
32
- `deploy.yml` with `sdk_version=<version>` (threads `AEX_USER_TEST_SDK_VERSION`
32
+ `deploy.yml` with `sdk_version=<version>` (threads `AEX_USER_TEST_VERSION`
33
33
  into the suite). The local plane gates prod, and both planes' user-tests run
34
34
  against `@aexhq/sdk@<version>`.
35
35
  6. On a green platform release, run the **Promote** workflow
@@ -13,7 +13,7 @@ Allowed fields:
13
13
  - `system` - optional system message.
14
14
  - `skills` - array of storage-neutral `kind:"asset"` refs. Config files cannot carry local draft bytes; use `Skill.fromPath(...)` / `Skill.fromFiles(...)` in SDK code first, or reference an existing asset/catalog skill.
15
15
  - `mcpServers` - array of `McpServerRef`; headers are split into `secrets.mcpServers` server-side.
16
- - `environment` - `{ networking?, packages?, envVars? }`. `envVars` are merged into the in-container `RUNTIME.env` / `RUNTIME.json` mounts.
16
+ - `environment` - `{ networking?, packages?, envVars? }`. Networking is open by default; set `networking.mode` to `limited` only when you want an allowlist. `envVars` are merged into the in-container `RUNTIME.env` / `RUNTIME.json` mounts.
17
17
  - `runtimeSize` - optional managed-runtime preset. Prefer `RuntimeSizes` in TypeScript.
18
18
  - `timeout` - optional run deadline duration string such as `"30m"` or `"2h"`.
19
19
  - `postHook` - optional post-agent verifier `{ command, timeout?, maxTurns?, maxChars? }`. It runs after a successful agent process; a failing or timed-out command is sent back to the agent for repair until `maxTurns` is exhausted. Empty `command` is treated as omitted.
@@ -22,7 +22,7 @@ Allowed fields:
22
22
 
23
23
  `agentsMd`, `files`, `outputs`, `builtins`, and `outputMode` are top-level `submit` options, not run-config fields. They carry bytes, capture behavior, or agent tool/output controls that belong on a concrete run submission.
24
24
 
25
- Secrets never live in run config. Pass credentials through `submit({ ...config, secrets })` in the SDK or the equivalent host-mode flags (`--anthropic-api-key`, `--mcp-auth`, `--proxy-auth`) in the CLI. See [Credentials](credentials.md) for the proxy endpoint policy/auth split and retry fields.
25
+ Secrets never live in run config. Pass credentials through `submit({ ...config, secrets })` in the SDK or the equivalent host-mode flags (`--anthropic-api-key`, `--mcp-auth`, `--proxy-auth`) in the CLI. See [Secrets](secrets.md) for secret lifecycles and [Credentials](credentials.md) for the proxy endpoint policy/auth split and retry fields.
26
26
 
27
27
  ## Reuse in code
28
28
 
@@ -0,0 +1,123 @@
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
+ ## Use A Provider Key For One Run
12
+
13
+ ### TypeScript
14
+
15
+ ```ts
16
+ import { AgentExecutor, Models, Providers } from "@aexhq/sdk";
17
+
18
+ const aex = new AgentExecutor({ apiToken: process.env.AEX_API_TOKEN! });
19
+
20
+ await aex.submit({
21
+ provider: Providers.ANTHROPIC,
22
+ model: Models.CLAUDE_HAIKU_4_5,
23
+ prompt: "Write the report and save outputs.",
24
+ secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
25
+ });
26
+ ```
27
+
28
+ ### CLI
29
+
30
+ ```bash
31
+ aex run \
32
+ --api-token "$AEX_API_TOKEN" \
33
+ --anthropic-api-key "$ANTHROPIC_API_KEY" \
34
+ --model claude-haiku-4-5 \
35
+ --prompt "Write the report and save outputs."
36
+ ```
37
+
38
+ ## Upload An Env Secret
39
+
40
+ Use `Secret.value(...).upload(...)` when you start with an ephemeral value and
41
+ want to persist it as a named workspace secret for later runs.
42
+
43
+ ```ts
44
+ import { AgentExecutor, Models, Providers, Secret } from "@aexhq/sdk";
45
+
46
+ const aex = new AgentExecutor({ apiToken: process.env.AEX_API_TOKEN! });
47
+
48
+ const githubToken = await Secret.value(process.env.GITHUB_TOKEN!).upload(aex, {
49
+ name: "github-token"
50
+ });
51
+
52
+ await aex.submit({
53
+ provider: Providers.ANTHROPIC,
54
+ model: Models.CLAUDE_HAIKU_4_5,
55
+ prompt: "Inspect the repository issues.",
56
+ secretEnv: { GITHUB_TOKEN: githubToken },
57
+ secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
58
+ });
59
+ ```
60
+
61
+ ## Set Or Rotate A Workspace Secret
62
+
63
+ Use `client.secrets.set(...)` to create a named secret directly. Use
64
+ `client.secrets.rotate(...)` to replace its value while keeping the same name.
65
+
66
+ ```ts
67
+ await aex.secrets.set({
68
+ name: "serper-api-key",
69
+ value: process.env.SERPER_API_KEY!
70
+ });
71
+
72
+ await aex.secrets.rotate({
73
+ name: "serper-api-key",
74
+ value: process.env.SERPER_API_KEY_NEXT!
75
+ });
76
+ ```
77
+
78
+ ## Retrieve Secret Metadata
79
+
80
+ `list` and `get` return metadata only. They never return the secret value.
81
+
82
+ ```ts
83
+ const secrets = await aex.secrets.list();
84
+ const metadata = await aex.secrets.get("serper-api-key");
85
+ ```
86
+
87
+ ## Get A Secret Value
88
+
89
+ Use `get_value` only when the value is intentionally needed outside a run. It is
90
+ the explicit audited value-read path.
91
+
92
+ ```ts
93
+ const secretValue = await aex.secrets.get_value("serper-api-key");
94
+ console.log(secretValue.value);
95
+ ```
96
+
97
+ ## Inject A Workspace Secret Into A Run
98
+
99
+ Reference workspace secrets with `Secret.ref(name)`. The value resolves
100
+ server-side and is injected as the named environment variable.
101
+
102
+ ```ts
103
+ import { Models, Providers, Secret } from "@aexhq/sdk";
104
+
105
+ await aex.submit({
106
+ provider: Providers.ANTHROPIC,
107
+ model: Models.CLAUDE_HAIKU_4_5,
108
+ prompt: "Use SERPER_API_KEY for web search.",
109
+ secretEnv: {
110
+ SERPER_API_KEY: Secret.ref("serper-api-key")
111
+ },
112
+ secrets: { apiKey: process.env.ANTHROPIC_API_KEY! }
113
+ });
114
+ ```
115
+
116
+ ## Delete A Workspace Secret
117
+
118
+ ```ts
119
+ await aex.secrets.delete("serper-api-key");
120
+ ```
121
+
122
+ The CLI supports per-run provider, MCP, and proxy credentials. Workspace secret
123
+ administration is exposed through the SDK.
package/docs/skills.md CHANGED
@@ -7,7 +7,7 @@ title: Skills
7
7
  A skill is executable or instructional content that is mounted into a run before
8
8
  the first agent turn. Every accepted skill ends up as a storage-neutral
9
9
  `kind:"asset"` reference in the run submission, and the hosted platform snapshots
10
- that asset into the run's object-storage prefix before dispatch.
10
+ that asset into durable run asset storage before dispatch.
11
11
 
12
12
  There are three sources for skill bytes:
13
13
 
@@ -32,8 +32,8 @@ an aex asset instead.
32
32
 
33
33
  ## Materialization
34
34
 
35
- For each run, the platform copies referenced skill assets into that run's
36
- object-storage directory (`runs/<runId>/assets/<hash>`) and the runner downloads them into the
35
+ For each run, the platform copies referenced skill assets into durable run asset
36
+ storage (`runs/<runId>/assets/<hash>`) and the runner downloads them into the
37
37
  workspace under `skills/<name>/`.
38
38
 
39
39
  A bundle's `SKILL.md` is composed into the agent's instructions, so the agent is
@@ -32,7 +32,7 @@ a raised `maxRequestBytes` (so the base64 image fits):
32
32
  ```ts
33
33
  import { AgentExecutor, RunModels, Skill, ProxyEndpoint, validateProxyAuth } from "@aexhq/sdk";
34
34
 
35
- const aex = new AgentExecutor({ apiToken: process.env.AEX_WORKSPACE_TOKEN! });
35
+ const aex = new AgentExecutor({ apiToken: process.env.AEX_API_TOKEN! });
36
36
 
37
37
  const proxyEndpoints = [
38
38
  ProxyEndpoint.bearer({
@@ -124,33 +124,28 @@ upstream = json.loads(base64.b64decode(envelope["upstreamBodyBase64"]).decode())
124
124
  content = upstream["choices"][0]["message"]["content"] # the model's JSON answer
125
125
  ```
126
126
 
127
- The key is injected by the BFF on the outbound call; it never appears on disk in
127
+ The key is injected by the hosted proxy on the outbound call; it never appears on disk in
128
128
  the container or in the model's context.
129
129
 
130
- ## `maxRequestBytes` is required for image POSTs
130
+ ## `maxRequestBytes` and timeout defaults
131
131
 
132
- The per-endpoint `maxRequestBytes` default is **1 MiB**. A base64 data-URL image
133
- is ~1.33x the raw bytes, so a ~480px JPEG (~40-150 KB raw) becomes ~55-200 KB in
134
- the request within the default, but only just. **Set `maxRequestBytes`
135
- explicitly** (a couple of MB) whenever you POST images so a higher-res frame or a
136
- larger prompt does not trip the cap. If a body does exceed the cap, the proxy
137
- rejects it before any upstream call with an explicit error naming the observed
138
- size, the configured cap, and how to raise it:
132
+ The per-endpoint `maxRequestBytes` default is **10 MiB** and the default timeout
133
+ is **5 minutes**. That fits typical base64 image/model POSTs without extra
134
+ configuration. If a body does exceed the cap, the proxy rejects it before any
135
+ upstream call with an explicit error naming the observed size, the configured
136
+ cap, and how to raise it:
139
137
 
140
138
  > request body is 2400000 bytes, which exceeds this endpoint's maxRequestBytes
141
- > (1048576). Raise the per-endpoint maxRequestBytes in the proxy endpoint policy …
139
+ > (10485760). Raise the per-endpoint maxRequestBytes in the proxy endpoint policy …
142
140
 
143
- Two ways to stay under the cap: raise `maxRequestBytes`, and/or scale frames to
144
- ~480px wide before captioning (`ffmpeg -i source.mp4 -vf fps=1,scale=480:-1
145
- frame_%03d.jpg`) full-res adds payload and cost, not signal.
141
+ Two ways to stay under the cap: raise `maxRequestBytes`, and/or scale frames
142
+ before captioning (`ffmpeg -i source.mp4 -vf fps=1,scale=960:-1 frame_%03d.jpg`)
143
+ so full-res frames do not add payload and model cost without useful signal.
146
144
 
147
145
  ## Notes
148
146
 
149
- - **Egress.** The named proxy reaches any HTTPS host you declare as `baseUrl`
150
- (no upstream allow-list; only a literal-IP SSRF deny-list). The international
151
- BytePlus host (`ark.ap-southeast.bytepluses.com`) is a normal public host. The
152
- China host (`ark.cn-beijing.volces.com`) is reachable in principle but the
153
- platform's egress to Beijing is currently unverified — prefer the BytePlus host.
147
+ - **Host selection.** Use the provider endpoint that matches your account and
148
+ declare it as the proxy endpoint `baseUrl`.
154
149
  - **Keyless model hosts.** If the upstream takes no credential, declare the
155
150
  endpoint with `authShape: { type: "none" }` and omit the `proxyEndpointAuth`
156
151
  entry (see `credentials.md`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexhq/sdk",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "TypeScript SDK for running autonomous agent sessions across providers (Anthropic, OpenAI, DeepSeek, Gemini, Mistral) behind one interface.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "examples"
27
27
  ],
28
28
  "devDependencies": {
29
- "@aexhq/contracts": "0.24.0"
29
+ "@aexhq/contracts": "0.25.1"
30
30
  },
31
31
  "engines": {
32
32
  "node": ">=20"
@@ -1,101 +0,0 @@
1
- import type { RunProvider, RuntimeKind } from "./submission.js";
2
- import type { RunModel } from "./models.js";
3
- export declare const CREDENTIAL_MODES: readonly ["byok", "managed"];
4
- export type CredentialMode = (typeof CREDENTIAL_MODES)[number];
5
- export declare const DEFAULT_CREDENTIAL_MODE: CredentialMode;
6
- export declare const MANAGED_KEY_POLICY_SCHEMA_VERSION = 1;
7
- export declare const MANAGED_KEY_RESERVATION_SCHEMA_VERSION = 1;
8
- export declare const MANAGED_KEY_LAUNCH_STAGES: readonly ["blocked", "pilot", "ga"];
9
- export type ManagedKeyLaunchStage = (typeof MANAGED_KEY_LAUNCH_STAGES)[number];
10
- export declare const MANAGED_KEY_FEATURE_DECISIONS: readonly ["disabled", "allowed"];
11
- export type ManagedKeyFeatureDecision = (typeof MANAGED_KEY_FEATURE_DECISIONS)[number];
12
- export declare const MANAGED_KEY_RESERVATION_STATUSES: readonly ["open", "settled", "released"];
13
- export type ManagedKeyReservationStatus = (typeof MANAGED_KEY_RESERVATION_STATUSES)[number];
14
- export interface ManagedKeyFeaturePolicyV1 {
15
- readonly files: ManagedKeyFeatureDecision;
16
- readonly packages: ManagedKeyFeatureDecision;
17
- readonly builtins: ManagedKeyFeatureDecision;
18
- readonly mcpServers: ManagedKeyFeatureDecision;
19
- readonly proxyEndpoints: ManagedKeyFeatureDecision;
20
- readonly openNetworking: ManagedKeyFeatureDecision;
21
- }
22
- /**
23
- * Public managed-key policy contract. Concrete policy values, account
24
- * selection, financial calculation, and deployment wiring live outside this
25
- * public module.
26
- */
27
- export interface ManagedKeyPolicyV1 {
28
- readonly schemaVersion: typeof MANAGED_KEY_POLICY_SCHEMA_VERSION;
29
- readonly credentialMode: "managed";
30
- readonly launchStage: ManagedKeyLaunchStage;
31
- readonly privateImplementationAvailable: boolean;
32
- readonly billingRequired: true;
33
- readonly providers: readonly RunProvider[];
34
- readonly runtimes: readonly RuntimeKind[];
35
- readonly models?: readonly RunModel[];
36
- readonly features: ManagedKeyFeaturePolicyV1;
37
- }
38
- /**
39
- * Public-safe reservation lifecycle summary. It intentionally carries only
40
- * credit-unit invariants and public row identifiers; private key handles,
41
- * account selection, rate cards, margins, and payment-provider references
42
- * remain outside this contract.
43
- */
44
- export interface ManagedKeyReservationLifecycleV1 {
45
- readonly schemaVersion: typeof MANAGED_KEY_RESERVATION_SCHEMA_VERSION;
46
- readonly reservationId: string;
47
- readonly workspaceId: string;
48
- readonly runId: string;
49
- readonly credentialMode: "managed";
50
- readonly status: ManagedKeyReservationStatus;
51
- readonly reservedCreditUnits: number;
52
- readonly chargedCreditUnits: number;
53
- readonly releasedCreditUnits: number;
54
- readonly createdAt?: string;
55
- readonly closedAt?: string;
56
- }
57
- export type ManagedKeyReservationLifecycleInput = Omit<ManagedKeyReservationLifecycleV1, "schemaVersion" | "credentialMode"> & {
58
- readonly credentialMode?: "managed";
59
- };
60
- export declare const BLOCKED_MANAGED_KEY_FEATURE_POLICY_V1: ManagedKeyFeaturePolicyV1;
61
- export declare const BLOCKED_MANAGED_KEY_POLICY_V1: ManagedKeyPolicyV1;
62
- export declare class ManagedKeyUnavailableError extends Error {
63
- readonly code = "managed_key_unavailable";
64
- constructor(message?: string);
65
- }
66
- export declare function parseCredentialMode(input: unknown): CredentialMode;
67
- export declare function credentialModeOrDefault(input: CredentialMode | undefined): CredentialMode;
68
- export declare function isCredentialMode(input: unknown): input is CredentialMode;
69
- export declare function buildManagedKeyReservationLifecycle(input: ManagedKeyReservationLifecycleInput): ManagedKeyReservationLifecycleV1;
70
- export declare function isManagedKeyGenerallyAvailable(policy: ManagedKeyPolicyV1): boolean;
71
- export declare function isManagedKeyAdmissionAllowed(policy: ManagedKeyPolicyV1): boolean;
72
- export declare function assertManagedKeyModeAvailable(policy?: ManagedKeyPolicyV1): void;
73
- export declare function assertManagedKeyAdmissionAllowed(policy?: ManagedKeyPolicyV1): void;
74
- export interface ManagedCredentialResolutionInput {
75
- readonly workspaceId: string;
76
- readonly runId: string;
77
- readonly provider: RunProvider;
78
- readonly runtime: RuntimeKind;
79
- readonly model: RunModel;
80
- readonly policy: ManagedKeyPolicyV1;
81
- }
82
- export interface ManagedCredentialLease {
83
- readonly credentialMode: "managed";
84
- readonly provider: RunProvider;
85
- readonly runtime: RuntimeKind;
86
- readonly custodyClass: "managed-provider-credential";
87
- }
88
- export type ManagedCredentialResolution = {
89
- readonly ok: true;
90
- readonly lease: ManagedCredentialLease;
91
- } | {
92
- readonly ok: false;
93
- readonly code: "managed_key_unavailable" | "provider_not_allowed" | "runtime_not_allowed" | "model_not_allowed";
94
- readonly message: string;
95
- };
96
- export interface ManagedCredentialResolver {
97
- resolveManagedCredential(input: ManagedCredentialResolutionInput): Promise<ManagedCredentialResolution>;
98
- }
99
- export declare class FakeManagedCredentialResolver implements ManagedCredentialResolver {
100
- resolveManagedCredential(input: ManagedCredentialResolutionInput): Promise<ManagedCredentialResolution>;
101
- }