@aexhq/sdk 0.33.0 → 0.34.0

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 (76) hide show
  1. package/README.md +19 -27
  2. package/dist/_contracts/operations.d.ts +2 -54
  3. package/dist/_contracts/operations.js +2 -87
  4. package/dist/_contracts/run-config.d.ts +19 -13
  5. package/dist/_contracts/run-config.js +6 -33
  6. package/dist/_contracts/run-unit.d.ts +1 -33
  7. package/dist/_contracts/run-unit.js +2 -21
  8. package/dist/_contracts/runtime-sizes.d.ts +2 -2
  9. package/dist/_contracts/runtime-sizes.js +2 -2
  10. package/dist/_contracts/status.d.ts +2 -2
  11. package/dist/_contracts/status.js +3 -0
  12. package/dist/_contracts/submission.d.ts +22 -18
  13. package/dist/_contracts/submission.js +60 -42
  14. package/dist/agents-md.d.ts +5 -5
  15. package/dist/agents-md.js +7 -7
  16. package/dist/agents-md.js.map +1 -1
  17. package/dist/asset-upload.d.ts +4 -4
  18. package/dist/asset-upload.js +4 -4
  19. package/dist/bundle.d.ts +2 -2
  20. package/dist/bundle.js +2 -2
  21. package/dist/cli.mjs +354 -12982
  22. package/dist/cli.mjs.sha256 +1 -1
  23. package/dist/client.d.ts +218 -386
  24. package/dist/client.js +375 -645
  25. package/dist/client.js.map +1 -1
  26. package/dist/data-tools.d.ts +25 -22
  27. package/dist/data-tools.js +75 -62
  28. package/dist/data-tools.js.map +1 -1
  29. package/dist/fetch-archive.js +16 -16
  30. package/dist/fetch-archive.js.map +1 -1
  31. package/dist/file.d.ts +5 -5
  32. package/dist/file.js +7 -7
  33. package/dist/file.js.map +1 -1
  34. package/dist/index.d.ts +9 -9
  35. package/dist/index.js +14 -13
  36. package/dist/index.js.map +1 -1
  37. package/dist/mcp-server.d.ts +4 -4
  38. package/dist/mcp-server.js +4 -4
  39. package/dist/proxy-endpoint.d.ts +4 -4
  40. package/dist/proxy-endpoint.js +1 -1
  41. package/dist/secret.d.ts +8 -8
  42. package/dist/secret.js +8 -8
  43. package/dist/secret.js.map +1 -1
  44. package/dist/skill-tool.d.ts +102 -0
  45. package/dist/skill-tool.js +190 -0
  46. package/dist/skill-tool.js.map +1 -0
  47. package/dist/tool.d.ts +1 -1
  48. package/dist/tool.js +3 -3
  49. package/dist/tool.js.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/docs/cleanup.md +3 -3
  53. package/docs/concepts/agent-tools.md +6 -25
  54. package/docs/concepts/composition.md +15 -12
  55. package/docs/concepts/providers-and-runtimes.md +3 -3
  56. package/docs/concepts/runs.md +27 -22
  57. package/docs/credentials.md +52 -84
  58. package/docs/defaults.md +6 -6
  59. package/docs/events.md +65 -44
  60. package/docs/limits-and-quotas.md +3 -4
  61. package/docs/mcp.md +3 -3
  62. package/docs/networking.md +8 -8
  63. package/docs/outputs.md +44 -40
  64. package/docs/provider-runtime-capabilities.md +1 -1
  65. package/docs/public-surface.json +2 -2
  66. package/docs/quickstart.md +20 -10
  67. package/docs/run-config.md +12 -14
  68. package/docs/run-record.md +8 -8
  69. package/docs/secrets.md +16 -26
  70. package/docs/skills.md +55 -110
  71. package/docs/vision-skills.md +29 -40
  72. package/examples/chat-corpus.ts +8 -9
  73. package/package.json +1 -1
  74. package/dist/skill.d.ts +0 -149
  75. package/dist/skill.js +0 -198
  76. package/dist/skill.js.map +0 -1
package/docs/skills.md CHANGED
@@ -4,132 +4,77 @@ title: Skills
4
4
 
5
5
  # Skills
6
6
 
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 durable run asset storage 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. `submit` 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
- ## Materialization
28
-
29
- For each run, the platform copies referenced skill assets into durable run asset
30
- storage (`runs/<runId>/assets/<hash>`) and the runner downloads them into the
31
- workspace under `skills/<name>/`.
32
-
33
- A bundle's `SKILL.md` is mounted at `skills/<name>/SKILL.md`, and the agent's
34
- instructions list each mounted skill with that path. The full skill body stays
35
- on disk, so prompts or `AGENTS.md` guidance that rely on a skill should tell the
36
- agent when to read or use it. Bundles without `SKILL.md` are still mounted as
37
- files at `skills/<name>/`, but nothing prompts the agent to read them; reference
38
- them explicitly from the prompt or your `AGENTS.md`.
39
-
40
- The platform also mounts the `aex` CLI and a per-run manifest into every run.
41
- Skills call managed HTTP proxy endpoints through the mounted CLI
42
- (`aex proxy ...`); see `credentials.md` for the policy and auth model.
43
-
44
- Run-scoped asset copies are part of the run record and are removed by run deletion
45
- or retention cleanup. Catalog assets are separate workspace records: deleting a
46
- catalog skill hard-deletes its metadata and removes the shared asset object only
47
- when no other catalog row still references those bytes. Existing run snapshots
48
- keep their run-scoped copy.
49
-
50
- ## Inline And Local Drafts
51
-
52
- `Skill.fromFiles({ name, files })`, `Skill.fromPath(rootDir, { name })`, and
53
- `Skill.fromUrl(url, { name })` build an unstaged `Skill`. The instance carries
54
- canonical zip bytes and a `sha256:<hex>` content hash.
7
+ A skill is a bundle of instructional or executable content (`SKILL.md` plus any
8
+ supporting files) that the agent can pull into context on demand. In the SDK a
9
+ skill is expressed as a per-skill **load-tool**: it rides in the session's
10
+ `tools` array next to builtin tool names and custom `Tool` bundles, and the model
11
+ loads it by calling it.
12
+
13
+ Build a skill-tool with the `Tools.fromSkill*` factories. Each factory reads a
14
+ skill bundle, lifts the tool `name` and `description` from the `SKILL.md` YAML
15
+ frontmatter (an explicit `{ name }` overrides the frontmatter), canonically
16
+ zips + hashes the bytes, and returns a `SkillTool`:
17
+
18
+ - **Local directory:** `Tools.fromSkillDir(rootDir, { name? })` reads a folder
19
+ that has `SKILL.md` at its root (Bun/Node filesystem runtimes).
20
+ - **Signed URL:** `Tools.fromSkillUrl(url, { name?, sha256?, timeoutMs?, fetch? })`
21
+ fetches a zip archive with `SKILL.md` at the archive root (universal — needs a
22
+ global `fetch`, or pass one).
55
23
 
56
24
  ```ts
57
- import { AgentExecutor, Models, Skill } from "@aexhq/sdk";
25
+ import { Aex, Models, Tools } from "@aexhq/sdk";
58
26
 
59
- const aex = new AgentExecutor({ apiToken });
27
+ const aex = new Aex({ apiToken });
60
28
 
61
- await aex.submit({
29
+ await aex.run({
62
30
  model: Models.CLAUDE_HAIKU_4_5,
63
- prompt,
64
- skills: [await Skill.fromPath("./skills/rules", { name: "rules" })],
65
- secrets: { apiKeys: { anthropic: apiKey } }
31
+ message,
32
+ tools: [await Tools.fromSkillDir("./skills/rules", { name: "rules" })],
33
+ apiKeys: { anthropic: apiKey }
66
34
  });
67
35
  ```
68
36
 
69
- Before it posts `/runs`, the SDK uploads each draft through the asset upload
70
- flow:
37
+ `Tools.fromSkillDir("./skills/rules", …)` resolves relative to the process CWD,
38
+ so run the script from the directory that *contains* `skills/`. The `SKILL.md`
39
+ frontmatter must supply a `description` (max 2048 chars) and, unless you pass
40
+ `{ name }`, a `name`. Names must match the tool-name pattern and must not contain
41
+ `__` (that separator is reserved for MCP tools).
42
+
43
+ ## How a skill-tool rides on the wire
44
+
45
+ Before the session lands, `openSession` / `run` walks the `tools` array and
46
+ uploads each draft skill-tool's bundle through the asset upload flow:
71
47
 
72
48
  1. `POST /assets/presign` checks for a dedup hit and, when needed, returns a
73
49
  signed upload URL.
74
50
  2. The SDK PUTs bytes directly to object storage with the signed checksum headers.
75
51
  3. `POST /assets/finalize` confirms the object exists.
76
52
 
77
- The runner re-verifies the content hash when it downloads the asset.
78
-
79
- ## Pre-Upload For Reuse
80
-
81
- If you want to build a local skill once and reuse the materialized asset across
82
- multiple submissions, upload the draft explicitly:
83
-
84
- ```ts
85
- const draft = await Skill.fromFiles({ name: "rules", files });
86
- const uploaded = await draft.upload(aex);
87
-
88
- await aex.submit({
89
- model: Models.CLAUDE_HAIKU_4_5,
90
- prompt,
91
- skills: [uploaded],
92
- secrets: { apiKeys: { anthropic: apiKey } }
93
- });
94
- ```
95
-
96
- The returned `uploaded` skill carries a plain `kind:"asset"` ref. Submitting it
97
- does not upload bytes again.
98
-
99
- ## Fetch From A Signed URL
100
-
101
- When your app runs in the cloud with limited local storage, host the skill
102
- yourself as a zip archive with `SKILL.md` at the archive root and pass a
103
- temporary signed URL:
104
-
105
- ```ts
106
- const skill = await Skill.fromUrl(signedUrl, {
107
- name: "rules",
108
- sha256: "sha256:<hex>"
109
- });
110
- ```
111
-
112
- `Skill.fromUrl` fetches the archive in the SDK process. The hosted platform does
113
- not fetch the caller-controlled URL. The signed URL only needs to be valid for
114
- this call; the SDK snapshots the bytes into the asset store before the run is
115
- submitted.
53
+ The wire ref then becomes a `{ kind:"skill", assetId, name, description }` entry
54
+ inside `submission.tools`. Identical bytes dedup by content hash, so re-submitting
55
+ the same bundle is a no-op upload; a `SkillTool` instance also caches its resolved
56
+ asset id, so reusing the same instance across sessions skips the re-upload. A URL
57
+ is an ingestion source, not a persistent reference the SDK snapshots the fetched
58
+ bytes into the asset store, and the hosted platform never fetches the
59
+ caller-controlled URL.
116
60
 
117
- ## Workspace Catalog
61
+ ## Loading and materialization
118
62
 
119
- Catalog skills are workspace records backed by the same content-addressed
120
- assets. Use them when a team wants a named, listed skill record:
63
+ The skill-tool's `name` and `description` are what the agent sees in its tool
64
+ list. At run time the model calls the **no-arg load-tool** to pull the skill's
65
+ `SKILL.md` body into context — the description tells the agent when that is worth
66
+ doing.
121
67
 
122
- ```ts
123
- const [record] = await aex.skills.list();
68
+ Independently of whether the model calls the load-tool, the platform copies the
69
+ referenced skill asset into durable run asset storage
70
+ (`runs/<runId>/assets/<hash>`) and the runner **eagerly stages** the bundle's
71
+ files into the workspace under `/workspace/skills/<name>/`. So the `SKILL.md` body
72
+ and every supporting file are on disk from the first turn; the load-tool call is
73
+ how that body enters the model's context, not how the files get written.
124
74
 
125
- await aex.submit({
126
- model: Models.CLAUDE_HAIKU_4_5,
127
- prompt,
128
- skills: [Skill.fromCatalog(record)],
129
- secrets: { apiKeys: { anthropic: apiKey } }
130
- });
131
- ```
75
+ The platform also mounts the `aex` CLI and a per-run manifest into every run.
76
+ Skills call managed HTTP proxy endpoints through the mounted CLI
77
+ (`aex proxy ...`); see [Credentials](credentials.md) for the policy and auth model.
132
78
 
133
- The record must be `ready` and carry a content hash. `Skill.fromCatalog` performs
134
- no upload; it produces a `kind:"asset"` ref directly against bytes already in
135
- the catalog.
79
+ Run-scoped asset copies are part of the run record and are removed by run deletion
80
+ or retention cleanup.
@@ -8,8 +8,8 @@ aex has no built-in vision tool. The agent's `provider`/`model` selects the
8
8
  *reasoning* model — it is not an endpoint a skill can POST an image to mid-run.
9
9
  To give a run image understanding (or to call any other model/HTTP API), ship a
10
10
  **skill** that POSTs to the provider's OpenAI-compatible endpoint **through the
11
- managed proxy**, with the key supplied via `secrets.proxyEndpointAuth`. The raw
12
- key never enters the container.
11
+ managed proxy**, with the key supplied on a `ProxyEndpoint.bearer(...)` instance.
12
+ The raw key never enters the container.
13
13
 
14
14
  This is the same proxy described in `credentials.md` — this page is the worked
15
15
  recipe for the model-API case, which has two wrinkles a plain JSON call does not:
@@ -18,54 +18,44 @@ large enough to need a raised `maxRequestBytes`.
18
18
 
19
19
  The canonical, runnable example lives in the repo at
20
20
  [`examples/vision-skill/`](../../../examples/vision-skill) (`SKILL.md`,
21
- `caption_frame.py`, `verify_frame.py`, `submit_with_vision_skill.mjs`). It
21
+ `caption_frame.py`, `verify_frame.py`, `run_with_vision_skill.mjs`). It
22
22
  captions a frame with ByteDance Doubao Seed Vision (Ark) and returns a per-noun
23
23
  "does the frame depict X?" verdict. Everything below is taken from it.
24
24
 
25
25
  ## 1. Declare the model endpoint as a proxy endpoint
26
26
 
27
- The vision provider's API is just an HTTPS host. Declare it as a `bearer` proxy
28
- endpoint and supply the key in `secrets.proxyEndpointAuth`. The two model-specific
29
- settings are `responseMode: "full"` (so the skill gets the upstream JSON back) and
30
- a raised `maxRequestBytes` (so the base64 image fits):
27
+ The vision provider's API is just an HTTPS host. Declare it with
28
+ `ProxyEndpoint.bearer(...)`, which carries the key on the instance. The two
29
+ model-specific settings are `responseMode: "full"` (so the skill gets the upstream
30
+ JSON back) and a raised `maxRequestBytes` (so the base64 image fits):
31
31
 
32
32
  ```ts
33
- import { AgentExecutor, Models, Skill, ProxyEndpoint, validateProxyAuth } from "@aexhq/sdk";
34
-
35
- const aex = new AgentExecutor({ apiToken: process.env.AEX_API_TOKEN! });
36
-
37
- const proxyEndpoints = [
38
- ProxyEndpoint.bearer({
39
- name: "doubao-ark",
40
- baseUrl: "https://ark.ap-southeast.bytepluses.com", // intl BytePlus gateway
41
- allowMethods: ["POST"],
42
- allowPathPrefixes: ["/api/v3/chat/completions"],
43
- maxRequestBytes: 2_000_000, // base64 image POSTs — see note below
44
- responseMode: "full",
45
- timeoutMs: 60_000
46
- })
47
- ];
48
-
49
- const proxyEndpointAuth = [
50
- { name: "doubao-ark", value: { type: "bearer", token: process.env.DOUBAO_API_KEY! } }
51
- ];
52
-
53
- validateProxyAuth(proxyEndpoints, proxyEndpointAuth); // fail fast at submit time
33
+ import { Aex, Models, Tools, ProxyEndpoint } from "@aexhq/sdk";
34
+
35
+ const aex = new Aex({ apiToken: process.env.AEX_API_TOKEN! });
36
+
37
+ const doubaoArk = ProxyEndpoint.bearer({
38
+ name: "doubao-ark",
39
+ baseUrl: "https://ark.ap-southeast.bytepluses.com", // intl BytePlus gateway
40
+ token: process.env.DOUBAO_API_KEY!,
41
+ allowMethods: ["POST"],
42
+ allowPathPrefixes: ["/api/v3/chat/completions"],
43
+ maxRequestBytes: 2_000_000, // base64 image POSTs — see note below
44
+ responseMode: "full",
45
+ timeoutMs: 60_000
46
+ });
54
47
 
55
- const runId = await aex.submit({
48
+ await aex.run({
56
49
  model: Models.CLAUDE_HAIKU_4_5,
57
- prompt: "…read skills/frame-vision-gate/SKILL.md, then caption + verify the frame…",
58
- skills: [await Skill.fromPath("./vision-skill", { name: "frame-vision-gate" })],
59
- proxyEndpoints,
60
- secrets: {
61
- apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! },
62
- proxyEndpointAuth
63
- }
50
+ message: "…read skills/frame-vision-gate/SKILL.md, then caption + verify the frame…",
51
+ tools: [await Tools.fromSkillDir("./vision-skill", { name: "frame-vision-gate" })],
52
+ proxyEndpoints: [doubaoArk],
53
+ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
64
54
  });
65
55
  ```
66
56
 
67
- `Skill.fromPath("./vision-skill", …)` is resolved relative to the process CWD, so
68
- run the submit script from the directory that *contains* `vision-skill/` (in the
57
+ `Tools.fromSkillDir("./vision-skill", …)` is resolved relative to the process CWD, so
58
+ run the script from the directory that *contains* `vision-skill/` (in the
69
59
  repo, that is `examples/`). The same pattern works for OpenAI, Gemini's
70
60
  OpenAI-compatible endpoint, or any other OpenAI-chat-shaped vision API — only
71
61
  `baseUrl` and the path prefix change.
@@ -147,8 +137,7 @@ so full-res frames do not add payload and model cost without useful signal.
147
137
  - **Host selection.** Use the provider endpoint that matches your account and
148
138
  declare it as the proxy endpoint `baseUrl`.
149
139
  - **Keyless model hosts.** If the upstream takes no credential, declare the
150
- endpoint with `authShape: { type: "none" }` and omit the `proxyEndpointAuth`
151
- entry (see `credentials.md`).
140
+ endpoint with `ProxyEndpoint.none(...)` (see `credentials.md`).
152
141
  - **Response size.** `responseMode: "full"` is required to read the model's reply
153
142
  back. Leave `maxResponseBytes` at its default (`0` = unlimited, streamed) unless
154
143
  you want a truncation cap.
@@ -3,10 +3,9 @@
3
3
  *
4
4
  * Combines the public `@aexhq/sdk` corpus read-tools (`createCorpusTools`) with a
5
5
  * direct Claude chat loop (`@anthropic-ai/sdk`). The importable `@aexhq/sdk` stays
6
- * LLM-vendor-free; the vendor dependency lives only here in the example (and in
7
- * the bundled `aex chat` CLI command).
6
+ * LLM-vendor-free; the vendor dependency lives only here in the example.
8
7
  *
9
- * Run (Bun): ANTHROPIC_API_KEY=… AEX_TOKEN=… bun examples/chat-corpus.ts <runId> [runId…]
8
+ * Run (Bun): ANTHROPIC_API_KEY=… AEX_API_TOKEN=… bun examples/chat-corpus.ts <sessionId> [sessionId…]
10
9
  *
11
10
  * The model answers ONLY from the named runs' outputs (read via the corpus
12
11
  * tools); a run outside the corpus is refused by the tool layer.
@@ -14,17 +13,17 @@
14
13
  import Anthropic from "@anthropic-ai/sdk";
15
14
  import { AgentExecutor, createCorpusTools, DataToolError } from "@aexhq/sdk";
16
15
 
17
- const runIds = process.argv.slice(2);
18
- if (runIds.length === 0) {
19
- console.error("usage: bun examples/chat-corpus.ts <runId> [runId…]");
16
+ const sessionIds = process.argv.slice(2);
17
+ if (sessionIds.length === 0) {
18
+ console.error("usage: bun examples/chat-corpus.ts <sessionId> [sessionId…]");
20
19
  process.exit(2);
21
20
  }
22
21
 
23
22
  const aex = new AgentExecutor({
24
- apiToken: process.env.AEX_TOKEN!,
25
- ...(process.env.AEX_URL ? { baseUrl: process.env.AEX_URL } : {})
23
+ apiToken: process.env.AEX_API_TOKEN!,
24
+ ...(process.env.AEX_API_URL ? { baseUrl: process.env.AEX_API_URL } : {})
26
25
  });
27
- const tools = createCorpusTools(aex, { runIds });
26
+ const tools = createCorpusTools(aex, { sessionIds });
28
27
  const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
29
28
 
30
29
  const SYSTEM =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexhq/sdk",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
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": {
package/dist/skill.d.ts DELETED
@@ -1,149 +0,0 @@
1
- import { type AssetRef, type FetchLike, type SkillRef } from "./_contracts/index.js";
2
- import { type SkillFiles } from "./bundle.js";
3
- /**
4
- * One `Skill` class for skill bytes. `client.submit` materializes the bytes
5
- * as an uploaded asset before the run lands; the wire ref becomes
6
- * `kind:"asset"`.
7
- *
8
- * Build from an inline files map (`Skill.fromFiles`), a local directory
9
- * (`Skill.fromPath`), or a remote zip archive over a signed URL
10
- * (`Skill.fromUrl`). All three converge on the same canonical bundle, so
11
- * identical content dedups across sources.
12
- *
13
- * Asset deduplication makes the same bytes a no-op upload on subsequent runs.
14
- * There is no `Skill.fromId(...)`. A URL is an ingestion source, not a
15
- * persistent reference.
16
- *
17
- * An inline draft is auto-staged to the content-addressable asset store at
18
- * submit time (the bytes upload before `POST /runs`; the wire ref becomes
19
- * `kind:"asset"`). Call `await skill.upload(client)` to pre-stage the bytes
20
- * explicitly — useful when you want to reuse the resulting `kind:"asset"`
21
- * Skill across multiple runs.
22
- */
23
- export declare class Skill {
24
- #private;
25
- /**
26
- * Internal constructor. Use `Skill.fromFiles` or `Skill.fromPath` to create
27
- * instances.
28
- */
29
- private constructor();
30
- /**
31
- * The wire-level reference. Returns the SDK-private draft shape for
32
- * un-materialized skills (kind:"draft", with name + contentHash).
33
- * `client.submit` walks these and uploads them before the run
34
- * lands.
35
- */
36
- get ref(): AssetRef | DraftSkillRef;
37
- /** True for local-bytes Skills that haven't been uploaded yet. */
38
- get isDraft(): boolean;
39
- /** Internal: the asset id resolved on a prior submit, or undefined. */
40
- get _cachedAssetId(): string | undefined;
41
- /** Internal: remember the asset id resolved for this draft's bytes. */
42
- _rememberAsset(assetId: string): void;
43
- /**
44
- * Build a draft Skill from an inline files map. The SDK validates
45
- * basic safety (no path traversal, size caps, has `SKILL.md`),
46
- * deterministically zips the bundle, and computes the
47
- * `sha256:<hex>` content hash. `client.submit` materializes
48
- * these before the run lands.
49
- */
50
- static fromFiles(args: {
51
- readonly name: string;
52
- readonly files: SkillFiles;
53
- }): Promise<Skill>;
54
- /**
55
- * Read a local directory and build a draft Skill. Symlinks and
56
- * non-regular files are skipped. Bun/Node filesystem runtimes only.
57
- */
58
- static fromPath(rootDir: string, args: {
59
- readonly name: string;
60
- }): Promise<Skill>;
61
- /**
62
- * Fetch a zip-archived skill from a URL and build a draft Skill. The archive
63
- * is downloaded in the SDK process, so the URL is caller-controlled — host
64
- * the skill yourself and pass a temporary signed URL (e.g. an S3 presigned
65
- * URL). Its bytes are optionally integrity-checked against `sha256`, unzipped,
66
- * and reduced to the same files map as `Skill.fromFiles` — so a URL-sourced
67
- * skill and the identical local skill produce the same canonical asset and
68
- * dedup against each other.
69
- *
70
- * The archive must contain `SKILL.md` at its root, or inside a single
71
- * top-level folder (which is stripped). The signed URL only needs to be valid
72
- * for this call; `client.submit` snapshots the bytes into the run.
73
- *
74
- * Universal (Bun / Node 18+ / browser): requires a global `fetch`, or pass one.
75
- */
76
- static fromUrl(url: string, args: {
77
- readonly name: string;
78
- readonly sha256?: string;
79
- readonly timeoutMs?: number;
80
- readonly fetch?: FetchLike;
81
- }): Promise<Skill>;
82
- /**
83
- * Reference a skill already uploaded to the workspace catalog
84
- * (`aex skills upload`) in a run.
85
- *
86
- * A catalog skill's bytes are a content-addressed asset, so referencing it
87
- * is just an `{ kind:"asset" }` ref — once a run snapshots the bytes, it is
88
- * the identical normalized flow as an inline or file-sourced skill. Pass the
89
- * `Skill` record returned by `client.skills.list()` / `.get()`:
90
- *
91
- * const [s] = await client.skills.list();
92
- * await client.submit({ ..., skills: [Skill.fromCatalog(s)] });
93
- *
94
- * The record must be `ready` (it has a content hash). Unlike the draft
95
- * builders this performs no upload — the bytes already live in the catalog.
96
- */
97
- static fromCatalog(record: {
98
- readonly name: string;
99
- readonly hash?: string | null;
100
- }): Skill;
101
- /**
102
- * Internal: yield the draft's bytes + metadata so `client.submit` can upload
103
- * the asset. Idempotent (non-consuming): a Skill is reusable across submits —
104
- * the first submit caches the resolved asset id (see `_rememberAsset`) so
105
- * later submits reuse it instead of re-uploading.
106
- *
107
- * Returns undefined for already-materialized Skills.
108
- */
109
- _takeDraftBundle(): {
110
- name: string;
111
- contentHash: string;
112
- bytes: Uint8Array;
113
- } | undefined;
114
- /**
115
- * Pre-upload a draft Skill's bytes to the workspace asset store and return a
116
- * NEW materialized Skill carrying a `kind:"asset"` ref. Blocking: the upload
117
- * completes before this resolves. Submitting the returned Skill sends a plain
118
- * asset ref and the run pulls the bytes from storage.
119
- *
120
- * Consumes this draft (a draft becomes an asset exactly once); call only on a
121
- * draft built via `Skill.fromFiles` / `Skill.fromPath` / `Skill.fromUrl`.
122
- */
123
- upload(client: SkillUploader): Promise<Skill>;
124
- toJSON(): SkillRef;
125
- }
126
- /**
127
- * SDK-internal draft skill marker. Never reaches the wire; the
128
- * materialize step inside `client.submit` converts these to
129
- * `kind:"asset"` refs.
130
- */
131
- export interface DraftSkillRef {
132
- readonly kind: "draft";
133
- readonly name: string;
134
- readonly contentHash: string;
135
- }
136
- /**
137
- * Minimal client surface `Skill.upload` needs. `AgentExecutor` satisfies it via
138
- * its internal `_uploadAsset`; defined structurally here so `skill.ts` does not
139
- * import `client.ts` (which would be circular — `client.ts` imports `Skill`).
140
- */
141
- export interface SkillUploader {
142
- _uploadAsset(args: {
143
- readonly bytes: Uint8Array;
144
- readonly hash: string;
145
- readonly contentType?: string;
146
- }): Promise<{
147
- readonly assetId: string;
148
- }>;
149
- }