@aexhq/sdk 0.16.0 → 0.16.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.
@@ -11,13 +11,16 @@ Allowed fields:
11
11
  - `model` - required.
12
12
  - `prompt` - required, string or array of strings.
13
13
  - `system` - optional system message.
14
- - `skills` - array of `SkillRef`, either workspace, provider, or inline.
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
- - `proxyEndpoints` - array of `PlatformProxyEndpoint`.
17
16
  - `environment` - `{ networking?, packages?, envVars? }`. `envVars` are merged into the in-container `RUNTIME.env` / `RUNTIME.json` mounts.
17
+ - `runtimeSize` - optional managed-runtime preset. Prefer `RuntimeSizes` in TypeScript.
18
+ - `timeout` - optional run deadline duration string such as `"30m"` or `"2h"`.
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.
20
+ - `proxyEndpoints` - array of `PlatformProxyEndpoint`.
18
21
  - `metadata` - non-secret structured metadata.
19
22
 
20
- `agentsMd`, `files`, and `outputs` are top-level `submitRun` options, not run-config fields. They carry bytes or capture behavior that belongs on a concrete run submission.
23
+ `agentsMd`, `files`, `outputs`, `builtins`, and `outputMode` are top-level `submitRun` options, not run-config fields. They carry bytes, capture behavior, or agent tool/output controls that belong on a concrete run submission.
21
24
 
22
25
  Secrets never live in run config. Pass credentials through `submitRun({ ...config, secrets })` in the SDK or the equivalent host-mode flags (`--anthropic-api-key`, `--mcp-auth`, `--proxy-auth`) in the CLI.
23
26
 
@@ -26,11 +29,13 @@ Secrets never live in run config. Pass credentials through `submitRun({ ...confi
26
29
  Use an ordinary function when you want reusable typed parameters. aex does not store or execute this function; it only receives the run parameters you submit.
27
30
 
28
31
  ```ts
32
+ import { RunModels } from "@aexhq/sdk";
33
+
29
34
  function summarise(topic: string) {
30
35
  return {
31
- model: "claude-haiku-4-5",
32
- system: "You are a concise automation agent.",
33
- prompt: `Write a short answer about ${topic}.`
36
+ model: RunModels.CLAUDE_HAIKU_4_5,
37
+ system: "You are a concise automation agent.",
38
+ prompt: `Write a short answer about ${topic}.`
34
39
  };
35
40
  }
36
41
 
@@ -50,4 +55,4 @@ aex run --config ./run.json \
50
55
  --anthropic-api-key "$ANTHROPIC_API_KEY"
51
56
  ```
52
57
 
53
- ...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--proxy-endpoint`, `--proxy-auth`, `--metadata`). The two modes are mutually exclusive.
58
+ ...or as explicit flags (`--model`, `--system`, `--prompt`, `--mcp`, `--mcp-auth`, `--runtime-size`, `--run-timeout`, `--proxy-endpoint`, `--proxy-auth`, `--metadata`). The two modes are mutually exclusive. `postHook` is available through `--config`; there are no standalone hook flags.
package/docs/skills.md CHANGED
@@ -4,154 +4,139 @@ title: Skills
4
4
 
5
5
  # Skills
6
6
 
7
- Skill inputs accepted by the platform:
8
-
9
- - workspace skill bundles (persistent, referenced by `skl_*` id);
10
- - inline-supplied bundles passed directly at `submitRun` these
11
- persist on aex as workspace skills with auto-suffixed names,
12
- one row per submission (see "Inline supply" below).
13
-
14
- Provider-hosted skill refs (`kind:"provider"`, e.g. Anthropic prebuilt
15
- Agent Skills or custom provider skill IDs) are **not supported on the
16
- managed runtime** every new submission dispatches to managed and a
17
- `kind:"provider"` ref is rejected at submission time with
18
- `feature_runtime_mismatch`. Supply the bytes as a workspace or inline
19
- bundle instead.
20
-
21
- **How skills reach the agent:** every skill bundle is materialized into
22
- the run's workspace under `skills/<name>/` before the first agent turn.
23
- A bundle's `SKILL.md` is composed into the agent's instructions, so the
24
- agent is told the skill exists and what it does without the model having
25
- to discover it. Bundles without `SKILL.md` are still mounted as files at
26
- `skills/<name>/`, but nothing prompts the agent to read them — reference
27
- them explicitly from the prompt or your `AGENTS.md`.
28
-
29
- The platform also mounts the `aex` CLI and a per-run manifest into the
30
- workspace on **every** run. Skills invoke the managed HTTP proxy via the
31
- mounted CLI (`aex proxy …`) — see `credentials.md` for the policy/auth
32
- model.
33
-
34
- ## Inline supply at `submitRun`
35
-
36
- `Skill.fromFiles({name, files})` and `Skill.fromPath(rootDir, {name})`
37
- build an **unstaged** `Skill`. The instance carries the canonicalised
38
- zip bytes and the `sha256:<hex>` content hash:
7
+ A skill is executable or instructional content that is mounted into a run before
8
+ the first agent turn. Every accepted skill ends up as a storage-neutral
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.
11
+
12
+ There are three sources for skill bytes:
13
+
14
+ - **Inline/local draft:** `Skill.fromFiles(...)`, `Skill.fromPath(...)`, or
15
+ `Skill.fromUrl(...)` builds a draft in the SDK process. `submitRun` uploads
16
+ it before posting `/runs`.
17
+ - **Pre-uploaded workspace asset:** call `await draft.upload(aex)` and reuse the
18
+ returned materialized `Skill`, or pass an existing `kind:"asset"` ref from a
19
+ config file.
20
+ - **Workspace skill catalog:** upload with `aex skills upload` or the dashboard,
21
+ then pass the returned record to `Skill.fromCatalog(record)`.
22
+
23
+ All three sources normalize to the same content-addressed asset. Identical bytes
24
+ dedup by hash, so repeated submissions of the same bundle are no-op uploads.
25
+ There is no per-run auto-suffixed `skl_*` row for inline skills.
26
+
27
+ Provider-hosted skill refs (`kind:"provider"`, e.g. Anthropic prebuilt Agent
28
+ Skills or custom provider skill IDs) are not supported on the managed runtime.
29
+ Every new submission dispatches to managed, so a `kind:"provider"` ref is
30
+ rejected at submission time with `feature_runtime_mismatch`. Supply the bytes as
31
+ an aex asset instead.
32
+
33
+ ## Materialization
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
37
+ workspace under `skills/<name>/`.
38
+
39
+ A bundle's `SKILL.md` is composed into the agent's instructions, so the agent is
40
+ told the skill exists and what it does without needing to discover it. Bundles
41
+ without `SKILL.md` are still mounted as files at `skills/<name>/`, but nothing
42
+ prompts the agent to read them; reference them explicitly from the prompt or
43
+ your `AGENTS.md`.
44
+
45
+ The platform also mounts the `aex` CLI and a per-run manifest into every run.
46
+ Skills call managed HTTP proxy endpoints through the mounted CLI
47
+ (`aex proxy ...`); see `credentials.md` for the policy and auth model.
48
+
49
+ Run-scoped asset copies are part of the run record and are removed by run deletion
50
+ or retention cleanup. Catalog assets are separate workspace records: deleting a
51
+ catalog skill hard-deletes its metadata and removes the shared asset object only
52
+ when no other catalog row still references those bytes. Existing run snapshots
53
+ keep their run-scoped copy.
54
+
55
+ ## Inline And Local Drafts
56
+
57
+ `Skill.fromFiles({ name, files })`, `Skill.fromPath(rootDir, { name })`, and
58
+ `Skill.fromUrl(url, { name })` build an unstaged `Skill`. The instance carries
59
+ canonical zip bytes and a `sha256:<hex>` content hash.
39
60
 
40
61
  ```ts
41
- import { AgentExecutor, Skill } from "@aexhq/sdk";
62
+ import { AgentExecutor, RunModels, Skill } from "@aexhq/sdk";
42
63
 
43
64
  const aex = new AgentExecutor({ apiToken });
44
65
 
45
66
  await aex.submitRun({
46
- model, prompt,
47
- skills: [await Skill.fromFiles({ name: "rules", files })],
67
+ model: RunModels.CLAUDE_HAIKU_4_5,
68
+ prompt,
69
+ skills: [await Skill.fromPath("./skills/rules", { name: "rules" })],
48
70
  secrets: { apiKey }
49
71
  });
50
72
  ```
51
73
 
52
- `aex.submitRun` walks the `skills` array, sends a multipart body
53
- alongside the JSON submission, and materializes the bytes to
54
- content-addressable, workspace-scoped asset storage before the run lands.
55
- The BFF re-canonicalises the bundle, verifies the advisory hash, and
56
- persists it as a workspace skill — but with an **auto-suffixed name**
57
- (`rules-x8q7lk2`) so repeated supplies of the same logical skill across
58
- many runs produce distinct `skl_*` rows. This is by design: a
59
- submitted skill is a **per-run artifact**.
74
+ Before it posts `/runs`, the SDK uploads each draft through the asset upload
75
+ flow:
60
76
 
61
- `contentHash` is `sha256:<hex>` of the canonical bundle zip (the SDK
62
- normalises file order, mtime, and permissions before hashing).
63
- Identical inputs always produce the same hash, so the same bytes are a
64
- no-op upload (content-addressable dedup) on subsequent runs. There is no
65
- separate workspace pre-upload step.
77
+ 1. `POST /assets/presign` checks for a dedup hit and, when needed, returns a
78
+ signed upload URL.
79
+ 2. The SDK PUTs bytes directly to object storage with the signed checksum headers.
80
+ 3. `POST /assets/finalize` confirms the object exists.
66
81
 
67
- ### What auto-suffix looks like
82
+ When direct upload credentials are not configured, small bundles fall back to
83
+ the buffered `/assets` upload path. The runner re-verifies the content hash when
84
+ it downloads the asset.
68
85
 
69
- Submit `Skill.fromFiles({ name: "rules", files })` three times across
70
- three runs and the dashboard shows three distinct skills:
86
+ ## Pre-Upload For Reuse
71
87
 
72
- ```
73
- rules-x8q7lk2
74
- rules-mp2vqa1
75
- rules-az3lkmt
76
- ```
88
+ If you want to build a local skill once and reuse the materialized asset across
89
+ multiple submissions, upload the draft explicitly:
77
90
 
78
- Same prefix, different suffix. Each is a real workspace skill — you
79
- can list, get, download, and delete it through the regular
80
- `aex.skills.*` verbs.
81
-
82
- ### Deletion semantics
91
+ ```ts
92
+ const draft = await Skill.fromFiles({ name: "rules", files });
93
+ const uploaded = await draft.upload(aex);
83
94
 
84
- Soft-deleting a skill (`aex.skills.delete(skl_id)` or the
85
- dashboard's Delete button) marks the row tombstoned. Existing runs
86
- that pinned a snapshot of the skill keep working — they read from
87
- `run_skill_snapshots` which preserves the name, hash, size, and
88
- manifest. The run detail view shows a "deleted" badge for those
89
- orphan references; the download endpoint returns
90
- **HTTP 410 Gone** with `{ error: { code: "skill_deleted", … } }`
91
- when the caller tries to fetch the bytes of a deleted skill.
95
+ await aex.submitRun({
96
+ model: RunModels.CLAUDE_HAIKU_4_5,
97
+ prompt,
98
+ skills: [uploaded],
99
+ secrets: { apiKey }
100
+ });
101
+ ```
92
102
 
93
- New `submitRun` calls referencing a soft-deleted `skl_*` id are
94
- rejected before insertion.
103
+ The returned `uploaded` skill carries a plain `kind:"asset"` ref. Submitting it
104
+ does not upload bytes again.
95
105
 
96
- ## Fetch from a signed URL (`Skill.fromUrl`)
106
+ ## Fetch From A Signed URL
97
107
 
98
- When your app runs in the cloud with limited storage, you may not want to
99
- bundle skill bytes with it. Host the skill yourself as a **zip archive**
100
- (with `SKILL.md` at the archive root) and hand the SDK a temporary signed
101
- URL — e.g. an S3 presigned URL:
108
+ When your app runs in the cloud with limited local storage, host the skill
109
+ yourself as a zip archive with `SKILL.md` at the archive root and pass a
110
+ temporary signed URL:
102
111
 
103
112
  ```ts
104
- import { AgentExecutor, Skill } from "@aexhq/sdk";
105
-
106
- const aex = new AgentExecutor({ apiToken });
107
-
108
- await aex.submitRun({
109
- model, prompt,
110
- skills: [
111
- await Skill.fromUrl(signedUrl, { name: "rules", sha256: "sha256:<hex>" })
112
- ],
113
- secrets: { apiKey }
113
+ const skill = await Skill.fromUrl(signedUrl, {
114
+ name: "rules",
115
+ sha256: "sha256:<hex>"
114
116
  });
115
117
  ```
116
118
 
117
- `Skill.fromUrl` fetches the archive **in the SDK process** the URL is
118
- caller-controlled, so there is no server-side fetch optionally verifies the
119
- download against `sha256`, unzips it, and reduces it to the same files map as
120
- `Skill.fromFiles`. A URL-sourced skill and the identical local skill therefore
121
- produce the **same canonical asset** and dedup against each other.
122
-
123
- - The archive must contain `SKILL.md` at its root, or inside a single
124
- top-level folder, which is stripped automatically. Anything else is
125
- rejected with an error listing the archive's actual top-level entries.
126
- - The signed URL only needs to be valid **for this call**. `aex.submitRun`
127
- snapshots the bytes into the run immediately, so the URL can expire
128
- afterwards with no effect on the run.
129
- - `sha256` is an optional source-integrity check on the downloaded archive
130
- (distinct from the canonical bundle hash); a mismatch fails fast before the
131
- unzip. Signed-URL query strings are never echoed in error messages.
132
- - The same upload caps apply (10 MB compressed / 50 MB decompressed /
133
- 1000 files); `Skill.fromUrl` materialises the whole bundle, it is not a
134
- streaming mount.
135
-
136
- `Skill.fromUrl` is universal (Node 18+ / browser): it uses the global `fetch`,
137
- or pass one via `{ fetch }`.
138
-
139
- ## Re-reference an uploaded skill (`Skill.fromCatalog`)
140
-
141
- To re-use a skill already persisted in the workspace catalog (e.g. one an
142
- earlier inline supply created), pass the `Skill` record returned by
143
- `aex.skills.list()` / `aex.skills.get()` to `Skill.fromCatalog`:
119
+ `Skill.fromUrl` fetches the archive in the SDK process. The hosted platform does
120
+ not fetch the caller-controlled URL. The signed URL only needs to be valid for
121
+ this call; the SDK snapshots the bytes into the asset store before the run is
122
+ submitted.
123
+
124
+ ## Workspace Catalog
125
+
126
+ Catalog skills are workspace records backed by the same content-addressed
127
+ assets. Use them when a team wants a named, listed skill record:
144
128
 
145
129
  ```ts
146
130
  const [record] = await aex.skills.list();
147
131
 
148
132
  await aex.submitRun({
149
- model, prompt,
133
+ model: RunModels.CLAUDE_HAIKU_4_5,
134
+ prompt,
150
135
  skills: [Skill.fromCatalog(record)],
151
136
  secrets: { apiKey }
152
137
  });
153
138
  ```
154
139
 
155
- The record must be `ready` (it carries a content hash). Unlike the draft
156
- builders this performs no upload `fromCatalog` produces a `kind:"asset"`
157
- ref directly against the bytes already in the catalog.
140
+ The record must be `ready` and carry a content hash. `Skill.fromCatalog` performs
141
+ no upload; it produces a `kind:"asset"` ref directly against bytes already in
142
+ the catalog.
package/docs/testing.md CHANGED
@@ -23,7 +23,12 @@ pnpm run pack:sdk # SDK publish dry-run + public bo
23
23
 
24
24
  Unit tests are deterministic and may use fakes. Offline user tests install the
25
25
  packed or published SDK into clean temp projects and do not need provider
26
- credentials. Live user tests run against a hosted aex API and fail loudly
27
- when required env is missing: `AEX_API_URL`, `AEX_API_TOKEN`,
28
- `DEEPSEEK_API_KEY`, and exactly one of `AEX_USER_TEST_TARBALL` or
29
- `AEX_USER_TEST_VERSION`.
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_TOKEN`, and `DEEPSEEK_API_KEY`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexhq/sdk",
3
- "version": "0.16.0",
3
+ "version": "0.16.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.16.0"
29
+ "@aexhq/contracts": "0.16.1"
30
30
  },
31
31
  "engines": {
32
32
  "node": ">=20"