@aexhq/sdk 0.40.16 → 0.41.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 (130) hide show
  1. package/README.md +25 -25
  2. package/dist/_contracts/api-key.d.ts +6 -4
  3. package/dist/_contracts/api-key.js +20 -7
  4. package/dist/_contracts/connection-ticket.d.ts +5 -5
  5. package/dist/_contracts/connection-ticket.js +7 -7
  6. package/dist/_contracts/error-codes.d.ts +1 -1
  7. package/dist/_contracts/error-codes.js +4 -4
  8. package/dist/_contracts/error-factory.js +1 -1
  9. package/dist/_contracts/event-envelope.d.ts +41 -41
  10. package/dist/_contracts/event-envelope.js +49 -49
  11. package/dist/_contracts/event-stream-client.d.ts +9 -9
  12. package/dist/_contracts/event-stream-client.js +5 -5
  13. package/dist/_contracts/event-view.d.ts +6 -6
  14. package/dist/_contracts/event-view.js +12 -12
  15. package/dist/_contracts/http.js +1 -1
  16. package/dist/_contracts/index.d.ts +10 -10
  17. package/dist/_contracts/index.js +9 -9
  18. package/dist/_contracts/models.d.ts +17 -17
  19. package/dist/_contracts/models.js +14 -14
  20. package/dist/_contracts/operations.d.ts +79 -82
  21. package/dist/_contracts/operations.js +233 -243
  22. package/dist/_contracts/provider-support.d.ts +2 -2
  23. package/dist/_contracts/runner-event.d.ts +8 -8
  24. package/dist/_contracts/runner-event.js +9 -9
  25. package/dist/_contracts/runtime-manifest.d.ts +12 -12
  26. package/dist/_contracts/runtime-manifest.js +1 -1
  27. package/dist/_contracts/runtime-sizes.d.ts +12 -12
  28. package/dist/_contracts/runtime-sizes.js +18 -18
  29. package/dist/_contracts/runtime-types.d.ts +117 -117
  30. package/dist/_contracts/runtime-types.js +2 -2
  31. package/dist/_contracts/sdk-errors.d.ts +3 -3
  32. package/dist/_contracts/sdk-errors.js +4 -4
  33. package/dist/_contracts/sdk-secrets.js +10 -10
  34. package/dist/_contracts/session-artifacts.d.ts +151 -0
  35. package/dist/_contracts/session-artifacts.js +234 -0
  36. package/dist/_contracts/{run-config.d.ts → session-config.d.ts} +29 -29
  37. package/dist/_contracts/{run-config.js → session-config.js} +37 -37
  38. package/dist/_contracts/session-cost.d.ts +140 -0
  39. package/dist/_contracts/{run-cost.js → session-cost.js} +94 -94
  40. package/dist/_contracts/{run-custody.d.ts → session-custody.d.ts} +16 -16
  41. package/dist/_contracts/{run-custody.js → session-custody.js} +42 -42
  42. package/dist/_contracts/session-record.d.ts +112 -0
  43. package/dist/_contracts/{run-record.js → session-record.js} +23 -23
  44. package/dist/_contracts/session-retention.d.ts +209 -0
  45. package/dist/_contracts/{run-retention.js → session-retention.js} +116 -116
  46. package/dist/_contracts/{run-unit.d.ts → session-unit.d.ts} +38 -38
  47. package/dist/_contracts/{run-unit.js → session-unit.js} +33 -33
  48. package/dist/_contracts/side-effect-audit.d.ts +14 -14
  49. package/dist/_contracts/side-effect-audit.js +38 -38
  50. package/dist/_contracts/sse.d.ts +1 -1
  51. package/dist/_contracts/sse.js +0 -0
  52. package/dist/_contracts/stable.d.ts +1 -1
  53. package/dist/_contracts/stable.js +1 -1
  54. package/dist/_contracts/status.d.ts +18 -18
  55. package/dist/_contracts/status.js +19 -19
  56. package/dist/_contracts/submission.d.ts +101 -99
  57. package/dist/_contracts/submission.js +96 -96
  58. package/dist/_contracts/{run-trace.d.ts → turn-trace.d.ts} +1 -1
  59. package/dist/_contracts/webhook-verify.d.ts +1 -1
  60. package/dist/_contracts/webhook-verify.js +1 -1
  61. package/dist/agents-md.d.ts +4 -4
  62. package/dist/agents-md.js +5 -5
  63. package/dist/agents-md.js.map +1 -1
  64. package/dist/bundle.d.ts +1 -1
  65. package/dist/bundle.js +1 -1
  66. package/dist/bundle.js.map +1 -1
  67. package/dist/cli.mjs +578 -621
  68. package/dist/cli.mjs.sha256 +1 -1
  69. package/dist/client.d.ts +141 -155
  70. package/dist/client.js +236 -245
  71. package/dist/client.js.map +1 -1
  72. package/dist/fetch-archive.js +3 -3
  73. package/dist/file.d.ts +7 -7
  74. package/dist/file.js +8 -8
  75. package/dist/file.js.map +1 -1
  76. package/dist/index.d.ts +10 -10
  77. package/dist/index.js +8 -8
  78. package/dist/index.js.map +1 -1
  79. package/dist/mcp-server.d.ts +1 -1
  80. package/dist/retry.d.ts +1 -1
  81. package/dist/secret.d.ts +6 -6
  82. package/dist/secret.js +6 -6
  83. package/dist/secret.js.map +1 -1
  84. package/dist/skill.d.ts +2 -2
  85. package/dist/skill.js +3 -3
  86. package/dist/skill.js.map +1 -1
  87. package/dist/tool.js +2 -2
  88. package/dist/tool.js.map +1 -1
  89. package/dist/version.d.ts +1 -1
  90. package/dist/version.js +1 -1
  91. package/dist/version.js.map +1 -1
  92. package/docs/authentication.md +11 -11
  93. package/docs/billing.md +3 -3
  94. package/docs/cleanup.md +5 -5
  95. package/docs/concepts/agent-tools.md +5 -5
  96. package/docs/concepts/composition.md +3 -3
  97. package/docs/concepts/providers-and-runtimes.md +3 -3
  98. package/docs/concepts/{runs.md → sessions.md} +8 -8
  99. package/docs/concepts/subagents.md +16 -16
  100. package/docs/credentials.md +7 -7
  101. package/docs/defaults.md +14 -14
  102. package/docs/errors.md +10 -10
  103. package/docs/events.md +26 -26
  104. package/docs/files.md +267 -0
  105. package/docs/limits-and-quotas.md +35 -35
  106. package/docs/limits.md +6 -6
  107. package/docs/mcp.md +3 -3
  108. package/docs/networking.md +24 -24
  109. package/docs/provider-runtime-capabilities.md +1 -1
  110. package/docs/public-surface.json +6 -6
  111. package/docs/quickstart.md +13 -13
  112. package/docs/release.md +5 -5
  113. package/docs/retries.md +3 -3
  114. package/docs/secrets.md +16 -16
  115. package/docs/session-config.md +58 -0
  116. package/docs/{run-record.md → session-record.md} +16 -16
  117. package/docs/skills.md +9 -9
  118. package/docs/vision-skills.md +7 -7
  119. package/docs/webhooks.md +11 -11
  120. package/examples/feature-tour.ts +17 -17
  121. package/examples/spike-settle-latency.ts +11 -11
  122. package/package.json +1 -1
  123. package/dist/_contracts/run-artifacts.d.ts +0 -49
  124. package/dist/_contracts/run-artifacts.js +0 -102
  125. package/dist/_contracts/run-cost.d.ts +0 -140
  126. package/dist/_contracts/run-record.d.ts +0 -116
  127. package/dist/_contracts/run-retention.d.ts +0 -209
  128. package/docs/outputs.md +0 -267
  129. package/docs/run-config.md +0 -58
  130. /package/dist/_contracts/{run-trace.js → turn-trace.js} +0 -0
package/dist/client.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AexApiError, CredentialValidationError, HttpClient, PLANE_BASE_URLS, RunConfigValidationError, RunStateError, SecretString, asAexEventView, customName, isSessionParked as isSessionParkedEvent, isRunSettled, assertStreamableOutputMode, operations, parseApiKey, resolveModelProvider, streamCoordinatorEvents, usageFromProviderUsage, parseRunLimits, parseRuntimeSize, parseRunTimeout, parseRunWebhook, BUILTIN_TOOL_NAMES, SKILLS_MAX, TERMINAL_RUN_STATUSES } from "./_contracts/index.js";
1
+ import { AexApiError, CredentialValidationError, HttpClient, PLANE_BASE_URLS, SessionConfigValidationError, SessionStateError, SecretString, asAexEventView, customName, isSessionParked as isSessionParkedEvent, isSessionSettled, assertStreamableOutputMode, operations, parseApiKey, resolveModelProvider, streamCoordinatorEvents, usageFromProviderUsage, parseSessionLimits, parseRuntimeSize, parseSessionTimeout, parseSessionWebhook, BUILTIN_TOOL_NAMES, SKILLS_MAX, TERMINAL_SESSION_CONTROL_STATUSES } from "./_contracts/index.js";
2
2
  import { AgentsMd } from "./agents-md.js";
3
3
  import { uploadAsset, uploadAssetMultipart } from "./asset-upload.js";
4
4
  import { File } from "./file.js";
@@ -8,12 +8,12 @@ import { splitSecretEnv } from "./secret.js";
8
8
  import { Skill } from "./skill.js";
9
9
  import { Tool } from "./tool.js";
10
10
  export class SessionTurnStream {
11
- #run;
11
+ #stream;
12
12
  #generator;
13
13
  #outcome;
14
14
  #done;
15
- constructor(run) {
16
- this.#run = run;
15
+ constructor(stream) {
16
+ this.#stream = stream;
17
17
  }
18
18
  /**
19
19
  * ONE underlying send per turn: iterating the stream and calling `done()`
@@ -22,7 +22,7 @@ export class SessionTurnStream {
22
22
  * message as a second billable turn (or 409 `session_busy` mid-turn).
23
23
  */
24
24
  #shared() {
25
- this.#generator ??= this.#capture(this.#run());
25
+ this.#generator ??= this.#capture(this.#stream());
26
26
  return this.#generator;
27
27
  }
28
28
  async *#capture(generator) {
@@ -114,7 +114,7 @@ export class SessionHandle {
114
114
  assertNoSessionSendSignal(options, "SessionHandle.replayLast");
115
115
  const last = this.#lastSend;
116
116
  if (last === undefined) {
117
- throw new RunStateError("SessionHandle.replayLast: no message has been sent on this session yet");
117
+ throw new SessionStateError("SessionHandle.replayLast: no message has been sent on this session yet");
118
118
  }
119
119
  return sendSessionInternal(this, last.input, {
120
120
  ...options,
@@ -144,9 +144,9 @@ export class SessionHandle {
144
144
  ? readSession
145
145
  : (await settledSessionRecord(this.#http, this.id, readSession, options.signal)) ?? readSession;
146
146
  this.#session = withTerminalSessionStatus(settled, read);
147
- const outputs = await operations.listSessionOutputs(this.#http, this.id).catch(() => []);
147
+ const files = await operations.listSessionFiles(this.#http, this.id).catch(() => []);
148
148
  const messages = projectAssistantMessages(events);
149
- return settledTurnResult(this.id, this.#session, turn, events, outputs, messages, read);
149
+ return settledTurnResult(this.id, this.#session, turn, events, files, messages, read);
150
150
  }
151
151
  /**
152
152
  * POST the next turn, reconciling the settle-lag race. A turn stream ends on
@@ -157,7 +157,7 @@ export class SessionHandle {
157
157
  * `running`, then retry the SAME idempotent POST (a replay de-duplicates, so
158
158
  * this never creates a second billable turn). A session that stays busy past
159
159
  * {@link SESSION_BUSY_RECONCILE_DEADLINE_MS} is a genuinely in-flight turn: we
160
- * surface a clear, bounded {@link RunStateError} rather than masking it or
160
+ * surface a clear, bounded {@link SessionStateError} rather than masking it or
161
161
  * waiting forever. Any non-`running` busy status (suspended / cancelling /
162
162
  * deleted) is a real rejection and passes straight through.
163
163
  */
@@ -172,7 +172,7 @@ export class SessionHandle {
172
172
  throw err;
173
173
  if (Date.now() >= deadline) {
174
174
  const apiErr = err instanceof AexApiError ? err : undefined;
175
- throw new RunStateError(`SessionHandle.send: session ${this.id} was still running ${SESSION_BUSY_RECONCILE_DEADLINE_MS}ms after the previous turn parked — a turn is still in flight`, {
175
+ throw new SessionStateError(`SessionHandle.send: session ${this.id} was still running ${SESSION_BUSY_RECONCILE_DEADLINE_MS}ms after the previous turn parked — a turn is still in flight`, {
176
176
  sessionId: this.id,
177
177
  sessionStatus: "running",
178
178
  ...(apiErr !== undefined ? { httpStatus: apiErr.status, apiCode: apiErr.apiCode, requestId: apiErr.requestId } : {}),
@@ -251,14 +251,14 @@ export class SessionHandle {
251
251
  return accepted;
252
252
  }
253
253
  /**
254
- * Enumerate this run's subagent CHILD runs (`GET /runs/:id/children`). Each is
255
- * a {@link ChildRunHandle} backed by the RUN facade (getRun/events/outputs) —
254
+ * Enumerate this session's subagent CHILD sessions (`GET /sessions/:id/children`). Each is
255
+ * a {@link ChildSessionHandle} backed by the RUN facade (getSessionRecord/events/files) —
256
256
  * NOT `openSession` — so every child the platform hands you is resolvable, with
257
- * its lineage (`parentRunId`/`depth`) and terminal outcome exposed.
257
+ * its lineage (`parentSessionId`/`depth`) and terminal outcome exposed.
258
258
  */
259
259
  async children() {
260
- const refs = await operations.listRunChildren(this.#http, this.id);
261
- return refs.map((ref) => new ChildRunHandle(this.#http, ref, this.#fetch));
260
+ const refs = await operations.listSessionChildren(this.#http, this.id);
261
+ return refs.map((ref) => new ChildSessionHandle(this.#http, ref, this.#fetch));
262
262
  }
263
263
  /**
264
264
  * Accessor for the session's assistant messages. `all()` returns them
@@ -316,13 +316,13 @@ export class SessionHandle {
316
316
  };
317
317
  }
318
318
  /**
319
- * Accessor for the session's captured output files: `list`/`last`/`first`
319
+ * Accessor for the session's captured files: `list`/`last`/`first`
320
320
  * enumerate them; `read` streams one as capped text; `find`/`findOne`/`link`/
321
- * `fetch` locate and resolve them; `download` fetches the outputs-namespace
321
+ * `fetch` locate and resolve them; `download` fetches the files-namespace
322
322
  * zip (no selector) or one file's raw bytes (with selector).
323
323
  */
324
- outputs() {
325
- return sessionOutputs(this.#http, this.id, this.#fetch);
324
+ files() {
325
+ return sessionFiles(this.#http, this.id, this.#fetch);
326
326
  }
327
327
  /**
328
328
  * Accessor for the session's webhook delivery ledger: `list()` returns the
@@ -333,8 +333,8 @@ export class SessionHandle {
333
333
  const http = this.#http;
334
334
  const id = this.id;
335
335
  return {
336
- list: () => operations.getRunWebhookDeliveries(http, id),
337
- redeliver: (deliveryId) => operations.redeliverRunWebhook(http, id, deliveryId)
336
+ list: () => operations.getSessionWebhookDeliveries(http, id),
337
+ redeliver: (deliveryId) => operations.redeliverSessionWebhook(http, id, deliveryId)
338
338
  };
339
339
  }
340
340
  /** Re-read the session record from the server and store it as the current record. */
@@ -344,7 +344,7 @@ export class SessionHandle {
344
344
  }
345
345
  /**
346
346
  * Poll the session record until it reaches a parked/terminal status (idle,
347
- * suspended, error, or any terminal run status). Throws if `timeoutMs`
347
+ * suspended, error, or any terminal session status). Throws if `timeoutMs`
348
348
  * elapses first. Updates the stored record.
349
349
  */
350
350
  async wait(options = {}) {
@@ -365,23 +365,23 @@ export class SessionHandle {
365
365
  throw new Error("SessionHandle.wait: aborted");
366
366
  }
367
367
  /**
368
- * Fetch the self-contained `RunUnit` for this session: parsed submission,
369
- * attempts, indexed events, outputs, capture failures, proxy-call audit, and
368
+ * Fetch the self-contained `SessionUnit` for this session: parsed submission,
369
+ * attempts, indexed events, session files, capture failures, proxy-call audit, and
370
370
  * resolved skills. Use this when you need fields beyond the session record.
371
371
  *
372
372
  * On the managed plane this is a LEAN summary — the aggregate collections
373
- * (`attempts` / `events.entries` / `outputs` / `rawEventPages`) default to
374
- * empty. For authoritative per-run data use `outputs()` / `events()` /
373
+ * (`attempts` / `events.entries` / `sessionFiles` / `rawEventPages`) default to
374
+ * empty. For authoritative per-session data use `files()` / `events()` /
375
375
  * `messages()`. The returned shape is always type-valid (never `undefined`
376
376
  * where the type promises an array/page), so array/page access is safe.
377
377
  */
378
378
  unit() {
379
- return operations.getRunUnit(this.#http, this.id);
379
+ return operations.getSessionUnit(this.#http, this.id);
380
380
  }
381
381
  /**
382
382
  * Download EVERYTHING public about this session as one zip, assembled
383
383
  * client-side from the public read endpoints. Organised into `metadata/`,
384
- * `events/`, and `outputs/` folders, plus a `manifest.json`. Pass `to` to
384
+ * `events/`, and `files/` folders, plus a `manifest.json`. Pass `to` to
385
385
  * also write the bytes to a file path while still returning them.
386
386
  */
387
387
  async download(options) {
@@ -419,22 +419,22 @@ export class SessionClient {
419
419
  return operations.listSessions(this.#http, query);
420
420
  }
421
421
  /**
422
- * Accessor over one session's captured output files, addressed by id without
423
- * opening a handle. Returns the SAME rich {@link SessionOutputs} surface as
424
- * `session.outputs()` — `aex.sessions.outputs(id).list()` /
422
+ * Accessor over one session's captured files, addressed by id without
423
+ * opening a handle. Returns the SAME rich {@link SessionFiles} surface as
424
+ * `session.files()` — `aex.sessions.files(id).list()` /
425
425
  * `.read(selector)` / `.download()` / … — so the workspace client and the
426
426
  * live handle share one accessor convention.
427
427
  */
428
- outputs(sessionId) {
429
- return sessionOutputs(this.#http, sessionId, this.#fetch);
428
+ files(sessionId) {
429
+ return sessionFiles(this.#http, sessionId, this.#fetch);
430
430
  }
431
- async run(options) {
431
+ async start(options) {
432
432
  const { message, deleteAfter, messageIdempotencyKey, stream, ...createOptions } = options;
433
- assertNoLegacySessionFields(options, "Aex.sessions.run");
434
- const input = normaliseSessionInput(message, "Aex.sessions.run", "message");
435
- // Derive the message key from the create key (like the CLI) so a retried run
433
+ assertNoLegacySessionFields(options, "Aex.sessions.start");
434
+ const input = normaliseSessionInput(message, "Aex.sessions.start", "message");
435
+ // Derive the message key from the create key (like the CLI) so a retried session
436
436
  // with the same `idempotencyKey` de-duplicates BOTH the create and the
437
- // billable turn — never a duplicate billable run.
437
+ // billable turn — never a duplicate billable session turn.
438
438
  const createKey = operations.resolveIdempotencyKey(createOptions.idempotencyKey);
439
439
  const messageKey = messageIdempotencyKey !== undefined ? operations.resolveIdempotencyKey(messageIdempotencyKey) : deriveMessageKey(createKey);
440
440
  const session = await this.create({ ...createOptions, idempotencyKey: createKey });
@@ -449,38 +449,38 @@ export class SessionClient {
449
449
  }
450
450
  }
451
451
  /**
452
- * Cross-run output search (`aex.outputs`). Composed client-side (per-run
453
- * `listSessionOutputs` + the contracts output filter): scope a corpus with
454
- * `query.runIds`, or omit it to scan every run in the workspace. Metadata-only
452
+ * Cross-session captured-file search (`aex.files.search`). Composed client-side (per-session
453
+ * `listSessionFiles` + the contracts file filter): scope a corpus with
454
+ * `query.sessionIds`, or omit it to scan every session in the workspace. Metadata-only
455
455
  * (reference hits, no bytes); a content-shaped query throws a typed
456
456
  * "content search unsupported".
457
457
  */
458
- export class OutputsClient {
458
+ class SessionFilesSearchClient {
459
459
  #http;
460
460
  constructor(http) {
461
461
  this.#http = http;
462
462
  }
463
463
  async search(query = {}) {
464
- assertMetadataOnlyOutputSearch(query, "aex.outputs.search");
465
- // Dedup the caller-supplied allow-list so a run repeated in `runIds` (from
464
+ assertMetadataOnlyFileSearch(query, "aex.files.search");
465
+ // Dedup the caller-supplied allow-list so a session repeated in `sessionIds` (from
466
466
  // concatenating corpora) isn't scanned twice and doesn't inflate hit count.
467
- const unscoped = query.runIds === undefined;
467
+ const unscoped = query.sessionIds === undefined;
468
468
  const limit = query.limit ?? 100;
469
469
  if (unscoped)
470
470
  return this.#searchWorkspace(query, limit);
471
- const runIds = [...new Set(query.runIds)];
471
+ const sessionIds = [...new Set(query.sessionIds)];
472
472
  const hits = [];
473
- for (const runId of runIds) {
474
- let outputs;
473
+ for (const sessionId of sessionIds) {
474
+ let files;
475
475
  try {
476
- outputs = await searchRunOutputs(this.#http, runId, query);
476
+ files = await searchSessionFiles(this.#http, sessionId, query);
477
477
  }
478
478
  catch (err) {
479
- if (unscoped && isMissingOutputsSession(err))
479
+ if (unscoped && isMissingFilesSession(err))
480
480
  continue;
481
481
  throw err;
482
482
  }
483
- for (const hit of outputHits(runId, outputs, limit - hits.length)) {
483
+ for (const hit of fileHits(sessionId, files, limit - hits.length)) {
484
484
  hits.push(hit);
485
485
  if (hits.length >= limit)
486
486
  return { hits };
@@ -496,7 +496,7 @@ export class OutputsClient {
496
496
  do {
497
497
  if (cursor !== undefined) {
498
498
  if (seenCursors.has(cursor)) {
499
- throw new Error("aex.outputs.search: listSessions returned a repeated cursor");
499
+ throw new Error("aex.files.search: listSessions returned a repeated cursor");
500
500
  }
501
501
  seenCursors.add(cursor);
502
502
  }
@@ -505,16 +505,16 @@ export class OutputsClient {
505
505
  ...(cursor ? { cursor } : {})
506
506
  });
507
507
  for (const session of page.sessions) {
508
- let outputs;
508
+ let files;
509
509
  try {
510
- outputs = await searchRunOutputs(this.#http, session.id, query);
510
+ files = await searchSessionFiles(this.#http, session.id, query);
511
511
  }
512
512
  catch (err) {
513
- if (isMissingOutputsSession(err))
513
+ if (isMissingFilesSession(err))
514
514
  continue;
515
515
  throw err;
516
516
  }
517
- for (const hit of outputHits(session.id, outputs, limit - hits.length)) {
517
+ for (const hit of fileHits(session.id, files, limit - hits.length)) {
518
518
  hits.push(hit);
519
519
  if (hits.length >= limit)
520
520
  return { hits };
@@ -526,13 +526,13 @@ export class OutputsClient {
526
526
  }
527
527
  }
528
528
  /**
529
- * A first-class, lineage-discoverable SUBAGENT CHILD run — handed out by
530
- * `session.children()` / `run.children()`, backed by the RUN facade
531
- * (getRun/events/outputs), NOT `openSession`. Every child the platform hands you
532
- * is resolvable through this handle; its lineage (`parentRunId`/`depth`) and
529
+ * A first-class, lineage-discoverable SUBAGENT child session — handed out by
530
+ * `session.children()`, backed by the session-record facade
531
+ * (getSessionRecord/events/files), NOT `openSession`. Every child the platform hands you
532
+ * is resolvable through this handle; its lineage (`parentSessionId`/`depth`) and
533
533
  * terminal outcome (`status`) are first-class.
534
534
  */
535
- export class ChildRunHandle {
535
+ export class ChildSessionHandle {
536
536
  #http;
537
537
  #fetch;
538
538
  #ref;
@@ -544,81 +544,70 @@ export class ChildRunHandle {
544
544
  get id() {
545
545
  return this.#ref.id;
546
546
  }
547
- get parentRunId() {
548
- return this.#ref.parentRunId;
547
+ get parentSessionId() {
548
+ return this.#ref.parentSessionId;
549
549
  }
550
550
  get depth() {
551
551
  return this.#ref.depth;
552
552
  }
553
- /** The child's run status (a real run terminal outcome once settled). */
553
+ /** The child's session status (a real session terminal outcome once settled). */
554
554
  get status() {
555
555
  return this.#ref.status;
556
556
  }
557
557
  get ref() {
558
558
  return this.#ref;
559
559
  }
560
- /** Re-read the child run record (status, lineage, costTelemetry). */
560
+ /** Re-read the child session record (status, lineage, costTelemetry). */
561
561
  get() {
562
- return operations.getRun(this.#http, this.id);
562
+ return operations.getSessionRecord(this.#http, this.id);
563
563
  }
564
- /** The child's events over the RUN facade (`/runs/:id/events`). */
564
+ /** The child's events over the session-record facade (`/sessions/:id/events`). */
565
565
  events() {
566
- return runEventsAccessor(this.#http, this.id);
566
+ return childSessionEventsAccessor(this.#http, this.id);
567
567
  }
568
- /** The child's captured outputs over the RUN facade (`/runs/:id/outputs`). */
569
- outputs() {
570
- return runOutputsAccessor(this.#http, this.id, this.#fetch);
568
+ /** The child's captured files over the session-record facade (`/sessions/:id/files`). */
569
+ files() {
570
+ return sessionFiles(this.#http, this.id, this.#fetch);
571
571
  }
572
572
  /** This child's own subagent children (recursive lineage). */
573
573
  async children() {
574
- const refs = await operations.listRunChildren(this.#http, this.id);
575
- return refs.map((ref) => new ChildRunHandle(this.#http, ref, this.#fetch));
574
+ const refs = await operations.listSessionChildren(this.#http, this.id);
575
+ return refs.map((ref) => new ChildSessionHandle(this.#http, ref, this.#fetch));
576
576
  }
577
- /** Cancel the child run (run facade `POST /runs/:id/cancel`). */
577
+ /** Cancel the child session (session record facade `POST /sessions/:id/cancel`). */
578
578
  cancel() {
579
- return operations.cancelRun(this.#http, this.id);
579
+ return operations.cancelSession(this.#http, this.id).then(() => undefined);
580
580
  }
581
581
  }
582
- /** Run-facade events accessor (list + polling stream) — used by {@link ChildRunHandle}. */
583
- function runEventsAccessor(http, id) {
582
+ /** SessionRecord-facade events accessor (list + polling stream) — used by {@link ChildSessionHandle}. */
583
+ function childSessionEventsAccessor(http, id) {
584
584
  return {
585
- list: async () => (await operations.listRunEvents(http, id)).map(asAexEventView),
586
- stream: (options) => streamRunEventsPolling(http, id, options ?? {})
587
- };
588
- }
589
- /** Run-facade outputs accessor — used by {@link ChildRunHandle}. */
590
- function runOutputsAccessor(http, id, fetchLike) {
591
- void fetchLike;
592
- return {
593
- list: (query) => operations.listOutputs(http, id, query),
594
- find: (query) => operations.findOutputs(http, id, query),
595
- findOne: (query) => operations.findOutput(http, id, query),
596
- read: (selector, options) => operations.readOutputText(http, id, selector, options),
597
- link: (selectorOrQuery, options) => operations.outputLink(http, id, selectorOrQuery, options),
598
- download: (selector, options) => downloadSessionOutput(http, id, selector, options)
585
+ list: async () => (await operations.listSessionRecordEvents(http, id)).map(asAexEventView),
586
+ stream: (options) => streamChildSessionEventsPolling(http, id, options ?? {})
599
587
  };
600
588
  }
589
+ /** SessionRecord-facade files accessor — used by {@link ChildSessionHandle}. */
601
590
  /**
602
- * Poll a RUN's events (via the run facade) until it reaches a terminal status,
603
- * the signal aborts, or the caller breaks the iterator. Uses `getRun` (not
604
- * `getSession`) so it resolves for a child run that has no session facade.
591
+ * Poll a child session's events (via the session record facade) until it reaches a terminal status,
592
+ * the signal aborts, or the caller breaks the iterator. Uses `getSessionRecord` (not
593
+ * `getSession`) so it resolves for a child session that has no session facade.
605
594
  */
606
- async function* streamRunEventsPolling(http, id, options) {
595
+ async function* streamChildSessionEventsPolling(http, id, options) {
607
596
  if (options.signal?.aborted)
608
597
  return;
609
598
  const seenIds = new Set();
610
599
  const intervalMs = options.intervalMs ?? 1_000;
611
600
  const signal = options.signal;
612
601
  while (!signal?.aborted) {
613
- const events = await operations.listRunEvents(http, id);
602
+ const events = await operations.listSessionRecordEvents(http, id);
614
603
  for (const event of events) {
615
604
  if (!seenIds.has(event.id)) {
616
605
  seenIds.add(event.id);
617
606
  yield asAexEventView(event);
618
607
  }
619
608
  }
620
- const run = await operations.getRun(http, id);
621
- if (isSessionParked(run.status))
609
+ const session = await operations.getSessionRecord(http, id);
610
+ if (isSessionParked(session.status))
622
611
  return;
623
612
  try {
624
613
  await sleep(intervalMs, signal);
@@ -696,7 +685,7 @@ async function* streamSessionEventsPolling(http, id, options) {
696
685
  const intervalMs = options.intervalMs ?? 1_000;
697
686
  const signal = options.signal;
698
687
  while (!signal?.aborted) {
699
- const events = await operations.listRunEvents(http, id);
688
+ const events = await operations.listSessionRecordEvents(http, id);
700
689
  for (const event of events) {
701
690
  if (!seenIds.has(event.id)) {
702
691
  seenIds.add(event.id);
@@ -728,8 +717,8 @@ async function* streamSessionEnvelopes(http, id, options) {
728
717
  from: options.from ?? 0,
729
718
  fetchTicket: async () => (await operations.getSessionCoordinatorTicket(http, id)).ticket,
730
719
  // settleConsistent ends the stream on the post-mirror barrier instead of
731
- // the earlier RUN_FINISHED UX signal.
732
- isTerminal: options.settleConsistent ? isRunSettled : isSessionEnvelopeTerminal,
720
+ // the earlier TURN_FINISHED UX signal.
721
+ isTerminal: options.settleConsistent ? isSessionSettled : isSessionEnvelopeTerminal,
733
722
  ...(options.signal ? { signal: options.signal } : {}),
734
723
  ...(options.idleTimeoutMs !== undefined ? { idleTimeoutMs: options.idleTimeoutMs } : {}),
735
724
  ...(options.pingIntervalMs !== undefined ? { pingIntervalMs: options.pingIntervalMs } : {}),
@@ -740,93 +729,93 @@ async function* streamSessionEnvelopes(http, id, options) {
740
729
  }
741
730
  }
742
731
  function isSessionEnvelopeTerminal(event) {
743
- return event.type === "RUN_FINISHED" || event.type === "RUN_ERROR" || isSessionParkedEvent(event);
732
+ return event.type === "TURN_FINISHED" || event.type === "TURN_ERROR" || isSessionParkedEvent(event);
744
733
  }
745
734
  /**
746
- * Download captured deliverables. No selector → the full outputs namespace as a
735
+ * Download captured files. No selector → the full files namespace as a
747
736
  * zip; a selector → one file's raw bytes. Module-level so
748
- * `SessionHandle.outputs()` can hand it to its accessor object literal.
737
+ * `SessionHandle.files()` can hand it to its accessor object literal.
749
738
  */
750
- async function downloadSessionOutput(http, id, selector, options) {
751
- // One selector-resolution path: the contracts `downloadOutput` lists-if-path
739
+ async function downloadSessionFile(http, id, selector, options) {
740
+ // One selector-resolution path: the contracts `downloadSessionFile` lists-if-path
752
741
  // then downloads, throwing with PUBLIC verb names — no duplicated resolver.
753
742
  const transferOptions = options?.timeoutMs === undefined ? undefined : { timeoutMs: options.timeoutMs };
754
743
  const bytes = selector === undefined
755
- ? await operations.downloadOutputs(http, id, transferOptions)
756
- : (await operations.downloadOutput(http, id, selector, transferOptions)).bytes;
744
+ ? await operations.downloadSessionFiles(http, id, transferOptions)
745
+ : (await operations.downloadSessionFile(http, id, selector, transferOptions)).bytes;
757
746
  return writeOptionalFile(bytes, options?.to);
758
747
  }
759
748
  /**
760
- * Build the outputs accessor for a session id. Shared by
761
- * `SessionHandle.outputs()` (bound to the live handle) and
762
- * `SessionClient.outputs(id)` (addressed by id without opening a handle), so both
763
- * expose the identical rich {@link SessionOutputs} surface — one accessor
749
+ * Build the files accessor for a session id. Shared by
750
+ * `SessionHandle.files()` (bound to the live handle) and
751
+ * `SessionClient.files(id)` (addressed by id without opening a handle), so both
752
+ * expose the identical rich {@link SessionFiles} surface — one accessor
764
753
  * convention, one implementation.
765
754
  */
766
- function sessionOutputs(http, id, fetchLike) {
767
- const list = (query) => operations.listSessionOutputs(http, id, query);
755
+ function sessionFiles(http, id, fetchLike) {
756
+ const list = (query) => operations.listSessionFiles(http, id, query);
768
757
  return {
769
758
  list,
770
759
  last: async () => (await list()).at(-1),
771
760
  first: async () => (await list())[0],
772
- read: (selector, options) => operations.readOutputText(http, id, selector, options),
773
- find: (query) => operations.findOutputs(http, id, query),
774
- findOne: (query) => operations.findOutput(http, id, query),
761
+ read: (selector, options) => operations.readSessionFileText(http, id, selector, options),
762
+ find: (query) => operations.findSessionFiles(http, id, query),
763
+ findOne: (query) => operations.findSessionFile(http, id, query),
775
764
  search: async (query = {}) => {
776
- assertMetadataOnlyOutputSearch(query, "outputs().search");
777
- const outputs = await searchRunOutputs(http, id, query);
778
- return { hits: outputHits(id, outputs, query.limit ?? 100) };
765
+ assertMetadataOnlyFileSearch(query, "files().search");
766
+ const files = await searchSessionFiles(http, id, query);
767
+ return { hits: fileHits(id, files, query.limit ?? 100) };
779
768
  },
780
- link: (selectorOrQuery, options) => operations.outputLink(http, id, selectorOrQuery, options),
769
+ link: (selectorOrQuery, options) => operations.sessionFileLink(http, id, selectorOrQuery, options),
781
770
  fetch: async (selectorOrQuery, options) => {
782
- const link = await operations.outputLink(http, id, selectorOrQuery, options);
771
+ const link = await operations.sessionFileLink(http, id, selectorOrQuery, options);
783
772
  return (fetchLike ?? globalThis.fetch)(link.url);
784
773
  },
785
- download: (selector, options) => downloadSessionOutput(http, id, selector, options)
774
+ download: (selector, options) => downloadSessionFile(http, id, selector, options)
786
775
  };
787
776
  }
788
777
  /**
789
- * List one run's outputs matching a metadata search: `extension`/`contentType`
778
+ * List one session's files matching a metadata search: `extension`/`contentType`
790
779
  * via the contracts filter, and `filename` via {@link operations.toFilenameMatcher}
791
780
  * — a case-insensitive SUBSTRING for a string, `.test` for a RegExp (RegExp-safe;
792
781
  * no `escapeRegExp` footgun on a reused pattern).
793
782
  */
794
- async function searchRunOutputs(http, runId, query) {
783
+ async function searchSessionFiles(http, sessionId, query) {
795
784
  const listQuery = {
796
785
  ...(query.extension !== undefined ? { extension: query.extension } : {}),
797
786
  ...(query.contentType !== undefined ? { contentType: query.contentType } : {})
798
787
  };
799
- const outputs = await operations.listSessionOutputs(http, runId, Object.keys(listQuery).length > 0 ? listQuery : undefined);
788
+ const files = await operations.listSessionFiles(http, sessionId, Object.keys(listQuery).length > 0 ? listQuery : undefined);
800
789
  if (query.filename === undefined)
801
- return outputs;
790
+ return files;
802
791
  const match = operations.toFilenameMatcher(query.filename);
803
- return outputs.filter((output) => typeof output.filename === "string" && match(output.filename));
792
+ return files.filter((file) => typeof file.filename === "string" && match(file.filename));
804
793
  }
805
794
  /**
806
795
  * Fail-fast on a CONTENT-shaped search: the search surface is metadata-only, so
807
796
  * a `content`/`text`/`query` needle throws a typed error rather than silently
808
- * returning 0 hits (which reads as "no matches" for a query that was never run).
797
+ * returning 0 hits (which reads as "no matches" for a query that was never executed).
809
798
  */
810
- function assertMetadataOnlyOutputSearch(query, surface) {
799
+ function assertMetadataOnlyFileSearch(query, surface) {
811
800
  for (const key of ["content", "text", "query", "grep", "body"]) {
812
801
  if (Object.prototype.hasOwnProperty.call(query, key)) {
813
- throw new RunConfigValidationError(`${surface}: content search is not supported — search matches on filename/extension/contentType metadata only`, { field: key });
802
+ throw new SessionConfigValidationError(`${surface}: content search is not supported — search matches on filename/extension/contentType metadata only`, { field: key });
814
803
  }
815
804
  }
816
805
  }
817
- /** Project a run's output files to reference-only {@link OutputSearchHit}s, capped. */
818
- function outputHits(runId, outputs, limit) {
806
+ /** Project a session's files to reference-only {@link SessionFileSearchHit}s, capped. */
807
+ function fileHits(sessionId, files, limit) {
819
808
  const cap = Math.max(0, Math.floor(limit));
820
809
  if (cap === 0)
821
810
  return [];
822
811
  const hits = [];
823
- for (const o of outputs) {
812
+ for (const file of files) {
824
813
  hits.push({
825
- runId,
826
- outputId: o.id,
827
- ...(o.filename !== undefined ? { filename: o.filename } : {}),
828
- ...(o.sizeBytes !== undefined ? { sizeBytes: o.sizeBytes } : {}),
829
- ...(o.contentType !== undefined ? { contentType: o.contentType } : {})
814
+ sessionId,
815
+ fileId: file.id,
816
+ ...(file.filename !== undefined ? { filename: file.filename } : {}),
817
+ ...(file.sizeBytes !== undefined ? { sizeBytes: file.sizeBytes } : {}),
818
+ ...(file.contentType !== undefined ? { contentType: file.contentType } : {})
830
819
  });
831
820
  if (hits.length >= cap)
832
821
  break;
@@ -846,7 +835,7 @@ function messageFromWire(message) {
846
835
  function isMissingMessagesEndpoint(err) {
847
836
  return err instanceof AexApiError && (err.status === 404 || err.status === 405 || err.status === 501);
848
837
  }
849
- function isMissingOutputsSession(err) {
838
+ function isMissingFilesSession(err) {
850
839
  return err instanceof AexApiError && err.status === 404;
851
840
  }
852
841
  function projectAssistantMessages(events) {
@@ -893,7 +882,7 @@ function projectAssistantMessages(events) {
893
882
  function assistantTextFromEvents(events) {
894
883
  return assistantTextEntriesFromEvents(events).map((entry) => entry.text).join("");
895
884
  }
896
- function runTraceFromEvents(events) {
885
+ function turnTraceFromEvents(events) {
897
886
  return {
898
887
  toolCalls: toolCallsFromEvents(events),
899
888
  usage: usageFromEvents(events),
@@ -1059,7 +1048,7 @@ function carriedOutcome(event) {
1059
1048
  : undefined;
1060
1049
  }
1061
1050
  function isSessionTurnTerminalEvent(event, turnSeq) {
1062
- if (event.type === "RUN_FINISHED" || event.type === "RUN_ERROR") {
1051
+ if (event.type === "TURN_FINISHED" || event.type === "TURN_ERROR") {
1063
1052
  return true;
1064
1053
  }
1065
1054
  const name = customName(event);
@@ -1076,7 +1065,7 @@ function isSessionTurnTerminalEvent(event, turnSeq) {
1076
1065
  /**
1077
1066
  * Read the CARRIED terminal outcome/park from the turn's terminal event — never
1078
1067
  * a lossy re-derivation. Prefers an explicit `data.value.outcome`, then the
1079
- * `aex.session.<name>` name, then RUN_ERROR→failed / RUN_FINISHED→succeeded.
1068
+ * `aex.session.<name>` name, then TURN_ERROR→failed / TURN_FINISHED→succeeded.
1080
1069
  */
1081
1070
  function terminalSessionStatusFromEvents(events, turnSeq) {
1082
1071
  for (let i = events.length - 1; i >= 0; i--) {
@@ -1086,9 +1075,9 @@ function terminalSessionStatusFromEvents(events, turnSeq) {
1086
1075
  const carried = carriedOutcome(event);
1087
1076
  if (carried !== undefined)
1088
1077
  return carried;
1089
- if (event.type === "RUN_ERROR")
1078
+ if (event.type === "TURN_ERROR")
1090
1079
  return "failed";
1091
- if (event.type === "RUN_FINISHED")
1080
+ if (event.type === "TURN_FINISHED")
1092
1081
  return "succeeded";
1093
1082
  const name = customName(event);
1094
1083
  if (name !== null && name in SESSION_TERMINAL_EVENT_READS)
@@ -1135,15 +1124,15 @@ function providerUsageOf(record) {
1135
1124
  return telemetry?.providerUsage;
1136
1125
  }
1137
1126
  /**
1138
- * The IMMEDIATE authoritative failure text — the terminal `RUN_ERROR` event's
1127
+ * The IMMEDIATE authoritative failure text — the terminal `TURN_ERROR` event's
1139
1128
  * `data.failureMessage`. `result.error` reads this FIRST so a failed (e.g.
1140
- * bad-BYOK) run's error is never empty even before the settle-lagged
1129
+ * bad-BYOK) session's error is never empty even before the settle-lagged
1141
1130
  * `sessionRecord.errorMessage` lands.
1142
1131
  */
1143
1132
  function failureFromEvents(events) {
1144
1133
  for (let i = events.length - 1; i >= 0; i--) {
1145
1134
  const event = events[i];
1146
- if (event.type !== "RUN_ERROR")
1135
+ if (event.type !== "TURN_ERROR")
1147
1136
  continue;
1148
1137
  const data = asRecord(event.data);
1149
1138
  for (const key of ["failureMessage", "message", "error"]) {
@@ -1180,10 +1169,10 @@ function outcomeFromEvents(events) {
1180
1169
  }
1181
1170
  /**
1182
1171
  * Build the ONE unified settled turn result (shared by `session.send().done()`
1183
- * and `Aex.run`): the terminal outcome `status`, `ok`, `costUsd` (>= 0),
1172
+ * and `Aex.start`): the terminal outcome `status`, `ok`, `costUsd` (>= 0),
1184
1173
  * `usage` (from `costTelemetry.providerUsage`), and event-first `error`.
1185
1174
  */
1186
- function settledTurnResult(sessionId, session, turn, events, outputs, messages, read) {
1175
+ function settledTurnResult(sessionId, session, turn, events, files, messages, read) {
1187
1176
  const effectiveRead = read ?? sessionRecordRead(session);
1188
1177
  const status = readToOutcome(effectiveRead);
1189
1178
  const ok = isTerminalReadOk(effectiveRead);
@@ -1203,7 +1192,7 @@ function settledTurnResult(sessionId, session, turn, events, outputs, messages,
1203
1192
  ...(error !== undefined ? { error } : {}),
1204
1193
  text: assistantTextFromEvents(events),
1205
1194
  events,
1206
- outputs,
1195
+ files,
1207
1196
  messages,
1208
1197
  ...(outcome !== undefined ? { outcome } : {})
1209
1198
  };
@@ -1241,8 +1230,10 @@ export class AgentsMdClient {
1241
1230
  */
1242
1231
  export class FilesClient {
1243
1232
  #http;
1233
+ #sessionSearch;
1244
1234
  constructor(http) {
1245
1235
  this.#http = http;
1236
+ this.#sessionSearch = new SessionFilesSearchClient(http);
1246
1237
  }
1247
1238
  list() {
1248
1239
  return operations.listFiles(this.#http);
@@ -1253,6 +1244,10 @@ export class FilesClient {
1253
1244
  delete(fileId) {
1254
1245
  return operations.deleteFile(this.#http, fileId);
1255
1246
  }
1247
+ /** Cross-session captured-file metadata search. */
1248
+ search(query) {
1249
+ return this.#sessionSearch.search(query);
1250
+ }
1256
1251
  }
1257
1252
  /**
1258
1253
  * Workspace skill registry operations exposed under `client.skills`.
@@ -1280,7 +1275,7 @@ export class SkillsClient {
1280
1275
  * Workspace secret management exposed under `client.secrets`, mirroring
1281
1276
  * `client.agentsMd` / `client.files`.
1282
1277
  *
1283
- * Lifecycle parity with assets: a `Secret.value(...)` is per-run and
1278
+ * Lifecycle parity with assets: a `Secret.value(...)` is per-session and
1284
1279
  * gone at terminal; `set` (or promoting an ephemeral via `secret.upload`)
1285
1280
  * persists a named, searchable workspace secret you can `get` (metadata),
1286
1281
  * `rotate`, `list`, and `delete`. The identity is the `name`; the value rotates
@@ -1335,7 +1330,7 @@ function unwrapSecretValue(value) {
1335
1330
  * Unified user-facing client for the aex platform. The same class
1336
1331
  * powers the published `@aexhq/sdk` SDK and (under the hood) every host-side
1337
1332
  * subcommand of the in-container `aex` CLI. All operations talk to
1338
- * the dashboard BFF and operate on durable run records.
1333
+ * the dashboard BFF and operate on durable session records.
1339
1334
  *
1340
1335
  * The SDK never asks the caller for a workspace id — workspace identity
1341
1336
  * is derived server-side from the API key on every request. Use
@@ -1351,8 +1346,6 @@ export class Aex {
1351
1346
  skills;
1352
1347
  secrets;
1353
1348
  sessions;
1354
- /** Cross-run output search (`aex.outputs.search(...)`). */
1355
- outputs;
1356
1349
  constructor(options, overrides = {}) {
1357
1350
  const resolved = typeof options === "string" ? { ...overrides, apiKey: options } : options;
1358
1351
  const apiKey = resolved.apiKey;
@@ -1390,7 +1383,6 @@ export class Aex {
1390
1383
  this.skills = new SkillsClient(this.#http);
1391
1384
  this.secrets = new SecretsClient(this.#http);
1392
1385
  this.sessions = new SessionClient(this.#http, (options) => this.#buildSessionCreateRequest(options), this.#fetch);
1393
- this.outputs = new OutputsClient(this.#http);
1394
1386
  }
1395
1387
  /**
1396
1388
  * Internal: satisfies the `SecretUploader` surface so a
@@ -1436,7 +1428,7 @@ export class Aex {
1436
1428
  /**
1437
1429
  * Internal: upsert already-uploaded skill metadata into the workspace registry.
1438
1430
  * The bytes are staged through `_uploadAsset`; this call binds the content hash
1439
- * to a mutable workspace skill name before the run references that name.
1431
+ * to a mutable workspace skill name before the session references that name.
1440
1432
  */
1441
1433
  async _upsertSkill(args) {
1442
1434
  return operations.upsertSkill(this.#http, args);
@@ -1445,34 +1437,34 @@ export class Aex {
1445
1437
  * Convenience one-shot on top of the canonical session API:
1446
1438
  * open a session, send `message` as the first turn, stream until the session
1447
1439
  * parks (`idle` / `suspended` / `error`), then return the collected text,
1448
- * events, outputs, and session record. The returned `runId` is the session id,
1449
- * so callers can resume later with `openSession(runId)`.
1440
+ * events, files, and session record. The returned `sessionId` is the session id,
1441
+ * so callers can resume later with `openSession(sessionId)`.
1450
1442
  */
1451
- async run(options, opts = {}) {
1443
+ async start(options, opts = {}) {
1452
1444
  const scopedSignal = scopedAbortSignal(opts.timeoutMs);
1453
1445
  try {
1454
1446
  const { message, deleteAfter, messageIdempotencyKey, stream, ...createOptions } = options;
1455
- assertNoLegacySessionFields(options, "Aex.run");
1456
- const input = normaliseSessionInput(message, "Aex.run", "message");
1457
- assertNoSessionSendSignal(stream, "Aex.run stream");
1447
+ assertNoLegacySessionFields(options, "Aex.start");
1448
+ const input = normaliseSessionInput(message, "Aex.start", "message");
1449
+ assertNoSessionSendSignal(stream, "Aex.start stream");
1458
1450
  const sendOptions = {
1459
1451
  ...(stream ?? {}),
1460
1452
  ...(scopedSignal?.signal ? { signal: scopedSignal.signal } : {}),
1461
1453
  ...(opts.webSocketFactory ? { webSocketFactory: opts.webSocketFactory } : {}),
1462
1454
  ...(opts.idleTimeoutMs !== undefined ? { idleTimeoutMs: opts.idleTimeoutMs } : {}),
1463
1455
  ...(opts.pingIntervalMs !== undefined ? { pingIntervalMs: opts.pingIntervalMs } : {}),
1464
- // run()/done() await settle by DEFAULT; opt out with `await: 'park'`.
1456
+ // start()/done() await settle by DEFAULT; opt out with `await: 'park'`.
1465
1457
  ...(opts.await !== undefined ? { await: opts.await } : {})
1466
1458
  };
1467
1459
  // Derive the message key from the create key (like the CLI) so a retried
1468
- // run with the same `idempotencyKey` de-duplicates BOTH the create and the
1469
- // billable turn server-side — never a duplicate billable run (sdk-dx-3).
1460
+ // session with the same `idempotencyKey` de-duplicates BOTH the create and the
1461
+ // billable turn server-side — never a duplicate billable session turn (sdk-dx-3).
1470
1462
  const createKey = operations.resolveIdempotencyKey(createOptions.idempotencyKey);
1471
1463
  const messageKey = messageIdempotencyKey !== undefined ? operations.resolveIdempotencyKey(messageIdempotencyKey) : deriveMessageKey(createKey);
1472
1464
  const session = await this.sessions.create({ ...createOptions, idempotencyKey: createKey });
1473
1465
  // ONE terminal boundary: `done()` awaits settle by default, so the turn
1474
- // result already carries the terminal outcome + cost + usage. `run()` just
1475
- // reshapes it — `done()` == `run()` (WS3).
1466
+ // result already carries the terminal outcome + cost + usage. `start()` just
1467
+ // reshapes it — `done()` == `start()` (WS3).
1476
1468
  let turnResult;
1477
1469
  try {
1478
1470
  turnResult = await sendSessionInternal(session, input, { ...sendOptions, idempotencyKey: messageKey }).done();
@@ -1481,24 +1473,23 @@ export class Aex {
1481
1473
  if (scopedSignal?.signal.aborted) {
1482
1474
  // The client-side wait budget (opts.timeoutMs) expired. Parity with
1483
1475
  // SessionHandle.wait(): THROW rather than a misleading silent result;
1484
- // the run continues server-side.
1485
- throw new RunStateError(`Aex.run: timed out after ${opts.timeoutMs}ms waiting for run ${session.id} to park; the run ` +
1476
+ // the session continues server-side.
1477
+ throw new SessionStateError(`Aex.start: timed out after ${opts.timeoutMs}ms waiting for session ${session.id} to park; the session ` +
1486
1478
  `continues server-side — cancel via session.cancel() or resume with openSession(${JSON.stringify(session.id)})`);
1487
1479
  }
1488
1480
  throw err;
1489
1481
  }
1490
- const runId = turnResult.sessionId;
1482
+ const sessionId = turnResult.sessionId;
1491
1483
  if (deleteAfter) {
1492
1484
  await session.delete();
1493
1485
  }
1494
1486
  const sessionRecord = turnResult.session;
1495
- const run = sessionToRun(sessionRecord);
1496
- const trace = runTraceFromEvents(turnResult.events);
1487
+ const record = sessionToSessionRecord(sessionRecord);
1488
+ const trace = turnTraceFromEvents(turnResult.events);
1497
1489
  const outcome = turnResult.outcome;
1498
1490
  const result = {
1499
- runId,
1500
- run,
1501
- sessionId: runId,
1491
+ sessionId,
1492
+ record,
1502
1493
  session: sessionRecord,
1503
1494
  turn: turnResult.turn,
1504
1495
  status: turnResult.status,
@@ -1509,7 +1500,7 @@ export class Aex {
1509
1500
  messages: turnResult.messages,
1510
1501
  events: turnResult.events,
1511
1502
  trace,
1512
- outputs: turnResult.outputs,
1503
+ files: turnResult.files,
1513
1504
  ...(turnResult.error !== undefined ? { error: turnResult.error } : {}),
1514
1505
  ...(outcome !== undefined ? { outcome } : {})
1515
1506
  };
@@ -1527,7 +1518,7 @@ export class Aex {
1527
1518
  ...(throttle.retryAfterMs !== undefined ? { retryAfterMs: throttle.retryAfterMs } : {})
1528
1519
  });
1529
1520
  }
1530
- throw new RunStateError(`Aex.run: session ${runId} ended ${turnResult.status}${turnResult.error ? `: ${turnResult.error}` : ""}`, { runId, status: turnResult.status });
1521
+ throw new SessionStateError(`Aex.start: session ${sessionId} ended ${turnResult.status}${turnResult.error ? `: ${turnResult.error}` : ""}`, { sessionId, status: turnResult.status });
1531
1522
  }
1532
1523
  return result;
1533
1524
  }
@@ -1537,37 +1528,37 @@ export class Aex {
1537
1528
  }
1538
1529
  /**
1539
1530
  * Fire-and-forget: create the session and POST its first turn WITHOUT awaiting
1540
- * settle (the honest counterpart to await-settle `run()`). Resolves with the
1541
- * `runId` + a resumable {@link SessionHandle} immediately; observe the run via
1542
- * a `webhook`, the event stream, or `openSession(runId)`.
1531
+ * settle (the honest counterpart to await-settle `start()`). Resolves with the
1532
+ * `sessionId` + a resumable {@link SessionHandle} immediately; observe the session via
1533
+ * a `webhook`, the event stream, or `openSession(sessionId)`.
1543
1534
  */
1544
1535
  async submit(options) {
1545
1536
  const { message, deleteAfter: _deleteAfter, messageIdempotencyKey, stream: _stream, ...createOptions } = options;
1546
1537
  assertNoLegacySessionFields(options, "Aex.submit");
1547
1538
  const input = normaliseSessionInput(message, "Aex.submit", "message");
1548
1539
  const request = await this.#buildSessionCreateRequest(createOptions);
1549
- const { runId, session } = await operations.submit(this.#http, { ...request, input }, {
1540
+ const { sessionId, session } = await operations.submit(this.#http, { ...request, input }, {
1550
1541
  idempotencyKey: operations.resolveIdempotencyKey(createOptions.idempotencyKey),
1551
1542
  ...(messageIdempotencyKey !== undefined ? { messageIdempotencyKey } : {})
1552
1543
  });
1553
- return { runId, session: new SessionHandle(this.#http, session, this.#fetch) };
1544
+ return { sessionId, session: new SessionHandle(this.#http, session, this.#fetch) };
1554
1545
  }
1555
1546
  /**
1556
- * Run a batch of one-shot items with a bounded worker pool, returning every
1547
+ * SessionRecord a batch of one-shot items with a bounded worker pool, returning every
1557
1548
  * item's settled result PLUS a REAL cost/usage rollup. The rollup is honest
1558
- * because each `run()` awaits settle, so each item's `costUsd`/`usage` is
1549
+ * because each `start()` awaits settle, so each item's `costUsd`/`usage` is
1559
1550
  * populated — a failed item lands in `failed[]`, never a silent `$0` success.
1560
1551
  * Concurrency is clamped below the workspace tier cap.
1561
1552
  */
1562
1553
  async batch(items, options = {}) {
1563
1554
  if (!Array.isArray(items)) {
1564
- throw new RunConfigValidationError("Aex.batch: items must be an array of run options");
1555
+ throw new SessionConfigValidationError("Aex.batch: items must be an array of session options");
1565
1556
  }
1566
1557
  const concurrency = Math.min(Math.max(1, Math.floor(options.concurrency ?? DEFAULT_BATCH_CONCURRENCY)), BATCH_MAX_CONCURRENCY);
1567
1558
  const results = (await mapWithConcurrency(items, concurrency, async (item) => {
1568
- const result = await this.run(item);
1559
+ const result = await this.start(item);
1569
1560
  return {
1570
- runId: result.runId,
1561
+ sessionId: result.sessionId,
1571
1562
  status: result.status,
1572
1563
  ok: result.ok,
1573
1564
  costUsd: result.costUsd,
@@ -1585,15 +1576,15 @@ export class Aex {
1585
1576
  }
1586
1577
  async #buildSessionCreateRequest(options) {
1587
1578
  if (!options || typeof options !== "object") {
1588
- throw new RunConfigValidationError("Aex.openSession: options is required");
1579
+ throw new SessionConfigValidationError("Aex.openSession: options is required");
1589
1580
  }
1590
1581
  assertNoLegacySessionFields(options, "Aex.openSession");
1591
- // `message` belongs to the one-shot surfaces (`run` / `sessions.run`), which
1582
+ // `message` belongs to the one-shot surfaces (`run` / `sessions.start`), which
1592
1583
  // strip it before creating the session. Passing it here used to be SILENTLY
1593
1584
  // dropped — the session was created empty, idled from birth, and auto-
1594
1585
  // suspended at the idle TTL without ever running a turn.
1595
1586
  if (Object.prototype.hasOwnProperty.call(options, "message")) {
1596
- throw new RunConfigValidationError("Aex.openSession: message is not a supported option; sessions are created without a first " +
1587
+ throw new SessionConfigValidationError("Aex.openSession: message is not a supported option; sessions are created without a first " +
1597
1588
  "message — send it with session.send(...), or use run({ message }) for a one-shot.");
1598
1589
  }
1599
1590
  // Model is REQUIRED and checked BEFORE the provider key, so omitting `model`
@@ -1634,12 +1625,12 @@ export class Aex {
1634
1625
  // server).
1635
1626
  try {
1636
1627
  parseRuntimeSize(options.runtime);
1637
- parseRunTimeout(options.overrides?.timeout);
1628
+ parseSessionTimeout(options.overrides?.timeout);
1638
1629
  if (options.webhook !== undefined)
1639
- parseRunWebhook(options.webhook);
1630
+ parseSessionWebhook(options.webhook);
1640
1631
  }
1641
1632
  catch (err) {
1642
- throw new RunConfigValidationError(`Aex.openSession: ${err instanceof Error ? err.message : String(err)}`);
1633
+ throw new SessionConfigValidationError(`Aex.openSession: ${err instanceof Error ? err.message : String(err)}`);
1643
1634
  }
1644
1635
  const { declarations: secretEnvDeclarations, values: envSecretValues } = splitSecretEnv(options.environment?.secrets);
1645
1636
  let limits;
@@ -1649,11 +1640,11 @@ export class Aex {
1649
1640
  if (options.overrides?.maxTurns !== undefined)
1650
1641
  limitsInput.maxTurns = options.overrides.maxTurns;
1651
1642
  try {
1652
- limits = parseRunLimits(Object.keys(limitsInput).length > 0 ? limitsInput : undefined);
1643
+ limits = parseSessionLimits(Object.keys(limitsInput).length > 0 ? limitsInput : undefined);
1653
1644
  }
1654
1645
  catch (err) {
1655
1646
  // One `configError` factory for every client-side validation throw, so
1656
- // maxSpendUsd/maxTurns are `RunConfigValidationError` (not a base AexError).
1647
+ // maxSpendUsd/maxTurns are `SessionConfigValidationError` (not a base AexError).
1657
1648
  throw configError("Aex.openSession", "limits", err instanceof Error ? err.message : String(err));
1658
1649
  }
1659
1650
  const uploader = (args) => this._uploadAsset(args);
@@ -1667,7 +1658,7 @@ export class Aex {
1667
1658
  prepareFiles(options.files ?? [], uploader, streamUploader)
1668
1659
  ]);
1669
1660
  const { submissionMcpServers, mergedMcpSecrets } = mergeMcpServers(options.mcpServers ?? [], []);
1670
- const outputCapture = outputsForWire(options.outputs);
1661
+ const fileCapture = fileCaptureForWire(options.fileCapture);
1671
1662
  const environment = sessionEnvironmentForWire(options.environment);
1672
1663
  const submission = {
1673
1664
  model: options.model,
@@ -1685,7 +1676,7 @@ export class Aex {
1685
1676
  ...(Object.keys(secretEnvDeclarations).length > 0 ? { secretEnv: secretEnvDeclarations } : {}),
1686
1677
  ...(environment ? { environment: environment } : {}),
1687
1678
  ...(options.metadata ? { metadata: options.metadata } : {}),
1688
- ...(outputCapture ? { outputs: outputCapture } : {}),
1679
+ ...(fileCapture ? { fileCapture } : {}),
1689
1680
  ...(options.includeBuiltinTools !== undefined
1690
1681
  ? { includeBuiltinTools: options.includeBuiltinTools }
1691
1682
  : {}),
@@ -1715,7 +1706,7 @@ export class Aex {
1715
1706
  /**
1716
1707
  * Delete a workspace asset blob from the shared content-addressed store
1717
1708
  * (`assets/<workspaceId>/<hash>`). Accepts `sha256:<hex>` or a bare
1718
- * 64-hex digest. Runs that already snapshotted the asset are unaffected.
1709
+ * 64-hex digest. Sessions that already snapshotted the asset are unaffected.
1719
1710
  */
1720
1711
  deleteWorkspaceAsset(hash) {
1721
1712
  return operations.deleteWorkspaceAsset(this.#http, hash);
@@ -1790,14 +1781,14 @@ function resolveBaseUrlForKey(apiKey, baseUrl) {
1790
1781
  }
1791
1782
  return baseUrl;
1792
1783
  }
1793
- // `Run.status` is a loose `string` on the wire shape, so we membership-test
1784
+ // `SessionRecord.status` is a loose `string` on the wire shape, so we membership-test
1794
1785
  // against the canonical terminal set rather than re-deriving one (which is how
1795
1786
  // `timed_out` got dropped from the old hardcoded list).
1796
- const TERMINAL_STATUSES = new Set(TERMINAL_RUN_STATUSES);
1787
+ const TERMINAL_STATUSES = new Set(TERMINAL_SESSION_CONTROL_STATUSES);
1797
1788
  function isTerminal(status) {
1798
1789
  return typeof status === "string" && TERMINAL_STATUSES.has(status);
1799
1790
  }
1800
- /** How long `Aex.run` waits for the settle write after the park event (ms). */
1791
+ /** How long `Aex.start` waits for the settle write after the park event (ms). */
1801
1792
  const SETTLE_POLL_DEADLINE_MS = 60_000;
1802
1793
  /** Interval between settle-poll reads (ms). */
1803
1794
  const SETTLE_POLL_INTERVAL_MS = 750;
@@ -1875,7 +1866,7 @@ function isSessionParked(status) {
1875
1866
  status === "expired" ||
1876
1867
  isTerminal(status));
1877
1868
  }
1878
- function sessionToRun(session) {
1869
+ function sessionToSessionRecord(session) {
1879
1870
  const id = session.sessionId ?? session.id;
1880
1871
  return {
1881
1872
  id,
@@ -1927,15 +1918,15 @@ function sleep(ms, signal) {
1927
1918
  }
1928
1919
  /**
1929
1920
  * ONE factory for every client-side validation throw: a
1930
- * {@link RunConfigValidationError} carrying structured `details: { field, value? }`
1921
+ * {@link SessionConfigValidationError} carrying structured `details: { field, value? }`
1931
1922
  * so callers branch on `err.details.field` instead of string-parsing the message.
1932
1923
  */
1933
1924
  function configError(surface, field, message, value) {
1934
- return new RunConfigValidationError(`${surface}: ${message}`, value === undefined ? { field } : { field, value });
1925
+ return new SessionConfigValidationError(`${surface}: ${message}`, value === undefined ? { field } : { field, value });
1935
1926
  }
1936
1927
  /**
1937
1928
  * Derive the message idempotency key from the session-create key. Mirrors the
1938
- * CLI (`<createKey>:message`) so a retried `run` / `sessions.run` that reuses
1929
+ * CLI (`<createKey>:message`) so a retried `run` / `sessions.start` that reuses
1939
1930
  * one `idempotencyKey` de-duplicates BOTH the create and the billable turn.
1940
1931
  */
1941
1932
  function deriveMessageKey(createKey) {
@@ -1954,7 +1945,7 @@ function throttleFromSession(session) {
1954
1945
  faultFromErrorMessage(typeof session.errorMessage === "string" ? session.errorMessage : undefined);
1955
1946
  return fault && isThrottleFault(fault) ? fault : undefined;
1956
1947
  }
1957
- /** Last-resort throttle detection from a free-text run error message. */
1948
+ /** Last-resort throttle detection from a free-text turn error message. */
1958
1949
  function faultFromErrorMessage(message) {
1959
1950
  if (message === undefined || message.length === 0)
1960
1951
  return undefined;
@@ -1970,16 +1961,16 @@ function faultFromErrorMessage(message) {
1970
1961
  function normaliseSessionInput(input, surface, field) {
1971
1962
  if (typeof input === "string") {
1972
1963
  if (!input) {
1973
- throw new RunConfigValidationError(`${surface}: ${field} must be a non-empty string`);
1964
+ throw new SessionConfigValidationError(`${surface}: ${field} must be a non-empty string`);
1974
1965
  }
1975
1966
  return input;
1976
1967
  }
1977
1968
  if (!Array.isArray(input) || input.length === 0) {
1978
- throw new RunConfigValidationError(`${surface}: ${field} must be a non-empty string or string array`);
1969
+ throw new SessionConfigValidationError(`${surface}: ${field} must be a non-empty string or string array`);
1979
1970
  }
1980
1971
  for (const segment of input) {
1981
1972
  if (typeof segment !== "string" || !segment) {
1982
- throw new RunConfigValidationError(`${surface}: ${field} segments must be non-empty strings`);
1973
+ throw new SessionConfigValidationError(`${surface}: ${field} segments must be non-empty strings`);
1983
1974
  }
1984
1975
  }
1985
1976
  return [...input];
@@ -1989,66 +1980,66 @@ function assertNoLegacySessionFields(options, surface) {
1989
1980
  const removedProxyField = ["proxy", "Endpoints"].join("");
1990
1981
  const messages = {
1991
1982
  input: "send user messages with session.send(...) or use run({ message }).",
1992
- prompt: "use message for one-shot run input or session.send(...) for follow-up messages.",
1983
+ prompt: "use message for one-shot session input or session.send(...) for follow-up messages.",
1993
1984
  instructions: "use system.",
1994
1985
  idleSuspendAfter: "use overrides.idleTtl.",
1995
1986
  idleTtl: "use overrides.idleTtl.",
1996
1987
  retention: "use overrides.idleTtl.",
1997
1988
  secretEnv: "use environment.secrets.",
1998
- secrets: "use top-level apiKeys for provider keys and environment.secrets for run secrets.",
1989
+ secrets: "use top-level apiKeys for provider keys and environment.secrets for session secrets.",
1999
1990
  runtimeSize: "use runtime.",
2000
1991
  limits: "use overrides.",
2001
1992
  timeout: "use overrides.timeout.",
2002
- parentRunId: "subagent lineage is assigned by the platform.",
1993
+ parentSessionId: "subagent lineage is assigned by the platform.",
2003
1994
  signal: "use session.cancel() / session.suspend() for remote control.",
2004
1995
  postHook: "send a follow-up validation message when the session returns idle.",
2005
1996
  [removedProxyField]: "proxy endpoints are not part of the public SDK session API."
2006
1997
  };
2007
1998
  for (const [field, message] of Object.entries(messages)) {
2008
1999
  if (Object.prototype.hasOwnProperty.call(record, field)) {
2009
- throw new RunConfigValidationError(`${surface}: ${field} is not a supported option; ${message}`);
2000
+ throw new SessionConfigValidationError(`${surface}: ${field} is not a supported option; ${message}`);
2010
2001
  }
2011
2002
  }
2012
2003
  const overrides = record.overrides;
2013
2004
  if (overrides && typeof overrides === "object" && !Array.isArray(overrides)) {
2014
2005
  const overrideRecord = overrides;
2015
2006
  if (Object.prototype.hasOwnProperty.call(overrideRecord, "idleSuspendAfter")) {
2016
- throw new RunConfigValidationError(`${surface}: overrides.idleSuspendAfter is not a supported option; use overrides.idleTtl.`);
2007
+ throw new SessionConfigValidationError(`${surface}: overrides.idleSuspendAfter is not a supported option; use overrides.idleTtl.`);
2017
2008
  }
2018
2009
  }
2019
2010
  }
2020
2011
  function assertNoSessionSendSignal(options, surface) {
2021
2012
  const record = options;
2022
2013
  if (record && typeof record === "object" && Object.prototype.hasOwnProperty.call(record, "signal")) {
2023
- throw new RunConfigValidationError(`${surface}: signal is not a supported option; use session.cancel() / session.suspend() for remote control.`);
2014
+ throw new SessionConfigValidationError(`${surface}: signal is not a supported option; use session.cancel() / session.suspend() for remote control.`);
2024
2015
  }
2025
2016
  }
2026
2017
  function validateApiKeys(apiKeys, provider, surface) {
2027
2018
  const key = apiKeys?.[provider];
2028
2019
  if (typeof key !== "string" || key.length === 0) {
2029
- throw new RunConfigValidationError(`${surface}: a provider API key is required — pass apiKeys[${JSON.stringify(provider)}].`);
2020
+ throw new SessionConfigValidationError(`${surface}: a provider API key is required — pass apiKeys[${JSON.stringify(provider)}].`);
2030
2021
  }
2031
2022
  }
2032
- function outputsForWire(outputs) {
2033
- if (outputs === undefined) {
2023
+ function fileCaptureForWire(fileCapture) {
2024
+ if (fileCapture === undefined) {
2034
2025
  return undefined;
2035
2026
  }
2036
- const allowedDirs = outputs.allowedDirs?.filter((dir) => dir.length > 0);
2037
- const deniedDirs = outputs.deniedDirs?.filter((dir) => dir.length > 0);
2038
- const hasNumericOverride = outputs.captureTimeoutMs !== undefined ||
2039
- outputs.maxFileBytes !== undefined ||
2040
- outputs.maxTotalBytes !== undefined ||
2041
- outputs.maxFiles !== undefined;
2027
+ const allowedDirs = fileCapture.allowedDirs?.filter((dir) => dir.length > 0);
2028
+ const deniedDirs = fileCapture.deniedDirs?.filter((dir) => dir.length > 0);
2029
+ const hasNumericOverride = fileCapture.captureTimeoutMs !== undefined ||
2030
+ fileCapture.maxFileBytes !== undefined ||
2031
+ fileCapture.maxTotalBytes !== undefined ||
2032
+ fileCapture.maxFiles !== undefined;
2042
2033
  if ((allowedDirs?.length ?? 0) === 0 && (deniedDirs?.length ?? 0) === 0 && !hasNumericOverride) {
2043
2034
  return undefined;
2044
2035
  }
2045
2036
  return {
2046
2037
  ...(allowedDirs && allowedDirs.length > 0 ? { allowedDirs } : {}),
2047
2038
  ...(deniedDirs && deniedDirs.length > 0 ? { deniedDirs } : {}),
2048
- ...(outputs.captureTimeoutMs !== undefined ? { captureTimeoutMs: outputs.captureTimeoutMs } : {}),
2049
- ...(outputs.maxFileBytes !== undefined ? { maxFileBytes: outputs.maxFileBytes } : {}),
2050
- ...(outputs.maxTotalBytes !== undefined ? { maxTotalBytes: outputs.maxTotalBytes } : {}),
2051
- ...(outputs.maxFiles !== undefined ? { maxFiles: outputs.maxFiles } : {})
2039
+ ...(fileCapture.captureTimeoutMs !== undefined ? { captureTimeoutMs: fileCapture.captureTimeoutMs } : {}),
2040
+ ...(fileCapture.maxFileBytes !== undefined ? { maxFileBytes: fileCapture.maxFileBytes } : {}),
2041
+ ...(fileCapture.maxTotalBytes !== undefined ? { maxTotalBytes: fileCapture.maxTotalBytes } : {}),
2042
+ ...(fileCapture.maxFiles !== undefined ? { maxFiles: fileCapture.maxFiles } : {})
2052
2043
  };
2053
2044
  }
2054
2045
  const DEFAULT_SESSION_IDLE_TTL = "3m";
@@ -2121,7 +2112,7 @@ async function prepareTools(tools, uploader) {
2121
2112
  // A bare string is a builtin tool reference.
2122
2113
  if (typeof entry === "string") {
2123
2114
  if (!BUILTIN_TOOL_NAMES.includes(entry)) {
2124
- throw new RunConfigValidationError(`aex: tools[${i}] (${JSON.stringify(entry)}) is not a builtin tool name; ` +
2115
+ throw new SessionConfigValidationError(`aex: tools[${i}] (${JSON.stringify(entry)}) is not a builtin tool name; ` +
2125
2116
  `expected a Tool or one of: ${BUILTIN_TOOL_NAMES.join(", ")}`);
2126
2117
  }
2127
2118
  return { kind: "builtin", name: entry };
@@ -2129,15 +2120,15 @@ async function prepareTools(tools, uploader) {
2129
2120
  if (!(entry instanceof Tool)) {
2130
2121
  const maybeEntry = entry;
2131
2122
  if (maybeEntry instanceof Skill) {
2132
- throw new RunConfigValidationError(`aex: tools[${i}] is a Skill; pass skills via the top-level skills option`);
2123
+ throw new SessionConfigValidationError(`aex: tools[${i}] is a Skill; pass skills via the top-level skills option`);
2133
2124
  }
2134
- throw new RunConfigValidationError(`aex: tools[${i}] must be a Tool or a builtin tool name`);
2125
+ throw new SessionConfigValidationError(`aex: tools[${i}] must be a Tool or a builtin tool name`);
2135
2126
  }
2136
2127
  const ref = entry.ref;
2137
2128
  if (ref.kind === "draft") {
2138
2129
  const bundle = entry._takeDraftBundle();
2139
2130
  if (!bundle) {
2140
- throw new RunConfigValidationError(`aex: tools[${i}] is draft but has no bytes`);
2131
+ throw new SessionConfigValidationError(`aex: tools[${i}] is draft but has no bytes`);
2141
2132
  }
2142
2133
  const assetId = await resolveAssetId(entry, bundle, uploader);
2143
2134
  return { kind: "ref", ref: { ...bundle.ref, assetId } };
@@ -2163,21 +2154,21 @@ async function prepareTools(tools, uploader) {
2163
2154
  /**
2164
2155
  * Upload/upsert workspace skills and return public name-only refs. Draft skills
2165
2156
  * auto-upsert (bytes → asset store, then registry PUT) by name; already-uploaded
2166
- * skills pass through. Duplicate names are pre-checked (uploads run in parallel,
2167
- * so the dedup cannot be a running set inside the map).
2157
+ * skills pass through. Duplicate names are pre-checked (uploads session in parallel,
2158
+ * so the dedup cannot be a sessionning set inside the map).
2168
2159
  */
2169
2160
  async function prepareSkills(skills, uploader) {
2170
2161
  if (skills.length > SKILLS_MAX) {
2171
- throw new RunConfigValidationError(`aex: skills exceeds the ${SKILLS_MAX}-skill limit (got ${skills.length})`);
2162
+ throw new SessionConfigValidationError(`aex: skills exceeds the ${SKILLS_MAX}-skill limit (got ${skills.length})`);
2172
2163
  }
2173
2164
  const seen = new Set();
2174
2165
  for (let i = 0; i < skills.length; i++) {
2175
2166
  const entry = skills[i];
2176
2167
  if (!(entry instanceof Skill)) {
2177
- throw new RunConfigValidationError(`aex: skills[${i}] must be a Skill (Skill.fromDir / fromUrl / fromFiles / fromContent / fromBytes)`);
2168
+ throw new SessionConfigValidationError(`aex: skills[${i}] must be a Skill (Skill.fromDir / fromUrl / fromFiles / fromContent / fromBytes)`);
2178
2169
  }
2179
2170
  if (seen.has(entry.name)) {
2180
- throw new RunConfigValidationError(`aex: skills duplicate name: ${entry.name}`);
2171
+ throw new SessionConfigValidationError(`aex: skills duplicate name: ${entry.name}`);
2181
2172
  }
2182
2173
  seen.add(entry.name);
2183
2174
  }
@@ -2185,7 +2176,7 @@ async function prepareSkills(skills, uploader) {
2185
2176
  const uploaded = entry.isDraft ? await entry.upload(uploader) : entry;
2186
2177
  const ref = uploaded.ref;
2187
2178
  if (ref.kind !== "skill") {
2188
- throw new RunConfigValidationError(`aex: skills[${i}] did not resolve to a workspace skill ref`);
2179
+ throw new SessionConfigValidationError(`aex: skills[${i}] did not resolve to a workspace skill ref`);
2189
2180
  }
2190
2181
  return ref;
2191
2182
  });
@@ -2194,13 +2185,13 @@ async function prepareSkills(skills, uploader) {
2194
2185
  async function prepareAgentsMd(agentsMds, uploader) {
2195
2186
  return mapWithConcurrency(agentsMds, UPLOAD_CONCURRENCY, async (entry, i) => {
2196
2187
  if (!(entry instanceof AgentsMd)) {
2197
- throw new RunConfigValidationError(`aex: agentsMd[${i}] must be an AgentsMd instance`);
2188
+ throw new SessionConfigValidationError(`aex: agentsMd[${i}] must be an AgentsMd instance`);
2198
2189
  }
2199
2190
  const ref = entry.ref;
2200
2191
  if (ref.kind === "draft") {
2201
2192
  const bundle = entry._takeDraftBundle();
2202
2193
  if (!bundle) {
2203
- throw new RunConfigValidationError(`aex: agentsMd[${i}] is draft but has no bytes`);
2194
+ throw new SessionConfigValidationError(`aex: agentsMd[${i}] is draft but has no bytes`);
2204
2195
  }
2205
2196
  const assetId = await resolveAssetId(entry, bundle, uploader);
2206
2197
  return { kind: "asset", assetId, name: bundle.name };
@@ -2212,7 +2203,7 @@ async function prepareAgentsMd(agentsMds, uploader) {
2212
2203
  async function prepareFiles(files, uploader, streamUploader) {
2213
2204
  return mapWithConcurrency(files, UPLOAD_CONCURRENCY, async (entry, i) => {
2214
2205
  if (!(entry instanceof File)) {
2215
- throw new RunConfigValidationError(`aex: files[${i}] must be a File instance`);
2206
+ throw new SessionConfigValidationError(`aex: files[${i}] must be a File instance`);
2216
2207
  }
2217
2208
  const ref = entry.ref;
2218
2209
  if (ref.kind === "draft") {
@@ -2229,7 +2220,7 @@ async function prepareFiles(files, uploader, streamUploader) {
2229
2220
  }
2230
2221
  const bundle = entry._takeDraftBundle();
2231
2222
  if (!bundle) {
2232
- throw new RunConfigValidationError(`aex: files[${i}] is draft but has no bytes`);
2223
+ throw new SessionConfigValidationError(`aex: files[${i}] is draft but has no bytes`);
2233
2224
  }
2234
2225
  const assetId = await resolveAssetId(entry, bundle, uploader);
2235
2226
  return { kind: "asset", assetId, name: bundle.name, mountPath: bundle.mountPath };
@@ -2246,14 +2237,14 @@ function mergeMcpServers(inputs, explicitSecrets) {
2246
2237
  for (let i = 0; i < inputs.length; i++) {
2247
2238
  const entry = inputs[i];
2248
2239
  if (!(entry instanceof McpServer)) {
2249
- throw new RunConfigValidationError(`aex: mcpServers[${i}] must be an McpServer instance`);
2240
+ throw new SessionConfigValidationError(`aex: mcpServers[${i}] must be an McpServer instance`);
2250
2241
  }
2251
2242
  submissionMcpServers.push(entry.toSubmissionEntry());
2252
2243
  const secret = entry.toSecretEntry();
2253
2244
  if (secret) {
2254
2245
  const existing = secretByName.get(secret.name);
2255
2246
  if (existing && existing.url !== secret.url) {
2256
- throw new RunConfigValidationError(`aex: mcpServers[${i}].url conflicts with secrets.mcpServers["${secret.name}"]`);
2247
+ throw new SessionConfigValidationError(`aex: mcpServers[${i}].url conflicts with secrets.mcpServers["${secret.name}"]`);
2257
2248
  }
2258
2249
  secretByName.set(secret.name, secret);
2259
2250
  }