@aexhq/sdk 0.38.1 → 0.40.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 (100) hide show
  1. package/README.md +23 -17
  2. package/dist/_contracts/api-key.d.ts +49 -0
  3. package/dist/_contracts/api-key.js +87 -0
  4. package/dist/_contracts/bundle-manifest.d.ts +86 -0
  5. package/dist/_contracts/bundle-manifest.js +157 -0
  6. package/dist/_contracts/error-codes.d.ts +26 -0
  7. package/dist/_contracts/error-codes.js +79 -0
  8. package/dist/_contracts/error-factory.d.ts +32 -0
  9. package/dist/_contracts/error-factory.js +142 -0
  10. package/dist/_contracts/event-envelope.d.ts +33 -10
  11. package/dist/_contracts/event-envelope.js +46 -10
  12. package/dist/_contracts/event-view.d.ts +123 -0
  13. package/dist/_contracts/event-view.js +120 -0
  14. package/dist/_contracts/http.js +12 -3
  15. package/dist/_contracts/index.d.ts +8 -4
  16. package/dist/_contracts/index.js +11 -7
  17. package/dist/_contracts/models.d.ts +15 -0
  18. package/dist/_contracts/models.js +33 -0
  19. package/dist/_contracts/operations.d.ts +70 -2
  20. package/dist/_contracts/operations.js +143 -7
  21. package/dist/_contracts/run-config.d.ts +35 -10
  22. package/dist/_contracts/run-config.js +17 -5
  23. package/dist/_contracts/run-record.d.ts +3 -2
  24. package/dist/_contracts/runtime-types.d.ts +148 -36
  25. package/dist/_contracts/runtime-types.js +33 -1
  26. package/dist/_contracts/sdk-errors.d.ts +61 -2
  27. package/dist/_contracts/sdk-errors.js +83 -3
  28. package/dist/_contracts/sdk-secrets.js +31 -6
  29. package/dist/_contracts/stable.d.ts +14 -0
  30. package/dist/_contracts/stable.js +14 -0
  31. package/dist/_contracts/status.d.ts +28 -1
  32. package/dist/_contracts/status.js +48 -3
  33. package/dist/_contracts/submission.d.ts +157 -14
  34. package/dist/_contracts/submission.js +319 -55
  35. package/dist/_contracts/suggest.d.ts +15 -0
  36. package/dist/_contracts/suggest.js +54 -0
  37. package/dist/asset-upload.d.ts +27 -1
  38. package/dist/asset-upload.js +219 -4
  39. package/dist/asset-upload.js.map +1 -1
  40. package/dist/bundle.d.ts +23 -14
  41. package/dist/bundle.js +39 -20
  42. package/dist/bundle.js.map +1 -1
  43. package/dist/canonical-zip.d.ts +68 -0
  44. package/dist/canonical-zip.js +307 -0
  45. package/dist/canonical-zip.js.map +1 -0
  46. package/dist/cli.mjs +1927 -326
  47. package/dist/cli.mjs.sha256 +1 -1
  48. package/dist/client.d.ts +273 -70
  49. package/dist/client.js +885 -335
  50. package/dist/client.js.map +1 -1
  51. package/dist/fetch-archive.js +14 -14
  52. package/dist/fetch-archive.js.map +1 -1
  53. package/dist/file.d.ts +33 -6
  54. package/dist/file.js +120 -54
  55. package/dist/file.js.map +1 -1
  56. package/dist/index.d.ts +23 -14
  57. package/dist/index.js +31 -14
  58. package/dist/index.js.map +1 -1
  59. package/dist/node-fs.d.ts +26 -9
  60. package/dist/node-fs.js +13 -38
  61. package/dist/node-fs.js.map +1 -1
  62. package/dist/node-walk.d.ts +69 -0
  63. package/dist/node-walk.js +146 -0
  64. package/dist/node-walk.js.map +1 -0
  65. package/dist/retry.d.ts +9 -13
  66. package/dist/retry.js +18 -17
  67. package/dist/retry.js.map +1 -1
  68. package/dist/skill.d.ts +151 -0
  69. package/dist/skill.js +298 -0
  70. package/dist/skill.js.map +1 -0
  71. package/dist/tool.d.ts +14 -2
  72. package/dist/tool.js +33 -7
  73. package/dist/tool.js.map +1 -1
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/docs/authentication.md +29 -5
  77. package/docs/billing.md +6 -0
  78. package/docs/concepts/agent-tools.md +11 -0
  79. package/docs/concepts/composition.md +3 -3
  80. package/docs/defaults.md +1 -0
  81. package/docs/errors.md +64 -4
  82. package/docs/events.md +84 -49
  83. package/docs/limits-and-quotas.md +24 -0
  84. package/docs/mcp.md +3 -2
  85. package/docs/networking.md +7 -1
  86. package/docs/outputs.md +36 -7
  87. package/docs/provider-runtime-capabilities.md +1 -1
  88. package/docs/quickstart.md +17 -7
  89. package/docs/run-config.md +3 -3
  90. package/docs/secrets.md +14 -0
  91. package/docs/skills.md +74 -44
  92. package/docs/vision-skills.md +3 -3
  93. package/examples/feature-tour.ts +4 -6
  94. package/examples/spike-settle-latency.ts +125 -0
  95. package/package.json +4 -3
  96. package/dist/_contracts/event-guards.d.ts +0 -67
  97. package/dist/_contracts/event-guards.js +0 -36
  98. package/dist/skill-tool.d.ts +0 -102
  99. package/dist/skill-tool.js +0 -190
  100. package/dist/skill-tool.js.map +0 -1
package/docs/skills.md CHANGED
@@ -5,76 +5,106 @@ title: Skills
5
5
  # Skills
6
6
 
7
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? })`
8
+ supporting files) that the agent can pull into context on demand. Skills are a
9
+ **first-class concept, separate from tools**: you pass them on the session's own
10
+ `skills` input (not `tools`), and the run gets a single `skills` meta-tool the
11
+ model uses to list and load them.
12
+
13
+ Build a skill with the `Skill.from*` factories. Each reads a bundle, lifts the
14
+ `name` and `description` from the `SKILL.md` YAML frontmatter (an explicit
15
+ `{ name }` overrides the frontmatter), canonically zips + hashes the bytes, and
16
+ returns a `Skill`:
17
+
18
+ - **Local directory:** `Skill.fromDir(rootDir, { name? })` reads a folder with
19
+ `SKILL.md` at its root. With no `{ name }` and no frontmatter `name:`, the
20
+ slugified directory basename is used (Bun/Node filesystem runtimes).
21
+ - **Signed URL:** `Skill.fromUrl(url, { name?, sha256?, timeoutMs?, fetch? })`
21
22
  fetches a zip archive with `SKILL.md` at the archive root (universal — needs a
22
23
  global `fetch`, or pass one).
24
+ - **In-memory:** `Skill.fromFiles({ name?, files })` from a path→bytes map,
25
+ `Skill.fromContent(skillMd, { name? })` from a single `SKILL.md` string, or
26
+ `Skill.fromBytes({ name?, zip })` from a pre-zipped bundle.
27
+
28
+ Names must match the skill-name pattern (`^[a-z0-9][a-z0-9_-]{0,127}$`), must not
29
+ contain `__` (reserved for MCP tools), and must not be the reserved names
30
+ `skills` / `skill`. The frontmatter `description` (max 2048 chars) is required.
23
31
 
24
32
  ```ts
25
- import { Aex, Models, Tools } from "@aexhq/sdk";
33
+ import { Aex, Models, Skill } from "@aexhq/sdk";
26
34
 
27
35
  const aex = new Aex({ apiKey });
28
36
 
29
37
  await aex.run({
30
38
  model: Models.CLAUDE_HAIKU_4_5,
31
39
  message,
32
- tools: [await Tools.fromSkillDir("./skills/rules", { name: "rules" })],
40
+ skills: [await Skill.fromDir("./skills/rules", { name: "rules" })],
33
41
  apiKeys: { anthropic: apiKey }
34
42
  });
35
43
  ```
36
44
 
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).
45
+ ## By-name, mutable binding
46
+
47
+ A skill is bound to the workspace **by name**. `skill.upload(client)` UPSERTS the
48
+ workspace skill under its name; a re-upload under the same name changes what
49
+ every future run referencing that name sees. The wire ref is name-only —
50
+ `{ kind:"skill", name }` — with no `assetId` and no hash, so the idempotency hash
51
+ of two runs that reference the same skill name is identical even if the skill's
52
+ bytes changed between them.
53
+
54
+ Passing a **draft** `Skill` in `skills:` auto-upserts it on submit (the same
55
+ ergonomic as a draft `Tool` / `File`). To upsert explicitly and reuse the name
56
+ across many runs:
57
+
58
+ ```ts
59
+ const rules = await Skill.fromDir("./skills/rules", { name: "rules" });
60
+ await rules.upload(aex); // stage bytes + PUT /skills/rules
61
+ await aex.run({ model, message, skills: [rules], apiKeys });
62
+ ```
63
+
64
+ `upload()` is idempotent on an instance (it caches the resolved name, so reuse
65
+ across submits skips the round-trip) and identical bytes are a server no-op.
42
66
 
43
- ## How a skill-tool rides on the wire
67
+ ### How a skill rides on the wire
44
68
 
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:
69
+ Before the session lands, `openSession` / `run` walks the `skills` array and, for
70
+ each draft, upserts it by name:
47
71
 
48
72
  1. `POST /assets/presign` checks for a dedup hit and, when needed, returns a
49
73
  signed upload URL.
50
74
  2. The SDK PUTs bytes directly to object storage with the signed checksum headers.
51
75
  3. `POST /assets/finalize` confirms the object exists.
76
+ 4. `PUT /skills/{name}` binds that content hash to the mutable workspace skill
77
+ name (identical `contentHash` ⇒ no-op).
78
+
79
+ The submission then carries only `submission.skills: [{ kind:"skill", name }]`.
80
+ The four prepare passes (tools, skills, agentsMd, files) upload with bounded
81
+ concurrency and run concurrently with each other.
82
+
83
+ ## The `skills` meta-tool
52
84
 
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.
85
+ When a run references at least one skill, the platform injects a single `skills`
86
+ meta-tool:
60
87
 
61
- ## Loading and materialization
88
+ - `skills({ action: "list" })` returns each skill's `name` + `description`
89
+ (cheap — do this first).
90
+ - `skills({ action: "load", name: "<skill>"})` reads that skill's full `SKILL.md`
91
+ instructions into context.
62
92
 
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.
93
+ A skill's supporting files are staged to disk under `/workspace/skills/<name>/`
94
+ from the first turn, so `load` pulls the instructions while `read_file` / `bash`
95
+ read the rest. The run resolves each referenced name to the workspace skill's
96
+ current bytes at submit time and snapshots them into durable run asset storage
97
+ (`runs/<runId>/assets/<hash>`); run-scoped copies are removed by run deletion or
98
+ retention cleanup.
67
99
 
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.
100
+ ## Workspace skill admin
101
+
102
+ `client.skills` is the metadata surface for the named registry:
103
+
104
+ - `client.skills.list()` skill metadata (never bytes).
105
+ - `client.skills.get(name)` one skill's metadata.
106
+ - `client.skills.delete(name)` — remove a workspace skill.
74
107
 
75
108
  Skills that call external HTTP APIs should read credentials from
76
109
  `environment.secrets` and use the normal client for that service. See
77
110
  [Credentials](credentials.md) for the secret model.
78
-
79
- Run-scoped asset copies are part of the run record and are removed by run deletion
80
- or retention cleanup.
@@ -16,14 +16,14 @@ per-noun "does the frame depict X?" verdict.
16
16
  ## Submit the run
17
17
 
18
18
  ```ts
19
- import { Aex, Models, Secret, Tools } from "@aexhq/sdk";
19
+ import { Aex, Models, Secret, Skill } from "@aexhq/sdk";
20
20
 
21
21
  const aex = new Aex({ apiKey: process.env.AEX_API_KEY! });
22
22
 
23
23
  const result = await aex.run({
24
24
  model: Models.CLAUDE_HAIKU_4_5,
25
25
  message: "Read skills/frame-vision-gate/SKILL.md, then caption and verify the frame.",
26
- tools: [await Tools.fromSkillDir("./vision-skill", { name: "frame-vision-gate" })],
26
+ skills: [await Skill.fromDir("./vision-skill", { name: "frame-vision-gate" })],
27
27
  environment: {
28
28
  secrets: {
29
29
  DOUBAO_API_KEY: Secret.value(process.env.DOUBAO_API_KEY!)
@@ -39,7 +39,7 @@ const result = await aex.run({
39
39
  console.log(result.runId, result.text);
40
40
  ```
41
41
 
42
- `Tools.fromSkillDir("./vision-skill", ...)` is resolved relative to the process
42
+ `Skill.fromDir("./vision-skill", ...)` is resolved relative to the process
43
43
  CWD. Run the script from the directory that contains `vision-skill/` (in this
44
44
  repo, `examples/`).
45
45
 
@@ -22,7 +22,6 @@ import {
22
22
  BuiltinTools,
23
23
  File,
24
24
  isRateLimited,
25
- isTextMessage,
26
25
  McpServer,
27
26
  Models,
28
27
  Providers,
@@ -200,12 +199,11 @@ for (;;) {
200
199
  break;
201
200
  }
202
201
  const event = next.value;
203
- if (isTextMessage(event)) {
202
+ if (event.isTextMessage()) {
204
203
  process.stdout.write(event.data.text);
205
- } else if (event.type === "TOOL_CALL_START") {
206
- const name = typeof event.data.name === "string" ? event.data.name : "tool";
207
- process.stdout.write(`\n[tool:start] ${name}\n`);
208
- } else if (event.type === "TOOL_CALL_RESULT") {
204
+ } else if (event.isToolCallStart()) {
205
+ process.stdout.write(`\n[tool:start] ${event.data.name}\n`);
206
+ } else if (event.isToolCallResult()) {
209
207
  process.stdout.write("[tool:result]\n");
210
208
  }
211
209
  }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * SPIKE (throwaway): measure the real park-event -> record-idle gap for interactive
3
+ * session turns. This is the extra wall-clock Option B (settle-consistent turn stream)
4
+ * would force onto EVERY turn: the turn stream currently ends on the RUN_FINISHED park
5
+ * event, but the session RECORD flips running->idle later, in the async settle lambda.
6
+ *
7
+ * AEX_API_KEY=... DEEPSEEK_API_KEY=... AEX_API_URL=... bun packages/sdk/examples/spike-settle-latency.ts
8
+ *
9
+ * Optional: SPIKE_TURNS=8 (samples), SPIKE_POLL_MS=120 (record poll interval).
10
+ *
11
+ * Method per turn:
12
+ * 1) send a tiny prompt, stream events, capture t_runFinished at the RUN_FINISHED event
13
+ * (this is where the default turn stream ends), then stop consuming.
14
+ * 2) tight-poll session.refresh() until status === "idle"; capture t_idle.
15
+ * 3) settleGapMs = t_idle - t_runFinished <-- the Option B tax.
16
+ * The next turn is only sent AFTER idle, so each measurement is isolated (no reconcile).
17
+ */
18
+ import { Aex, Models, Providers, Sizes } from "@aexhq/sdk";
19
+
20
+ const apiKey = required("AEX_API_KEY");
21
+ const deepseekKey = required("DEEPSEEK_API_KEY");
22
+ const apiUrl = process.env.AEX_API_URL;
23
+ const turns = Number(process.env.SPIKE_TURNS ?? "8");
24
+ const pollMs = Number(process.env.SPIKE_POLL_MS ?? "120");
25
+
26
+ const aex = new Aex({
27
+ apiKey,
28
+ ...(apiUrl ? { baseUrl: apiUrl } : {}),
29
+ retry: { maxAttempts: 4, initialDelayMs: 500, maxDelayMs: 10_000, maxElapsedMs: 90_000 }
30
+ });
31
+
32
+ console.log(`opening session (dev)...`);
33
+ const session = await aex.openSession({
34
+ provider: Providers.DEEPSEEK,
35
+ model: Models.DEEPSEEK_V4_FLASH,
36
+ system: "Reply with a single short sentence. Never use tools or write files.",
37
+ includeBuiltinTools: false,
38
+ tools: [],
39
+ runtime: Sizes.SHARED_0_25X_1GB,
40
+ overrides: { idleTtl: "10m", timeout: "5m", maxSpendUsd: 1 },
41
+ apiKeys: { deepseek: deepseekKey }
42
+ });
43
+ console.log(`session: ${session.id}`);
44
+
45
+ type Sample = { turn: number; turnMs: number; settleGapMs: number; polls: number; timedOut: boolean };
46
+ const samples: Sample[] = [];
47
+
48
+ for (let i = 0; i < turns; i++) {
49
+ const stream = session.send(`Say hello #${i + 1} in one short sentence.`);
50
+ const tStart = performance.now();
51
+ let tRunFinished = 0;
52
+ let errored = false;
53
+ for await (const event of stream) {
54
+ if (event.isRunError()) {
55
+ errored = true;
56
+ tRunFinished = performance.now();
57
+ break;
58
+ }
59
+ if (event.isRunFinished()) {
60
+ tRunFinished = performance.now();
61
+ break; // default turn stream would end HERE
62
+ }
63
+ }
64
+ if (tRunFinished === 0) tRunFinished = performance.now();
65
+ const turnMs = tRunFinished - tStart;
66
+
67
+ // Tight-poll the authoritative record until it leaves running.
68
+ let polls = 0;
69
+ let tIdle = 0;
70
+ let timedOut = false;
71
+ const deadline = performance.now() + 90_000;
72
+ for (;;) {
73
+ polls++;
74
+ const rec = await session.refresh().catch(() => undefined);
75
+ const status = rec?.status;
76
+ if (status === "idle" || status === "suspended" || status === "error") {
77
+ tIdle = performance.now();
78
+ break;
79
+ }
80
+ if (performance.now() >= deadline) {
81
+ tIdle = performance.now();
82
+ timedOut = true;
83
+ break;
84
+ }
85
+ await sleep(pollMs);
86
+ }
87
+ const settleGapMs = tIdle - tRunFinished;
88
+ samples.push({ turn: i + 1, turnMs, settleGapMs, polls, timedOut });
89
+ console.log(
90
+ `turn ${String(i + 1).padStart(2)} turn=${fmt(turnMs)} settleGap=${fmt(settleGapMs)} polls=${polls}` +
91
+ (errored ? " (RUN_ERROR)" : "") +
92
+ (timedOut ? " (TIMEOUT>90s)" : "")
93
+ );
94
+ }
95
+
96
+ const gaps = samples.map((s) => s.settleGapMs).sort((a, b) => a - b);
97
+ console.log("\n=== settle-gap distribution (park event -> record idle) ===");
98
+ console.log(`n=${gaps.length}`);
99
+ console.log(`min ${fmt(gaps[0])}`);
100
+ console.log(`median ${fmt(pct(gaps, 50))}`);
101
+ console.log(`p90 ${fmt(pct(gaps, 90))}`);
102
+ console.log(`max ${fmt(gaps[gaps.length - 1])}`);
103
+ console.log(`mean ${fmt(gaps.reduce((a, b) => a + b, 0) / gaps.length)}`);
104
+ console.log("\nInterpretation: settleGap is the per-turn latency Option B adds before the");
105
+ console.log("caller regains control (the turn stream would block until the record is idle).");
106
+
107
+ function pct(sorted: number[], p: number): number {
108
+ if (sorted.length === 0) return 0;
109
+ const idx = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));
110
+ return sorted[idx];
111
+ }
112
+ function fmt(ms: number): string {
113
+ return ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${Math.round(ms)}ms`;
114
+ }
115
+ function sleep(ms: number): Promise<void> {
116
+ return new Promise((r) => setTimeout(r, ms));
117
+ }
118
+ function required(name: string): string {
119
+ const v = process.env[name];
120
+ if (!v) {
121
+ console.error(`Missing env var ${name}`);
122
+ process.exit(1);
123
+ }
124
+ return v;
125
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexhq/sdk",
3
- "version": "0.38.1",
3
+ "version": "0.40.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": {
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "scripts": {
29
29
  "build": "bun ../../scripts/with-generated-dist-lock.mjs bun run build:unlocked",
30
- "build:unlocked": "bun ./scripts/clean-dist.mjs && bun run --cwd ../.. --filter @aexhq/contracts build && bun run --cwd ../.. --filter @aexhq/cli build && tsc -p tsconfig.build.json && bun ./scripts/bundle-cli.mjs && bun ./scripts/inline-contracts.mjs",
30
+ "build:unlocked": "bun ./scripts/clean-dist.mjs && bun run --cwd ../.. --filter @aexhq/contracts build && tsc -p tsconfig.build.json && bun run --cwd ../.. --filter @aexhq/cli build && bun ./scripts/bundle-cli.mjs && bun ./scripts/inline-contracts.mjs",
31
31
  "lint": "bun ../../scripts/with-generated-dist-lock.mjs bun run lint:unlocked",
32
32
  "lint:unlocked": "tsc --noEmit",
33
33
  "test": "bun run test:unit",
@@ -47,6 +47,7 @@
47
47
  "bun": ">=1.3.14"
48
48
  },
49
49
  "dependencies": {
50
- "fflate": "0.8.2"
50
+ "fflate": "0.8.2",
51
+ "ignore": "7.0.5"
51
52
  }
52
53
  }
@@ -1,67 +0,0 @@
1
- /**
2
- * Type-narrowing guards over the loose {@link RunEvent} `listEvents` shape.
3
- *
4
- * The envelope guards in `event-envelope.ts` operate on the coordinator
5
- * {@link import("./event-envelope.js").AexEvent} and return a plain `boolean`.
6
- * These mirror the same AG-UI discriminants but operate on the {@link RunEvent}
7
- * snapshot shape returned by `listEvents` / `RunResult.events` and NARROW
8
- * `event.data` to the fields that event type carries, so a consumer reads
9
- * `event.data.text` (etc.) without a manual cast.
10
- *
11
- * They are DISCRIMINANT-only at runtime (they test `event.type`), matching the
12
- * envelope guards: the narrowed `data` shape is a typing convenience, not a deep
13
- * runtime validation. Parse/verify the payload fields before trusting them when
14
- * the producer is untrusted.
15
- *
16
- * The parameter is the minimal `{ type }` discriminant both the {@link RunEvent}
17
- * snapshot and the coordinator `AexEvent` envelope satisfy, so the guards accept
18
- * either shape while narrowing to the {@link RunEvent}-typed payloads.
19
- */
20
- import type { RunEvent } from "./runtime-types.js";
21
- /** A `TEXT_MESSAGE_CONTENT` run event with its assistant-text payload narrowed. */
22
- export interface TextMessageRunEvent extends RunEvent {
23
- readonly type: "TEXT_MESSAGE_CONTENT";
24
- readonly data: {
25
- readonly text: string;
26
- readonly messageId?: string;
27
- };
28
- }
29
- /** A `TOOL_CALL_START` run event with the tool-call id/name/args narrowed. */
30
- export interface ToolCallStartRunEvent extends RunEvent {
31
- readonly type: "TOOL_CALL_START";
32
- readonly data: {
33
- readonly id: string;
34
- readonly name: string;
35
- readonly arguments?: Record<string, unknown>;
36
- };
37
- }
38
- /** A `TOOL_CALL_RESULT` run event with the correlating id + result narrowed. */
39
- export interface ToolCallResultRunEvent extends RunEvent {
40
- readonly type: "TOOL_CALL_RESULT";
41
- readonly data: {
42
- readonly id: string;
43
- readonly content?: unknown;
44
- readonly isError?: boolean;
45
- };
46
- }
47
- /** A terminal `RUN_FINISHED` run event. */
48
- export interface RunFinishedRunEvent extends RunEvent {
49
- readonly type: "RUN_FINISHED";
50
- readonly data: Record<string, unknown>;
51
- }
52
- /** True for an assistant text event; narrows `event.data.text` to `string`. */
53
- export declare function isTextMessage(event: {
54
- readonly type: string;
55
- }): event is TextMessageRunEvent;
56
- /** True for a tool-call start event; narrows `event.data` to `{ id, name }`. */
57
- export declare function isToolCallStart(event: {
58
- readonly type: string;
59
- }): event is ToolCallStartRunEvent;
60
- /** True for a tool-call result event; narrows `event.data` to `{ id, content }`. */
61
- export declare function isToolCallResult(event: {
62
- readonly type: string;
63
- }): event is ToolCallResultRunEvent;
64
- /** True for the terminal success event; narrows `event.data` to a record. */
65
- export declare function isRunFinished(event: {
66
- readonly type: string;
67
- }): event is RunFinishedRunEvent;
@@ -1,36 +0,0 @@
1
- /**
2
- * Type-narrowing guards over the loose {@link RunEvent} `listEvents` shape.
3
- *
4
- * The envelope guards in `event-envelope.ts` operate on the coordinator
5
- * {@link import("./event-envelope.js").AexEvent} and return a plain `boolean`.
6
- * These mirror the same AG-UI discriminants but operate on the {@link RunEvent}
7
- * snapshot shape returned by `listEvents` / `RunResult.events` and NARROW
8
- * `event.data` to the fields that event type carries, so a consumer reads
9
- * `event.data.text` (etc.) without a manual cast.
10
- *
11
- * They are DISCRIMINANT-only at runtime (they test `event.type`), matching the
12
- * envelope guards: the narrowed `data` shape is a typing convenience, not a deep
13
- * runtime validation. Parse/verify the payload fields before trusting them when
14
- * the producer is untrusted.
15
- *
16
- * The parameter is the minimal `{ type }` discriminant both the {@link RunEvent}
17
- * snapshot and the coordinator `AexEvent` envelope satisfy, so the guards accept
18
- * either shape while narrowing to the {@link RunEvent}-typed payloads.
19
- */
20
- /** True for an assistant text event; narrows `event.data.text` to `string`. */
21
- export function isTextMessage(event) {
22
- return event.type === "TEXT_MESSAGE_CONTENT";
23
- }
24
- /** True for a tool-call start event; narrows `event.data` to `{ id, name }`. */
25
- export function isToolCallStart(event) {
26
- return event.type === "TOOL_CALL_START";
27
- }
28
- /** True for a tool-call result event; narrows `event.data` to `{ id, content }`. */
29
- export function isToolCallResult(event) {
30
- return event.type === "TOOL_CALL_RESULT";
31
- }
32
- /** True for the terminal success event; narrows `event.data` to a record. */
33
- export function isRunFinished(event) {
34
- return event.type === "RUN_FINISHED";
35
- }
36
- //# sourceMappingURL=event-guards.js.map
@@ -1,102 +0,0 @@
1
- import { type FetchLike, type SkillToolRef } from "./_contracts/index.js";
2
- import { type SkillFiles } from "./bundle.js";
3
- /**
4
- * A skill re-expressed as a TOOL. `Tools.fromSkillDir` / `Tools.fromSkillUrl`
5
- * read a skill folder/zip, lift the tool `name` + `description` from the
6
- * `SKILL.md` YAML frontmatter, and canonically bundle+hash the bytes. The
7
- * result rides in the session's `tools` array (next to builtin names and custom
8
- * {@link Tool} bundles); `client.run` / `openSession` uploads the bundle as an
9
- * asset before the run lands, and the wire ref becomes a
10
- * `{ kind:"skill", assetId, name, description }` {@link SkillToolRef}.
11
- *
12
- * At run time the model calls the no-arg load-tool to pull the skill's
13
- * `SKILL.md` body into context; the bundle's files are eagerly staged to
14
- * `/workspace/skills/<name>/`.
15
- *
16
- * Asset deduplication makes the same bytes a no-op upload on subsequent runs.
17
- * A URL is an ingestion source, not a persistent reference.
18
- */
19
- export declare class SkillTool {
20
- #private;
21
- /** Internal constructor. Use the `Tools.fromSkill*` factories. */
22
- private constructor();
23
- /**
24
- * The wire-level reference. Returns the SDK-private draft shape for
25
- * un-uploaded skill-tools (kind:"draft", with name + description +
26
- * contentHash). `client.run` / `openSession` walks these and uploads them
27
- * before the run lands.
28
- */
29
- get ref(): SkillToolRef | DraftSkillToolRef;
30
- /** True for local-bytes skill-tools that haven't been uploaded yet. */
31
- get isDraft(): boolean;
32
- /** Internal: the asset id resolved on a prior use, or undefined. */
33
- get _cachedAssetId(): string | undefined;
34
- /** Internal: remember the asset id resolved for this draft's bytes. */
35
- _rememberAsset(assetId: string): void;
36
- /** Internal: build a draft from an already-loaded skill files map. */
37
- static _fromFiles(source: string, files: SkillFiles, nameOverride: string | undefined): Promise<SkillTool>;
38
- /**
39
- * Internal: yield the draft's bytes + metadata so `client.run` / `openSession`
40
- * can upload the asset. Idempotent (non-consuming): a SkillTool is reusable
41
- * across sessions — the first use caches the resolved asset id (see
42
- * `_rememberAsset`) so later uses reuse it instead of re-uploading.
43
- *
44
- * Returns undefined for already-uploaded skill-tools.
45
- */
46
- _takeDraftBundle(): {
47
- name: string;
48
- description: string;
49
- contentHash: string;
50
- bytes: Uint8Array;
51
- } | undefined;
52
- toJSON(): SkillToolRef;
53
- }
54
- /**
55
- * SDK-internal draft skill-tool marker. Never reaches the wire; the
56
- * materialize step inside `client.run` / `openSession` converts these to
57
- * `kind:"skill"` refs once the bundle is uploaded.
58
- */
59
- export interface DraftSkillToolRef {
60
- readonly kind: "draft";
61
- readonly name: string;
62
- readonly description: string;
63
- readonly contentHash: string;
64
- }
65
- /**
66
- * Factory namespace for skill-tools. Each factory reads a skill bundle, lifts
67
- * `name` + `description` from the `SKILL.md` frontmatter (an explicit `name`
68
- * argument overrides the frontmatter), and produces a {@link SkillTool} to pass
69
- * in the session's `tools` array.
70
- */
71
- export declare const Tools: {
72
- /**
73
- * Read a local skill directory and build a skill-tool. The directory must
74
- * contain `SKILL.md` at its root, whose YAML frontmatter provides the tool
75
- * `name` (unless overridden via `args.name`) and `description`. Symlinks and
76
- * non-regular files are skipped. Bun/Node filesystem runtimes only.
77
- */
78
- readonly fromSkillDir: (rootDir: string, args?: {
79
- readonly name?: string;
80
- }) => Promise<SkillTool>;
81
- /**
82
- * Fetch a zip-archived skill from a URL and build a skill-tool. The archive is
83
- * downloaded in the SDK process, so the URL is caller-controlled — host the
84
- * skill yourself and pass a temporary signed URL (e.g. an S3 presigned URL).
85
- * Its bytes are optionally integrity-checked against `sha256`, unzipped, and
86
- * reduced to the same files map as `Tools.fromSkillDir`, so a URL-sourced
87
- * skill and the identical local skill produce the same canonical asset and
88
- * dedup against each other.
89
- *
90
- * The archive must contain `SKILL.md` at its root, or inside a single
91
- * top-level folder (which is stripped). The signed URL only needs to be valid
92
- * for this call; `client.run` / `openSession` snapshots the bytes into the run.
93
- *
94
- * Universal (Bun / Node 18+ / browser): requires a global `fetch`, or pass one.
95
- */
96
- readonly fromSkillUrl: (url: string, args?: {
97
- readonly name?: string;
98
- readonly sha256?: string;
99
- readonly timeoutMs?: number;
100
- readonly fetch?: FetchLike;
101
- }) => Promise<SkillTool>;
102
- };