@aexhq/sdk 0.34.0 → 0.35.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.
- package/dist/_contracts/submission.d.ts +58 -23
- package/dist/_contracts/submission.js +54 -10
- package/dist/cli.mjs +79 -0
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +19 -0
- package/dist/client.js +92 -6
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/retry.d.ts +162 -0
- package/dist/retry.js +320 -0
- package/dist/retry.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/retries.md +129 -0
- package/examples/feature-tour.ts +301 -0
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK feature tour: one managed session that uses typed model/runtime constants,
|
|
3
|
+
* inline AGENTS.md guidance, uploaded files, a custom tool bundle, selected
|
|
4
|
+
* built-in tools, runtime env vars/secrets, streamed events, output reads, and
|
|
5
|
+
* corpus-scoped data tools.
|
|
6
|
+
*
|
|
7
|
+
* Run from the repository root after building the workspace package:
|
|
8
|
+
*
|
|
9
|
+
* AEX_API_TOKEN=... DEEPSEEK_API_KEY=... bun packages/sdk/examples/feature-tour.ts
|
|
10
|
+
*
|
|
11
|
+
* Optional:
|
|
12
|
+
*
|
|
13
|
+
* AEX_API_URL=https://api.aex.dev
|
|
14
|
+
* AEX_FEATURE_TOUR_DOWNLOAD=./feature-tour-session.zip
|
|
15
|
+
* AEX_DEMO_RUNTIME_SECRET=... # mounted as a runtime secret; never printed
|
|
16
|
+
* AEX_DEMO_MCP_URL=https://... # declares an optional remote MCP server
|
|
17
|
+
* AEX_DEMO_MCP_TOKEN=... # optional bearer auth for that MCP server
|
|
18
|
+
*/
|
|
19
|
+
import {
|
|
20
|
+
AgentsMd,
|
|
21
|
+
Aex,
|
|
22
|
+
BuiltinTools,
|
|
23
|
+
createCorpusTools,
|
|
24
|
+
File,
|
|
25
|
+
isRateLimited,
|
|
26
|
+
isTextMessage,
|
|
27
|
+
McpServer,
|
|
28
|
+
Models,
|
|
29
|
+
ProxyEndpoint,
|
|
30
|
+
Providers,
|
|
31
|
+
Secret,
|
|
32
|
+
Sizes,
|
|
33
|
+
Tool
|
|
34
|
+
} from "@aexhq/sdk";
|
|
35
|
+
|
|
36
|
+
process.on("uncaughtException", handleFatal);
|
|
37
|
+
process.on("unhandledRejection", handleFatal);
|
|
38
|
+
|
|
39
|
+
const apiToken = required("AEX_API_TOKEN");
|
|
40
|
+
const deepseekKey = required("DEEPSEEK_API_KEY");
|
|
41
|
+
const apiUrl = process.env.AEX_API_URL;
|
|
42
|
+
const demoMcpUrl = process.env.AEX_DEMO_MCP_URL;
|
|
43
|
+
const demoMcpToken = process.env.AEX_DEMO_MCP_TOKEN;
|
|
44
|
+
const demoRuntimeSecret = process.env.AEX_DEMO_RUNTIME_SECRET;
|
|
45
|
+
const downloadPath = process.env.AEX_FEATURE_TOUR_DOWNLOAD;
|
|
46
|
+
const textEncoder = new TextEncoder();
|
|
47
|
+
|
|
48
|
+
const aex = new Aex({
|
|
49
|
+
apiToken,
|
|
50
|
+
...(apiUrl ? { baseUrl: apiUrl } : {}),
|
|
51
|
+
retry: {
|
|
52
|
+
maxAttempts: 4,
|
|
53
|
+
initialDelayMs: 500,
|
|
54
|
+
maxDelayMs: 10_000,
|
|
55
|
+
maxElapsedMs: 90_000
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const metricLookup = await Tool.fromFiles({
|
|
60
|
+
name: "metric_lookup",
|
|
61
|
+
description: "Looks up normalized demo metrics for one product line.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
properties: {
|
|
66
|
+
product: {
|
|
67
|
+
type: "string",
|
|
68
|
+
enum: ["atlas", "beacon", "cinder"],
|
|
69
|
+
description: "Product line to inspect."
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
required: ["product"]
|
|
73
|
+
},
|
|
74
|
+
entry: "index.js",
|
|
75
|
+
files: {
|
|
76
|
+
"index.js": `
|
|
77
|
+
const DATA = {
|
|
78
|
+
atlas: { customerCount: 118, activationHealthPct: 72.4, supportTickets: 11 },
|
|
79
|
+
beacon: { customerCount: 74, activationHealthPct: 65.1, supportTickets: 19 },
|
|
80
|
+
cinder: { customerCount: 43, activationHealthPct: 58.8, supportTickets: 7 }
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default async function ({ input }) {
|
|
84
|
+
const key = String(input.product ?? "").toLowerCase();
|
|
85
|
+
const row = DATA[key];
|
|
86
|
+
if (!row) {
|
|
87
|
+
return { content: [{ type: "text", text: \`unknown product: \${key}\` }], is_error: true };
|
|
88
|
+
}
|
|
89
|
+
return { content: [{ type: "text", text: JSON.stringify({ product: key, ...row }) }] };
|
|
90
|
+
}
|
|
91
|
+
`
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const demoCsv = [
|
|
96
|
+
"product,region,q1_revenue_usd,q2_revenue_usd,activation_rate",
|
|
97
|
+
"atlas,na,120000,139500,0.84",
|
|
98
|
+
"beacon,emea,98000,104300,0.79",
|
|
99
|
+
"cinder,apac,67000,81000,0.91"
|
|
100
|
+
].join("\n");
|
|
101
|
+
|
|
102
|
+
const attachedFile = await File.fromBytes({
|
|
103
|
+
name: "quarterly-metrics.csv",
|
|
104
|
+
bytes: textEncoder.encode(`${demoCsv}\n`),
|
|
105
|
+
mountPath: "/workspace/input"
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const runRules = await AgentsMd.fromContent(
|
|
109
|
+
[
|
|
110
|
+
"# Feature tour rules",
|
|
111
|
+
"- Use `/workspace/input/quarterly-metrics.csv` as the source table.",
|
|
112
|
+
"- Call `metric_lookup` for atlas, beacon, and cinder before writing conclusions.",
|
|
113
|
+
"- Write final artifacts under `/workspace/outputs`.",
|
|
114
|
+
"- Never print runtime secret values or provider keys."
|
|
115
|
+
].join("\n"),
|
|
116
|
+
{ name: "feature-tour-rules" }
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const mcpServers = demoMcpUrl
|
|
120
|
+
? [
|
|
121
|
+
McpServer.remote({
|
|
122
|
+
name: "demo-mcp",
|
|
123
|
+
url: demoMcpUrl,
|
|
124
|
+
...(demoMcpToken
|
|
125
|
+
? { headers: { Authorization: `Bearer ${demoMcpToken}` } }
|
|
126
|
+
: {})
|
|
127
|
+
})
|
|
128
|
+
]
|
|
129
|
+
: [];
|
|
130
|
+
|
|
131
|
+
const environmentSecrets = demoRuntimeSecret
|
|
132
|
+
? { DEMO_RUNTIME_SECRET: Secret.value(demoRuntimeSecret) }
|
|
133
|
+
: undefined;
|
|
134
|
+
|
|
135
|
+
const proxyEndpoints = [
|
|
136
|
+
ProxyEndpoint.none({
|
|
137
|
+
name: "httpbin",
|
|
138
|
+
baseUrl: "https://httpbin.org",
|
|
139
|
+
allowMethods: ["GET"],
|
|
140
|
+
allowPathPrefixes: ["/json"],
|
|
141
|
+
responseMode: "full",
|
|
142
|
+
timeoutMs: 10_000
|
|
143
|
+
})
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
console.log("creating feature-tour session...");
|
|
147
|
+
console.log(`optional mcp: ${mcpServers.length > 0 ? "enabled" : "disabled"}`);
|
|
148
|
+
console.log(`optional runtime secret: ${environmentSecrets ? "enabled" : "disabled"}`);
|
|
149
|
+
|
|
150
|
+
const session = await aex.openSession({
|
|
151
|
+
provider: Providers.DEEPSEEK,
|
|
152
|
+
model: Models.DEEPSEEK_V4_FLASH,
|
|
153
|
+
system: [
|
|
154
|
+
"You are a concise analytics agent.",
|
|
155
|
+
"Prefer exact calculations and write durable files for the caller."
|
|
156
|
+
].join(" "),
|
|
157
|
+
agentsMd: [runRules],
|
|
158
|
+
files: [attachedFile],
|
|
159
|
+
includeBuiltinTools: false,
|
|
160
|
+
tools: [
|
|
161
|
+
BuiltinTools.read_file,
|
|
162
|
+
BuiltinTools.write_file,
|
|
163
|
+
BuiltinTools.bash,
|
|
164
|
+
BuiltinTools.grep,
|
|
165
|
+
BuiltinTools.code_execution,
|
|
166
|
+
metricLookup
|
|
167
|
+
],
|
|
168
|
+
proxyEndpoints,
|
|
169
|
+
mcpServers,
|
|
170
|
+
environment: {
|
|
171
|
+
networking: { mode: "open" },
|
|
172
|
+
variables: {
|
|
173
|
+
FEATURE_TOUR: "true",
|
|
174
|
+
REPORT_DIR: "/workspace/outputs"
|
|
175
|
+
},
|
|
176
|
+
...(environmentSecrets ? { secrets: environmentSecrets } : {})
|
|
177
|
+
},
|
|
178
|
+
outputs: {
|
|
179
|
+
allowedDirs: ["/workspace/outputs"],
|
|
180
|
+
deniedDirs: ["*.tmp"],
|
|
181
|
+
maxFiles: 10,
|
|
182
|
+
maxFileBytes: 1_000_000
|
|
183
|
+
},
|
|
184
|
+
outputMode: "stream",
|
|
185
|
+
runtime: Sizes.SHARED_0_25X_1GB,
|
|
186
|
+
metadata: {
|
|
187
|
+
example: "sdk-feature-tour",
|
|
188
|
+
sdkSurface: "public"
|
|
189
|
+
},
|
|
190
|
+
overrides: {
|
|
191
|
+
idleTtl: "5m",
|
|
192
|
+
timeout: "10m",
|
|
193
|
+
maxSpendUsd: 2
|
|
194
|
+
},
|
|
195
|
+
apiKeys: { deepseek: deepseekKey }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
console.log(`session: ${session.id}`);
|
|
199
|
+
|
|
200
|
+
const prompt = [
|
|
201
|
+
"Analyze the attached quarterly metrics.",
|
|
202
|
+
"Call metric_lookup for atlas, beacon, and cinder.",
|
|
203
|
+
"Create /workspace/outputs/feature-tour-report.md with a short table, a ranking by q2_revenue_usd, and two risks.",
|
|
204
|
+
"Create /workspace/outputs/summary.json with keys topProduct, totalQ2RevenueUsd, highestActivationProduct, and riskCount.",
|
|
205
|
+
"Mention whether the httpbin proxy endpoint is declared, but do not call it unless you need to."
|
|
206
|
+
].join(" ");
|
|
207
|
+
|
|
208
|
+
const firstTurn = session.send(prompt);
|
|
209
|
+
const firstTurnIterator = firstTurn[Symbol.asyncIterator]();
|
|
210
|
+
let result: Awaited<ReturnType<typeof firstTurn.done>> | undefined;
|
|
211
|
+
for (;;) {
|
|
212
|
+
const next = await firstTurnIterator.next();
|
|
213
|
+
if (next.done) {
|
|
214
|
+
result = next.value as Awaited<ReturnType<typeof firstTurn.done>>;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
const event = next.value;
|
|
218
|
+
if (isTextMessage(event)) {
|
|
219
|
+
process.stdout.write(event.data.text);
|
|
220
|
+
} else if (event.type === "TOOL_CALL_START") {
|
|
221
|
+
const name = typeof event.data.name === "string" ? event.data.name : "tool";
|
|
222
|
+
process.stdout.write(`\n[tool:start] ${name}\n`);
|
|
223
|
+
} else if (event.type === "TOOL_CALL_RESULT") {
|
|
224
|
+
process.stdout.write("[tool:result]\n");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!result) {
|
|
229
|
+
throw new Error("first turn stream ended without a result");
|
|
230
|
+
}
|
|
231
|
+
console.log(`\nfirst turn parked with status: ${result.status}`);
|
|
232
|
+
|
|
233
|
+
const followUp = await session
|
|
234
|
+
.send("Read summary.json back and answer with one sentence confirming the top product and total Q2 revenue.")
|
|
235
|
+
.done();
|
|
236
|
+
console.log(`follow-up status: ${followUp.status}`);
|
|
237
|
+
if (followUp.text) {
|
|
238
|
+
console.log(`follow-up text: ${followUp.text.trim()}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const parked = await session.wait({ timeoutMs: 60_000, intervalMs: 2_000 });
|
|
242
|
+
console.log(`settled session status: ${parked.status}`);
|
|
243
|
+
|
|
244
|
+
const messages = await session.messages().list();
|
|
245
|
+
console.log(`assistant messages: ${messages.length}`);
|
|
246
|
+
|
|
247
|
+
const events = await session.events().list();
|
|
248
|
+
console.log(`captured events: ${events.length}`);
|
|
249
|
+
|
|
250
|
+
const outputs = await session.outputs().list();
|
|
251
|
+
console.log("outputs:");
|
|
252
|
+
for (const output of outputs) {
|
|
253
|
+
console.log(`- ${output.filename ?? output.id} (${output.contentType ?? "unknown"})`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const summary = await session.outputs().read(
|
|
257
|
+
{ path: "summary.json", match: "suffix" },
|
|
258
|
+
{ maxBytes: 20_000 }
|
|
259
|
+
);
|
|
260
|
+
console.log("summary.json:");
|
|
261
|
+
console.log(summary.text);
|
|
262
|
+
|
|
263
|
+
const report = await session.outputs().findOne({
|
|
264
|
+
filename: "feature-tour-report.md"
|
|
265
|
+
});
|
|
266
|
+
if (report) {
|
|
267
|
+
const reportPreview = await session.outputs().read(report, {
|
|
268
|
+
maxBytes: 4_000,
|
|
269
|
+
grep: "risk"
|
|
270
|
+
});
|
|
271
|
+
console.log("report risk lines:");
|
|
272
|
+
console.log(reportPreview.text || "(no risk lines found)");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const corpusTools = createCorpusTools(aex, { sessionIds: [session.id] }, { defaultReadBytes: 4_000 });
|
|
276
|
+
const corpusOutputs = await corpusTools.execute("list_outputs", { session_id: session.id });
|
|
277
|
+
console.log("corpus-scoped list_outputs result:");
|
|
278
|
+
console.log(JSON.stringify(corpusOutputs, null, 2));
|
|
279
|
+
|
|
280
|
+
if (downloadPath) {
|
|
281
|
+
const bytes = await session.download({ to: downloadPath });
|
|
282
|
+
console.log(`downloaded session archive: ${downloadPath} (${bytes.byteLength} bytes)`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function required(name: string): string {
|
|
286
|
+
const value = process.env[name];
|
|
287
|
+
if (!value) {
|
|
288
|
+
console.error(`Missing env var ${name}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function handleFatal(err: unknown): void {
|
|
295
|
+
if (isRateLimited(err)) {
|
|
296
|
+
console.error(`rate limited after ${err.attempts} attempts; retry after ${err.retryAfterMs ?? "unknown"}ms`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
console.error(err instanceof Error ? err.stack ?? err.message : String(err));
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aexhq/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.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": {
|