@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.
Files changed (72) hide show
  1. package/README.md +17 -16
  2. package/dist/_contracts/event-envelope.d.ts +22 -1
  3. package/dist/_contracts/event-envelope.js +26 -2
  4. package/dist/_contracts/event-stream-client.js +7 -1
  5. package/dist/_contracts/index.d.ts +3 -4
  6. package/dist/_contracts/index.js +1 -4
  7. package/dist/_contracts/operations.d.ts +31 -1
  8. package/dist/_contracts/operations.js +64 -1
  9. package/dist/_contracts/run-config.d.ts +2 -4
  10. package/dist/_contracts/run-config.js +2 -7
  11. package/dist/_contracts/run-trace.d.ts +0 -86
  12. package/dist/_contracts/run-trace.js +1 -184
  13. package/dist/_contracts/run-unit.d.ts +14 -25
  14. package/dist/_contracts/run-unit.js +56 -2
  15. package/dist/_contracts/runtime-manifest.d.ts +1 -1
  16. package/dist/_contracts/runtime-security-profile.d.ts +0 -2
  17. package/dist/_contracts/runtime-security-profile.js +0 -9
  18. package/dist/_contracts/runtime-sizes.d.ts +2 -2
  19. package/dist/_contracts/runtime-sizes.js +5 -5
  20. package/dist/_contracts/runtime-types.d.ts +123 -4
  21. package/dist/_contracts/stable.d.ts +1 -1
  22. package/dist/_contracts/stable.js +1 -1
  23. package/dist/_contracts/submission.d.ts +8 -76
  24. package/dist/_contracts/submission.js +5 -472
  25. package/dist/cli.mjs +574 -511
  26. package/dist/cli.mjs.sha256 +1 -1
  27. package/dist/client.d.ts +69 -25
  28. package/dist/client.js +338 -68
  29. package/dist/client.js.map +1 -1
  30. package/dist/index.d.ts +8 -16
  31. package/dist/index.js +5 -17
  32. package/dist/index.js.map +1 -1
  33. package/dist/secret.d.ts +2 -2
  34. package/dist/secret.js +1 -1
  35. package/dist/version.d.ts +1 -1
  36. package/dist/version.js +1 -1
  37. package/docs/authentication.md +92 -0
  38. package/docs/billing.md +112 -0
  39. package/docs/concepts/agent-tools.md +4 -4
  40. package/docs/concepts/composition.md +8 -14
  41. package/docs/concepts/providers-and-runtimes.md +4 -1
  42. package/docs/concepts/runs.md +2 -1
  43. package/docs/concepts/subagents.md +85 -0
  44. package/docs/credentials.md +78 -96
  45. package/docs/defaults.md +9 -15
  46. package/docs/errors.md +132 -0
  47. package/docs/events.md +44 -32
  48. package/docs/limits-and-quotas.md +30 -17
  49. package/docs/limits.md +4 -8
  50. package/docs/mcp.md +5 -6
  51. package/docs/networking.md +75 -59
  52. package/docs/outputs.md +4 -7
  53. package/docs/public-surface.json +4 -4
  54. package/docs/quickstart.md +12 -13
  55. package/docs/run-config.md +7 -4
  56. package/docs/secrets.md +6 -1
  57. package/docs/skills.md +3 -3
  58. package/docs/vision-skills.md +52 -101
  59. package/docs/webhooks.md +132 -0
  60. package/examples/feature-tour.ts +4 -21
  61. package/package.json +1 -1
  62. package/dist/_contracts/proxy-protocol.d.ts +0 -305
  63. package/dist/_contracts/proxy-protocol.js +0 -297
  64. package/dist/_contracts/proxy-validation.d.ts +0 -19
  65. package/dist/_contracts/proxy-validation.js +0 -51
  66. package/dist/data-tools.d.ts +0 -82
  67. package/dist/data-tools.js +0 -251
  68. package/dist/data-tools.js.map +0 -1
  69. package/dist/proxy-endpoint.d.ts +0 -131
  70. package/dist/proxy-endpoint.js +0 -144
  71. package/dist/proxy-endpoint.js.map +0 -1
  72. 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, decodeAssistantText, summarizeRunTrace, textOf, parseRunLimits, BUILTIN_TOOL_NAMES, TERMINAL_RUN_STATUSES } from "./_contracts/index.js";
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: textOf(events),
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 decoded assistant messages (buffered output
131
- * mode: one entry per assistant message). `list()` returns them oldest-first;
132
- * `last()`/`first()` return a single entry or `undefined` when empty.
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 list = async () => decodeAssistantText((await operations.listSessionEvents(http, id)));
138
- return {
139
- list,
140
- last: async () => (await list()).at(-1),
141
- first: async () => (await list())[0]
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
- const sessionIds = query.runIds ?? (await this.#allSessionIds());
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 AgentExecutor {
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
- if (!options.apiToken) {
612
- throw new Error("AgentExecutor: apiToken is required");
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 = options.fetch ?? ((input, init) => fetch(input, init));
619
- const retryingFetch = withRetry(baseFetch, options.retry);
845
+ const baseFetch = resolved.fetch ?? ((input, init) => fetch(input, init));
846
+ const retryingFetch = withRetry(baseFetch, resolved.retry);
620
847
  this.#http = new HttpClient({
621
- ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
622
- apiToken: options.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
- ...(options.debug
628
- ? { debug: typeof options.debug === "function" ? options.debug : (line) => console.error(line) }
854
+ ...(resolved.debug
855
+ ? { debug: typeof resolved.debug === "function" ? resolved.debug : (line) => console.error(line) }
629
856
  : {})
630
857
  });
631
- this.#fetch = options.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: summarizeRunTrace(events),
955
+ trace,
714
956
  outputs,
715
- ...(turnResult.session.usage ? { usage: turnResult.session.usage } : {}),
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(`AgentExecutor.run: session ${runId} ended ${turnResult.status}${errorMessage ? `: ${errorMessage}` : ""}`, { runId, status: turnResult.status });
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
- const { endpoints: proxyEndpointDeclarations, auth: proxyEndpointAuthFromInstances } = splitProxyEndpoints(options.proxyEndpoints ?? []);
764
- const mergedProxyAuth = mergeProxyEndpointAuth(proxyEndpointAuthFromInstances, []);
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
- /** Canonical SDK client name. `AgentExecutor` remains as a compatibility alias. */
840
- export class Aex extends AgentExecutor {
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("AgentExecutor.downloadOutput: output path must be non-empty", {
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(`AgentExecutor.downloadOutput: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
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(`AgentExecutor.downloadOutput: output path "${selector.path}" was not found`, {
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("AgentExecutor.downloadOutput: selector must include an output id or path", { runId });
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