@aexhq/sdk 0.19.0 → 0.20.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.
@@ -80,7 +80,9 @@ export const RUN_PROVIDERS = [
80
80
  "openai",
81
81
  "gemini",
82
82
  "mistral",
83
- "openrouter"
83
+ "openrouter",
84
+ "doubao",
85
+ "doubao-cn"
84
86
  ];
85
87
  export const DEFAULT_RUN_PROVIDER = "anthropic";
86
88
  /**
@@ -107,7 +109,11 @@ export const Providers = {
107
109
  /** Mistral. */
108
110
  MISTRAL: "mistral",
109
111
  /** OpenRouter — OpenAI-compatible aggregator routing to many upstream models. */
110
- OPENROUTER: "openrouter"
112
+ OPENROUTER: "openrouter",
113
+ /** Doubao (ByteDance) via the official international BytePlus ModelArk gateway. */
114
+ DOUBAO: "doubao",
115
+ /** Doubao (ByteDance) via the official China Volcengine Ark gateway. */
116
+ DOUBAO_CN: "doubao-cn"
111
117
  };
112
118
  /**
113
119
  * Customer-facing runtime selector. Optional on the wire; absent resolves
@@ -126,6 +132,10 @@ export function checkRuntimeSupported(provider, runtime) {
126
132
  return { ok: true };
127
133
  }
128
134
  export const SECRETS_KEY = "secrets";
135
+ /** POSIX-style env var name a `secretEnv` entry binds to (e.g. `SERPER_API_KEY`). */
136
+ export const SECRET_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
137
+ /** Workspace secret handle a `secretEnv` ref points at (and the name `secret.upload` persists to). */
138
+ export const SECRET_HANDLE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
129
139
  export const PROXY_ENDPOINT_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/;
130
140
  export const RESERVED_PROXY_ENDPOINT_NAMES = new Set(["proxy", "aex", "internal", "admin"]);
131
141
  /**
@@ -633,9 +643,40 @@ export function crossValidateProxyEndpointsAndAuth(endpoints, auth) {
633
643
  }
634
644
  }
635
645
  }
646
+ /**
647
+ * Cross-check `submission.secretEnv` declarations against `secrets.envSecrets`
648
+ * values. Mirrors {@link crossValidateProxyEndpointsAndAuth}:
649
+ *
650
+ * - `{ ephemeral: true }` MUST have a matching `secrets.envSecrets` value.
651
+ * - `{ ref }` MUST NOT supply a value (the value lives in the workspace store).
652
+ * - every `secrets.envSecrets` value MUST have a matching `{ ephemeral: true }`
653
+ * declaration (no orphan values that would never be injected).
654
+ */
655
+ export function crossValidateSecretEnvAndValues(secretEnv, envSecrets) {
656
+ const declarations = secretEnv ?? {};
657
+ const values = envSecrets ?? {};
658
+ for (const [envName, entry] of Object.entries(declarations)) {
659
+ const hasValue = Object.prototype.hasOwnProperty.call(values, envName);
660
+ if ("ref" in entry) {
661
+ if (hasValue) {
662
+ throw new Error(`submission.secretEnv[${envName}] is a workspace ref and must not supply a value in secrets.envSecrets[${envName}]; the value resolves server-side`);
663
+ }
664
+ continue;
665
+ }
666
+ if (!hasValue) {
667
+ throw new Error(`submission.secretEnv[${envName}] is ephemeral but has no matching secrets.envSecrets[${envName}] value`);
668
+ }
669
+ }
670
+ for (const envName of Object.keys(values)) {
671
+ const entry = declarations[envName];
672
+ if (!entry || !("ephemeral" in entry)) {
673
+ throw new Error(`secrets.envSecrets[${envName}] has no matching submission.secretEnv[${envName}] ephemeral declaration`);
674
+ }
675
+ }
676
+ }
636
677
  export function parseInlineSecrets(input) {
637
678
  const value = requireRecord(input, "secrets");
638
- const allowedTopLevel = new Set(["apiKey", "mcpServers", "proxyEndpointAuth"]);
679
+ const allowedTopLevel = new Set(["apiKey", "mcpServers", "proxyEndpointAuth", "envSecrets"]);
639
680
  for (const key of Object.keys(value)) {
640
681
  if (key.startsWith("__aex_")) {
641
682
  // Platform-internal namespace (e.g. __aex_proxy_token). The BFF
@@ -651,12 +692,30 @@ export function parseInlineSecrets(input) {
651
692
  const apiKey = value.apiKey !== undefined ? requireString(value.apiKey, "secrets.apiKey") : undefined;
652
693
  const mcpServers = parseMcpServerSecrets(value.mcpServers);
653
694
  const proxyEndpointAuth = parseProxyEndpointAuth(value.proxyEndpointAuth);
695
+ const envSecrets = parseEnvSecrets(value.envSecrets);
654
696
  return {
655
697
  ...(apiKey !== undefined ? { apiKey } : {}),
656
698
  ...(mcpServers ? { mcpServers } : {}),
657
- ...(proxyEndpointAuth ? { proxyEndpointAuth } : {})
699
+ ...(proxyEndpointAuth ? { proxyEndpointAuth } : {}),
700
+ ...(envSecrets ? { envSecrets } : {})
658
701
  };
659
702
  }
703
+ function parseEnvSecrets(input) {
704
+ if (input === undefined || input === null)
705
+ return undefined;
706
+ const value = requireRecord(input, "secrets.envSecrets");
707
+ const out = {};
708
+ for (const [envName, entry] of Object.entries(value)) {
709
+ if (!SECRET_ENV_NAME_PATTERN.test(envName)) {
710
+ throw new Error(`secrets.envSecrets key "${envName}" must be a valid env var name matching ${SECRET_ENV_NAME_PATTERN.source}`);
711
+ }
712
+ if (typeof entry !== "string" || entry.length === 0) {
713
+ throw new Error(`secrets.envSecrets.${envName} must be a non-empty string`);
714
+ }
715
+ out[envName] = entry;
716
+ }
717
+ return Object.keys(out).length > 0 ? out : undefined;
718
+ }
660
719
  function parseMcpServerSecrets(input) {
661
720
  if (input === undefined) {
662
721
  return undefined;
@@ -917,6 +976,7 @@ export function parseRunSubmissionRequest(input, options = {}) {
917
976
  "timeout",
918
977
  "postHook",
919
978
  "proxyEndpoints",
979
+ "parentRunId",
920
980
  SECRETS_KEY
921
981
  ]);
922
982
  for (const key of Object.keys(value)) {
@@ -949,6 +1009,9 @@ export function parseRunSubmissionRequest(input, options = {}) {
949
1009
  }
950
1010
  const runtimeSize = parseRuntimeSize(value.runtimeSize);
951
1011
  const timeoutMs = parseRunTimeout(value.timeout);
1012
+ // Lineage parent only. `depth` is NEVER accepted from the wire — the server
1013
+ // derives it from the parent row (a forged depth must not bypass the cap).
1014
+ const parentRunId = optionalString(value.parentRunId, "submission.parentRunId");
952
1015
  const postHook = parsePostHook(value.postHook, "submission.postHook");
953
1016
  const proxyEndpoints = parseProxyEndpoints(value.proxyEndpoints);
954
1017
  const secrets = parseInlineSecrets(value.secrets);
@@ -956,6 +1019,7 @@ export function parseRunSubmissionRequest(input, options = {}) {
956
1019
  crossValidateProxyEndpointsAndAuth(proxyEndpoints, secrets.proxyEndpointAuth);
957
1020
  const submission = parseSubmission(value.submission);
958
1021
  assertRunModelMatchesProvider(provider, submission.model);
1022
+ crossValidateSecretEnvAndValues(submission.secretEnv, secrets.envSecrets);
959
1023
  // mcpServers names must agree across the submission half and the
960
1024
  // secrets half — every secrets.mcpServers[i].name MUST resolve to a
961
1025
  // submission.mcpServers entry (no orphan secrets) AND the URL must
@@ -999,6 +1063,7 @@ export function parseRunSubmissionRequest(input, options = {}) {
999
1063
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
1000
1064
  ...(postHook !== undefined ? { postHook } : {}),
1001
1065
  ...(proxyEndpoints ? { proxyEndpoints } : {}),
1066
+ ...(parentRunId !== undefined ? { parentRunId } : {}),
1002
1067
  secrets
1003
1068
  };
1004
1069
  }
@@ -1051,6 +1116,7 @@ export function parseSubmission(input) {
1051
1116
  "agentsMd",
1052
1117
  "files",
1053
1118
  "mcpServers",
1119
+ "secretEnv",
1054
1120
  "environment",
1055
1121
  "securityProfile",
1056
1122
  "metadata",
@@ -1071,6 +1137,7 @@ export function parseSubmission(input) {
1071
1137
  const agentsMd = parseAgentsMd(value.agentsMd);
1072
1138
  const files = parseFiles(value.files);
1073
1139
  const mcpServers = parseMcpServers(value.mcpServers);
1140
+ const secretEnv = parseSecretEnv(value.secretEnv);
1074
1141
  const environment = parseEnvironment(value.environment);
1075
1142
  const securityProfile = parseRuntimeSecurityProfile(value.securityProfile);
1076
1143
  const metadata = optionalJsonRecord(value.metadata, "submission.metadata");
@@ -1086,6 +1153,7 @@ export function parseSubmission(input) {
1086
1153
  agentsMd,
1087
1154
  files,
1088
1155
  mcpServers,
1156
+ ...(secretEnv ? { secretEnv } : {}),
1089
1157
  ...(environment ? { environment } : {}),
1090
1158
  ...(securityProfile ? { securityProfile } : {}),
1091
1159
  ...(metadata ? { metadata } : {}),
@@ -1095,6 +1163,37 @@ export function parseSubmission(input) {
1095
1163
  ...(platform ? { platform } : {})
1096
1164
  };
1097
1165
  }
1166
+ function parseSecretEnv(input) {
1167
+ if (input === undefined || input === null)
1168
+ return undefined;
1169
+ const value = requireRecord(input, "submission.secretEnv");
1170
+ const out = {};
1171
+ for (const [envName, entry] of Object.entries(value)) {
1172
+ if (!SECRET_ENV_NAME_PATTERN.test(envName)) {
1173
+ throw new Error(`submission.secretEnv key "${envName}" must be a valid env var name matching ${SECRET_ENV_NAME_PATTERN.source}`);
1174
+ }
1175
+ const path = `submission.secretEnv.${envName}`;
1176
+ const record = requireRecord(entry, path);
1177
+ const keys = Object.keys(record);
1178
+ if (keys.length !== 1 || (!("ref" in record) && !("ephemeral" in record))) {
1179
+ throw new Error(`${path} must be exactly one of { ref } or { ephemeral: true }`);
1180
+ }
1181
+ if ("ref" in record) {
1182
+ const handle = requireString(record.ref, `${path}.ref`);
1183
+ if (!SECRET_HANDLE_PATTERN.test(handle)) {
1184
+ throw new Error(`${path}.ref handle must match ${SECRET_HANDLE_PATTERN.source}`);
1185
+ }
1186
+ out[envName] = { ref: handle };
1187
+ }
1188
+ else {
1189
+ if (record.ephemeral !== true) {
1190
+ throw new Error(`${path}.ephemeral must be the literal true`);
1191
+ }
1192
+ out[envName] = { ephemeral: true };
1193
+ }
1194
+ }
1195
+ return Object.keys(out).length > 0 ? out : undefined;
1196
+ }
1098
1197
  function parsePlatformConfig(input) {
1099
1198
  if (input === undefined || input === null)
1100
1199
  return undefined;
package/dist/cli.mjs CHANGED
@@ -126,6 +126,34 @@ var PROVIDER_PUBLIC_SUPPORT = {
126
126
  runtimeEvidence: {
127
127
  managed: COMMON_EVIDENCE
128
128
  }
129
+ },
130
+ // Doubao (ByteDance) via the official Ark API — international BytePlus gateway.
131
+ // Wired + parser/routing-verified but not yet proven against a live BytePlus
132
+ // account, so `live-unverified`. Promote to `supported` (and swap COMMON_EVIDENCE
133
+ // for a DOUBAO_MANAGED_EVIDENCE pointer to live-sdk-doubao.test.ts) once the
134
+ // live run passes.
135
+ doubao: {
136
+ displayName: "Doubao",
137
+ status: "live-unverified",
138
+ docsAnchor: "doubao",
139
+ docs: COMMON_DOCS,
140
+ evidence: COMMON_EVIDENCE,
141
+ runtimeEvidence: {
142
+ managed: COMMON_EVIDENCE
143
+ }
144
+ },
145
+ // Doubao (ByteDance) via the official Ark API — China Volcengine gateway.
146
+ // Same wiring as `doubao`; additionally gated on CF Worker egress reaching the
147
+ // Beijing host (see apps/egress-probe). `live-unverified` until proven live.
148
+ "doubao-cn": {
149
+ displayName: "Doubao (China)",
150
+ status: "live-unverified",
151
+ docsAnchor: "doubao-cn",
152
+ docs: COMMON_DOCS,
153
+ evidence: COMMON_EVIDENCE,
154
+ runtimeEvidence: {
155
+ managed: COMMON_EVIDENCE
156
+ }
129
157
  }
130
158
  };
131
159
 
@@ -144,7 +172,21 @@ var MODEL_PROVIDER_IDS = {
144
172
  "gemini-2.0-flash": { gemini: "gemini-2.0-flash", openrouter: "google/gemini-2.0-flash-001" },
145
173
  "gemini-2.5-flash": { gemini: "gemini-2.5-flash" },
146
174
  "mistral-large-latest": { mistral: "mistral-large-latest" },
147
- "mistral-small-latest": { mistral: "mistral-small-latest" }
175
+ "mistral-small-latest": { mistral: "mistral-small-latest" },
176
+ // Doubao (ByteDance) via the official Ark API. Served by both the
177
+ // international BytePlus ModelArk gateway (`doubao`, the default) and the
178
+ // China Volcengine Ark gateway (`doubao-cn`). Ark accepts the API-format
179
+ // model NAME directly in the chat-completions `model` field (no `ep-…`
180
+ // inference-endpoint id). The native strings are the same Ark catalog ids on
181
+ // both gateways; BytePlus per-account availability is confirmed at live-verify
182
+ // (both providers ship `live-unverified` until then — provider-support.ts).
183
+ // pro — Doubao Seed 1.8 (flagship, 256K context).
184
+ // flash — Doubao Seed 1.6 Flash (fast/cheap, 256K context).
185
+ "doubao-seed-pro": { doubao: "doubao-seed-1-8-251228", "doubao-cn": "doubao-seed-1-8-251228" },
186
+ "doubao-seed-flash": {
187
+ doubao: "doubao-seed-1-6-flash-250828",
188
+ "doubao-cn": "doubao-seed-1-6-flash-250828"
189
+ }
148
190
  };
149
191
  var RUN_MODELS = Object.keys(MODEL_PROVIDER_IDS);
150
192
  var PROVIDERS_BY_MODEL = (() => {
@@ -743,7 +785,9 @@ var RUN_PROVIDERS = [
743
785
  "openai",
744
786
  "gemini",
745
787
  "mistral",
746
- "openrouter"
788
+ "openrouter",
789
+ "doubao",
790
+ "doubao-cn"
747
791
  ];
748
792
  var DEFAULT_RUN_PROVIDER = "anthropic";
749
793
  var RUNTIME_KINDS = ["managed"];
@@ -1246,11 +1290,13 @@ __export(operations_exports, {
1246
1290
  createAgentsMd: () => createAgentsMd,
1247
1291
  createFile: () => createFile,
1248
1292
  createOutputLink: () => createOutputLink,
1293
+ createSecret: () => createSecret,
1249
1294
  createSkillBundle: () => createSkillBundle,
1250
1295
  createSkillBundleDirect: () => createSkillBundleDirect,
1251
1296
  deleteAgentsMd: () => deleteAgentsMd,
1252
1297
  deleteFile: () => deleteFile,
1253
1298
  deleteRun: () => deleteRun,
1299
+ deleteSecret: () => deleteSecret,
1254
1300
  deleteSkill: () => deleteSkill,
1255
1301
  deleteWorkspaceAsset: () => deleteWorkspaceAsset,
1256
1302
  download: () => download,
@@ -1265,13 +1311,17 @@ __export(operations_exports, {
1265
1311
  getFile: () => getFile,
1266
1312
  getRun: () => getRun,
1267
1313
  getRunUnit: () => getRunUnit,
1314
+ getSecret: () => getSecret,
1268
1315
  getSkill: () => getSkill,
1269
1316
  listAgentsMd: () => listAgentsMd,
1270
1317
  listFiles: () => listFiles,
1271
1318
  listOutputs: () => listOutputs,
1272
1319
  listRunEvents: () => listRunEvents,
1320
+ listSecrets: () => listSecrets,
1273
1321
  listSkills: () => listSkills,
1274
1322
  resolveOutputFileSelector: () => resolveOutputFileSelector,
1323
+ revealSecret: () => revealSecret,
1324
+ rotateSecret: () => rotateSecret,
1275
1325
  submitRun: () => submitRun,
1276
1326
  submitRunMultipart: () => submitRunMultipart,
1277
1327
  uploadWorkspaceAsset: () => uploadWorkspaceAsset,
@@ -2380,6 +2430,44 @@ function unwrapFile(result) {
2380
2430
  }
2381
2431
  return result;
2382
2432
  }
2433
+ async function createSecret(http, args) {
2434
+ const result = await http.request("/api/secrets", {
2435
+ method: "POST",
2436
+ body: JSON.stringify({ name: args.name, value: args.value })
2437
+ });
2438
+ return unwrapSecret(result);
2439
+ }
2440
+ async function listSecrets(http) {
2441
+ const result = await http.request("/api/secrets");
2442
+ if (Array.isArray(result)) {
2443
+ return result;
2444
+ }
2445
+ return result.secrets;
2446
+ }
2447
+ async function getSecret(http, name) {
2448
+ const result = await http.request(`/api/secrets/${encodeURIComponent(name)}`);
2449
+ return unwrapSecret(result);
2450
+ }
2451
+ async function revealSecret(http, name) {
2452
+ return http.request(`/api/secrets/${encodeURIComponent(name)}/reveal`, {
2453
+ method: "POST"
2454
+ });
2455
+ }
2456
+ async function rotateSecret(http, args) {
2457
+ const result = await http.request(`/api/secrets/${encodeURIComponent(args.name)}/rotate`, { method: "POST", body: JSON.stringify({ value: args.value }) });
2458
+ return unwrapSecret(result);
2459
+ }
2460
+ async function deleteSecret(http, name) {
2461
+ await http.request(`/api/secrets/${encodeURIComponent(name)}`, {
2462
+ method: "DELETE"
2463
+ });
2464
+ }
2465
+ function unwrapSecret(result) {
2466
+ if (result && typeof result === "object" && "secret" in result) {
2467
+ return result.secret;
2468
+ }
2469
+ return result;
2470
+ }
2383
2471
  function unwrapSkill(result) {
2384
2472
  if (result && typeof result === "object" && "skill" in result) {
2385
2473
  return result.skill;
@@ -1 +1 @@
1
- 479aa815e56005b54f89791ddaac4e41b564ffc377e3077deb11258dba46aaf5 cli.mjs
1
+ cbf31722f64f1ca909287456236e1e689291516e227dcdd69935300bbc0994c9 cli.mjs
package/dist/client.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { HttpClient, type AexEvent, type AgentsMdRecord, type CredentialMode, type DebugSink, type FetchLike, type FileRecord, type Output, type OutputMode, type PlatformEnvironmentInput, type PlatformSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type PlatformPostHookInput, type Run, type RunModel, type RunEvent, type RunProvider, type RunUnit, type Builtin, type RuntimeSize, type RuntimeKind, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_contracts/index.js";
1
+ import { HttpClient, SecretString, type AexEvent, type AgentsMdRecord, type CredentialMode, type DebugSink, type FetchLike, type FileRecord, type Output, type OutputMode, type PlatformEnvironmentInput, type PlatformSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type PlatformPostHookInput, type Run, type RunModel, type RunEvent, type RunProvider, type SecretRecord, type SecretReveal, type RunUnit, type Builtin, type RuntimeSize, type RuntimeKind, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_contracts/index.js";
2
2
  import { AgentsMd } from "./agents-md.js";
3
3
  import { type UploadedAsset } from "./asset-upload.js";
4
4
  import { File } from "./file.js";
5
5
  import { McpServer } from "./mcp-server.js";
6
6
  import { ProxyEndpoint } from "./proxy-endpoint.js";
7
+ import { Secret } from "./secret.js";
7
8
  import { Skill } from "./skill.js";
8
9
  export interface AgentExecutorOptions {
9
10
  /** Workspace-scoped SDK API token. */
@@ -85,6 +86,16 @@ export interface SubmitOptions {
85
86
  readonly agentsMd?: readonly AgentsMd[];
86
87
  readonly files?: readonly File[];
87
88
  readonly mcpServers?: readonly McpServer[];
89
+ /**
90
+ * Env-var secrets, keyed by env name. Each value is a {@link Secret}:
91
+ * `Secret.value(v)` (ephemeral per-run — vaulted at submit, deleted at the
92
+ * run's terminal) or `Secret.ref(handle)` (a persisted workspace secret,
93
+ * resolved server-side). The SDK splits these into value-free declarations on
94
+ * the hashed submission and ephemeral values into the vaulted secrets channel,
95
+ * so a value never enters the run snapshot or the idempotency hash. The
96
+ * runtime injects each as the named env var.
97
+ */
98
+ readonly secretEnv?: Readonly<Record<string, Secret>>;
88
99
  readonly environment?: PlatformEnvironmentInput;
89
100
  readonly metadata?: PlatformSubmission["metadata"];
90
101
  /**
@@ -148,6 +159,14 @@ export interface SubmitOptions {
148
159
  readonly outputMode?: OutputMode;
149
160
  readonly secrets: PlatformInlineSecrets;
150
161
  readonly idempotencyKey?: string;
162
+ /**
163
+ * Lineage parent (agent-session §9). When set, the server admits this run as
164
+ * a CHILD of `parentRunId` (same workspace required), enforcing the
165
+ * max-subagent-depth + per-root concurrency caps and persisting the lineage.
166
+ * The depth is always derived server-side from the parent row — clients name
167
+ * the parent, never the depth.
168
+ */
169
+ readonly parentRunId?: string;
151
170
  readonly signal?: AbortSignal;
152
171
  }
153
172
  /** @deprecated Renamed to {@link SubmitOptions}. Kept for one release. */
@@ -287,6 +306,52 @@ export declare class FilesClient {
287
306
  readonly bytes: Uint8Array;
288
307
  }): Promise<FileRecord>;
289
308
  }
309
+ /**
310
+ * Workspace secret management exposed under `client.secrets`, mirroring
311
+ * `client.skills` / `client.files`.
312
+ *
313
+ * Lifecycle parity with assets/skills: a `Secret.value(...)` is per-run and
314
+ * gone at terminal; `set` (or promoting an ephemeral via `secret.upload`)
315
+ * persists a named, searchable workspace secret you can `get` (metadata),
316
+ * `reveal` (audited value), `rotate`, `list`, and `delete`. The identity is the
317
+ * `name`; the value rotates under that stable name.
318
+ *
319
+ * Values are write-only: `set`/`rotate` send the value in the request BODY (never
320
+ * the URL); `get`/`list` return metadata only; `reveal` is the explicit audited
321
+ * value read.
322
+ */
323
+ export declare class SecretsClient {
324
+ #private;
325
+ constructor(http: HttpClient);
326
+ /** Create a named workspace secret. Accepts a raw string or a `SecretString`. */
327
+ set(args: {
328
+ readonly name: string;
329
+ readonly value: string | SecretString;
330
+ }): Promise<SecretRecord>;
331
+ /** List workspace secret metadata (searchable by name). Never returns values. */
332
+ list(): Promise<readonly SecretRecord[]>;
333
+ /** Metadata for one workspace secret by name. Never returns the value. */
334
+ get(name: string): Promise<SecretRecord>;
335
+ /** Audited value read — the only path that returns a workspace secret value. */
336
+ reveal(name: string): Promise<SecretReveal>;
337
+ /** Replace the value of an existing workspace secret; bumps its version. */
338
+ rotate(args: {
339
+ readonly name: string;
340
+ readonly value: string | SecretString;
341
+ }): Promise<SecretRecord>;
342
+ delete(name: string): Promise<void>;
343
+ /**
344
+ * Internal: create a workspace secret from an ephemeral `Secret.value(...)`
345
+ * being promoted via `secret.upload(client, { name })`. NOT part of the
346
+ * public API — callers use `set`.
347
+ */
348
+ _createWorkspaceSecret(args: {
349
+ readonly name: string;
350
+ readonly value: string;
351
+ }): Promise<{
352
+ readonly name: string;
353
+ }>;
354
+ }
290
355
  /**
291
356
  * Unified user-facing client for the aex platform. The same class
292
357
  * powers the published `@aexhq/sdk` SDK and (under the hood) every host-side
@@ -303,6 +368,7 @@ export declare class AgentExecutor {
303
368
  readonly skills: SkillsClient;
304
369
  readonly agentsMd: AgentsMdClient;
305
370
  readonly files: FilesClient;
371
+ readonly secrets: SecretsClient;
306
372
  constructor(options: AgentExecutorOptions);
307
373
  /**
308
374
  * Internal: forwards to `SkillsClient._uploadSkillBundle`. NOT part of
@@ -338,6 +404,18 @@ export declare class AgentExecutor {
338
404
  readonly name: string;
339
405
  readonly bytes: Uint8Array;
340
406
  }): Promise<FileRecord>;
407
+ /**
408
+ * Internal: satisfies the `SecretUploader` surface so a
409
+ * `Secret.value(...).upload(client, { name })` promotes an ephemeral secret
410
+ * into the workspace store. Forwarded to `SecretsClient._createWorkspaceSecret`.
411
+ * NOT part of the public API.
412
+ */
413
+ _createWorkspaceSecret(args: {
414
+ readonly name: string;
415
+ readonly value: string;
416
+ }): Promise<{
417
+ readonly name: string;
418
+ }>;
341
419
  /**
342
420
  * Internal: materialize raw bytes to the content-addressable asset store
343
421
  * (`/assets/presign` → PUT → `/assets/finalize`). Used by `Skill.upload(this)`
package/dist/client.js CHANGED
@@ -1,9 +1,10 @@
1
- import { AexError, DEFAULT_CREDENTIAL_MODE, DEFAULT_RUN_PROVIDER, HttpClient, RUNTIME_KINDS, RunStateError, isRunSettled, operations, parseCredentialMode, providersForModel, streamCoordinatorEvents, TERMINAL_RUN_STATUSES } from "./_contracts/index.js";
1
+ import { AexError, DEFAULT_CREDENTIAL_MODE, DEFAULT_RUN_PROVIDER, HttpClient, RUNTIME_KINDS, RunStateError, SecretString, isRunSettled, operations, parseCredentialMode, providersForModel, streamCoordinatorEvents, 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
6
  import { splitProxyEndpoints } from "./proxy-endpoint.js";
7
+ import { splitSecretEnv } from "./secret.js";
7
8
  import { Skill } from "./skill.js";
8
9
  /**
9
10
  * Workspace skill admin operations exposed under `client.skills`.
@@ -128,6 +129,66 @@ export class FilesClient {
128
129
  return operations.createFile(this.#http, args);
129
130
  }
130
131
  }
132
+ /**
133
+ * Workspace secret management exposed under `client.secrets`, mirroring
134
+ * `client.skills` / `client.files`.
135
+ *
136
+ * Lifecycle parity with assets/skills: a `Secret.value(...)` is per-run and
137
+ * gone at terminal; `set` (or promoting an ephemeral via `secret.upload`)
138
+ * persists a named, searchable workspace secret you can `get` (metadata),
139
+ * `reveal` (audited value), `rotate`, `list`, and `delete`. The identity is the
140
+ * `name`; the value rotates under that stable name.
141
+ *
142
+ * Values are write-only: `set`/`rotate` send the value in the request BODY (never
143
+ * the URL); `get`/`list` return metadata only; `reveal` is the explicit audited
144
+ * value read.
145
+ */
146
+ export class SecretsClient {
147
+ #http;
148
+ constructor(http) {
149
+ this.#http = http;
150
+ }
151
+ /** Create a named workspace secret. Accepts a raw string or a `SecretString`. */
152
+ set(args) {
153
+ return operations.createSecret(this.#http, { name: args.name, value: unwrapSecretValue(args.value) });
154
+ }
155
+ /** List workspace secret metadata (searchable by name). Never returns values. */
156
+ list() {
157
+ return operations.listSecrets(this.#http);
158
+ }
159
+ /** Metadata for one workspace secret by name. Never returns the value. */
160
+ get(name) {
161
+ return operations.getSecret(this.#http, name);
162
+ }
163
+ /** Audited value read — the only path that returns a workspace secret value. */
164
+ reveal(name) {
165
+ return operations.revealSecret(this.#http, name);
166
+ }
167
+ /** Replace the value of an existing workspace secret; bumps its version. */
168
+ rotate(args) {
169
+ return operations.rotateSecret(this.#http, { name: args.name, value: unwrapSecretValue(args.value) });
170
+ }
171
+ delete(name) {
172
+ return operations.deleteSecret(this.#http, name);
173
+ }
174
+ /**
175
+ * Internal: create a workspace secret from an ephemeral `Secret.value(...)`
176
+ * being promoted via `secret.upload(client, { name })`. NOT part of the
177
+ * public API — callers use `set`.
178
+ */
179
+ async _createWorkspaceSecret(args) {
180
+ const record = await operations.createSecret(this.#http, args);
181
+ return { name: record.name };
182
+ }
183
+ }
184
+ /** Accept a raw string or a `SecretString`; return the raw value for the wire. */
185
+ function unwrapSecretValue(value) {
186
+ const raw = value instanceof SecretString ? value.unwrap() : value;
187
+ if (typeof raw !== "string" || !raw) {
188
+ throw new Error("secrets: value must be a non-empty string");
189
+ }
190
+ return raw;
191
+ }
131
192
  /**
132
193
  * Unified user-facing client for the aex platform. The same class
133
194
  * powers the published `@aexhq/sdk` SDK and (under the hood) every host-side
@@ -146,6 +207,7 @@ export class AgentExecutor {
146
207
  skills;
147
208
  agentsMd;
148
209
  files;
210
+ secrets;
149
211
  constructor(options) {
150
212
  if (!options.apiToken) {
151
213
  throw new Error("AgentExecutor: apiToken is required");
@@ -165,6 +227,7 @@ export class AgentExecutor {
165
227
  this.skills = new SkillsClient(this.#http);
166
228
  this.agentsMd = new AgentsMdClient(this.#http);
167
229
  this.files = new FilesClient(this.#http);
230
+ this.secrets = new SecretsClient(this.#http);
168
231
  }
169
232
  /**
170
233
  * Internal: forwards to `SkillsClient._uploadSkillBundle`. NOT part of
@@ -197,6 +260,15 @@ export class AgentExecutor {
197
260
  async _uploadFile(args) {
198
261
  return this.files._uploadFile(args);
199
262
  }
263
+ /**
264
+ * Internal: satisfies the `SecretUploader` surface so a
265
+ * `Secret.value(...).upload(client, { name })` promotes an ephemeral secret
266
+ * into the workspace store. Forwarded to `SecretsClient._createWorkspaceSecret`.
267
+ * NOT part of the public API.
268
+ */
269
+ async _createWorkspaceSecret(args) {
270
+ return this.secrets._createWorkspaceSecret(args);
271
+ }
200
272
  /**
201
273
  * Internal: materialize raw bytes to the content-addressable asset store
202
274
  * (`/assets/presign` → PUT → `/assets/finalize`). Used by `Skill.upload(this)`
@@ -277,6 +349,9 @@ export class AgentExecutor {
277
349
  const prompt = normalisePrompt(options.prompt);
278
350
  const { endpoints: proxyEndpointDeclarations, auth: proxyEndpointAuthFromInstances } = splitProxyEndpoints(options.proxyEndpoints ?? []);
279
351
  const mergedProxyAuth = mergeProxyEndpointAuth(proxyEndpointAuthFromInstances, options.secrets.proxyEndpointAuth ?? []);
352
+ // Split secretEnv into value-free declarations (hashed submission) and
353
+ // ephemeral values (vaulted secrets channel), mirroring the proxy split.
354
+ const { declarations: secretEnvDeclarations, values: envSecretValues } = splitSecretEnv(options.secretEnv);
280
355
  // Validate the runtime selector before any network I/O — inline drafts
281
356
  // are uploaded below, so an invalid runtime must reject first rather than
282
357
  // leak an asset upload.
@@ -309,6 +384,7 @@ export class AgentExecutor {
309
384
  // shape matches McpServerRef. The cast acknowledges that the
310
385
  // SDK is producing pre-resolution wire input here.
311
386
  mcpServers: submissionMcpServers,
387
+ ...(Object.keys(secretEnvDeclarations).length > 0 ? { secretEnv: secretEnvDeclarations } : {}),
312
388
  // `options.environment.packages` carry the customer wire shape
313
389
  // (`{name:"pip:pandas"}`); the shared parser resolves the ecosystem
314
390
  // prefix into PlatformPackage. The cast acknowledges the SDK is
@@ -330,7 +406,8 @@ export class AgentExecutor {
330
406
  const secrets = {
331
407
  ...options.secrets,
332
408
  ...(mergedMcpSecrets.length > 0 ? { mcpServers: mergedMcpSecrets } : {}),
333
- ...(mergedProxyAuth.length > 0 ? { proxyEndpointAuth: mergedProxyAuth } : {})
409
+ ...(mergedProxyAuth.length > 0 ? { proxyEndpointAuth: mergedProxyAuth } : {}),
410
+ ...(Object.keys(envSecretValues).length > 0 ? { envSecrets: envSecretValues } : {})
334
411
  };
335
412
  const postHook = postHookForWire(options.postHook);
336
413
  const request = {
@@ -349,6 +426,7 @@ export class AgentExecutor {
349
426
  ...(options.runtimeSize ? { runtimeSize: options.runtimeSize } : {}),
350
427
  ...(options.timeout ? { timeout: options.timeout } : {}),
351
428
  ...(postHook ? { postHook } : {}),
429
+ ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),
352
430
  secrets,
353
431
  ...(proxyEndpointDeclarations.length > 0
354
432
  ? { proxyEndpoints: proxyEndpointDeclarations }