@aexhq/sdk 0.40.15 → 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 (132) 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/asset-upload-helper.d.ts +2 -2
  5. package/dist/_contracts/asset-upload-helper.js +2 -2
  6. package/dist/_contracts/connection-ticket.d.ts +5 -5
  7. package/dist/_contracts/connection-ticket.js +7 -7
  8. package/dist/_contracts/error-codes.d.ts +1 -1
  9. package/dist/_contracts/error-codes.js +4 -4
  10. package/dist/_contracts/error-factory.js +1 -1
  11. package/dist/_contracts/event-envelope.d.ts +41 -41
  12. package/dist/_contracts/event-envelope.js +49 -49
  13. package/dist/_contracts/event-stream-client.d.ts +9 -9
  14. package/dist/_contracts/event-stream-client.js +5 -5
  15. package/dist/_contracts/event-view.d.ts +6 -6
  16. package/dist/_contracts/event-view.js +12 -12
  17. package/dist/_contracts/http.js +1 -1
  18. package/dist/_contracts/index.d.ts +10 -10
  19. package/dist/_contracts/index.js +9 -9
  20. package/dist/_contracts/models.d.ts +17 -17
  21. package/dist/_contracts/models.js +14 -14
  22. package/dist/_contracts/operations.d.ts +79 -82
  23. package/dist/_contracts/operations.js +233 -243
  24. package/dist/_contracts/provider-support.d.ts +2 -2
  25. package/dist/_contracts/runner-event.d.ts +8 -8
  26. package/dist/_contracts/runner-event.js +9 -9
  27. package/dist/_contracts/runtime-manifest.d.ts +12 -12
  28. package/dist/_contracts/runtime-manifest.js +1 -1
  29. package/dist/_contracts/runtime-sizes.d.ts +12 -12
  30. package/dist/_contracts/runtime-sizes.js +18 -18
  31. package/dist/_contracts/runtime-types.d.ts +117 -117
  32. package/dist/_contracts/runtime-types.js +2 -2
  33. package/dist/_contracts/sdk-errors.d.ts +3 -3
  34. package/dist/_contracts/sdk-errors.js +4 -4
  35. package/dist/_contracts/sdk-secrets.js +10 -10
  36. package/dist/_contracts/session-artifacts.d.ts +151 -0
  37. package/dist/_contracts/session-artifacts.js +234 -0
  38. package/dist/_contracts/{run-config.d.ts → session-config.d.ts} +29 -29
  39. package/dist/_contracts/{run-config.js → session-config.js} +37 -37
  40. package/dist/_contracts/session-cost.d.ts +140 -0
  41. package/dist/_contracts/{run-cost.js → session-cost.js} +94 -94
  42. package/dist/_contracts/{run-custody.d.ts → session-custody.d.ts} +16 -16
  43. package/dist/_contracts/{run-custody.js → session-custody.js} +42 -42
  44. package/dist/_contracts/session-record.d.ts +112 -0
  45. package/dist/_contracts/{run-record.js → session-record.js} +23 -23
  46. package/dist/_contracts/session-retention.d.ts +209 -0
  47. package/dist/_contracts/{run-retention.js → session-retention.js} +116 -116
  48. package/dist/_contracts/{run-unit.d.ts → session-unit.d.ts} +38 -38
  49. package/dist/_contracts/{run-unit.js → session-unit.js} +33 -33
  50. package/dist/_contracts/side-effect-audit.d.ts +14 -14
  51. package/dist/_contracts/side-effect-audit.js +38 -38
  52. package/dist/_contracts/sse.d.ts +1 -1
  53. package/dist/_contracts/sse.js +0 -0
  54. package/dist/_contracts/stable.d.ts +1 -1
  55. package/dist/_contracts/stable.js +1 -1
  56. package/dist/_contracts/status.d.ts +18 -18
  57. package/dist/_contracts/status.js +19 -19
  58. package/dist/_contracts/submission.d.ts +101 -99
  59. package/dist/_contracts/submission.js +96 -96
  60. package/dist/_contracts/{run-trace.d.ts → turn-trace.d.ts} +1 -1
  61. package/dist/_contracts/webhook-verify.d.ts +1 -1
  62. package/dist/_contracts/webhook-verify.js +1 -1
  63. package/dist/agents-md.d.ts +4 -4
  64. package/dist/agents-md.js +5 -5
  65. package/dist/agents-md.js.map +1 -1
  66. package/dist/bundle.d.ts +1 -1
  67. package/dist/bundle.js +1 -1
  68. package/dist/bundle.js.map +1 -1
  69. package/dist/cli.mjs +580 -623
  70. package/dist/cli.mjs.sha256 +1 -1
  71. package/dist/client.d.ts +141 -155
  72. package/dist/client.js +236 -245
  73. package/dist/client.js.map +1 -1
  74. package/dist/fetch-archive.js +3 -3
  75. package/dist/file.d.ts +7 -7
  76. package/dist/file.js +8 -8
  77. package/dist/file.js.map +1 -1
  78. package/dist/index.d.ts +10 -10
  79. package/dist/index.js +8 -8
  80. package/dist/index.js.map +1 -1
  81. package/dist/mcp-server.d.ts +1 -1
  82. package/dist/retry.d.ts +1 -1
  83. package/dist/secret.d.ts +6 -6
  84. package/dist/secret.js +6 -6
  85. package/dist/secret.js.map +1 -1
  86. package/dist/skill.d.ts +2 -2
  87. package/dist/skill.js +3 -3
  88. package/dist/skill.js.map +1 -1
  89. package/dist/tool.js +2 -2
  90. package/dist/tool.js.map +1 -1
  91. package/dist/version.d.ts +1 -1
  92. package/dist/version.js +1 -1
  93. package/dist/version.js.map +1 -1
  94. package/docs/authentication.md +11 -11
  95. package/docs/billing.md +3 -3
  96. package/docs/cleanup.md +5 -5
  97. package/docs/concepts/agent-tools.md +5 -5
  98. package/docs/concepts/composition.md +3 -3
  99. package/docs/concepts/providers-and-runtimes.md +3 -3
  100. package/docs/concepts/{runs.md → sessions.md} +8 -8
  101. package/docs/concepts/subagents.md +16 -16
  102. package/docs/credentials.md +7 -7
  103. package/docs/defaults.md +14 -14
  104. package/docs/errors.md +10 -10
  105. package/docs/events.md +26 -26
  106. package/docs/files.md +267 -0
  107. package/docs/limits-and-quotas.md +35 -35
  108. package/docs/limits.md +6 -6
  109. package/docs/mcp.md +3 -3
  110. package/docs/networking.md +24 -24
  111. package/docs/provider-runtime-capabilities.md +1 -1
  112. package/docs/public-surface.json +6 -6
  113. package/docs/quickstart.md +13 -13
  114. package/docs/release.md +5 -5
  115. package/docs/retries.md +3 -3
  116. package/docs/secrets.md +16 -16
  117. package/docs/session-config.md +58 -0
  118. package/docs/{run-record.md → session-record.md} +16 -16
  119. package/docs/skills.md +9 -9
  120. package/docs/vision-skills.md +7 -7
  121. package/docs/webhooks.md +11 -11
  122. package/examples/feature-tour.ts +17 -17
  123. package/examples/spike-settle-latency.ts +11 -11
  124. package/package.json +1 -1
  125. package/dist/_contracts/run-artifacts.d.ts +0 -49
  126. package/dist/_contracts/run-artifacts.js +0 -102
  127. package/dist/_contracts/run-cost.d.ts +0 -140
  128. package/dist/_contracts/run-record.d.ts +0 -116
  129. package/dist/_contracts/run-retention.d.ts +0 -209
  130. package/docs/outputs.md +0 -267
  131. package/docs/run-config.md +0 -58
  132. /package/dist/_contracts/{run-trace.js → turn-trace.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  import { strToU8, zipSync } from "fflate";
2
2
  import { randomUUID } from "node:crypto";
3
- import { normalizeRunUnit } from "./run-unit.js";
4
- import { AexNetworkError, RunConfigValidationError, RunStateError } from "./sdk-errors.js";
5
- import { assertRunRecordArchivePublicSafeV1, buildRunRecordDownloadManifestV1 } from "./run-record.js";
3
+ import { normalizeSessionUnit } from "./session-unit.js";
4
+ import { AexNetworkError, SessionConfigValidationError, SessionStateError } from "./sdk-errors.js";
5
+ import { assertSessionRecordArchivePublicSafeV1, buildSessionRecordDownloadManifestV1 } from "./session-record.js";
6
6
  /**
7
7
  * The single source of truth for SDK<->BFF transport. The SDK class
8
8
  * AND the CLI subcommands both call these functions; neither
@@ -14,38 +14,38 @@ import { assertRunRecordArchivePublicSafeV1, buildRunRecordDownloadManifestV1 }
14
14
  * Workspace identity is derived server-side from the API key on
15
15
  * every request; callers do not pass `workspaceId`.
16
16
  */
17
- export async function getRun(http, runId) {
18
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
19
- return hasRun(result) ? result.run : result;
17
+ export async function getSessionRecord(http, sessionId) {
18
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}`);
19
+ return hasSessionRecord(result) ? result.session : result;
20
20
  }
21
21
  /**
22
- * Strongly-typed accessor for the full self-contained run unit:
22
+ * Strongly-typed accessor for the full self-contained session unit:
23
23
  * parsed submission inputs, attempts, indexed events (with
24
- * pagination cursor for large runs), raw-event Storage manifest,
25
- * outputs, capture failures, and the proxy-call audit.
24
+ * pagination cursor for large sessions), raw-event Storage manifest,
25
+ * files, capture failures, and the proxy-call audit.
26
26
  *
27
- * Backed by the same `GET /api/runs/:runId` endpoint that
28
- * `getRun` calls; this variant just narrows the return type to
29
- * the documented wire shape. Prefer this for new code; `getRun`
27
+ * Backed by the same `GET /api/sessions/:sessionId` endpoint that
28
+ * `getSessionRecord` calls; this variant just narrows the return type to
29
+ * the documented wire shape. Prefer this for new code; `getSessionRecord`
30
30
  * stays for callers that only need the loose record.
31
31
  */
32
- export async function getRunUnit(http, runId) {
33
- // Normalize so the RunUnit type contract holds at runtime: the managed plane
32
+ export async function getSessionUnit(http, sessionId) {
33
+ // Normalize so the SessionUnit type contract holds at runtime: the managed plane
34
34
  // returns a lean record and omits the aggregate collections (F25). The
35
- // aggregates default to empty (safe array/page access) — read outputs()/events()
36
- // for the authoritative per-run data on that plane.
37
- return normalizeRunUnit(await http.request(`/api/runs/${encodeURIComponent(runId)}`));
35
+ // aggregates default to empty (safe array/page access) — read files()/events()
36
+ // for the authoritative per-session data on that plane.
37
+ return normalizeSessionUnit(await http.request(`/api/sessions/${encodeURIComponent(sessionId)}`));
38
38
  }
39
39
  /**
40
- * List the runs in the token's workspace, most-recent first, one page at a time.
41
- * Backed by `GET /api/runs` (workspace-token gated; the bare collection path, NOT
42
- * the run-keyed `GET /api/runs/:runId`). The server clamps `limit` to [1, 100] and
40
+ * List the sessions in the token's workspace, most-recent first, one page at a time.
41
+ * Backed by `GET /api/sessions` (workspace-token gated; the bare collection path, NOT
42
+ * the session-keyed `GET /api/sessions/:sessionId`). The server clamps `limit` to [1, 100] and
43
43
  * returns an opaque `nextCursor` for the next page (absent on the last page).
44
44
  *
45
- * Returns public-safe {@link RunSummary} rows only — never the submission snapshot.
46
- * For a single page; callers wanting every run loop on `nextCursor` themselves.
45
+ * Returns public-safe {@link SessionRecordSummary} rows only — never the submission snapshot.
46
+ * For a single page; callers wanting every session loop on `nextCursor` themselves.
47
47
  */
48
- export async function listRuns(http, query) {
48
+ export async function listSessionRecords(http, query) {
49
49
  const params = {};
50
50
  if (query?.status !== undefined)
51
51
  params.status = query.status;
@@ -55,39 +55,39 @@ export async function listRuns(http, query) {
55
55
  params.limit = String(query.limit);
56
56
  if (query?.cursor !== undefined)
57
57
  params.cursor = query.cursor;
58
- const page = await http.request("/api/runs", {}, params);
59
- // Defensive contract enforcement: some deployed planes leak non-run marker
60
- // rows (settle-time ledger/spendmark items) into the run-list index. Those
58
+ const page = await http.request("/api/sessions", {}, params);
59
+ // Defensive contract enforcement: some deployed planes leak non-session marker
60
+ // rows (settle-time ledger/spendmark items) into the session-list index. Those
61
61
  // phantoms carry only { id, createdAt } and would surface as duplicate,
62
- // status-less RunSummary entries. Drop anything missing the fields
63
- // RunSummary declares required, so callers can trust the published type.
62
+ // status-less SessionRecordSummary entries. Drop anything missing the fields
63
+ // SessionRecordSummary declares required, so callers can trust the published type.
64
64
  // The same enforcement covers `costUsd`: deployed planes serve `null` for
65
- // runs with no settled telemetry, but RunSummary declares `costUsd?: number`
65
+ // sessions with no settled telemetry, but SessionRecordSummary declares `costUsd?: number`
66
66
  // — normalize `null` to absent so typed callers never see it.
67
67
  let changed = false;
68
- const runs = [];
69
- for (const run of page.runs) {
70
- if (typeof run.id !== "string" ||
71
- typeof run.status !== "string" ||
72
- typeof run.createdAt !== "string" ||
73
- typeof run.updatedAt !== "string") {
68
+ const sessions = [];
69
+ for (const session of page.sessions) {
70
+ if (typeof session.id !== "string" ||
71
+ typeof session.status !== "string" ||
72
+ typeof session.createdAt !== "string" ||
73
+ typeof session.updatedAt !== "string") {
74
74
  changed = true;
75
75
  continue;
76
76
  }
77
- if (typeof run.costUsd !== "number" && run.costUsd !== undefined) {
78
- const { costUsd: _dropped, ...rest } = run;
79
- runs.push(rest);
77
+ if (typeof session.costUsd !== "number" && session.costUsd !== undefined) {
78
+ const { costUsd: _dropped, ...rest } = session;
79
+ sessions.push(rest);
80
80
  changed = true;
81
81
  continue;
82
82
  }
83
- runs.push(run);
83
+ sessions.push(session);
84
84
  }
85
- return changed ? { ...page, runs } : page;
85
+ return changed ? { ...page, sessions } : page;
86
86
  }
87
87
  /**
88
88
  * Resolve a caller-supplied idempotency key to the value that ships on the
89
89
  * request. FAIL-FAST: an empty or whitespace-only key THROWS
90
- * {@link RunConfigValidationError} — a footgun that silently disabled dedup
90
+ * {@link SessionConfigValidationError} — a footgun that silently disabled dedup
91
91
  * (`?? generate()` kept `''`, then a downstream truthy header-drop shipped no
92
92
  * `Idempotency-Key`). An absent key generates a fresh one; a real key is
93
93
  * returned verbatim. The single choke point every send/create/run entry uses.
@@ -97,7 +97,7 @@ export function resolveIdempotencyKey(key) {
97
97
  return `aex-idem-${randomUUID()}`;
98
98
  }
99
99
  if (typeof key !== "string" || key.trim().length === 0) {
100
- throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
100
+ throw new SessionConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
101
101
  field: "idempotencyKey",
102
102
  value: key
103
103
  });
@@ -113,7 +113,7 @@ function idempotencyHeaders(options) {
113
113
  if (options?.idempotencyKey === undefined)
114
114
  return undefined;
115
115
  if (typeof options.idempotencyKey !== "string" || options.idempotencyKey.trim().length === 0) {
116
- throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
116
+ throw new SessionConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
117
117
  field: "idempotencyKey",
118
118
  value: options.idempotencyKey
119
119
  });
@@ -131,8 +131,8 @@ export async function createSession(http, request, options) {
131
131
  }
132
132
  /**
133
133
  * Fire-and-forget submit — create the session and post its first turn WITHOUT
134
- * awaiting the turn to settle (the honest counterpart to await-settle `run()`).
135
- * Returns the `runId` immediately; observe the run via a `webhook`, the event
134
+ * awaiting the turn to settle (the honest counterpart to await-settle `start()`).
135
+ * Returns the `sessionId` immediately; observe the session via a `webhook`, the event
136
136
  * stream, or by re-opening the session. Mirrors {@link createSession}'s
137
137
  * idempotency handling.
138
138
  */
@@ -147,13 +147,13 @@ export async function submit(http, request, options) {
147
147
  const sessionId = created.sessionId ?? created.id;
148
148
  const accepted = await sendSessionMessage(http, sessionId, { input }, { idempotencyKey: messageKey });
149
149
  const session = accepted.session;
150
- return { runId: session.sessionId ?? session.id, session };
150
+ return { sessionId: session.sessionId ?? session.id, session };
151
151
  }
152
152
  function assertSubmitInput(input) {
153
153
  const ok = (typeof input === "string" && input.length > 0) ||
154
154
  (Array.isArray(input) && input.length > 0 && input.every((segment) => typeof segment === "string" && segment.length > 0));
155
155
  if (!ok) {
156
- throw new RunConfigValidationError("submit: request.input must be a non-empty string or string array", {
156
+ throw new SessionConfigValidationError("submit: request.input must be a non-empty string or string array", {
157
157
  field: "input",
158
158
  value: input
159
159
  });
@@ -242,25 +242,25 @@ export async function listSessionEvents(http, sessionId) {
242
242
  }
243
243
  return all;
244
244
  }
245
- export async function listSessionOutputs(http, sessionId, query) {
246
- const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/outputs`);
247
- return query === undefined ? result.outputs : filterOutputs(result.outputs, query);
245
+ export async function listSessionFiles(http, sessionId, query) {
246
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/files`);
247
+ return query === undefined ? result.files : filterSessionFiles(result.files, query);
248
248
  }
249
249
  export async function getSessionCoordinatorTicket(http, sessionId) {
250
250
  return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/events/ticket`, { method: "POST" });
251
251
  }
252
252
  // Bound the transparent pager: the read route caps each page at 1000, so this
253
- // admits up to ~1e6 events before bailing — past any real run, but bounded so a
253
+ // admits up to ~1e6 events before bailing — past any real session, but bounded so a
254
254
  // server that never clears `nextCursor` can't loop forever.
255
255
  const LIST_EVENTS_PAGE_BUDGET = 1000;
256
256
  /**
257
- * List a run's events. The read endpoint is PAGED (bounded per response so a
257
+ * List a session's events. The read endpoint is PAGED (bounded per response so a
258
258
  * long run can't return an unbounded body); this follows `nextCursor` across
259
259
  * pages and returns the FULL accumulated list, preserving the prior single-call
260
260
  * contract for callers (download/*, CLI, streamEvents polling).
261
261
  */
262
- export async function listRunEvents(http, runId) {
263
- const path = `/api/runs/${encodeURIComponent(runId)}/events`;
262
+ export async function listSessionRecordEvents(http, sessionId) {
263
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/events`;
264
264
  const all = [];
265
265
  let cursor;
266
266
  for (let page = 0; page < LIST_EVENTS_PAGE_BUDGET; page++) {
@@ -275,35 +275,31 @@ export async function listRunEvents(http, runId) {
275
275
  }
276
276
  /**
277
277
  * Mint a short-lived coordinator WS ticket via the workspace-token-gated
278
- * broker (`/api/runs/:id/events/ticket`). The returned `wsUrl` + `ticket`
278
+ * broker (`/api/sessions/:id/events/ticket`). The returned `wsUrl` + `ticket`
279
279
  * open the live event stream directly against the coordinator. Throws if no
280
280
  * coordinator is configured for the deployment (HTTP 503).
281
281
  */
282
- export async function getCoordinatorTicket(http, runId) {
283
- return http.request(`/api/runs/${encodeURIComponent(runId)}/events/ticket`, { method: "POST" });
284
- }
285
- export async function listOutputs(http, runId, query) {
286
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/outputs`);
287
- return query === undefined ? result.outputs : filterOutputs(result.outputs, query);
282
+ export async function getCoordinatorTicket(http, sessionId) {
283
+ return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/events/ticket`, { method: "POST" });
288
284
  }
289
- export async function findOutputs(http, runId, query) {
290
- return listOutputs(http, runId, query);
285
+ export async function findSessionFiles(http, sessionId, query) {
286
+ return listSessionFiles(http, sessionId, query);
291
287
  }
292
- export async function findOutput(http, runId, query) {
293
- const matches = await findOutputs(http, runId, query);
288
+ export async function findSessionFile(http, sessionId, query) {
289
+ const matches = await findSessionFiles(http, sessionId, query);
294
290
  if (matches.length === 0)
295
291
  return null;
296
292
  if (matches.length === 1)
297
293
  return matches[0];
298
- throw new RunStateError("outputs.findOne: output query matched multiple files", {
299
- runId,
300
- matches: matches.map((output) => output.filename ?? output.id)
294
+ throw new SessionStateError("files.findOne: file query matched multiple files", {
295
+ sessionId,
296
+ matches: matches.map((file) => file.filename ?? file.id)
301
297
  });
302
298
  }
303
- export async function outputLink(http, runId, selectorOrQuery, options) {
304
- const output = await resolveOutputLinkTarget(http, runId, selectorOrQuery);
305
- const expiresInSeconds = normalizeOutputLinkExpiresIn(options?.expiresIn);
306
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/link`, {
299
+ export async function sessionFileLink(http, sessionId, selectorOrQuery, options) {
300
+ const file = await resolveSessionFileLinkTarget(http, sessionId, selectorOrQuery);
301
+ const expiresInSeconds = normalizeSessionFileLinkExpiresIn(options?.expiresIn);
302
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(file.id)}/link`, {
307
303
  method: "POST",
308
304
  body: JSON.stringify({ expiresInSeconds })
309
305
  });
@@ -312,7 +308,7 @@ export async function outputLink(http, runId, selectorOrQuery, options) {
312
308
  ...result,
313
309
  expiresInSeconds: effectiveExpiresIn,
314
310
  expiresAt: result.expiresAt ?? syntheticExpiresAt(effectiveExpiresIn),
315
- output: result.output ?? output
311
+ file: result.file ?? file
316
312
  };
317
313
  }
318
314
  /**
@@ -323,12 +319,12 @@ export async function outputLink(http, runId, selectorOrQuery, options) {
323
319
  function syntheticExpiresAt(expiresInSeconds) {
324
320
  return new Date(Date.now() + expiresInSeconds * 1000).toISOString();
325
321
  }
326
- export async function createOutputLink(http, runId, selectorOrQuery, options) {
327
- return outputLink(http, runId, selectorOrQuery, options);
322
+ export async function createSessionFileLink(http, sessionId, selectorOrQuery, options) {
323
+ return sessionFileLink(http, sessionId, selectorOrQuery, options);
328
324
  }
329
- export async function eventArchiveLink(http, runId, options) {
330
- const expiresInSeconds = normalizeOutputLinkExpiresIn(options?.expiresIn);
331
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/events/link`, {
325
+ export async function eventArchiveLink(http, sessionId, options) {
326
+ const expiresInSeconds = normalizeSessionFileLinkExpiresIn(options?.expiresIn);
327
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/events/link`, {
332
328
  method: "POST",
333
329
  body: JSON.stringify({ expiresInSeconds })
334
330
  });
@@ -339,16 +335,16 @@ export async function eventArchiveLink(http, runId, options) {
339
335
  expiresAt: result.expiresAt ?? syntheticExpiresAt(effectiveExpiresIn)
340
336
  };
341
337
  }
342
- export function resolveOutputFileSelector(outputs, selector, runId) {
338
+ export function resolveSessionFileSelector(files, selector, sessionId) {
343
339
  if (isPathSelector(selector)) {
344
- const target = normalizeOutputLookupPath(selector.path);
340
+ const target = normalizeSessionFileLookupPath(selector.path);
345
341
  if (!target) {
346
- throw new RunStateError("outputs.download: output path must be non-empty", { runId, path: selector.path });
342
+ throw new SessionStateError("files.download: file path must be non-empty", { sessionId, path: selector.path });
347
343
  }
348
- const matches = outputs.filter((output) => {
349
- if (typeof output.filename !== "string")
344
+ const matches = files.filter((file) => {
345
+ if (typeof file.filename !== "string")
350
346
  return false;
351
- const filename = normalizeOutputLookupPath(output.filename);
347
+ const filename = normalizeSessionFileLookupPath(file.filename);
352
348
  if (selector.match === "suffix") {
353
349
  return filename === target || filename.endsWith(`/${target}`);
354
350
  }
@@ -357,80 +353,80 @@ export function resolveOutputFileSelector(outputs, selector, runId) {
357
353
  if (matches.length === 1)
358
354
  return matches[0];
359
355
  if (matches.length > 1) {
360
- throw new RunStateError(`outputs.download: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
356
+ throw new SessionStateError(`files.download: file path "${selector.path}" matched multiple files`, { sessionId, path: selector.path, matches: matches.map((file) => file.filename ?? file.id) });
361
357
  }
362
- throw new RunStateError(`outputs.download: output path "${selector.path}" was not found`, {
363
- runId,
358
+ throw new SessionStateError(`files.download: file path "${selector.path}" was not found`, {
359
+ sessionId,
364
360
  path: selector.path
365
361
  });
366
362
  }
367
363
  if (typeof selector?.id !== "string" || selector.id.length === 0) {
368
- throw new RunStateError("outputs.download: selector must include an output id or path", { runId });
364
+ throw new SessionStateError("files.download: selector must include a file id or path", { sessionId });
369
365
  }
370
366
  return { ...selector, id: selector.id };
371
367
  }
372
- export async function downloadOutput(http, runId, selector, options) {
373
- const output = isPathSelector(selector)
374
- ? resolveOutputFileSelector(await listOutputs(http, runId), selector, runId)
375
- : resolveOutputFileSelector([], selector, runId);
376
- const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
377
- const path = `/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`;
378
- return { output, bytes: await downloadOutputBytesWithRetry(http, path, timeoutMs) };
379
- }
380
- /** Byte ceiling for {@link readOutputText} — a hard cap even if a caller asks for more. */
381
- export const READ_OUTPUT_TEXT_MAX_BYTES = 10_000_000;
382
- /** Default `maxBytes` for {@link readOutputText} — a chat-sized preview. */
383
- export const READ_OUTPUT_TEXT_DEFAULT_BYTES = 50_000;
384
- /** Default per-attempt timeout while fetching or reading one output body. */
385
- export const OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 30_000;
386
- /** Idempotent output GETs retry once on a transfer timeout. */
387
- export const OUTPUT_FILE_TRANSFER_ATTEMPTS = 2;
368
+ export async function downloadSessionFile(http, sessionId, selector, options) {
369
+ const file = isPathSelector(selector)
370
+ ? resolveSessionFileSelector(await listSessionFiles(http, sessionId), selector, sessionId)
371
+ : resolveSessionFileSelector([], selector, sessionId);
372
+ const timeoutMs = normalizeSessionFileTransferTimeoutMs(options?.timeoutMs);
373
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(file.id)}/download`;
374
+ return { file, bytes: await downloadSessionFileBytesWithRetry(http, path, timeoutMs) };
375
+ }
376
+ /** Byte ceiling for {@link readSessionFileText} — a hard cap even if a caller asks for more. */
377
+ export const READ_SESSION_FILE_TEXT_MAX_BYTES = 10_000_000;
378
+ /** Default `maxBytes` for {@link readSessionFileText} — a chat-sized preview. */
379
+ export const READ_SESSION_FILE_TEXT_DEFAULT_BYTES = 50_000;
380
+ /** Default per-attempt timeout while fetching or reading one session file body. */
381
+ export const SESSION_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 30_000;
382
+ /** Idempotent file GETs retry once on a transfer timeout. */
383
+ export const SESSION_FILE_TRANSFER_ATTEMPTS = 2;
388
384
  /**
389
- * Read ONE output file as byte-capped, decoded UTF-8 text. Built for handing a run
385
+ * Read ONE session file as byte-capped, decoded UTF-8 text. Built for handing a session
390
386
  * deliverable to an LLM tool: it streams the file body and STOPS at `maxBytes`, so
391
387
  * a 200 MB artifact never fully buffers in memory or context. `truncated` is true
392
388
  * when the file is larger than the cap. Optionally `grep` keeps only matching lines.
393
389
  *
394
- * Selector is the same `{ path }` / `{ id }` shape as `downloadOutput`. A path
395
- * selector lists the run's outputs to resolve the id; an id selector skips that.
390
+ * Selector is the same `{ path }` / `{ id }` shape as `downloadSessionFile`. A path
391
+ * selector lists the session's files to resolve the id; an id selector skips that.
396
392
  */
397
- export async function readOutputText(http, runId, selector, options) {
398
- const maxBytes = Math.max(1, Math.min(options?.maxBytes ?? READ_OUTPUT_TEXT_DEFAULT_BYTES, READ_OUTPUT_TEXT_MAX_BYTES));
399
- const output = isPathSelector(selector)
400
- ? resolveOutputFileSelector(await listOutputs(http, runId), selector, runId)
401
- : resolveOutputFileSelector([], selector, runId);
402
- const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
403
- const path = `/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`;
404
- const capped = await readOutputTextWithRetry(http, path, maxBytes, timeoutMs);
393
+ export async function readSessionFileText(http, sessionId, selector, options) {
394
+ const maxBytes = Math.max(1, Math.min(options?.maxBytes ?? READ_SESSION_FILE_TEXT_DEFAULT_BYTES, READ_SESSION_FILE_TEXT_MAX_BYTES));
395
+ const file = isPathSelector(selector)
396
+ ? resolveSessionFileSelector(await listSessionFiles(http, sessionId), selector, sessionId)
397
+ : resolveSessionFileSelector([], selector, sessionId);
398
+ const timeoutMs = normalizeSessionFileTransferTimeoutMs(options?.timeoutMs);
399
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(file.id)}/download`;
400
+ const capped = await readSessionFileTextWithRetry(http, path, maxBytes, timeoutMs);
405
401
  const text = options?.grep === undefined ? capped.text : grepLines(capped.text, options.grep);
406
- return { output, text, truncated: capped.truncated, totalBytes: capped.totalBytes };
402
+ return { file, text, truncated: capped.truncated, totalBytes: capped.totalBytes };
407
403
  }
408
- async function downloadOutputBytesWithRetry(http, path, timeoutMs) {
409
- return outputTransferWithRetry(path, timeoutMs, async () => {
410
- const response = await downloadOutputResponse(http, path, timeoutMs);
404
+ async function downloadSessionFileBytesWithRetry(http, path, timeoutMs) {
405
+ return sessionFileTransferWithRetry(path, timeoutMs, async () => {
406
+ const response = await downloadSessionFileResponse(http, path, timeoutMs);
411
407
  return readResponseBytes(response, timeoutMs);
412
408
  });
413
409
  }
414
- async function readOutputTextWithRetry(http, path, maxBytes, timeoutMs) {
415
- return outputTransferWithRetry(path, timeoutMs, async () => {
416
- const response = await downloadOutputResponse(http, path, timeoutMs);
410
+ async function readSessionFileTextWithRetry(http, path, maxBytes, timeoutMs) {
411
+ return sessionFileTransferWithRetry(path, timeoutMs, async () => {
412
+ const response = await downloadSessionFileResponse(http, path, timeoutMs);
417
413
  return readCappedText(response, maxBytes, timeoutMs);
418
414
  });
419
415
  }
420
- async function downloadOutputResponse(http, path, timeoutMs) {
416
+ async function downloadSessionFileResponse(http, path, timeoutMs) {
421
417
  const controller = new AbortController();
422
- const { response } = await withOutputTransferTimeout(http.download(path, { signal: controller.signal }), timeoutMs, () => controller.abort(), "download-open");
418
+ const { response } = await withSessionFileTransferTimeout(http.download(path, { signal: controller.signal }), timeoutMs, () => controller.abort(), "download-open");
423
419
  return response;
424
420
  }
425
- async function outputTransferWithRetry(path, timeoutMs, action) {
421
+ async function sessionFileTransferWithRetry(path, timeoutMs, action) {
426
422
  const startedMs = Date.now();
427
423
  let lastTimeout;
428
- for (let attempt = 1; attempt <= OUTPUT_FILE_TRANSFER_ATTEMPTS; attempt += 1) {
424
+ for (let attempt = 1; attempt <= SESSION_FILE_TRANSFER_ATTEMPTS; attempt += 1) {
429
425
  try {
430
426
  return await action();
431
427
  }
432
428
  catch (err) {
433
- if (!(err instanceof OutputTransferTimeoutError))
429
+ if (!(err instanceof SessionFileTransferTimeoutError))
434
430
  throw err;
435
431
  lastTimeout = err;
436
432
  }
@@ -439,37 +435,37 @@ async function outputTransferWithRetry(path, timeoutMs, action) {
439
435
  method: "GET",
440
436
  host: "",
441
437
  path,
442
- cause: lastTimeout ?? new OutputTransferTimeoutError("unknown", timeoutMs),
443
- attempts: OUTPUT_FILE_TRANSFER_ATTEMPTS,
438
+ cause: lastTimeout ?? new SessionFileTransferTimeoutError("unknown", timeoutMs),
439
+ attempts: SESSION_FILE_TRANSFER_ATTEMPTS,
444
440
  elapsedMs: Date.now() - startedMs
445
441
  });
446
442
  }
447
- function normalizeOutputTransferTimeoutMs(value) {
443
+ function normalizeSessionFileTransferTimeoutMs(value) {
448
444
  if (value === undefined)
449
- return OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS;
445
+ return SESSION_FILE_TRANSFER_DEFAULT_TIMEOUT_MS;
450
446
  if (!Number.isFinite(value) || value <= 0) {
451
- throw new RunConfigValidationError("outputs.download: timeoutMs must be a positive finite number", {
447
+ throw new SessionConfigValidationError("files.download: timeoutMs must be a positive finite number", {
452
448
  timeoutMs: value
453
449
  });
454
450
  }
455
451
  return Math.max(1, Math.floor(value));
456
452
  }
457
- class OutputTransferTimeoutError extends Error {
453
+ class SessionFileTransferTimeoutError extends Error {
458
454
  code = "ETIMEDOUT";
459
455
  phase;
460
456
  constructor(phase, timeoutMs) {
461
- super(`output transfer phase=${phase} timed out after ${timeoutMs}ms`);
462
- this.name = "OutputTransferTimeoutError";
457
+ super(`file transfer phase=${phase} timed out after ${timeoutMs}ms`);
458
+ this.name = "SessionFileTransferTimeoutError";
463
459
  this.phase = phase;
464
460
  }
465
461
  }
466
- async function withOutputTransferTimeout(promise, timeoutMs, abort, phase) {
462
+ async function withSessionFileTransferTimeout(promise, timeoutMs, abort, phase) {
467
463
  let timedOut = false;
468
464
  let timeout;
469
465
  const timeoutPromise = new Promise((_, reject) => {
470
466
  timeout = setTimeout(() => {
471
467
  timedOut = true;
472
- reject(new OutputTransferTimeoutError(phase, timeoutMs));
468
+ reject(new SessionFileTransferTimeoutError(phase, timeoutMs));
473
469
  queueMicrotask(() => {
474
470
  try {
475
471
  abort();
@@ -485,7 +481,7 @@ async function withOutputTransferTimeout(promise, timeoutMs, abort, phase) {
485
481
  }
486
482
  catch (err) {
487
483
  if (timedOut && isAbortLikeError(err))
488
- throw new OutputTransferTimeoutError(phase, timeoutMs);
484
+ throw new SessionFileTransferTimeoutError(phase, timeoutMs);
489
485
  throw err;
490
486
  }
491
487
  finally {
@@ -500,14 +496,14 @@ function isAbortLikeError(err) {
500
496
  async function readResponseBytes(response, timeoutMs) {
501
497
  const body = response.body;
502
498
  if (!body) {
503
- const buffer = await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => { }, "body-read");
499
+ const buffer = await withSessionFileTransferTimeout(response.arrayBuffer(), timeoutMs, () => { }, "body-read");
504
500
  return new Uint8Array(buffer);
505
501
  }
506
502
  const reader = body.getReader();
507
503
  const chunks = [];
508
504
  try {
509
505
  while (true) {
510
- const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
506
+ const { done, value } = await withSessionFileTransferTimeout(reader.read(), timeoutMs, () => {
511
507
  void reader.cancel().catch(() => { });
512
508
  }, "body-read");
513
509
  if (done)
@@ -534,7 +530,7 @@ async function readCappedText(response, maxBytes, timeoutMs) {
534
530
  const body = response.body;
535
531
  if (!body) {
536
532
  // No streaming body (some fetch polyfills) — buffer, then slice to the cap.
537
- const buf = new Uint8Array(await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => { }, "body-read"));
533
+ const buf = new Uint8Array(await withSessionFileTransferTimeout(response.arrayBuffer(), timeoutMs, () => { }, "body-read"));
538
534
  const total = declared ?? buf.byteLength;
539
535
  return {
540
536
  text: decoder.decode(buf.subarray(0, maxBytes)),
@@ -548,7 +544,7 @@ async function readCappedText(response, maxBytes, timeoutMs) {
548
544
  let sawMore = false;
549
545
  try {
550
546
  while (read < maxBytes) {
551
- const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
547
+ const { done, value } = await withSessionFileTransferTimeout(reader.read(), timeoutMs, () => {
552
548
  void reader.cancel().catch(() => { });
553
549
  }, "body-read");
554
550
  if (done)
@@ -560,7 +556,7 @@ async function readCappedText(response, maxBytes, timeoutMs) {
560
556
  }
561
557
  if (read >= maxBytes) {
562
558
  // We hit the cap; peek once more to learn whether bytes remain, then stop.
563
- const next = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
559
+ const next = await withSessionFileTransferTimeout(reader.read(), timeoutMs, () => {
564
560
  void reader.cancel().catch(() => { });
565
561
  }, "body-read");
566
562
  if (!next.done && next.value && next.value.byteLength > 0)
@@ -597,47 +593,41 @@ function grepLines(text, pattern) {
597
593
  .join("\n");
598
594
  }
599
595
  /**
600
- * List a run's subagent CHILD runs (`GET /runs/:id/children`). Each row is a
601
- * {@link ChildRunRef} whose `id` resolves through the run facade (getRun /
602
- * events / outputs) — so every child the platform hands you is resolvable. An
603
- * empty array means the run spawned no children.
596
+ * List a session's subagent child sessions (`GET /sessions/:id/children`). Each row is a
597
+ * {@link ChildSessionRef} whose `id` resolves through the session record facade (getSessionRecord /
598
+ * events / files) — so every child the platform hands you is resolvable. An
599
+ * empty array means the session spawned no children.
604
600
  */
605
- export async function listRunChildren(http, runId) {
606
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/children`);
601
+ export async function listSessionChildren(http, sessionId) {
602
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/children`);
607
603
  return Array.isArray(result)
608
604
  ? result
609
605
  : result.children;
610
606
  }
611
- export async function cancelRun(http, runId) {
612
- await http.request(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
613
- }
614
- export async function deleteRun(http, runId) {
615
- await http.request(`/api/runs/${encodeURIComponent(runId)}`, { method: "DELETE" });
616
- }
617
607
  /**
618
- * List a run's webhook delivery attempts (the per-run delivery ledger). Returns
619
- * the rows surfaced by `GET /api/runs/:id/webhook-deliveries`; an empty array
620
- * means the run carried no `webhook` or has not reached a terminal state yet.
608
+ * List a session's webhook delivery attempts (the per-session delivery ledger). Returns
609
+ * the rows surfaced by `GET /api/sessions/:id/webhook-deliveries`; an empty array
610
+ * means the session carried no `webhook` or has not reached a terminal state yet.
621
611
  */
622
- export async function getRunWebhookDeliveries(http, runId) {
623
- const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/webhook-deliveries`);
612
+ export async function getSessionWebhookDeliveries(http, sessionId) {
613
+ const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/webhook-deliveries`);
624
614
  return Array.isArray(result)
625
615
  ? result
626
616
  : result.deliveries;
627
617
  }
628
618
  /**
629
- * Manually re-trigger a run's webhook delivery: resets the row to `pending` and
619
+ * Manually re-trigger a session's webhook delivery: resets the row to `pending` and
630
620
  * re-sends the frozen payload with the SAME `webhook-id` so the consumer
631
621
  * dedupes. Idempotent from the caller's view.
632
622
  */
633
- export async function redeliverRunWebhook(http, runId, deliveryId) {
634
- await http.request(`/api/runs/${encodeURIComponent(runId)}/webhook-deliveries/${encodeURIComponent(deliveryId)}/redeliver`, { method: "POST" });
623
+ export async function redeliverSessionWebhook(http, sessionId, deliveryId) {
624
+ await http.request(`/api/sessions/${encodeURIComponent(sessionId)}/webhook-deliveries/${encodeURIComponent(deliveryId)}/redeliver`, { method: "POST" });
635
625
  }
636
626
  /**
637
627
  * Delete a workspace asset cache entry. Accepts an `asset_<id>` value,
638
628
  * `sha256:<hex>`, or a bare 64-hex digest. Workspace is derived server-side
639
629
  * from the token; idempotent.
640
- * Does NOT affect runs that already snapshotted the asset.
630
+ * Does NOT affect sessions that already snapshotted the asset.
641
631
  */
642
632
  export async function deleteWorkspaceAsset(http, hash) {
643
633
  const assetId = hash.startsWith("asset_")
@@ -698,23 +688,23 @@ export async function getWebhookSigningSecret(http) {
698
688
  }
699
689
  /**
700
690
  * Download each artifact's bytes into a zip-file map keyed by
701
- * `<zipPrefix><relative-path>`, fetched from the `outputs`
691
+ * `<zipPrefix><relative-path>`, fetched from the `files`
702
692
  * download route. Best-effort: a per-artifact fetch failure records an
703
693
  * `errors[]` entry rather than aborting the rest, so the failure is
704
694
  * surfaced (never silent) while a partially-available run still yields a
705
695
  * usable zip.
706
696
  */
707
- async function collectArtifactBytes(http, runId, items, zipPrefix, namespace, timeoutMs = OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS) {
697
+ async function collectArtifactBytes(http, sessionId, items, zipPrefix, namespace, timeoutMs = SESSION_FILE_TRANSFER_DEFAULT_TIMEOUT_MS) {
708
698
  const entries = [];
709
699
  const captured = [];
710
700
  const errors = [];
711
701
  for (const item of items) {
712
702
  const rel = item.filename ?? item.id;
713
703
  try {
714
- const path = `/api/runs/${encodeURIComponent(runId)}/${namespace}/${encodeURIComponent(item.id)}/download`;
704
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/${namespace}/${encodeURIComponent(item.id)}/download`;
715
705
  entries.push({
716
706
  path: `${zipPrefix}${rel}`,
717
- bytes: await downloadOutputBytesWithRetry(http, path, timeoutMs),
707
+ bytes: await downloadSessionFileBytesWithRetry(http, path, timeoutMs),
718
708
  ...(item.contentType !== undefined ? { contentType: item.contentType } : {}),
719
709
  customerContent: true
720
710
  });
@@ -737,17 +727,17 @@ function eventsJsonl(events) {
737
727
  function isPathSelector(selector) {
738
728
  return Boolean(selector && typeof selector === "object" && "path" in selector);
739
729
  }
740
- function normalizeOutputLookupPath(path) {
730
+ function normalizeSessionFileLookupPath(path) {
741
731
  return path.replace(/\\/g, "/").replace(/^\/+/, "");
742
732
  }
743
- export function filterOutputs(outputs, query) {
744
- return outputs.filter((output) => outputMatchesQuery(output, query));
733
+ export function filterSessionFiles(files, query) {
734
+ return files.filter((file) => sessionFileMatchesQuery(file, query));
745
735
  }
746
736
  /**
747
- * The single filename-matcher for cross-run / per-session output SEARCH. A
737
+ * The single filename-matcher for cross-session / per-session file SEARCH. A
748
738
  * string is a case-insensitive SUBSTRING match; a RegExp is tested as given (and
749
739
  * reset to `lastIndex = 0` so a reused `/g` regex is safe). Sharing this SSoT is
750
- * what closes the T16 crash class: `searchOutputs` no longer assumes `filename`
740
+ * what closes the T16 crash class: session-file search no longer assumes `filename`
751
741
  * is a string and passes a RegExp into `escapeRegExp(...).replace(...)`.
752
742
  */
753
743
  export function toFilenameMatcher(filename) {
@@ -760,8 +750,8 @@ export function toFilenameMatcher(filename) {
760
750
  return filename.test(name);
761
751
  };
762
752
  }
763
- export function classifyOutput(output) {
764
- const contentType = normalizeContentType(output.contentType);
753
+ export function classifySessionFile(file) {
754
+ const contentType = normalizeContentType(file.contentType);
765
755
  if (contentType) {
766
756
  if (contentType === "application/json" || contentType.endsWith("+json") || contentType.includes("json")) {
767
757
  return "json";
@@ -789,7 +779,7 @@ export function classifyOutput(output) {
789
779
  return "binary";
790
780
  return "unknown";
791
781
  }
792
- const extension = extensionOf(output.filename);
782
+ const extension = extensionOf(file.filename);
793
783
  if (!extension)
794
784
  return "unknown";
795
785
  if (["json", "jsonl", "ndjson"].includes(extension))
@@ -812,10 +802,10 @@ export function classifyOutput(output) {
812
802
  return "binary";
813
803
  return "unknown";
814
804
  }
815
- export function normalizeOutputLinkExpiresIn(input = "1h") {
805
+ export function normalizeSessionFileLinkExpiresIn(input = "1h") {
816
806
  if (typeof input === "number") {
817
807
  if (!Number.isFinite(input) || input <= 0) {
818
- throw new RunStateError("outputLink: expiresIn must be a positive number of seconds", {
808
+ throw new SessionStateError("sessionFileLink: expiresIn must be a positive number of seconds", {
819
809
  expiresIn: input
820
810
  });
821
811
  }
@@ -827,34 +817,34 @@ export function normalizeOutputLinkExpiresIn(input = "1h") {
827
817
  return 60 * 60;
828
818
  if (input === "1d")
829
819
  return 24 * 60 * 60;
830
- throw new RunStateError("outputLink: expiresIn must be seconds, \"15m\", \"1h\", or \"1d\"", {
820
+ throw new SessionStateError("sessionFileLink: expiresIn must be seconds, \"15m\", \"1h\", or \"1d\"", {
831
821
  expiresIn: input
832
822
  });
833
823
  }
834
- async function resolveOutputLinkTarget(http, runId, selectorOrQuery) {
824
+ async function resolveSessionFileLinkTarget(http, sessionId, selectorOrQuery) {
835
825
  if (typeof selectorOrQuery === "string") {
836
826
  if (selectorOrQuery.length === 0) {
837
- throw new RunStateError("outputLink: selector must include an output id or query", { runId });
827
+ throw new SessionStateError("sessionFileLink: selector must include a file id or query", { sessionId });
838
828
  }
839
829
  return { id: selectorOrQuery };
840
830
  }
841
- if (hasOutputId(selectorOrQuery)) {
831
+ if (hasSessionFileId(selectorOrQuery)) {
842
832
  if (selectorOrQuery.id.length === 0) {
843
- throw new RunStateError("outputLink: selector must include an output id or query", { runId });
833
+ throw new SessionStateError("sessionFileLink: selector must include a file id or query", { sessionId });
844
834
  }
845
835
  return selectorOrQuery;
846
836
  }
847
837
  if (isPathSelector(selectorOrQuery) && selectorOrQuery.match === "suffix") {
848
- return resolveOutputFileSelector(await listOutputs(http, runId), selectorOrQuery, runId);
838
+ return resolveSessionFileSelector(await listSessionFiles(http, sessionId), selectorOrQuery, sessionId);
849
839
  }
850
- const match = await findOutput(http, runId, selectorOrQuery);
840
+ const match = await findSessionFile(http, sessionId, selectorOrQuery);
851
841
  if (match)
852
842
  return match;
853
- throw new RunStateError("outputLink: output query matched no files", { runId });
843
+ throw new SessionStateError("sessionFileLink: file query matched no files", { sessionId });
854
844
  }
855
- function outputMatchesQuery(output, query) {
856
- const normalizedPath = typeof output.filename === "string" ? normalizeOutputQueryPath(output.filename) : "";
857
- if (query.path !== undefined && normalizedPath !== normalizeOutputQueryPath(query.path)) {
845
+ function sessionFileMatchesQuery(file, query) {
846
+ const normalizedPath = typeof file.filename === "string" ? normalizeSessionFileQueryPath(file.filename) : "";
847
+ if (query.path !== undefined && normalizedPath !== normalizeSessionFileQueryPath(query.path)) {
858
848
  return false;
859
849
  }
860
850
  if (query.filename !== undefined) {
@@ -875,21 +865,21 @@ function outputMatchesQuery(output, query) {
875
865
  if (query.extension !== undefined && extensionOf(normalizedPath) !== normalizeExtension(query.extension)) {
876
866
  return false;
877
867
  }
878
- if (query.contentType !== undefined && !contentTypeMatches(output.contentType, query.contentType)) {
868
+ if (query.contentType !== undefined && !contentTypeMatches(file.contentType, query.contentType)) {
879
869
  return false;
880
870
  }
881
- if (query.type !== undefined && classifyOutput(output) !== query.type) {
871
+ if (query.type !== undefined && classifySessionFile(file) !== query.type) {
882
872
  return false;
883
873
  }
884
874
  return true;
885
875
  }
886
- function hasOutputId(value) {
876
+ function hasSessionFileId(value) {
887
877
  return Boolean(value && typeof value === "object" && "id" in value && typeof value.id === "string");
888
878
  }
889
- function normalizeOutputQueryPath(path) {
879
+ function normalizeSessionFileQueryPath(path) {
890
880
  let normalized = path.replace(/\\/g, "/").replace(/^\/+/, "");
891
- while (normalized === "outputs" || normalized.startsWith("outputs/")) {
892
- normalized = normalized === "outputs" ? "" : normalized.slice("outputs/".length);
881
+ while (normalized === "files" || normalized.startsWith("files/")) {
882
+ normalized = normalized === "files" ? "" : normalized.slice("files/".length);
893
883
  }
894
884
  return normalized.replace(/\/+$/, "");
895
885
  }
@@ -897,7 +887,7 @@ function basenameOf(path) {
897
887
  return path.split("/").filter(Boolean).pop() ?? "";
898
888
  }
899
889
  function directoryMatches(path, dir, recursive) {
900
- const normalizedDir = normalizeOutputQueryPath(dir);
890
+ const normalizedDir = normalizeSessionFileQueryPath(dir);
901
891
  if (normalizedDir.length === 0)
902
892
  return true;
903
893
  const prefix = `${normalizedDir}/`;
@@ -912,7 +902,7 @@ function normalizeExtension(extension) {
912
902
  function extensionOf(path) {
913
903
  if (!path)
914
904
  return "";
915
- const basename = basenameOf(normalizeOutputQueryPath(path));
905
+ const basename = basenameOf(normalizeSessionFileQueryPath(path));
916
906
  const index = basename.lastIndexOf(".");
917
907
  return index > 0 && index < basename.length - 1 ? basename.slice(index + 1).toLowerCase() : "";
918
908
  }
@@ -930,64 +920,64 @@ function contentTypeMatches(actual, expected) {
930
920
  return normalizedActual === normalizedExpected;
931
921
  }
932
922
  /**
933
- * Download EVERYTHING public about a run as one zip, organised into the three
923
+ * Download EVERYTHING public about a session as one zip, organised into the three
934
924
  * namespace folders:
935
925
  *
936
- * metadata/run.json — the run record.
926
+ * metadata/session.json — the session record.
937
927
  * events/events.jsonl — typed event-channel records.
938
- * outputs/<rel> — the run's deliverables.
939
- * manifest.json — `RunRecordManifestV1`.
928
+ * files/<rel> — the session's captured files.
929
+ * manifest.json — `SessionRecordManifestV1`.
940
930
  */
941
- export async function download(http, runId) {
942
- const [run, events, outputs] = await Promise.all([
943
- getRun(http, runId),
944
- listRunEvents(http, runId),
945
- listOutputs(http, runId)
931
+ export async function download(http, sessionId) {
932
+ const [sessionRecord, events, files] = await Promise.all([
933
+ getSessionRecord(http, sessionId),
934
+ listSessionRecordEvents(http, sessionId),
935
+ listSessionFiles(http, sessionId)
946
936
  ]);
947
- const out = await collectArtifactBytes(http, runId, outputs, "outputs/", "outputs");
948
- const submissionSnapshot = extractSubmissionSnapshot(run);
949
- const costTelemetry = extractCostTelemetry(run);
950
- const manifest = buildRunRecordDownloadManifestV1({
951
- runId,
952
- outputs: out.captured,
953
- errors: out.errors,
937
+ const collectedFiles = await collectArtifactBytes(http, sessionId, files, "files/", "files");
938
+ const submissionSnapshot = extractSubmissionSnapshot(sessionRecord);
939
+ const costTelemetry = extractCostTelemetry(sessionRecord);
940
+ const manifest = buildSessionRecordDownloadManifestV1({
941
+ sessionId,
942
+ sessionFiles: collectedFiles.captured,
943
+ errors: collectedFiles.errors,
954
944
  typedEventCount: events.length,
955
945
  ...(submissionSnapshot ? { submission: { status: "present" } } : {}),
956
946
  ...(costTelemetry ? { cost: { status: "present" } } : {})
957
947
  });
958
948
  return zipEntries([
959
- jsonEntry("metadata/run.json", run),
949
+ jsonEntry("metadata/session.json", sessionRecord),
960
950
  ...(submissionSnapshot ? [jsonEntry("metadata/submission.json", submissionSnapshot)] : []),
961
951
  ...(costTelemetry ? [jsonEntry("metadata/cost.json", costTelemetry)] : []),
962
952
  jsonlEntry("events/events.jsonl", events),
963
- ...out.entries,
953
+ ...collectedFiles.entries,
964
954
  jsonEntry("manifest.json", manifest)
965
955
  ]);
966
956
  }
967
957
  /**
968
- * Download only the run's deliverables (the `outputs` namespace). Zip
958
+ * Download only the session's captured files (the `files` namespace). Zip
969
959
  * layout: `<rel>` per file plus a `manifest.json`
970
- * (`{ runId, namespace: "outputs", outputs[], errors[] }`).
960
+ * (`{ sessionId, namespace: "files", files[], errors[] }`).
971
961
  */
972
- export async function downloadOutputs(http, runId, options) {
973
- const outputs = await listOutputs(http, runId);
974
- const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
975
- const { entries, captured, errors } = await collectArtifactBytes(http, runId, outputs, "", "outputs", timeoutMs);
962
+ export async function downloadSessionFiles(http, sessionId, options) {
963
+ const files = await listSessionFiles(http, sessionId);
964
+ const timeoutMs = normalizeSessionFileTransferTimeoutMs(options?.timeoutMs);
965
+ const { entries, captured, errors } = await collectArtifactBytes(http, sessionId, files, "", "files", timeoutMs);
976
966
  return zipEntries([
977
967
  ...entries,
978
- jsonEntry("manifest.json", { runId, namespace: "outputs", outputs: captured, errors })
968
+ jsonEntry("manifest.json", { sessionId, namespace: "files", files: captured, errors })
979
969
  ]);
980
970
  }
981
971
  /**
982
972
  * Download only the event archive (the `events` namespace). Always includes
983
973
  * typed `events.jsonl` plus `manifest.json`.
984
974
  */
985
- export async function downloadEvents(http, runId) {
986
- const events = await listRunEvents(http, runId);
975
+ export async function downloadEvents(http, sessionId) {
976
+ const events = await listSessionRecordEvents(http, sessionId);
987
977
  return zipEntries([
988
978
  jsonlEntry("events.jsonl", events),
989
979
  jsonEntry("manifest.json", {
990
- runId,
980
+ sessionId,
991
981
  namespace: "events",
992
982
  files: [{ path: "events.jsonl", role: "typed_events", status: "present", recordCount: events.length }],
993
983
  errors: []
@@ -995,23 +985,23 @@ export async function downloadEvents(http, runId) {
995
985
  ]);
996
986
  }
997
987
  /**
998
- * Download only the run record (the `metadata` namespace) as a zip
999
- * containing `run.json` plus `manifest.json`.
988
+ * Download only the session record (the `metadata` namespace) as a zip
989
+ * containing `session.json` plus `manifest.json`.
1000
990
  */
1001
- export async function downloadMetadata(http, runId) {
1002
- const run = await getRun(http, runId);
991
+ export async function downloadMetadata(http, sessionId) {
992
+ const sessionRecord = await getSessionRecord(http, sessionId);
1003
993
  return zipEntries([
1004
- jsonEntry("run.json", run),
994
+ jsonEntry("session.json", sessionRecord),
1005
995
  jsonEntry("manifest.json", {
1006
- runId,
996
+ sessionId,
1007
997
  namespace: "metadata",
1008
- files: [{ path: "run.json", role: "run_metadata", status: "present" }],
998
+ files: [{ path: "session.json", role: "session_metadata", status: "present" }],
1009
999
  errors: []
1010
1000
  })
1011
1001
  ]);
1012
1002
  }
1013
1003
  function zipEntries(entries) {
1014
- assertRunRecordArchivePublicSafeV1(entries);
1004
+ assertSessionRecordArchivePublicSafeV1(entries);
1015
1005
  const files = {};
1016
1006
  for (const entry of entries) {
1017
1007
  files[entry.path] = entry.bytes;
@@ -1032,8 +1022,8 @@ function jsonlEntry(path, events) {
1032
1022
  contentType: "application/jsonl; charset=utf-8"
1033
1023
  };
1034
1024
  }
1035
- function extractSubmissionSnapshot(run) {
1036
- const raw = run.submission;
1025
+ function extractSubmissionSnapshot(sessionRecord) {
1026
+ const raw = sessionRecord.submission;
1037
1027
  if (!isRecord(raw) || raw.kind !== "submission" || !isRecord(raw.submission)) {
1038
1028
  return undefined;
1039
1029
  }
@@ -1041,18 +1031,18 @@ function extractSubmissionSnapshot(run) {
1041
1031
  submission: raw.submission
1042
1032
  };
1043
1033
  }
1044
- function extractCostTelemetry(run) {
1045
- const raw = run.costTelemetry;
1034
+ function extractCostTelemetry(sessionRecord) {
1035
+ const raw = sessionRecord.costTelemetry;
1046
1036
  return isRecord(raw) ? raw : undefined;
1047
1037
  }
1048
1038
  function isRecord(value) {
1049
1039
  return typeof value === "object" && value !== null && !Array.isArray(value);
1050
1040
  }
1051
1041
  // ===========================================================================
1052
- // Run submission operations (McpServer / run config composition)
1042
+ // SessionRecord submission operations (McpServer / session config composition)
1053
1043
  // ===========================================================================
1054
- export async function submitRun(http, request) {
1055
- return http.request("/api/runs", {
1044
+ export async function startSessionRecord(http, request) {
1045
+ return http.request("/api/sessions", {
1056
1046
  method: "POST",
1057
1047
  body: JSON.stringify(request)
1058
1048
  });
@@ -1200,8 +1190,8 @@ function unwrapSkill(result) {
1200
1190
  }
1201
1191
  return result;
1202
1192
  }
1203
- function hasRun(value) {
1204
- return Boolean(value && typeof value === "object" && "run" in value);
1193
+ function hasSessionRecord(value) {
1194
+ return Boolean(value && typeof value === "object" && "session" in value);
1205
1195
  }
1206
1196
  function unwrapSession(result) {
1207
1197
  if (result && typeof result === "object" && "session" in result) {
@@ -1212,7 +1202,7 @@ function unwrapSession(result) {
1212
1202
  /**
1213
1203
  * Upload bytes to the hosted API's content-addressable asset endpoint.
1214
1204
  * Returns a storage-neutral asset id suitable for `kind:"asset"` refs in a
1215
- * later run submission.
1205
+ * later session submission.
1216
1206
  */
1217
1207
  export async function uploadWorkspaceAsset(http, input) {
1218
1208
  return http.request("/assets", {