@aexhq/sdk 0.35.0 → 0.37.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/README.md +17 -16
- package/dist/_contracts/event-envelope.d.ts +22 -1
- package/dist/_contracts/event-envelope.js +26 -2
- package/dist/_contracts/event-stream-client.js +7 -1
- package/dist/_contracts/index.d.ts +3 -4
- package/dist/_contracts/index.js +1 -4
- package/dist/_contracts/operations.d.ts +31 -1
- package/dist/_contracts/operations.js +64 -1
- package/dist/_contracts/run-config.d.ts +2 -4
- package/dist/_contracts/run-config.js +2 -7
- package/dist/_contracts/run-trace.d.ts +0 -86
- package/dist/_contracts/run-trace.js +1 -184
- package/dist/_contracts/run-unit.d.ts +14 -25
- package/dist/_contracts/run-unit.js +56 -2
- package/dist/_contracts/runtime-manifest.d.ts +1 -1
- package/dist/_contracts/runtime-security-profile.d.ts +0 -2
- package/dist/_contracts/runtime-security-profile.js +0 -9
- package/dist/_contracts/runtime-sizes.d.ts +2 -2
- package/dist/_contracts/runtime-sizes.js +5 -5
- package/dist/_contracts/runtime-types.d.ts +123 -4
- package/dist/_contracts/stable.d.ts +1 -1
- package/dist/_contracts/stable.js +1 -1
- package/dist/_contracts/submission.d.ts +8 -76
- package/dist/_contracts/submission.js +5 -472
- package/dist/cli.mjs +574 -511
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +69 -25
- package/dist/client.js +338 -68
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +8 -16
- package/dist/index.js +5 -17
- package/dist/index.js.map +1 -1
- package/dist/secret.d.ts +2 -2
- package/dist/secret.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +92 -0
- package/docs/billing.md +112 -0
- package/docs/concepts/agent-tools.md +4 -4
- package/docs/concepts/composition.md +8 -14
- package/docs/concepts/providers-and-runtimes.md +4 -1
- package/docs/concepts/runs.md +2 -1
- package/docs/concepts/subagents.md +85 -0
- package/docs/credentials.md +78 -96
- package/docs/defaults.md +9 -15
- package/docs/errors.md +132 -0
- package/docs/events.md +44 -32
- package/docs/limits-and-quotas.md +30 -17
- package/docs/limits.md +4 -8
- package/docs/mcp.md +5 -6
- package/docs/networking.md +75 -59
- package/docs/outputs.md +4 -7
- package/docs/public-surface.json +4 -4
- package/docs/quickstart.md +12 -13
- package/docs/run-config.md +7 -4
- package/docs/secrets.md +6 -1
- package/docs/skills.md +3 -3
- package/docs/vision-skills.md +52 -101
- package/docs/webhooks.md +132 -0
- package/examples/feature-tour.ts +4 -21
- package/package.json +1 -1
- package/dist/_contracts/proxy-protocol.d.ts +0 -305
- package/dist/_contracts/proxy-protocol.js +0 -297
- package/dist/_contracts/proxy-validation.d.ts +0 -19
- package/dist/_contracts/proxy-validation.js +0 -51
- package/dist/data-tools.d.ts +0 -82
- package/dist/data-tools.js +0 -251
- package/dist/data-tools.js.map +0 -1
- package/dist/proxy-endpoint.d.ts +0 -131
- package/dist/proxy-endpoint.js +0 -144
- package/dist/proxy-endpoint.js.map +0 -1
- package/examples/chat-corpus.ts +0 -84
package/dist/client.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { AexError, DEFAULT_RUN_PROVIDER, HttpClient, RunConfigValidationError, RunStateError, SecretString, customName, isRunSettled, operations, providersForModel, streamCoordinatorEvents,
|
|
1
|
+
import { AexApiError, AexError, CredentialValidationError, DEFAULT_RUN_PROVIDER, HttpClient, RunConfigValidationError, RunStateError, SecretString, customName, isRunSettled, operations, providersForModel, streamCoordinatorEvents, parseRunLimits, parseRuntimeSize, parseRunTimeout, parseRunWebhook, BUILTIN_TOOL_NAMES, TERMINAL_RUN_STATUSES } from "./_contracts/index.js";
|
|
2
2
|
import { AgentsMd } from "./agents-md.js";
|
|
3
3
|
import { uploadAsset } from "./asset-upload.js";
|
|
4
4
|
import { File } from "./file.js";
|
|
5
5
|
import { McpServer } from "./mcp-server.js";
|
|
6
|
-
import { splitProxyEndpoints } from "./proxy-endpoint.js";
|
|
7
6
|
import { AexRateLimitError, isThrottleFault, parseProviderFault, withRetry } from "./retry.js";
|
|
8
7
|
import { splitSecretEnv } from "./secret.js";
|
|
9
8
|
import { SkillTool } from "./skill-tool.js";
|
|
@@ -95,14 +94,16 @@ export class SessionHandle {
|
|
|
95
94
|
const readSession = await operations.getSession(this.#http, this.id).catch(() => this.#session);
|
|
96
95
|
this.#session = withTerminalSessionStatus(readSession, terminalStatus);
|
|
97
96
|
const outputs = await operations.listSessionOutputs(this.#http, this.id).catch(() => []);
|
|
97
|
+
const messages = projectAssistantMessages(events);
|
|
98
98
|
return {
|
|
99
99
|
sessionId: this.id,
|
|
100
100
|
session: this.#session,
|
|
101
101
|
turn,
|
|
102
102
|
status: this.#session.status,
|
|
103
|
-
text:
|
|
103
|
+
text: assistantTextFromEvents(events),
|
|
104
104
|
events,
|
|
105
|
-
outputs
|
|
105
|
+
outputs,
|
|
106
|
+
messages
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
async suspend(options = {}) {
|
|
@@ -127,19 +128,39 @@ export class SessionHandle {
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
130
|
-
* Accessor for the session's
|
|
131
|
-
*
|
|
132
|
-
*
|
|
131
|
+
* Accessor for the session's assistant messages. `all()` returns them
|
|
132
|
+
* oldest-first; `last()`/`first()` return a single entry or `undefined` when
|
|
133
|
+
* empty. The accessor is callable as a compatibility shim for older
|
|
134
|
+
* `session.messages().list()` callers.
|
|
133
135
|
*/
|
|
134
|
-
messages() {
|
|
136
|
+
get messages() {
|
|
135
137
|
const http = this.#http;
|
|
136
138
|
const id = this.id;
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const fromEvents = async () => projectAssistantMessages(await operations.listSessionEvents(http, id));
|
|
140
|
+
const all = async () => {
|
|
141
|
+
try {
|
|
142
|
+
const page = await operations.listSessionMessages(http, id);
|
|
143
|
+
if (!Array.isArray(page.messages)) {
|
|
144
|
+
return fromEvents();
|
|
145
|
+
}
|
|
146
|
+
return page.messages.map(messageFromWire);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
if (!isMissingMessagesEndpoint(err)) {
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
return fromEvents();
|
|
153
|
+
}
|
|
142
154
|
};
|
|
155
|
+
let accessor;
|
|
156
|
+
accessor = (() => accessor);
|
|
157
|
+
Object.assign(accessor, {
|
|
158
|
+
all,
|
|
159
|
+
list: all,
|
|
160
|
+
last: async () => (await all()).at(-1),
|
|
161
|
+
first: async () => (await all())[0]
|
|
162
|
+
});
|
|
163
|
+
return accessor;
|
|
143
164
|
}
|
|
144
165
|
/**
|
|
145
166
|
* Accessor for the session's event stream: the buffered `SessionEvent`
|
|
@@ -214,6 +235,12 @@ export class SessionHandle {
|
|
|
214
235
|
* Fetch the self-contained `RunUnit` for this session: parsed submission,
|
|
215
236
|
* attempts, indexed events, outputs, capture failures, proxy-call audit, and
|
|
216
237
|
* resolved skills. Use this when you need fields beyond the session record.
|
|
238
|
+
*
|
|
239
|
+
* On the managed plane this is a LEAN summary — the aggregate collections
|
|
240
|
+
* (`attempts` / `events.entries` / `outputs` / `rawEventPages`) default to
|
|
241
|
+
* empty. For authoritative per-run data use `outputs()` / `events()` /
|
|
242
|
+
* `messages()`. The returned shape is always type-valid (never `undefined`
|
|
243
|
+
* where the type promises an array/page), so array/page access is safe.
|
|
217
244
|
*/
|
|
218
245
|
unit() {
|
|
219
246
|
return operations.getRunUnit(this.#http, this.id);
|
|
@@ -274,7 +301,10 @@ export class SessionClient {
|
|
|
274
301
|
* `query.limit` (default 100).
|
|
275
302
|
*/
|
|
276
303
|
async searchOutputs(query = {}) {
|
|
277
|
-
|
|
304
|
+
// Dedup the caller-supplied allow-list so a run repeated in `runIds` (e.g. from
|
|
305
|
+
// concatenating corpora) isn't scanned twice and doesn't inflate the hit count
|
|
306
|
+
// with duplicates (pre-launch edge-sweep F27).
|
|
307
|
+
const sessionIds = query.runIds ? [...new Set(query.runIds)] : await this.#allSessionIds();
|
|
278
308
|
const limit = query.limit ?? 100;
|
|
279
309
|
// Translate the search query to an OutputQuery so the contracts output
|
|
280
310
|
// filter does the matching — no re-derived filter logic here.
|
|
@@ -447,6 +477,199 @@ function sessionOutputs(http, id, fetchLike) {
|
|
|
447
477
|
download: (selector, options) => downloadSessionOutput(http, id, selector, options)
|
|
448
478
|
};
|
|
449
479
|
}
|
|
480
|
+
function messageFromWire(message) {
|
|
481
|
+
return {
|
|
482
|
+
id: message.id,
|
|
483
|
+
sender: message.sender,
|
|
484
|
+
text: message.text,
|
|
485
|
+
...(message.timestamp !== undefined ? { timestamp: message.timestamp } : {}),
|
|
486
|
+
...(message.turnSeq !== undefined ? { turnSeq: message.turnSeq } : {}),
|
|
487
|
+
...(message.sequence !== undefined ? { sequence: message.sequence } : {})
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function isMissingMessagesEndpoint(err) {
|
|
491
|
+
return err instanceof AexApiError && (err.status === 404 || err.status === 405 || err.status === 501);
|
|
492
|
+
}
|
|
493
|
+
function projectAssistantMessages(events) {
|
|
494
|
+
const out = [];
|
|
495
|
+
const byMessageId = new Map();
|
|
496
|
+
for (let i = 0; i < events.length; i++) {
|
|
497
|
+
const event = events[i];
|
|
498
|
+
if (event.type !== "TEXT_MESSAGE_CONTENT")
|
|
499
|
+
continue;
|
|
500
|
+
const data = asRecord(event.data);
|
|
501
|
+
const text = typeof data.text === "string" ? data.text : undefined;
|
|
502
|
+
if (text === undefined)
|
|
503
|
+
continue;
|
|
504
|
+
const messageId = typeof data.messageId === "string" && data.messageId ? data.messageId : undefined;
|
|
505
|
+
const sequence = event.sequence ?? event.seq;
|
|
506
|
+
const timestamp = event.time ?? event.recordedAt ?? timestampFromEpochMs(event.receivedAt);
|
|
507
|
+
const turnSeq = typeof data.turnSeq === "number" ? data.turnSeq : undefined;
|
|
508
|
+
if (messageId !== undefined) {
|
|
509
|
+
const existing = byMessageId.get(messageId);
|
|
510
|
+
if (existing !== undefined) {
|
|
511
|
+
const current = out[existing];
|
|
512
|
+
out[existing] = {
|
|
513
|
+
...current,
|
|
514
|
+
text: `${current.text}${text}`,
|
|
515
|
+
...(timestamp !== undefined ? { timestamp } : {}),
|
|
516
|
+
...(sequence !== undefined ? { sequence } : {}),
|
|
517
|
+
...(turnSeq !== undefined ? { turnSeq } : {})
|
|
518
|
+
};
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
byMessageId.set(messageId, out.length);
|
|
522
|
+
}
|
|
523
|
+
out.push({
|
|
524
|
+
id: messageId ?? (typeof event.id === "string" && event.id ? event.id : `message-${i}`),
|
|
525
|
+
sender: "assistant",
|
|
526
|
+
text,
|
|
527
|
+
...(timestamp !== undefined ? { timestamp } : {}),
|
|
528
|
+
...(sequence !== undefined ? { sequence } : {}),
|
|
529
|
+
...(turnSeq !== undefined ? { turnSeq } : {})
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return out;
|
|
533
|
+
}
|
|
534
|
+
function assistantTextFromEvents(events) {
|
|
535
|
+
return assistantTextEntriesFromEvents(events).map((entry) => entry.text).join("");
|
|
536
|
+
}
|
|
537
|
+
function runTraceFromEvents(events) {
|
|
538
|
+
return {
|
|
539
|
+
toolCalls: toolCallsFromEvents(events),
|
|
540
|
+
usage: usageFromEvents(events),
|
|
541
|
+
text: assistantTextEntriesFromEvents(events)
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function assistantTextEntriesFromEvents(events) {
|
|
545
|
+
const out = [];
|
|
546
|
+
for (const raw of events) {
|
|
547
|
+
const event = raw;
|
|
548
|
+
if (event.type !== "TEXT_MESSAGE_CONTENT")
|
|
549
|
+
continue;
|
|
550
|
+
const data = asRecord(event.data);
|
|
551
|
+
const text = typeof data.text === "string" ? data.text : undefined;
|
|
552
|
+
if (text === undefined)
|
|
553
|
+
continue;
|
|
554
|
+
const entry = { text };
|
|
555
|
+
const messageId = typeof data.messageId === "string" ? data.messageId : undefined;
|
|
556
|
+
if (messageId !== undefined)
|
|
557
|
+
entry.messageId = messageId;
|
|
558
|
+
if (typeof event.seq === "number")
|
|
559
|
+
entry.seq = event.seq;
|
|
560
|
+
const recordedAt = typeof event.recordedAt === "string" ? event.recordedAt : undefined;
|
|
561
|
+
if (recordedAt !== undefined)
|
|
562
|
+
entry.recordedAt = recordedAt;
|
|
563
|
+
out.push(entry);
|
|
564
|
+
}
|
|
565
|
+
return out;
|
|
566
|
+
}
|
|
567
|
+
function toolCallsFromEvents(events) {
|
|
568
|
+
const order = [];
|
|
569
|
+
const byId = new Map();
|
|
570
|
+
for (const event of events) {
|
|
571
|
+
const data = asRecord(event.data);
|
|
572
|
+
if (event.type === "TOOL_CALL_START") {
|
|
573
|
+
const id = typeof data.id === "string" ? data.id : undefined;
|
|
574
|
+
if (id === undefined)
|
|
575
|
+
continue;
|
|
576
|
+
const trace = {
|
|
577
|
+
id,
|
|
578
|
+
name: typeof data.name === "string" ? data.name : "",
|
|
579
|
+
args: asRecord(data.arguments)
|
|
580
|
+
};
|
|
581
|
+
const messageId = typeof data.messageId === "string" ? data.messageId : undefined;
|
|
582
|
+
if (messageId !== undefined)
|
|
583
|
+
trace.messageId = messageId;
|
|
584
|
+
if (typeof event.seq === "number")
|
|
585
|
+
trace.startSeq = event.seq;
|
|
586
|
+
if (typeof event.recordedAt === "string")
|
|
587
|
+
trace.startedAt = event.recordedAt;
|
|
588
|
+
if (!byId.has(id))
|
|
589
|
+
order.push(id);
|
|
590
|
+
byId.set(id, trace);
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (event.type === "TOOL_CALL_RESULT") {
|
|
594
|
+
const id = typeof data.id === "string" ? data.id : undefined;
|
|
595
|
+
if (id === undefined)
|
|
596
|
+
continue;
|
|
597
|
+
const result = {
|
|
598
|
+
isError: data.isError === true,
|
|
599
|
+
content: data.content ?? null
|
|
600
|
+
};
|
|
601
|
+
if (typeof event.seq === "number")
|
|
602
|
+
result.seq = event.seq;
|
|
603
|
+
if (typeof event.recordedAt === "string")
|
|
604
|
+
result.recordedAt = event.recordedAt;
|
|
605
|
+
let trace = byId.get(id);
|
|
606
|
+
if (trace === undefined) {
|
|
607
|
+
trace = { id, name: "", args: {} };
|
|
608
|
+
order.push(id);
|
|
609
|
+
byId.set(id, trace);
|
|
610
|
+
}
|
|
611
|
+
trace.result = result;
|
|
612
|
+
const duration = durationMs(trace.startedAt, result.recordedAt);
|
|
613
|
+
if (duration !== undefined)
|
|
614
|
+
trace.durationMs = duration;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return order.map((id) => byId.get(id));
|
|
618
|
+
}
|
|
619
|
+
function usageFromEvents(events) {
|
|
620
|
+
const totals = { inputTokens: 0, outputTokens: 0, cacheReadInputTokens: 0, cacheCreationInputTokens: 0 };
|
|
621
|
+
let seen = false;
|
|
622
|
+
for (const event of events) {
|
|
623
|
+
if (event.type !== "CUSTOM")
|
|
624
|
+
continue;
|
|
625
|
+
const data = asRecord(event.data);
|
|
626
|
+
if (data.name !== "aex.usage")
|
|
627
|
+
continue;
|
|
628
|
+
const value = asRecord(data.value);
|
|
629
|
+
const fields = [
|
|
630
|
+
["input_tokens", "inputTokens"],
|
|
631
|
+
["output_tokens", "outputTokens"],
|
|
632
|
+
["cache_read_input_tokens", "cacheReadInputTokens"],
|
|
633
|
+
["cache_creation_input_tokens", "cacheCreationInputTokens"]
|
|
634
|
+
];
|
|
635
|
+
for (const [wireName, apiName] of fields) {
|
|
636
|
+
const n = value[wireName];
|
|
637
|
+
if (typeof n === "number" && Number.isFinite(n)) {
|
|
638
|
+
totals[apiName] += n;
|
|
639
|
+
seen = true;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (!seen)
|
|
644
|
+
return {};
|
|
645
|
+
return {
|
|
646
|
+
inputTokens: totals.inputTokens,
|
|
647
|
+
outputTokens: totals.outputTokens,
|
|
648
|
+
cacheReadInputTokens: totals.cacheReadInputTokens,
|
|
649
|
+
cacheCreationInputTokens: totals.cacheCreationInputTokens,
|
|
650
|
+
totalTokens: totals.inputTokens + totals.outputTokens
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function asRecord(value) {
|
|
654
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
655
|
+
? value
|
|
656
|
+
: {};
|
|
657
|
+
}
|
|
658
|
+
function timestampFromEpochMs(value) {
|
|
659
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
660
|
+
? new Date(value).toISOString()
|
|
661
|
+
: undefined;
|
|
662
|
+
}
|
|
663
|
+
function durationMs(start, end) {
|
|
664
|
+
if (start === undefined || end === undefined)
|
|
665
|
+
return undefined;
|
|
666
|
+
const a = Date.parse(start);
|
|
667
|
+
const b = Date.parse(end);
|
|
668
|
+
if (!Number.isFinite(a) || !Number.isFinite(b))
|
|
669
|
+
return undefined;
|
|
670
|
+
const delta = b - a;
|
|
671
|
+
return delta >= 0 ? delta : undefined;
|
|
672
|
+
}
|
|
450
673
|
function isSessionTurnTerminalEvent(event, turnSeq) {
|
|
451
674
|
const name = customName(event);
|
|
452
675
|
if (name !== "aex.session.idle" &&
|
|
@@ -599,7 +822,7 @@ function unwrapSecretValue(value) {
|
|
|
599
822
|
* `client.whoami()` if you want to introspect which workspace the
|
|
600
823
|
* token resolves to.
|
|
601
824
|
*/
|
|
602
|
-
export class
|
|
825
|
+
export class Aex {
|
|
603
826
|
#http;
|
|
604
827
|
/** The same fetch the HttpClient uses, threaded into `_uploadAsset`. */
|
|
605
828
|
#fetch;
|
|
@@ -607,28 +830,32 @@ export class AgentExecutor {
|
|
|
607
830
|
files;
|
|
608
831
|
secrets;
|
|
609
832
|
sessions;
|
|
610
|
-
constructor(options) {
|
|
611
|
-
|
|
612
|
-
|
|
833
|
+
constructor(options, overrides = {}) {
|
|
834
|
+
const resolved = typeof options === "string" ? { ...overrides, apiKey: options } : options;
|
|
835
|
+
const apiKey = resolved.apiKey ?? resolved.apiToken;
|
|
836
|
+
if (!apiKey) {
|
|
837
|
+
// Typed so a caller catching AexError (the SDK's error base) catches a
|
|
838
|
+
// missing credential too, instead of a bare Error slipping the taxonomy.
|
|
839
|
+
throw new CredentialValidationError("Aex: apiKey is required");
|
|
613
840
|
}
|
|
614
841
|
// Wrap the transport fetch (the caller's override, or global `fetch`) with
|
|
615
842
|
// the bounded-retry layer so every BFF request gets default resilience.
|
|
616
843
|
// The raw `#fetch` below stays unwrapped for the direct-to-storage asset PUT
|
|
617
844
|
// and presigned output GETs, which target object storage, not the API plane.
|
|
618
|
-
const baseFetch =
|
|
619
|
-
const retryingFetch = withRetry(baseFetch,
|
|
845
|
+
const baseFetch = resolved.fetch ?? ((input, init) => fetch(input, init));
|
|
846
|
+
const retryingFetch = withRetry(baseFetch, resolved.retry);
|
|
620
847
|
this.#http = new HttpClient({
|
|
621
|
-
...(
|
|
622
|
-
apiToken:
|
|
848
|
+
...(resolved.baseUrl ? { baseUrl: resolved.baseUrl } : {}),
|
|
849
|
+
apiToken: apiKey,
|
|
623
850
|
fetch: retryingFetch,
|
|
624
851
|
// Opt-in local diagnostics: emit a redacted per-request trace to
|
|
625
852
|
// stderr. Uploads nothing. A caller wanting a custom sink can pass
|
|
626
853
|
// a function instead of `true`.
|
|
627
|
-
...(
|
|
628
|
-
? { debug: typeof
|
|
854
|
+
...(resolved.debug
|
|
855
|
+
? { debug: typeof resolved.debug === "function" ? resolved.debug : (line) => console.error(line) }
|
|
629
856
|
: {})
|
|
630
857
|
});
|
|
631
|
-
this.#fetch =
|
|
858
|
+
this.#fetch = resolved.fetch;
|
|
632
859
|
this.agentsMd = new AgentsMdClient(this.#http);
|
|
633
860
|
this.files = new FilesClient(this.#http);
|
|
634
861
|
this.secrets = new SecretsClient(this.#http);
|
|
@@ -698,6 +925,20 @@ export class AgentExecutor {
|
|
|
698
925
|
const events = turnResult.events;
|
|
699
926
|
const outputs = turnResult.outputs;
|
|
700
927
|
const ok = turnResult.status === "idle" || turnResult.status === "suspended";
|
|
928
|
+
if (!ok && scopedSignal?.signal.aborted) {
|
|
929
|
+
// The client-side wait budget (opts.timeoutMs) expired before the run
|
|
930
|
+
// reached a terminal park. Parity with SessionHandle.wait(): THROW rather
|
|
931
|
+
// than silently returning a misleading {ok:false,status:"running"} with no
|
|
932
|
+
// error (pre-launch edge-sweep F3). The run continues server-side.
|
|
933
|
+
throw new RunStateError(`Aex.run: timed out after ${opts.timeoutMs}ms waiting for run ${runId} to park (last status ` +
|
|
934
|
+
`${JSON.stringify(turnResult.status)}); the run continues server-side — cancel via ` +
|
|
935
|
+
`session.cancel() or resume with openSession(${JSON.stringify(runId)})`);
|
|
936
|
+
}
|
|
937
|
+
const trace = runTraceFromEvents(events);
|
|
938
|
+
// Surface the trace-derived usage at the top level when the run record does
|
|
939
|
+
// not carry its own usage (the managed plane doesn't populate session.usage);
|
|
940
|
+
// the per-event trace still yields token counts (pre-launch edge-sweep F5).
|
|
941
|
+
const usage = turnResult.session.usage ?? trace.usage;
|
|
701
942
|
const costUsd = typeof turnResult.session.costUsd === "number" ? turnResult.session.costUsd : undefined;
|
|
702
943
|
const errorMessage = typeof turnResult.session.errorMessage === "string" && turnResult.session.errorMessage ? turnResult.session.errorMessage : undefined;
|
|
703
944
|
const result = {
|
|
@@ -709,10 +950,11 @@ export class AgentExecutor {
|
|
|
709
950
|
status: turnResult.status,
|
|
710
951
|
ok,
|
|
711
952
|
text: turnResult.text,
|
|
953
|
+
messages: turnResult.messages,
|
|
712
954
|
events,
|
|
713
|
-
trace
|
|
955
|
+
trace,
|
|
714
956
|
outputs,
|
|
715
|
-
...(
|
|
957
|
+
...(usage ? { usage } : {}),
|
|
716
958
|
...(typeof costUsd === "number" ? { costUsd } : {}),
|
|
717
959
|
...(!ok && errorMessage ? { error: errorMessage } : {})
|
|
718
960
|
};
|
|
@@ -730,7 +972,7 @@ export class AgentExecutor {
|
|
|
730
972
|
...(throttle.retryAfterMs !== undefined ? { retryAfterMs: throttle.retryAfterMs } : {})
|
|
731
973
|
});
|
|
732
974
|
}
|
|
733
|
-
throw new RunStateError(`
|
|
975
|
+
throw new RunStateError(`Aex.run: session ${runId} ended ${turnResult.status}${errorMessage ? `: ${errorMessage}` : ""}`, { runId, status: turnResult.status });
|
|
734
976
|
}
|
|
735
977
|
return result;
|
|
736
978
|
}
|
|
@@ -760,8 +1002,23 @@ export class AgentExecutor {
|
|
|
760
1002
|
if (typeof options.model !== "string" || !options.model) {
|
|
761
1003
|
throw new RunConfigValidationError("Aex.openSession: model is required");
|
|
762
1004
|
}
|
|
763
|
-
|
|
764
|
-
|
|
1005
|
+
// Fast client-side validation via the contract parsers (the SSoT). runtimeSize
|
|
1006
|
+
// and timeout are STABLE closed sets whose invalid values the create endpoint
|
|
1007
|
+
// otherwise SILENTLY defaults (no error ever — pre-launch edge-sweep F11/F12);
|
|
1008
|
+
// reject them synchronously with a typed error instead. webhook shape is
|
|
1009
|
+
// re-checked here for a fast local fail (the server enforces it too). Model is
|
|
1010
|
+
// deliberately NOT hard-rejected here to preserve forward-compat with models
|
|
1011
|
+
// added server-side before an SDK upgrade (an unknown model still fails on the
|
|
1012
|
+
// server).
|
|
1013
|
+
try {
|
|
1014
|
+
parseRuntimeSize(options.runtime);
|
|
1015
|
+
parseRunTimeout(options.overrides?.timeout);
|
|
1016
|
+
if (options.webhook !== undefined)
|
|
1017
|
+
parseRunWebhook(options.webhook);
|
|
1018
|
+
}
|
|
1019
|
+
catch (err) {
|
|
1020
|
+
throw new RunConfigValidationError(`Aex.openSession: ${err instanceof Error ? err.message : String(err)}`);
|
|
1021
|
+
}
|
|
765
1022
|
const { declarations: secretEnvDeclarations, values: envSecretValues } = splitSecretEnv(options.environment?.secrets);
|
|
766
1023
|
let limits;
|
|
767
1024
|
try {
|
|
@@ -804,7 +1061,6 @@ export class AgentExecutor {
|
|
|
804
1061
|
const secrets = {
|
|
805
1062
|
...(options.apiKeys ? { apiKeys: options.apiKeys } : {}),
|
|
806
1063
|
...(mergedMcpSecrets.length > 0 ? { mcpServers: mergedMcpSecrets } : {}),
|
|
807
|
-
...(mergedProxyAuth.length > 0 ? { proxyEndpointAuth: mergedProxyAuth } : {}),
|
|
808
1064
|
...(Object.keys(envSecretValues).length > 0 ? { envSecrets: envSecretValues } : {})
|
|
809
1065
|
};
|
|
810
1066
|
const retention = sessionRetentionForWire(options);
|
|
@@ -818,10 +1074,7 @@ export class AgentExecutor {
|
|
|
818
1074
|
// Operational/delivery concern — sibling of secrets, NOT part of the
|
|
819
1075
|
// hashed submission. Delivered at the settle-consistent barrier.
|
|
820
1076
|
...(options.webhook ? { webhook: options.webhook } : {}),
|
|
821
|
-
secrets
|
|
822
|
-
...(proxyEndpointDeclarations.length > 0
|
|
823
|
-
? { proxyEndpoints: proxyEndpointDeclarations }
|
|
824
|
-
: {})
|
|
1077
|
+
secrets
|
|
825
1078
|
};
|
|
826
1079
|
}
|
|
827
1080
|
/**
|
|
@@ -835,9 +1088,49 @@ export class AgentExecutor {
|
|
|
835
1088
|
whoami() {
|
|
836
1089
|
return operations.whoami(this.#http);
|
|
837
1090
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1091
|
+
/**
|
|
1092
|
+
* Read the workspace billing summary: prepaid `balanceUsd`, current-month
|
|
1093
|
+
* `monthSpendUsd`, the enforced `spendCapUsd`, and plan fields. Backed by
|
|
1094
|
+
* `GET /api/billing` (scope `billing:read`). The result is additive-tolerant:
|
|
1095
|
+
* fields a newer deployment reports that this SDK does not know yet pass
|
|
1096
|
+
* through on the returned object.
|
|
1097
|
+
*/
|
|
1098
|
+
billing() {
|
|
1099
|
+
return operations.getBilling(this.#http);
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Create a hosted checkout session for a paid plan (`pro` or `team`).
|
|
1103
|
+
* Open the returned `url` in a browser. Plan activation happens after
|
|
1104
|
+
* checkout completes.
|
|
1105
|
+
*/
|
|
1106
|
+
billingCheckout(request) {
|
|
1107
|
+
return operations.createBillingCheckout(this.#http, request);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Create a hosted billing-portal session for the workspace customer.
|
|
1111
|
+
* Open the returned `url` in a browser.
|
|
1112
|
+
*/
|
|
1113
|
+
billingPortal(request = {}) {
|
|
1114
|
+
return operations.createBillingPortal(this.#http, request);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Read recent workspace credit-ledger rows, newest first — top-ups, run
|
|
1118
|
+
* charges, and redemptions with signed `amountUsd`. Backed by
|
|
1119
|
+
* `GET /api/billing/ledger`; `limit` is clamped server-side to [1, 100]
|
|
1120
|
+
* (default 25). Not cursor-paged.
|
|
1121
|
+
*/
|
|
1122
|
+
billingLedger(query) {
|
|
1123
|
+
return operations.getBillingLedger(this.#http, query);
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Reveal the workspace webhook signing secret (creating one on first use) —
|
|
1127
|
+
* the `whsec_<base64>` value `verifyAexWebhook` takes as `secret`. Backed by
|
|
1128
|
+
* `POST /api/webhook/signing-secret`; repeat calls return the SAME value (the
|
|
1129
|
+
* hosted API does not rotate it). Treat the reveal as sensitive: never log it.
|
|
1130
|
+
*/
|
|
1131
|
+
webhookSigningSecret() {
|
|
1132
|
+
return operations.getWebhookSigningSecret(this.#http);
|
|
1133
|
+
}
|
|
841
1134
|
}
|
|
842
1135
|
// `Run.status` is a loose `string` on the wire shape, so we membership-test
|
|
843
1136
|
// against the canonical terminal set rather than re-deriving one (which is how
|
|
@@ -895,7 +1188,7 @@ function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
895
1188
|
if (isOutputPathSelector(selector)) {
|
|
896
1189
|
const target = normalizeOutputLookupPath(selector.path);
|
|
897
1190
|
if (!target) {
|
|
898
|
-
throw new RunStateError("
|
|
1191
|
+
throw new RunStateError("Aex.downloadOutput: output path must be non-empty", {
|
|
899
1192
|
runId,
|
|
900
1193
|
path: selector.path
|
|
901
1194
|
});
|
|
@@ -912,15 +1205,15 @@ function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
912
1205
|
if (matches.length === 1)
|
|
913
1206
|
return matches[0];
|
|
914
1207
|
if (matches.length > 1) {
|
|
915
|
-
throw new RunStateError(`
|
|
1208
|
+
throw new RunStateError(`Aex.downloadOutput: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
|
|
916
1209
|
}
|
|
917
|
-
throw new RunStateError(`
|
|
1210
|
+
throw new RunStateError(`Aex.downloadOutput: output path "${selector.path}" was not found`, {
|
|
918
1211
|
runId,
|
|
919
1212
|
path: selector.path
|
|
920
1213
|
});
|
|
921
1214
|
}
|
|
922
1215
|
if (typeof selector.id !== "string" || selector.id.length === 0) {
|
|
923
|
-
throw new RunStateError("
|
|
1216
|
+
throw new RunStateError("Aex.downloadOutput: selector must include an output id or path", { runId });
|
|
924
1217
|
}
|
|
925
1218
|
return { ...selector, id: selector.id };
|
|
926
1219
|
}
|
|
@@ -1010,6 +1303,7 @@ function normaliseSessionInput(input, surface, field) {
|
|
|
1010
1303
|
}
|
|
1011
1304
|
function assertNoLegacySessionFields(options, surface) {
|
|
1012
1305
|
const record = options;
|
|
1306
|
+
const removedProxyField = "proxy" + "Endpoints";
|
|
1013
1307
|
const messages = {
|
|
1014
1308
|
input: "send user messages with session.send(...) or use run({ message }).",
|
|
1015
1309
|
prompt: "use message for one-shot run input or session.send(...) for follow-up messages.",
|
|
@@ -1025,7 +1319,8 @@ function assertNoLegacySessionFields(options, surface) {
|
|
|
1025
1319
|
limits: "use overrides.",
|
|
1026
1320
|
timeout: "use overrides.timeout.",
|
|
1027
1321
|
signal: "use session.cancel() / session.suspend() for remote control.",
|
|
1028
|
-
postHook: "send a follow-up validation message when the session returns idle."
|
|
1322
|
+
postHook: "send a follow-up validation message when the session returns idle.",
|
|
1323
|
+
[removedProxyField]: "proxy endpoints are not part of the public SDK session API."
|
|
1029
1324
|
};
|
|
1030
1325
|
for (const [field, message] of Object.entries(messages)) {
|
|
1031
1326
|
if (Object.prototype.hasOwnProperty.call(record, field)) {
|
|
@@ -1247,29 +1542,4 @@ function mergeMcpServers(inputs, explicitSecrets) {
|
|
|
1247
1542
|
mergedMcpSecrets: Array.from(secretByName.values())
|
|
1248
1543
|
};
|
|
1249
1544
|
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Merge `ProxyEndpoint`-derived auth entries with any
|
|
1252
|
-
* `secrets.proxyEndpointAuth` the caller passed explicitly. Per-instance
|
|
1253
|
-
* auth values win on the same `name`; a type mismatch (e.g. instance
|
|
1254
|
-
* declares `bearer` but secrets carry `header` for the same name) is a
|
|
1255
|
-
* call-site error and we throw at the SDK boundary instead of letting
|
|
1256
|
-
* the BFF reject the submission an HTTP request later.
|
|
1257
|
-
*/
|
|
1258
|
-
function mergeProxyEndpointAuth(fromInstances, fromExplicitSecrets) {
|
|
1259
|
-
if (fromInstances.length === 0 && fromExplicitSecrets.length === 0)
|
|
1260
|
-
return [];
|
|
1261
|
-
const byName = new Map();
|
|
1262
|
-
for (const entry of fromExplicitSecrets) {
|
|
1263
|
-
byName.set(entry.name, entry);
|
|
1264
|
-
}
|
|
1265
|
-
for (const entry of fromInstances) {
|
|
1266
|
-
const existing = byName.get(entry.name);
|
|
1267
|
-
if (existing && existing.value.type !== entry.value.type) {
|
|
1268
|
-
throw new RunConfigValidationError(`aex: proxyEndpoint "${entry.name}" auth type conflicts ` +
|
|
1269
|
-
`with secrets.proxyEndpointAuth (instance=${entry.value.type}, secrets=${existing.value.type})`);
|
|
1270
|
-
}
|
|
1271
|
-
byName.set(entry.name, entry);
|
|
1272
|
-
}
|
|
1273
|
-
return Array.from(byName.values());
|
|
1274
|
-
}
|
|
1275
1545
|
//# sourceMappingURL=client.js.map
|