@ai-matrx/agents 0.36.0 → 0.37.1

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 (54) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/content-transfer/index.cjs +12 -12
  3. package/dist/content-transfer/index.cjs.map +1 -1
  4. package/dist/content-transfer/index.d.cts +1 -1
  5. package/dist/content-transfer/index.d.ts +1 -1
  6. package/dist/content-transfer/index.js +12 -12
  7. package/dist/content-transfer/index.js.map +1 -1
  8. package/dist/content-transfer/react/index.cjs +12 -12
  9. package/dist/content-transfer/react/index.cjs.map +1 -1
  10. package/dist/content-transfer/react/index.d.cts +1 -1
  11. package/dist/content-transfer/react/index.d.ts +1 -1
  12. package/dist/content-transfer/react/index.js +12 -12
  13. package/dist/content-transfer/react/index.js.map +1 -1
  14. package/dist/generated/api-types.cjs.map +1 -1
  15. package/dist/generated/api-types.d.cts +5 -0
  16. package/dist/generated/api-types.d.ts +5 -0
  17. package/dist/index.cjs +1458 -1220
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +1461 -1223
  22. package/dist/index.js.map +1 -1
  23. package/dist/{keys.generated-DCtN66RS.d.cts → keys.generated-DL-5LCNz.d.cts} +4 -4
  24. package/dist/{keys.generated-DCtN66RS.d.ts → keys.generated-DL-5LCNz.d.ts} +4 -4
  25. package/dist/mandates/index.cjs +2 -2
  26. package/dist/mandates/index.cjs.map +1 -1
  27. package/dist/mandates/index.d.cts +2 -2
  28. package/dist/mandates/index.d.ts +2 -2
  29. package/dist/mandates/index.js +2 -2
  30. package/dist/mandates/index.js.map +1 -1
  31. package/dist/matrx/index.cjs +1721 -1483
  32. package/dist/matrx/index.cjs.map +1 -1
  33. package/dist/matrx/index.d.cts +376 -211
  34. package/dist/matrx/index.d.ts +376 -211
  35. package/dist/matrx/index.js +1724 -1486
  36. package/dist/matrx/index.js.map +1 -1
  37. package/dist/message-parts/index.d.cts +1 -100929
  38. package/dist/message-parts/index.d.ts +1 -100929
  39. package/dist/portable/mcp.cjs +99 -11
  40. package/dist/portable/mcp.cjs.map +1 -1
  41. package/dist/portable/mcp.js +99 -11
  42. package/dist/portable/mcp.js.map +1 -1
  43. package/dist/react/index.cjs +145 -57
  44. package/dist/react/index.cjs.map +1 -1
  45. package/dist/react/index.d.cts +3 -1
  46. package/dist/react/index.d.ts +3 -1
  47. package/dist/react/index.js +145 -57
  48. package/dist/react/index.js.map +1 -1
  49. package/generated/api-types.ts +5 -0
  50. package/mandates/snapshots/keys.0.37.0.json +653 -0
  51. package/mandates/snapshots/keys.0.37.1.json +653 -0
  52. package/package.json +4 -4
  53. package/dist/{operations-BT5kKMHl.d.cts → operations-DqOe2zob.d.cts} +34 -34
  54. package/dist/{operations-BT5kKMHl.d.ts → operations-DqOe2zob.d.ts} +34 -34
@@ -70,1669 +70,1896 @@ function extractMatrxErrorCode(serverDetail) {
70
70
  // matrx/client.ts
71
71
  import { isNetError as isNetError2 } from "@ai-matrx/data";
72
72
  import { credentialToHeaders } from "@ai-matrx/data";
73
+ import { extractErrorMessage } from "@ai-matrx/data/net";
73
74
 
74
- // matrx/org-context.ts
75
- import { isUuidShape } from "@ai-matrx/kit/uuid";
76
- var OrganizationContextError = class extends Error {
75
+ // matrx/backend-errors.ts
76
+ var BackendApiError = class extends Error {
77
+ /** Machine-readable error code */
77
78
  code;
78
- constructor(code, message) {
79
- super(message);
80
- this.name = "OrganizationContextError";
81
- this.code = code;
79
+ /** Developer-facing detail */
80
+ detail;
81
+ /** Safe to display directly in the UI */
82
+ userMessage;
83
+ /** Extra info (validation errors, etc.) */
84
+ details;
85
+ /** Unique request ID for support/debugging */
86
+ requestId;
87
+ /** HTTP status code (if from an HTTP response) */
88
+ status;
89
+ constructor(data) {
90
+ super(data.userMessage);
91
+ this.name = "BackendApiError";
92
+ this.code = data.code;
93
+ this.detail = data.detail;
94
+ this.userMessage = data.userMessage;
95
+ this.details = data.details ?? null;
96
+ this.requestId = data.requestId ?? "";
97
+ this.status = data.status ?? null;
98
+ }
99
+ /** Convert to the wire format for logging */
100
+ toJSON() {
101
+ return {
102
+ error: this.code,
103
+ message: this.detail,
104
+ user_message: this.userMessage,
105
+ details: this.details,
106
+ request_id: this.requestId
107
+ };
82
108
  }
83
109
  };
84
- function createOrganizationOperation(organizationId) {
85
- const organization_id = requireOrganizationContext(organizationId);
86
- return Object.freeze({ organization_id });
87
- }
88
- function assertOrganizationMatchesOperation(operation, organizationId) {
89
- if (operation.organization_id !== requireOrganizationContext(organizationId)) {
90
- throw new OrganizationContextError(
91
- "organization_context_mismatch",
92
- "Organization ID must match the request operation."
93
- );
110
+ var StreamTransportError = class extends BackendApiError {
111
+ /** Always true — reattach and let the server settle the outcome. */
112
+ resumable = true;
113
+ constructor(data) {
114
+ super({
115
+ code: "stream_transport_lost",
116
+ detail: data.detail,
117
+ userMessage: "The connection dropped. Your run is still going on the server \u2014 reconnecting to it now.",
118
+ details: data.details ?? null,
119
+ ...data.requestId !== void 0 ? { requestId: data.requestId } : {}
120
+ });
121
+ this.name = "StreamTransportError";
94
122
  }
95
- }
96
- function requireOrganizationContext(selectedOrganizationId, overrideOrganizationId) {
97
- const candidate = overrideOrganizationId ?? selectedOrganizationId;
98
- if (typeof candidate !== "string" || candidate.trim().length === 0) {
99
- throw new OrganizationContextError(
100
- "organization_context_required",
101
- "Select an organization before sending this request."
102
- );
123
+ };
124
+ function isStreamTransportLost(error) {
125
+ if (error instanceof StreamTransportError) return true;
126
+ if (error instanceof BackendApiError) {
127
+ return error.code === "stream_transport_lost";
103
128
  }
104
- const normalized = candidate.trim();
105
- if (!isUuidShape(normalized)) {
106
- throw new OrganizationContextError(
107
- "organization_context_invalid",
108
- "The selected organization ID is invalid."
109
- );
129
+ if (error && typeof error === "object") {
130
+ const code = error.code;
131
+ if (code === "stream_transport_lost") return true;
132
+ const errorType = error.error_type;
133
+ if (errorType === "transport_lost") return true;
110
134
  }
111
- return normalized.toLowerCase();
135
+ return false;
112
136
  }
113
- function applyOrganizationContextHeader(headers, organizationId) {
114
- const normalizedOrganizationId = requireOrganizationContext(organizationId);
115
- for (const [name, value] of Object.entries(headers)) {
116
- if (name.toLowerCase() === "x-organization-id" && value.trim().toLowerCase() !== normalizedOrganizationId) {
117
- throw new OrganizationContextError(
118
- "organization_context_mismatch",
119
- "X-Organization-Id must match the request context organization."
120
- );
137
+ async function parseHttpError(response) {
138
+ const status = response.status;
139
+ let body = null;
140
+ try {
141
+ body = await response.json();
142
+ } catch {
143
+ try {
144
+ const text = await response.text();
145
+ return new BackendApiError({
146
+ code: statusToCode(status),
147
+ detail: text || `HTTP ${status}`,
148
+ userMessage: text || `Request failed (${status})`,
149
+ status
150
+ });
151
+ } catch {
152
+ return new BackendApiError({
153
+ code: statusToCode(status),
154
+ detail: `HTTP ${status}`,
155
+ userMessage: `Request failed (${status})`,
156
+ status
157
+ });
121
158
  }
122
159
  }
123
- const withoutOrganizationHeader = Object.fromEntries(
124
- Object.entries(headers).filter(
125
- ([name]) => name.toLowerCase() !== "x-organization-id"
126
- )
127
- );
128
- return {
129
- ...withoutOrganizationHeader,
130
- "X-Organization-Id": normalizedOrganizationId
131
- };
132
- }
133
- function assertQueryOrganizationMatchesContext(queryParams, organizationId) {
134
- if (!queryParams || queryParams.organization_id === void 0) return;
135
- const queryOrganizationId = requireOrganizationContext(
136
- String(queryParams.organization_id)
137
- );
138
- if (queryOrganizationId !== requireOrganizationContext(organizationId)) {
139
- throw new OrganizationContextError(
140
- "organization_context_mismatch",
141
- "Query organization_id must match the request context organization."
142
- );
143
- }
160
+ return parseHttpErrorBody(body, status);
144
161
  }
145
-
146
- // matrx/protocol.ts
147
- import {
148
- isNetError,
149
- resilientFetch
150
- } from "@ai-matrx/data/net";
151
- var MATRX_AI_API_VERSION_DEFAULT = "v2";
152
- var V2_COVERED_AI_PATH_TEMPLATES = [
153
- "/ai/manual",
154
- "/ai/chat",
155
- "/ai/agents/{agent_id}",
156
- "/ai/agent/{agent_id}",
157
- // singular alias — behaves identically
158
- "/ai/conversations/{conversation_id}",
159
- "/ai/conversation/{conversation_id}",
160
- // singular alias
161
- "/ai/prompts/{prompt_id}",
162
- // saved-prompt execution
163
- "/ai/mandates/{mandate_key}"
164
- // THE MANDATE DOOR — `/v2` sibling exists (routers/v2.py)
165
- ];
166
- function toV2Path(path) {
167
- const withLead = path.startsWith("/") ? path : `/${path}`;
168
- if (withLead.startsWith("/api/")) {
169
- const rest = withLead.slice("/api".length);
170
- return rest.startsWith("/v2/") ? withLead : `/api/v2${rest}`;
162
+ function parseHttpErrorBody(body, status) {
163
+ if (!body) {
164
+ return new BackendApiError({
165
+ code: statusToCode(status),
166
+ detail: `HTTP ${status}`,
167
+ userMessage: `Request failed (${status})`,
168
+ status
169
+ });
171
170
  }
172
- return withLead.startsWith("/v2/") ? withLead : `/v2${withLead}`;
173
- }
174
- var COVERED_INTERPOLATED_PATH = [
175
- /^(?:\/api)?\/ai\/manual$/,
176
- /^(?:\/api)?\/ai\/chat$/,
177
- /^(?:\/api)?\/ai\/agents\/[^/]+$/,
178
- /^(?:\/api)?\/ai\/agent\/[^/]+$/,
179
- /^(?:\/api)?\/ai\/conversations\/[^/]+$/,
180
- /^(?:\/api)?\/ai\/conversation\/[^/]+$/,
181
- /^(?:\/api)?\/ai\/prompts\/[^/]+$/,
182
- // `/warm` etc. deliberately don't match
183
- /^(?:\/api)?\/ai\/mandates\/[^/]+$/
184
- // the mandate door
185
- ];
186
- function isCoveredAiPath(path) {
187
- return COVERED_INTERPOLATED_PATH.some((re) => re.test(path));
188
- }
189
- function isV2Path(pathOrUrl) {
190
- return /\/v2\/ai\//.test(pathOrUrl);
191
- }
192
- function toV1FallbackUrl(url) {
193
- return url.replace(/\/v2\/ai\//, "/ai/");
194
- }
195
- function applyAiApiVersion(path, version) {
196
- if (version !== "v2") return path;
197
- return isCoveredAiPath(path) ? toV2Path(path) : path;
198
- }
199
- async function fetchWithMatrxProtocolFallback(url, init, opts = {}) {
200
- const { onDowngrade, ...fetchOpts } = opts;
201
- const logDowngrade = (reason, status2) => {
202
- console.warn(
203
- `[matrx] ai_v2_downgrade \u2192 retrying on v1. v2=${url} reason=${reason}`
204
- );
205
- onDowngrade?.({ url, reason, ...status2 !== void 0 ? { status: status2 } : {} });
206
- };
207
- if (!isV2Path(url)) return resilientFetch(url, init, fetchOpts);
208
- let result;
209
- try {
210
- result = await resilientFetch(url, init, fetchOpts);
211
- } catch (err) {
212
- const isAbort = isNetError(err) && err.code === "aborted" || err instanceof Error && err.name === "AbortError";
213
- if (isAbort) throw err;
214
- logDowngrade(String(err));
215
- return resilientFetch(toV1FallbackUrl(url), init, fetchOpts);
171
+ if (typeof body.error === "string" && typeof body.user_message === "string") {
172
+ return new BackendApiError({
173
+ code: body.error,
174
+ detail: body.message || `HTTP ${status}`,
175
+ userMessage: body.user_message,
176
+ details: body.details ?? null,
177
+ requestId: typeof body.request_id === "string" ? body.request_id : void 0,
178
+ status
179
+ });
216
180
  }
217
- const status = result.response.status;
218
- if (status === 404 || status === 405 || status >= 500) {
219
- logDowngrade(`HTTP ${status}`, status);
220
- return resilientFetch(toV1FallbackUrl(url), init, fetchOpts);
181
+ if (typeof body.error === "object" && body.error !== null) {
182
+ const errorObj = body.error;
183
+ return new BackendApiError({
184
+ code: errorObj.type || errorObj.error || statusToCode(status),
185
+ detail: errorObj.message || `HTTP ${status}`,
186
+ userMessage: errorObj.user_message || errorObj.user_visible_message || errorObj.message || `Request failed (${status})`,
187
+ details: errorObj.details ?? null,
188
+ requestId: typeof errorObj.request_id === "string" ? errorObj.request_id : void 0,
189
+ status
190
+ });
221
191
  }
222
- return result;
223
- }
224
- function aiVersionPathOverrides(version) {
225
- if (version !== "v2") return {};
226
- const map = {};
227
- for (const template of V2_COVERED_AI_PATH_TEMPLATES) {
228
- map[template] = toV2Path(template);
192
+ if (Array.isArray(body.detail)) {
193
+ const first = body.detail[0];
194
+ const firstMsg = first && typeof first.msg === "string" ? first.msg : null;
195
+ return new BackendApiError({
196
+ code: statusToCode(status),
197
+ detail: firstMsg ? `Validation error: ${firstMsg}` : JSON.stringify(body.detail),
198
+ userMessage: firstMsg || `Request failed (${status})`,
199
+ details: body.detail,
200
+ status
201
+ });
229
202
  }
230
- return map;
231
- }
232
-
233
- // matrx/client.ts
234
- function classifyStatus(status) {
235
- return status >= 400 && status < 500 ? "validation_error" : "http_error";
236
- }
237
- function normalizeMatrxError(err) {
238
- if (err instanceof MatrxApiError) {
239
- return {
240
- type: classifyStatus(err.status),
241
- message: err.message,
242
- status: err.status,
243
- serverDetail: err.serverDetail,
244
- ...err.code ? { code: err.code } : {},
245
- name: err.name
246
- };
247
- }
248
- if (err instanceof OrganizationContextError) {
249
- return {
250
- type: "validation_error",
251
- message: err.message,
252
- code: err.code,
253
- name: err.name,
254
- ...err.stack !== void 0 ? { stack: err.stack } : {}
255
- };
256
- }
257
- if (err instanceof DOMException && err.name === "AbortError") {
258
- return { type: "abort_error", message: "Request was cancelled." };
259
- }
260
- if (isNetError2(err)) {
261
- if (err.code === "aborted") {
262
- return { type: "abort_error", message: err.message };
263
- }
264
- if (err.code === "http") {
265
- const status = err.status ?? 0;
266
- return { type: classifyStatus(status), message: err.message, status };
267
- }
268
- return { type: "network_error", message: err.message };
269
- }
270
- if (err instanceof Error) {
271
- const httpMatch = err.message.match(/HTTP (\d+):\s*(.*)/);
272
- if (httpMatch) {
273
- const status = parseInt(httpMatch[1], 10);
274
- return {
275
- type: classifyStatus(status),
276
- message: httpMatch[2] ?? err.message,
277
- status
278
- };
279
- }
280
- return { type: "network_error", message: err.message };
281
- }
282
- return { type: "unknown", message: String(err ?? "Unknown error") };
283
- }
284
- var PRODUCTION_CONNECT_TIMEOUT_MS = 15e3;
285
- function stripContentType(headers) {
286
- return Object.fromEntries(
287
- Object.entries(headers).filter(
288
- ([name]) => name.toLowerCase() !== "content-type"
289
- )
290
- );
291
- }
292
- function createMatrxTransport(options) {
293
- const {
294
- baseUrl,
295
- resolveTarget,
296
- credentials,
297
- organizationId,
298
- aiApiVersion,
299
- connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,
300
- totalTimeoutMs = null,
301
- expectedErrorStatuses,
302
- diagnostics,
303
- source = "matrxTransport"
304
- } = options;
305
- if (baseUrl === void 0 === (resolveTarget === void 0)) {
306
- throw new Error(
307
- "createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`."
308
- );
203
+ if (typeof body.detail === "object" && body.detail !== null && !Array.isArray(body.detail)) {
204
+ const d = body.detail;
205
+ const code = typeof d.code === "string" && d.code || typeof d.error === "string" && d.error || statusToCode(status);
206
+ const message = typeof d.message === "string" && d.message || typeof d.detail === "string" && d.detail || `HTTP ${status}`;
207
+ return new BackendApiError({
208
+ code,
209
+ detail: message,
210
+ userMessage: typeof d.user_message === "string" && d.user_message || typeof d.user_visible_message === "string" && d.user_visible_message || message,
211
+ details: d.details ?? d,
212
+ requestId: typeof d.request_id === "string" ? d.request_id : void 0,
213
+ status
214
+ });
309
215
  }
310
- const resolve = resolveTarget ?? (() => ({ baseUrl }));
311
- const resolveVersion = () => {
312
- if (aiApiVersion === void 0) return MATRX_AI_API_VERSION_DEFAULT;
313
- return typeof aiApiVersion === "function" ? aiApiVersion() : aiApiVersion;
314
- };
315
- const resolveOrganizationId = () => {
316
- if (organizationId === void 0) return null;
317
- const candidate = typeof organizationId === "function" ? organizationId() : organizationId;
318
- return requireOrganizationContext(candidate);
319
- };
320
- return {
321
- async fetch(path, init) {
322
- const target = resolve();
323
- const versionedPath = applyAiApiVersion(path, resolveVersion());
324
- const url = `${target.baseUrl}${versionedPath}`;
325
- const credentialHeaders = credentials ? credentialToHeaders(await credentials.get()) : {};
326
- let headers = {
327
- ...init.headers,
328
- ...stripContentType(credentialHeaders),
329
- ...stripContentType(target.policyHeaders ?? {})
330
- };
331
- const orgId = resolveOrganizationId();
332
- if (orgId !== null) {
333
- headers = applyOrganizationContextHeader(headers, orgId);
334
- }
335
- const info = {
336
- url,
337
- method: init.method,
338
- path,
339
- channel: target.channel ?? "default",
340
- source
341
- };
342
- diagnostics?.onRequest?.(info);
343
- let response;
344
- try {
345
- ({ response } = await fetchWithMatrxProtocolFallback(
346
- url,
347
- {
348
- method: init.method,
349
- headers,
350
- ...init.body !== void 0 ? { body: init.body } : {}
351
- },
352
- {
353
- ...init.signal ? { signal: init.signal } : {},
354
- connectTimeoutMs,
355
- totalTimeoutMs,
356
- throwOnHttpError: false,
357
- ...diagnostics?.onProtocolDowngrade ? { onDowngrade: diagnostics.onProtocolDowngrade } : {}
358
- }
359
- ));
360
- } catch (err) {
361
- diagnostics?.onError?.(normalizeMatrxError(err), info);
362
- throw err;
363
- }
364
- if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {
365
- const serverDetail = await response.clone().json().catch(() => void 0);
366
- const code = extractMatrxErrorCode(serverDetail);
367
- diagnostics?.onError?.(
368
- {
369
- type: classifyStatus(response.status),
370
- message: extractMatrxErrorMessage(serverDetail) ?? `HTTP ${response.status}`,
371
- status: response.status,
372
- serverDetail,
373
- ...code ? { code } : {}
374
- },
375
- info
376
- );
377
- }
378
- return response;
379
- }
380
- };
381
- }
382
-
383
- // matrx/conversation.ts
384
- function mintMatrxConversationId() {
385
- return crypto.randomUUID();
386
- }
387
- function newStoredConversationStart(conversationId) {
388
- return {
389
- conversation_id: conversationId ?? mintMatrxConversationId(),
390
- is_new: true,
391
- store: true
392
- };
393
- }
394
- function continueStoredConversationStart(conversationId) {
395
- return { conversation_id: conversationId, is_new: false, store: true };
396
- }
397
- function newEphemeralConversationStart(conversationId) {
398
- return {
399
- conversation_id: conversationId ?? mintMatrxConversationId(),
400
- is_new: true,
401
- store: false
402
- };
403
- }
404
- function continueEphemeralConversationStart(conversationId, priorMessages) {
405
- return {
406
- conversation_id: conversationId,
407
- is_new: false,
408
- store: false,
409
- prior_messages: priorMessages
410
- };
411
- }
412
-
413
- // stream/ndjson.ts
414
- var DEFAULT_MATRX_NDJSON_READ_AHEAD = 64;
415
- function isRecord2(value) {
416
- return typeof value === "object" && value !== null && !Array.isArray(value);
216
+ return new BackendApiError({
217
+ code: typeof body.error === "string" ? body.error : statusToCode(status),
218
+ detail: body.message || body.detail || body.error || `HTTP ${status}`,
219
+ userMessage: body.user_message || body.user_visible_message || body.message || body.detail || `Request failed (${status})`,
220
+ details: body.details ?? null,
221
+ requestId: typeof body.request_id === "string" ? body.request_id : void 0,
222
+ status
223
+ });
417
224
  }
418
- function normalizeMatrxStreamEnvelope(value) {
419
- if (!isRecord2(value)) return null;
420
- if (typeof value.event === "string") {
421
- const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
422
- const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
423
- return {
424
- event: value.event,
425
- data: value.data,
426
- ...streamId === void 0 ? {} : { stream_id: streamId },
427
- ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
428
- };
429
- }
430
- if (value.e === "c" && typeof value.t === "string") {
431
- const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
432
- const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
433
- return {
434
- event: "chunk",
435
- data: { text: value.t },
436
- ...streamId === void 0 ? {} : { stream_id: streamId },
437
- ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
438
- };
439
- }
440
- if (value.e === "r" && typeof value.t === "string") {
441
- const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
442
- const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
443
- return {
444
- event: "reasoning_chunk",
445
- data: { text: value.t },
446
- ...streamId === void 0 ? {} : { stream_id: streamId },
447
- ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
448
- };
449
- }
450
- return null;
225
+ function parseCallApiError(error) {
226
+ const status = error.status ?? 500;
227
+ const body = error.serverDetail && typeof error.serverDetail === "object" && !Array.isArray(error.serverDetail) ? error.serverDetail : { message: error.message };
228
+ return parseHttpErrorBody(body, status);
451
229
  }
452
- function createMatrxNdjsonFramer(options = {}) {
453
- const decoder = new TextDecoder();
454
- let buffer = "";
455
- let lineNumber = 0;
456
- let finished = false;
457
- const assertOpen = () => {
458
- if (finished) throw new Error("Matrx NDJSON framer is already finished");
459
- };
460
- const parseLine = (line, atCompletion) => {
461
- const currentLineNumber = ++lineNumber;
462
- const trimmed = line.trim();
463
- if (!trimmed) return null;
464
- let parsed;
465
- try {
466
- parsed = JSON.parse(trimmed);
467
- } catch (error) {
468
- options.onMalformedLine?.({
469
- line: trimmed,
470
- error,
471
- lineNumber: currentLineNumber,
472
- atCompletion
473
- });
474
- return null;
475
- }
476
- const envelope = normalizeMatrxStreamEnvelope(parsed);
477
- if (!envelope) {
478
- options.onUnknownEnvelope?.(parsed);
479
- return null;
480
- }
481
- options.onValidEnvelope?.({
482
- raw: parsed,
483
- envelope,
484
- line: trimmed,
485
- lineNumber: currentLineNumber,
486
- atCompletion
230
+ function parseStreamError(data) {
231
+ if (!data || typeof data !== "object") {
232
+ return new BackendApiError({
233
+ code: "internal_error",
234
+ detail: typeof data === "string" ? data : "Unknown streaming error",
235
+ userMessage: typeof data === "string" ? data : "Something went wrong"
487
236
  });
488
- return envelope;
489
- };
490
- const pushDecodedText = (fragment) => {
491
- buffer += fragment;
492
- const lines = buffer.split("\n");
493
- buffer = lines.pop() ?? "";
494
- const envelopes = [];
495
- for (const line of lines) {
496
- const envelope = parseLine(line, false);
497
- if (envelope) envelopes.push(envelope);
498
- }
499
- return envelopes;
500
- };
501
- return {
502
- pushText(fragment) {
503
- assertOpen();
504
- return pushDecodedText(decoder.decode() + fragment);
505
- },
506
- pushBytes(fragment) {
507
- assertOpen();
508
- return pushDecodedText(decoder.decode(fragment, { stream: true }));
509
- },
510
- finish() {
511
- assertOpen();
512
- finished = true;
513
- const envelopes = pushDecodedText(decoder.decode());
514
- if (buffer.length > 0) {
515
- const envelope = parseLine(buffer, true);
516
- buffer = "";
517
- if (envelope) envelopes.push(envelope);
518
- }
519
- return envelopes;
520
- }
521
- };
522
- }
523
- function readAheadLimit(value) {
524
- const limit = value ?? DEFAULT_MATRX_NDJSON_READ_AHEAD;
525
- if (!Number.isSafeInteger(limit) || limit < 1) {
526
- throw new RangeError("maxReadAhead must be a positive safe integer");
527
- }
528
- return limit;
529
- }
530
- async function* readMatrxNdjsonStream(body, options = {}) {
531
- const maxReadAhead = readAheadLimit(options.maxReadAhead);
532
- const queue = [];
533
- let queuedEventCount = 0;
534
- let wakeConsumer = null;
535
- let wakeProducer = null;
536
- let readerFinished = false;
537
- let consumerClosed = false;
538
- const wakeWaitingConsumer = () => {
539
- const wake = wakeConsumer;
540
- wakeConsumer = null;
541
- wake?.();
542
- };
543
- const wakeWaitingProducer = () => {
544
- const wake = wakeProducer;
545
- wakeProducer = null;
546
- wake?.();
547
- };
548
- const enqueueTerminal = (item) => {
549
- if (consumerClosed) return;
550
- queue.push(item);
551
- wakeWaitingConsumer();
552
- };
553
- const enqueueEvent = async (value) => {
554
- while (queuedEventCount >= maxReadAhead && !consumerClosed && !options.signal?.aborted) {
555
- await new Promise((resolve) => {
556
- wakeProducer = resolve;
557
- });
558
- }
559
- if (consumerClosed || options.signal?.aborted) return false;
560
- queue.push({ kind: "event", value });
561
- queuedEventCount += 1;
562
- wakeWaitingConsumer();
563
- return true;
564
- };
565
- const waitForReadCapacity = async () => {
566
- while (queuedEventCount >= maxReadAhead && !consumerClosed && !options.signal?.aborted) {
567
- await new Promise((resolve) => {
568
- wakeProducer = resolve;
569
- });
570
- }
571
- return !consumerClosed && !options.signal?.aborted;
572
- };
573
- const reader = body.getReader();
574
- const framer = createMatrxNdjsonFramer(options);
575
- const onAbort = () => {
576
- wakeWaitingProducer();
577
- wakeWaitingConsumer();
578
- void reader.cancel(options.signal?.reason).catch(() => void 0);
579
- };
580
- options.signal?.addEventListener("abort", onAbort, { once: true });
581
- if (options.signal?.aborted) onAbort();
582
- const readerPromise = (async () => {
583
- try {
584
- while (!options.signal?.aborted && !consumerClosed) {
585
- if (!await waitForReadCapacity()) return;
586
- const { value, done } = await reader.read();
587
- if (done) break;
588
- for (const envelope of framer.pushBytes(value)) {
589
- if (!await enqueueEvent(envelope)) return;
590
- }
591
- }
592
- if (!options.signal?.aborted && !consumerClosed) {
593
- for (const envelope of framer.finish()) {
594
- if (!await enqueueEvent(envelope)) return;
595
- }
596
- }
597
- } catch (error) {
598
- const aborted = options.signal?.aborted || consumerClosed || error instanceof Error && error.name === "AbortError";
599
- if (!aborted) enqueueTerminal({ kind: "error", error });
600
- } finally {
601
- readerFinished = true;
602
- reader.releaseLock();
603
- enqueueTerminal({ kind: "done" });
604
- }
605
- })();
606
- try {
607
- while (true) {
608
- if (queue.length === 0) {
609
- if (options.signal?.aborted || readerFinished) return;
610
- await new Promise((resolve) => {
611
- wakeConsumer = resolve;
612
- });
613
- }
614
- const item = queue.shift();
615
- if (item?.kind === "event") queuedEventCount -= 1;
616
- wakeWaitingProducer();
617
- if (!item || item.kind === "done") return;
618
- if (item.kind === "error") throw item.error;
619
- yield item.value;
620
- }
621
- } finally {
622
- consumerClosed = true;
623
- wakeWaitingProducer();
624
- wakeWaitingConsumer();
625
- options.signal?.removeEventListener("abort", onAbort);
626
- if (!readerFinished) {
627
- await reader.cancel().catch(() => void 0);
628
- }
629
- await readerPromise;
630
237
  }
238
+ const obj = data;
239
+ const details = typeof obj.details === "object" && obj.details !== null ? obj.details : null;
240
+ return new BackendApiError({
241
+ code: obj.code || obj.error_type || obj.error || "internal_error",
242
+ detail: obj.message || "Streaming error",
243
+ userMessage: obj.user_message || obj.message || "Something went wrong",
244
+ details,
245
+ requestId: typeof obj.request_id === "string" ? obj.request_id : typeof details?.request_id === "string" ? details.request_id : void 0
246
+ });
631
247
  }
632
-
633
- // matrx/internal.ts
634
- function encodePathSegment(value) {
635
- return encodeURIComponent(value);
248
+ function parsePersistedBackendError(data, requestId = "") {
249
+ if (!data || typeof data !== "object") return null;
250
+ const error = data;
251
+ const failures = Array.isArray(error.failures) ? error.failures : [];
252
+ const firstSpecific = failures.find(
253
+ (failure) => typeof failure === "object" && failure !== null && typeof failure.message === "string"
254
+ );
255
+ const summary = typeof error.message === "string" ? error.message : "Backend operation failed";
256
+ const specific = typeof firstSpecific?.message === "string" ? firstSpecific.message : summary;
257
+ const detail = specific === summary ? summary : `${summary}: ${specific}`;
258
+ const code = typeof error.type === "string" ? error.type : "internal_error";
259
+ const userMessage = typeof error.user_message === "string" && error.user_message ? error.user_message : detail;
260
+ return new BackendApiError({
261
+ code,
262
+ detail,
263
+ userMessage,
264
+ details: error,
265
+ requestId
266
+ });
636
267
  }
637
- function buildQuery(params) {
638
- const search = new URLSearchParams();
639
- for (const [key, value] of Object.entries(params)) {
640
- if (value === void 0) continue;
641
- if (Array.isArray(value)) {
642
- for (const entry of value) search.append(key, entry);
643
- } else {
644
- search.append(key, String(value));
645
- }
646
- }
647
- const encoded = search.toString();
648
- return encoded ? `?${encoded}` : "";
268
+ var GENERIC_MESSAGE_PATTERNS = [
269
+ /failed unexpectedly/i,
270
+ /^\s*something went wrong/i,
271
+ /please try again(\s+later)?\.?\s*$/i,
272
+ /^\s*request failed\b/i,
273
+ /^\s*unknown (streaming )?error/i,
274
+ /^\s*internal server error\.?\s*$/i
275
+ ];
276
+ function isGenericUserMessage(message) {
277
+ const value = (message ?? "").trim();
278
+ if (!value) return true;
279
+ return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));
649
280
  }
650
- async function readServerDetail(response) {
281
+ function unwrapUpstreamError(message) {
282
+ const start = message.indexOf("{");
283
+ const end = message.lastIndexOf("}");
284
+ if (start < 0 || end <= start) return null;
285
+ let parsed;
651
286
  try {
652
- return await response.json();
287
+ parsed = JSON.parse(message.slice(start, end + 1));
653
288
  } catch {
654
- return void 0;
289
+ return null;
655
290
  }
656
- }
657
- async function throwApiError(path, response) {
658
- throw new MatrxApiError({
659
- status: response.status,
660
- path,
661
- serverDetail: await readServerDetail(response)
662
- });
663
- }
664
- async function requestJson(transport, path, options) {
665
- const hasBody = options.method !== "GET" && options.body !== void 0;
666
- const response = await transport.fetch(path, {
667
- method: options.method,
668
- headers: hasBody ? { "Content-Type": "application/json" } : {},
669
- ...hasBody ? { body: JSON.stringify(options.body) } : {},
670
- ...options.signal ? { signal: options.signal } : {}
671
- });
672
- if (!response.ok) return throwApiError(path, response);
673
- return await response.json();
674
- }
675
- function toRunHandle(response, options) {
291
+ if (typeof parsed !== "object" || parsed === null) return null;
292
+ const body = parsed;
293
+ const inner = typeof body.message === "string" && body.message || typeof body.detail === "string" && body.detail || "";
294
+ if (!inner) return null;
295
+ const statusMatch = /\bHTTP (\d{3})\b/.exec(message.slice(0, start));
676
296
  return {
677
- requestId: response.headers.get("X-Request-ID"),
678
- conversationId: response.headers.get("X-Conversation-ID"),
679
- events: readMatrxNdjsonStream(response.body, {
680
- ...options.signal ? { signal: options.signal } : {},
681
- ...options.maxReadAhead !== void 0 ? { maxReadAhead: options.maxReadAhead } : {},
682
- ...options.onMalformedLine ? { onMalformedLine: options.onMalformedLine } : {},
683
- ...options.onUnknownEnvelope ? { onUnknownEnvelope: options.onUnknownEnvelope } : {},
684
- ...options.onValidEnvelope ? { onValidEnvelope: options.onValidEnvelope } : {}
685
- }),
686
- response
297
+ message: inner,
298
+ code: typeof body.error_type === "string" && body.error_type || typeof body.error === "string" && body.error || null,
299
+ userMessage: typeof body.user_message === "string" ? body.user_message : null,
300
+ requestId: typeof body.request_id === "string" ? body.request_id : null,
301
+ status: statusMatch ? Number(statusMatch[1]) : null
687
302
  };
688
303
  }
689
- async function requestStream(transport, path, options) {
690
- const hasBody = options.method !== "GET" && options.body !== void 0;
691
- const response = await transport.fetch(path, {
692
- method: options.method,
693
- headers: {
694
- ...hasBody ? { "Content-Type": "application/json" } : {},
695
- ...options.headers
696
- },
697
- ...hasBody ? { body: JSON.stringify(options.body) } : {},
698
- ...options.signal ? { signal: options.signal } : {}
699
- });
700
- if (!response.ok) return throwApiError(path, response);
701
- if (!response.body) {
702
- throw new MatrxApiError({
703
- status: response.status,
704
- path,
705
- serverDetail: { code: "missing_response_body" },
706
- message: "The streaming response carried no body."
707
- });
304
+ function describeBackendFailure(error) {
305
+ const chain = [];
306
+ let code = "internal_error";
307
+ let requestId = "";
308
+ let status = null;
309
+ let userFacing = null;
310
+ if (error instanceof BackendApiError) {
311
+ code = error.code;
312
+ requestId = error.requestId;
313
+ status = error.status;
314
+ userFacing = error.userMessage;
315
+ if (error.detail) chain.push(error.detail);
316
+ if (error.userMessage && error.userMessage !== error.detail) {
317
+ chain.push(error.userMessage);
318
+ }
319
+ } else if (error instanceof Error) {
320
+ chain.push(error.message);
321
+ userFacing = error.message;
322
+ } else if (typeof error === "string") {
323
+ chain.push(error);
324
+ userFacing = error;
325
+ } else {
326
+ chain.push("Unknown error");
708
327
  }
709
- return response;
710
- }
711
-
712
- // stream/sse.ts
713
- var FRAME_SEPARATOR = /\r\n\r\n|\n\n|\r\r/;
714
- var LINE_SEPARATOR = /\r\n|\n|\r/;
715
- function parseMatrxSseFrame(frame) {
716
- let event = "message";
717
- let id = null;
718
- const dataLines = [];
719
- let sawData = false;
720
- for (const line of frame.split(LINE_SEPARATOR)) {
721
- if (line.startsWith(":")) continue;
722
- if (line.startsWith("event:")) event = line.slice(6).trim();
723
- else if (line.startsWith("data:")) {
724
- sawData = true;
725
- dataLines.push(line.slice(5).replace(/^ /, ""));
726
- } else if (line.startsWith("id:")) id = line.slice(3).trim();
328
+ for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {
329
+ const upstream = unwrapUpstreamError(chain[cursor] ?? "");
330
+ if (!upstream || chain.includes(upstream.message)) continue;
331
+ chain.push(upstream.message);
332
+ if (upstream.code) code = upstream.code;
333
+ if (upstream.requestId) requestId = upstream.requestId;
334
+ if (upstream.status !== null) status = upstream.status;
727
335
  }
728
- const seqCandidate = id !== null && id !== "" ? Number(id) : NaN;
729
- const seq = Number.isSafeInteger(seqCandidate) && seqCandidate >= 0 ? seqCandidate : null;
730
- return { event, id, seq, data: sawData ? dataLines.join("\n") : null };
731
- }
732
- function createMatrxSseFramer() {
733
- let buffer = "";
336
+ const specific = [...chain].reverse().find((message) => !isGenericUserMessage(message));
337
+ const cause = specific ?? chain[0] ?? "Unknown error";
338
+ const headlineWasGeneric = isGenericUserMessage(userFacing);
734
339
  return {
735
- push(chunk) {
736
- buffer += chunk;
737
- const frames = [];
738
- for (; ; ) {
739
- const sep = FRAME_SEPARATOR.exec(buffer);
740
- if (sep === null) break;
741
- const frame = buffer.slice(0, sep.index);
742
- buffer = buffer.slice(sep.index + sep[0].length);
743
- frames.push(parseMatrxSseFrame(frame));
744
- }
745
- return frames;
746
- },
747
- flush() {
748
- const rest = buffer;
749
- buffer = "";
750
- return { incomplete: rest.length > 0 ? rest : null };
751
- }
340
+ code,
341
+ cause,
342
+ headline: headlineWasGeneric ? cause : userFacing ?? cause,
343
+ headlineWasGeneric,
344
+ chain,
345
+ requestId,
346
+ status
752
347
  };
753
348
  }
754
- async function* readMatrxSseStream(stream, options = {}) {
755
- const reader = stream.getReader();
756
- const decoder = new TextDecoder();
757
- const framer = createMatrxSseFramer();
758
- try {
759
- for (; ; ) {
760
- const { value, done } = await reader.read();
761
- if (done) break;
762
- const frames = framer.push(decoder.decode(value, { stream: true }));
763
- for (const frame of frames) yield frame;
764
- }
765
- const tail = framer.push(decoder.decode());
766
- for (const frame of tail) yield frame;
767
- const { incomplete } = framer.flush();
768
- if (incomplete !== null) options.onIncomplete?.(incomplete);
769
- } finally {
770
- reader.releaseLock();
349
+ function getUserMessage(error) {
350
+ if (error instanceof BackendApiError) {
351
+ return error.userMessage;
352
+ }
353
+ if (error instanceof Error) {
354
+ return error.message;
355
+ }
356
+ if (typeof error === "string") {
357
+ return error;
358
+ }
359
+ if (typeof error === "object" && error !== null && typeof error.message === "string" && error.message) {
360
+ return error.message;
771
361
  }
362
+ return "Something went wrong";
772
363
  }
773
-
774
- // matrx/operations.ts
775
- var TERMINAL_MATRX_RUNTIME_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
776
- var RUNTIME_STATUSES = /* @__PURE__ */ new Set([
777
- "pending",
778
- "running",
779
- "paused",
780
- "waiting_input",
781
- "completed",
782
- "failed",
783
- "cancelled"
784
- ]);
785
- async function getRuntimeOperationStatus(transport, requestId, options = {}) {
786
- try {
787
- return await requestJson(
788
- transport,
789
- `/runtime/operations/${encodePathSegment(requestId)}`,
790
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
791
- );
792
- } catch (error) {
793
- if (error instanceof MatrxApiError && error.status === 404) return null;
794
- throw error;
364
+ function statusToCode(status) {
365
+ switch (status) {
366
+ case 401:
367
+ return "auth_required";
368
+ case 403:
369
+ return "admin_required";
370
+ case 404:
371
+ return "not_found";
372
+ case 422:
373
+ return "validation_error";
374
+ default:
375
+ return "internal_error";
795
376
  }
796
377
  }
797
- async function getRuntimeOperationsByLink(transport, linkKind, linkId, options = {}) {
798
- const query = buildQuery(
799
- options.limit !== void 0 ? { limit: options.limit } : {}
800
- );
801
- try {
802
- return await requestJson(
803
- transport,
804
- `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,
805
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
806
- );
807
- } catch (error) {
808
- if (error instanceof MatrxApiError && error.status === 404) return null;
809
- throw error;
378
+
379
+ // stream/ndjson.ts
380
+ var DEFAULT_MATRX_NDJSON_READ_AHEAD = 64;
381
+ function isRecord2(value) {
382
+ return typeof value === "object" && value !== null && !Array.isArray(value);
383
+ }
384
+ function normalizeMatrxStreamEnvelope(value) {
385
+ if (!isRecord2(value)) return null;
386
+ if (typeof value.event === "string") {
387
+ const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
388
+ const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
389
+ return {
390
+ event: value.event,
391
+ data: value.data,
392
+ ...streamId === void 0 ? {} : { stream_id: streamId },
393
+ ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
394
+ };
395
+ }
396
+ if (value.e === "c" && typeof value.t === "string") {
397
+ const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
398
+ const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
399
+ return {
400
+ event: "chunk",
401
+ data: { text: value.t },
402
+ ...streamId === void 0 ? {} : { stream_id: streamId },
403
+ ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
404
+ };
810
405
  }
811
- }
812
- function listRuntimeOperationEvents(transport, executionId, options = {}) {
813
- const query = buildQuery({
814
- ...options.afterSeq !== void 0 ? { after_seq: options.afterSeq } : {},
815
- ...options.limit !== void 0 ? { limit: options.limit } : {},
816
- ...options.kinds !== void 0 ? { kind: options.kinds } : {}
817
- });
818
- return requestJson(
819
- transport,
820
- `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,
821
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
822
- );
823
- }
824
- function parseEndStatus(data) {
825
- if (data === null) return null;
826
- try {
827
- const parsed = JSON.parse(data);
828
- if (typeof parsed === "object" && parsed !== null && typeof parsed.status === "string") {
829
- const status = parsed.status;
830
- return RUNTIME_STATUSES.has(status) ? status : null;
831
- }
832
- } catch {
406
+ if (value.e === "r" && typeof value.t === "string") {
407
+ const streamId = typeof value.stream_id === "string" ? value.stream_id : void 0;
408
+ const streamSeq = typeof value.stream_seq === "number" && Number.isFinite(value.stream_seq) ? value.stream_seq : void 0;
409
+ return {
410
+ event: "reasoning_chunk",
411
+ data: { text: value.t },
412
+ ...streamId === void 0 ? {} : { stream_id: streamId },
413
+ ...streamSeq === void 0 ? {} : { stream_seq: streamSeq }
414
+ };
833
415
  }
834
416
  return null;
835
417
  }
836
- async function* followRuntimeOperationEvents(transport, executionId, options = {}) {
837
- let cursor = options.lastEventSeq ?? 0;
838
- const headers = { Accept: "text/event-stream" };
839
- if (cursor > 0) headers["Last-Event-ID"] = String(cursor);
840
- const response = await requestStream(
841
- transport,
842
- `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,
843
- {
844
- method: "GET",
845
- headers,
846
- ...options.signal ? { signal: options.signal } : {}
418
+ function createMatrxNdjsonFramer(options = {}) {
419
+ const decoder = new TextDecoder();
420
+ let buffer = "";
421
+ let lineNumber = 0;
422
+ let finished = false;
423
+ const assertOpen = () => {
424
+ if (finished) throw new Error("Matrx NDJSON framer is already finished");
425
+ };
426
+ const parseLine = (line, atCompletion) => {
427
+ const currentLineNumber = ++lineNumber;
428
+ const trimmed = line.trim();
429
+ if (!trimmed) return null;
430
+ let parsed;
431
+ try {
432
+ parsed = JSON.parse(trimmed);
433
+ } catch (error) {
434
+ options.onMalformedLine?.({
435
+ line: trimmed,
436
+ error,
437
+ lineNumber: currentLineNumber,
438
+ atCompletion
439
+ });
440
+ return null;
847
441
  }
848
- );
849
- const frames = readMatrxSseStream(
850
- response.body,
851
- options.onIncomplete ? { onIncomplete: options.onIncomplete } : {}
852
- );
853
- for await (const frame of frames) {
854
- if (frame.event === "end") {
855
- yield { type: "end", status: parseEndStatus(frame.data), cursor };
856
- return;
442
+ const envelope = normalizeMatrxStreamEnvelope(parsed);
443
+ if (!envelope) {
444
+ options.onUnknownEnvelope?.(parsed);
445
+ return null;
857
446
  }
858
- if (frame.event === "execution_event" && frame.data !== null) {
859
- let event;
860
- try {
861
- event = JSON.parse(frame.data);
862
- } catch (error) {
863
- options.onMalformedFrame?.(frame, error);
864
- yield { type: "liveness", cursor };
865
- continue;
447
+ options.onValidEnvelope?.({
448
+ raw: parsed,
449
+ envelope,
450
+ line: trimmed,
451
+ lineNumber: currentLineNumber,
452
+ atCompletion
453
+ });
454
+ return envelope;
455
+ };
456
+ const pushDecodedText = (fragment) => {
457
+ buffer += fragment;
458
+ const lines = buffer.split("\n");
459
+ buffer = lines.pop() ?? "";
460
+ const envelopes = [];
461
+ for (const line of lines) {
462
+ const envelope = parseLine(line, false);
463
+ if (envelope) envelopes.push(envelope);
464
+ }
465
+ return envelopes;
466
+ };
467
+ return {
468
+ pushText(fragment) {
469
+ assertOpen();
470
+ return pushDecodedText(decoder.decode() + fragment);
471
+ },
472
+ pushBytes(fragment) {
473
+ assertOpen();
474
+ return pushDecodedText(decoder.decode(fragment, { stream: true }));
475
+ },
476
+ finish() {
477
+ assertOpen();
478
+ finished = true;
479
+ const envelopes = pushDecodedText(decoder.decode());
480
+ if (buffer.length > 0) {
481
+ const envelope = parseLine(buffer, true);
482
+ buffer = "";
483
+ if (envelope) envelopes.push(envelope);
866
484
  }
867
- if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;
868
- yield { type: "event", event, seq: frame.seq, cursor };
869
- continue;
485
+ return envelopes;
870
486
  }
871
- yield { type: "liveness", cursor };
487
+ };
488
+ }
489
+ function readAheadLimit(value) {
490
+ const limit = value ?? DEFAULT_MATRX_NDJSON_READ_AHEAD;
491
+ if (!Number.isSafeInteger(limit) || limit < 1) {
492
+ throw new RangeError("maxReadAhead must be a positive safe integer");
872
493
  }
494
+ return limit;
873
495
  }
874
- async function followRuntimeOperationToEnd(transport, executionId, options) {
875
- const stallTimeoutMs = options.stallTimeoutMs ?? 45e3;
876
- const reconnectLimit = options.reconnectLimit ?? 60;
877
- const reconnectDelayMs = options.reconnectDelayMs ?? 2e3;
878
- const outer = options.signal;
879
- let cursor = options.lastEventSeq ?? 0;
880
- let failures = 0;
881
- while (!outer?.aborted && failures < reconnectLimit) {
882
- const attempt2 = new AbortController();
883
- const onOuterAbort = () => attempt2.abort();
884
- outer?.addEventListener("abort", onOuterAbort, { once: true });
885
- let stallTimer = null;
886
- const armStall = () => {
887
- if (stallTimer !== null) clearTimeout(stallTimer);
888
- stallTimer = setTimeout(() => attempt2.abort(), stallTimeoutMs);
889
- };
890
- try {
891
- const items = followRuntimeOperationEvents(transport, executionId, {
892
- lastEventSeq: cursor,
893
- signal: attempt2.signal,
894
- ...options.onMalformedFrame ? { onMalformedFrame: options.onMalformedFrame } : {}
496
+ async function* readMatrxNdjsonStream(body, options = {}) {
497
+ const maxReadAhead = readAheadLimit(options.maxReadAhead);
498
+ const queue = [];
499
+ let queuedEventCount = 0;
500
+ let wakeConsumer = null;
501
+ let wakeProducer = null;
502
+ let readerFinished = false;
503
+ let consumerClosed = false;
504
+ const wakeWaitingConsumer = () => {
505
+ const wake = wakeConsumer;
506
+ wakeConsumer = null;
507
+ wake?.();
508
+ };
509
+ const wakeWaitingProducer = () => {
510
+ const wake = wakeProducer;
511
+ wakeProducer = null;
512
+ wake?.();
513
+ };
514
+ const enqueueTerminal = (item) => {
515
+ if (consumerClosed) return;
516
+ queue.push(item);
517
+ wakeWaitingConsumer();
518
+ };
519
+ const enqueueEvent = async (value) => {
520
+ while (queuedEventCount >= maxReadAhead && !consumerClosed && !options.signal?.aborted) {
521
+ await new Promise((resolve) => {
522
+ wakeProducer = resolve;
895
523
  });
896
- armStall();
897
- for await (const item of items) {
898
- armStall();
899
- failures = 0;
900
- cursor = item.cursor;
901
- if (item.type === "end") {
902
- return { ended: true, status: item.status };
524
+ }
525
+ if (consumerClosed || options.signal?.aborted) return false;
526
+ queue.push({ kind: "event", value });
527
+ queuedEventCount += 1;
528
+ wakeWaitingConsumer();
529
+ return true;
530
+ };
531
+ const waitForReadCapacity = async () => {
532
+ while (queuedEventCount >= maxReadAhead && !consumerClosed && !options.signal?.aborted) {
533
+ await new Promise((resolve) => {
534
+ wakeProducer = resolve;
535
+ });
536
+ }
537
+ return !consumerClosed && !options.signal?.aborted;
538
+ };
539
+ const reader = body.getReader();
540
+ const framer = createMatrxNdjsonFramer(options);
541
+ const onAbort = () => {
542
+ wakeWaitingProducer();
543
+ wakeWaitingConsumer();
544
+ void reader.cancel(options.signal?.reason).catch(() => void 0);
545
+ };
546
+ options.signal?.addEventListener("abort", onAbort, { once: true });
547
+ if (options.signal?.aborted) onAbort();
548
+ const readerPromise = (async () => {
549
+ try {
550
+ while (!options.signal?.aborted && !consumerClosed) {
551
+ if (!await waitForReadCapacity()) return;
552
+ const { value, done } = await reader.read();
553
+ if (done) break;
554
+ for (const envelope of framer.pushBytes(value)) {
555
+ if (!await enqueueEvent(envelope)) return;
903
556
  }
904
- if (item.type === "event") {
905
- options.onEvent(item.event, item.seq);
557
+ }
558
+ if (!options.signal?.aborted && !consumerClosed) {
559
+ for (const envelope of framer.finish()) {
560
+ if (!await enqueueEvent(envelope)) return;
906
561
  }
907
562
  }
908
- failures += 1;
909
563
  } catch (error) {
910
- if (outer?.aborted) break;
911
- failures += 1;
912
- options.onAttemptError?.(error);
564
+ const aborted = options.signal?.aborted || consumerClosed || error instanceof Error && error.name === "AbortError";
565
+ if (!aborted) enqueueTerminal({ kind: "error", error });
913
566
  } finally {
914
- if (stallTimer !== null) clearTimeout(stallTimer);
915
- outer?.removeEventListener("abort", onOuterAbort);
567
+ readerFinished = true;
568
+ reader.releaseLock();
569
+ enqueueTerminal({ kind: "done" });
570
+ }
571
+ })();
572
+ try {
573
+ while (true) {
574
+ if (queue.length === 0) {
575
+ if (options.signal?.aborted || readerFinished) return;
576
+ await new Promise((resolve) => {
577
+ wakeConsumer = resolve;
578
+ });
579
+ }
580
+ const item = queue.shift();
581
+ if (item?.kind === "event") queuedEventCount -= 1;
582
+ wakeWaitingProducer();
583
+ if (!item || item.kind === "done") return;
584
+ if (item.kind === "error") throw item.error;
585
+ yield item.value;
916
586
  }
917
- if (!outer?.aborted && failures < reconnectLimit) {
918
- await new Promise((r) => setTimeout(r, reconnectDelayMs));
587
+ } finally {
588
+ consumerClosed = true;
589
+ wakeWaitingProducer();
590
+ wakeWaitingConsumer();
591
+ options.signal?.removeEventListener("abort", onAbort);
592
+ if (!readerFinished) {
593
+ await reader.cancel().catch(() => void 0);
919
594
  }
595
+ await readerPromise;
920
596
  }
921
- return { ended: false, status: null };
922
597
  }
923
- async function rejoinRuntimeOperation(transport, requestId, options = {}) {
924
- const response = await requestStream(
925
- transport,
926
- `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,
927
- {
928
- method: "POST",
929
- // The route takes no body model; the reference client posts an empty
930
- // JSON object. Match it so proxies see an ordinary JSON POST.
931
- body: {},
932
- ...options.signal ? { signal: options.signal } : {}
598
+
599
+ // matrx/org-context.ts
600
+ import { isUuidShape } from "@ai-matrx/kit/uuid";
601
+ var OrganizationContextError = class extends Error {
602
+ code;
603
+ constructor(code, message) {
604
+ super(message);
605
+ this.name = "OrganizationContextError";
606
+ this.code = code;
607
+ }
608
+ };
609
+ function createOrganizationOperation(organizationId) {
610
+ const organization_id = requireOrganizationContext(organizationId);
611
+ return Object.freeze({ organization_id });
612
+ }
613
+ function assertOrganizationMatchesOperation(operation, organizationId) {
614
+ if (operation.organization_id !== requireOrganizationContext(organizationId)) {
615
+ throw new OrganizationContextError(
616
+ "organization_context_mismatch",
617
+ "Organization ID must match the request operation."
618
+ );
619
+ }
620
+ }
621
+ function requireOrganizationContext(selectedOrganizationId, overrideOrganizationId) {
622
+ const candidate = overrideOrganizationId ?? selectedOrganizationId;
623
+ if (typeof candidate !== "string" || candidate.trim().length === 0) {
624
+ throw new OrganizationContextError(
625
+ "organization_context_required",
626
+ "Select an organization before sending this request."
627
+ );
628
+ }
629
+ const normalized = candidate.trim();
630
+ if (!isUuidShape(normalized)) {
631
+ throw new OrganizationContextError(
632
+ "organization_context_invalid",
633
+ "The selected organization ID is invalid."
634
+ );
635
+ }
636
+ return normalized.toLowerCase();
637
+ }
638
+ function applyOrganizationContextHeader(headers, organizationId) {
639
+ const normalizedOrganizationId = requireOrganizationContext(organizationId);
640
+ for (const [name, value] of Object.entries(headers)) {
641
+ if (name.toLowerCase() === "x-organization-id" && value.trim().toLowerCase() !== normalizedOrganizationId) {
642
+ throw new OrganizationContextError(
643
+ "organization_context_mismatch",
644
+ "X-Organization-Id must match the request context organization."
645
+ );
933
646
  }
647
+ }
648
+ const withoutOrganizationHeader = Object.fromEntries(
649
+ Object.entries(headers).filter(
650
+ ([name]) => name.toLowerCase() !== "x-organization-id"
651
+ )
934
652
  );
935
- return toRunHandle(response, options);
653
+ return {
654
+ ...withoutOrganizationHeader,
655
+ "X-Organization-Id": normalizedOrganizationId
656
+ };
657
+ }
658
+ function assertQueryOrganizationMatchesContext(queryParams, organizationId) {
659
+ if (!queryParams || queryParams.organization_id === void 0) return;
660
+ const queryOrganizationId = requireOrganizationContext(
661
+ String(queryParams.organization_id)
662
+ );
663
+ if (queryOrganizationId !== requireOrganizationContext(organizationId)) {
664
+ throw new OrganizationContextError(
665
+ "organization_context_mismatch",
666
+ "Query organization_id must match the request context organization."
667
+ );
668
+ }
936
669
  }
937
670
 
938
- // matrx/rejoin.ts
939
- var MATRX_RUN_IN_PROGRESS = "run_in_progress";
940
- var MATRX_RESUME_CONFLICT = "resume_conflict";
941
- var MATRX_LIVE_STREAM_UNAVAILABLE = "live_stream_unavailable";
942
- function isRecord3(value) {
943
- return typeof value === "object" && value !== null && !Array.isArray(value);
671
+ // matrx/protocol.ts
672
+ import {
673
+ isNetError,
674
+ resilientFetch
675
+ } from "@ai-matrx/data/net";
676
+ var MATRX_AI_API_VERSION_DEFAULT = "v2";
677
+ var V2_COVERED_AI_PATH_TEMPLATES = [
678
+ "/ai/manual",
679
+ "/ai/chat",
680
+ "/ai/agents/{agent_id}",
681
+ "/ai/agent/{agent_id}",
682
+ // singular alias — behaves identically
683
+ "/ai/conversations/{conversation_id}",
684
+ "/ai/conversation/{conversation_id}",
685
+ // singular alias
686
+ "/ai/prompts/{prompt_id}",
687
+ // saved-prompt execution
688
+ "/ai/mandates/{mandate_key}"
689
+ // THE MANDATE DOOR — `/v2` sibling exists (routers/v2.py)
690
+ ];
691
+ function toV2Path(path) {
692
+ const withLead = path.startsWith("/") ? path : `/${path}`;
693
+ if (withLead.startsWith("/api/")) {
694
+ const rest = withLead.slice("/api".length);
695
+ return rest.startsWith("/v2/") ? withLead : `/api/v2${rest}`;
696
+ }
697
+ return withLead.startsWith("/v2/") ? withLead : `/v2${withLead}`;
944
698
  }
945
- function nonBlank(value) {
946
- return typeof value === "string" && value.trim() ? value : null;
699
+ var COVERED_INTERPOLATED_PATH = [
700
+ /^(?:\/api)?\/ai\/manual$/,
701
+ /^(?:\/api)?\/ai\/chat$/,
702
+ /^(?:\/api)?\/ai\/agents\/[^/]+$/,
703
+ /^(?:\/api)?\/ai\/agent\/[^/]+$/,
704
+ /^(?:\/api)?\/ai\/conversations\/[^/]+$/,
705
+ /^(?:\/api)?\/ai\/conversation\/[^/]+$/,
706
+ /^(?:\/api)?\/ai\/prompts\/[^/]+$/,
707
+ // `/warm` etc. deliberately don't match
708
+ /^(?:\/api)?\/ai\/mandates\/[^/]+$/
709
+ // the mandate door
710
+ ];
711
+ function isCoveredAiPath(path) {
712
+ return COVERED_INTERPOLATED_PATH.some((re) => re.test(path));
947
713
  }
948
- function detailCandidates(body) {
949
- if (!isRecord3(body)) return [];
950
- const out = [];
951
- if (isRecord3(body.detail)) out.push(body.detail);
952
- if (isRecord3(body.details)) out.push(body.details);
953
- out.push(body);
954
- return out;
714
+ function isV2Path(pathOrUrl) {
715
+ return /\/v2\/ai\//.test(pathOrUrl);
955
716
  }
956
- function statusOf(error) {
957
- if (!isRecord3(error) && !(error instanceof Error)) return null;
958
- const status = error.status;
959
- return typeof status === "number" ? status : null;
717
+ function toV1FallbackUrl(url) {
718
+ return url.replace(/\/v2\/ai\//, "/ai/");
960
719
  }
961
- function bodyOf(error) {
962
- if (!isRecord3(error) && !(error instanceof Error)) return void 0;
963
- return error.serverDetail;
720
+ function applyAiApiVersion(path, version) {
721
+ if (version !== "v2") return path;
722
+ return isCoveredAiPath(path) ? toV2Path(path) : path;
964
723
  }
965
- function readMatrxErrorCode(error) {
966
- for (const candidate of detailCandidates(bodyOf(error))) {
967
- const code = nonBlank(candidate.code);
968
- if (code) return code;
724
+ async function fetchWithMatrxProtocolFallback(url, init, opts = {}) {
725
+ const { onDowngrade, ...fetchOpts } = opts;
726
+ const logDowngrade = (reason, status2) => {
727
+ console.warn(
728
+ `[matrx] ai_v2_downgrade \u2192 retrying on v1. v2=${url} reason=${reason}`
729
+ );
730
+ onDowngrade?.({ url, reason, ...status2 !== void 0 ? { status: status2 } : {} });
731
+ };
732
+ if (!isV2Path(url)) return resilientFetch(url, init, fetchOpts);
733
+ let result;
734
+ try {
735
+ result = await resilientFetch(url, init, fetchOpts);
736
+ } catch (err) {
737
+ const isAbort = isNetError(err) && err.code === "aborted" || err instanceof Error && err.name === "AbortError";
738
+ if (isAbort) throw err;
739
+ logDowngrade(String(err));
740
+ return resilientFetch(toV1FallbackUrl(url), init, fetchOpts);
969
741
  }
970
- for (const candidate of detailCandidates(bodyOf(error))) {
971
- const code = nonBlank(candidate.error);
972
- if (code) return code;
742
+ const status = result.response.status;
743
+ if (status === 404 || status === 405 || status >= 500) {
744
+ logDowngrade(`HTTP ${status}`, status);
745
+ return resilientFetch(toV1FallbackUrl(url), init, fetchOpts);
973
746
  }
974
- return null;
747
+ return result;
975
748
  }
976
- var PREFIXED_ERROR_CODES = [
977
- "resume_conflict",
978
- "not_resumable",
979
- "outstanding_delegated_calls",
980
- "user_request_not_found",
981
- "run_in_flight"
982
- ];
983
- function readMatrxErrorCodeFromMessage(message) {
984
- if (typeof message !== "string") return null;
985
- for (const code of PREFIXED_ERROR_CODES) {
986
- if (message.startsWith(`${code}:`)) return code;
749
+ function aiVersionPathOverrides(version) {
750
+ if (version !== "v2") return {};
751
+ const map = {};
752
+ for (const template of V2_COVERED_AI_PATH_TEMPLATES) {
753
+ map[template] = toV2Path(template);
987
754
  }
988
- return null;
755
+ return map;
989
756
  }
990
- async function readConversationOrganizationId(conversationId, read, onUnreadable) {
991
- try {
992
- const value = await read(conversationId);
993
- if (typeof value === "string" && value.trim()) return value;
994
- onUnreadable?.(null);
995
- } catch (error) {
996
- onUnreadable?.(error instanceof Error ? error.message : String(error));
757
+
758
+ // matrx/call.ts
759
+ function buildMatrxRequestUrl(baseUrl, pathTemplate, pathParams, queryParams) {
760
+ let resolvedPath = pathTemplate;
761
+ if (pathParams) {
762
+ for (const [key, value] of Object.entries(pathParams)) {
763
+ resolvedPath = resolvedPath.replace(`{${key}}`, encodeURIComponent(value));
764
+ }
997
765
  }
998
- return null;
999
- }
1000
- function firstString(candidates, key) {
1001
- for (const candidate of candidates) {
1002
- const value = nonBlank(candidate[key]);
1003
- if (value) return value.trim();
766
+ const fullPath = resolvedPath.startsWith("/api/") ? resolvedPath.slice(4) : resolvedPath;
767
+ const url = `${baseUrl}${fullPath}`;
768
+ if (queryParams && Object.keys(queryParams).length > 0) {
769
+ const qs = new URLSearchParams(
770
+ Object.fromEntries(
771
+ Object.entries(queryParams).map(([k, v]) => [k, String(v)])
772
+ )
773
+ ).toString();
774
+ return `${url}?${qs}`;
775
+ }
776
+ return url;
777
+ }
778
+ var MATRX_UI_ONLY_BODY_FIELDS = /* @__PURE__ */ new Set([
779
+ "youtube_videos",
780
+ "file_urls",
781
+ "image_urls"
782
+ ]);
783
+ function buildMatrxRequestBody(body, scope) {
784
+ const raw = body ?? {};
785
+ const base = {};
786
+ for (const [key, value] of Object.entries(raw)) {
787
+ if (!MATRX_UI_ONLY_BODY_FIELDS.has(key)) base[key] = value;
1004
788
  }
1005
- return null;
1006
- }
1007
- function readLiveRunRejoin(error) {
1008
- if (statusOf(error) !== 409) return null;
1009
- const candidates = detailCandidates(bodyOf(error));
1010
- if (candidates.length === 0) return null;
1011
- const code = readMatrxErrorCode(error);
1012
- if (code !== MATRX_RUN_IN_PROGRESS) return null;
1013
- const liveRequestId = firstString(candidates, "live_request_id");
1014
- const pathFromBody = firstString(candidates, "rejoin_path");
1015
- const rejoinPath = pathFromBody && pathFromBody.startsWith("/") ? pathFromBody : liveRequestId ? runtimeOperationRejoinPath(liveRequestId) : null;
1016
- if (!rejoinPath) return null;
1017
- const root = bodyOf(error);
1018
- return {
1019
- liveRequestId,
1020
- rejoinPath,
1021
- runId: firstString(candidates, "run_id"),
1022
- code,
1023
- body: isRecord3(root) ? root : {}
789
+ if (base.organization_id === null || typeof base.organization_id === "string" && base.organization_id.trim() === "") {
790
+ delete base.organization_id;
791
+ }
792
+ const bodyOrganizationId = base.organization_id;
793
+ if (scope.organization_id !== void 0 && bodyOrganizationId !== void 0 && (typeof bodyOrganizationId !== "string" || bodyOrganizationId.trim() !== scope.organization_id)) {
794
+ throw new OrganizationContextError(
795
+ "organization_context_mismatch",
796
+ "Request body organization_id must match the request context organization."
797
+ );
798
+ }
799
+ if (bodyOrganizationId !== void 0 && scope.organization_id !== void 0) {
800
+ base.organization_id = scope.organization_id;
801
+ }
802
+ const scopeFields = {
803
+ ...scope.organization_id !== void 0 ? { organization_id: scope.organization_id } : {}
1024
804
  };
805
+ if (scope.project_id !== void 0) scopeFields.project_id = scope.project_id;
806
+ if (scope.task_id !== void 0) scopeFields.task_id = scope.task_id;
807
+ return { ...scopeFields, ...base };
808
+ }
809
+ var BARE_TRANSPORT_CODE = /^\s*(?:HTTP|HTTP\s*Error|Status(?:\s*Code)?)?\s*[:\-]?\s*\d{3}\s*[.:!]?\s*$/i;
810
+ function isBareTransportCode(text) {
811
+ if (!text) return false;
812
+ return BARE_TRANSPORT_CODE.test(text);
813
+ }
814
+ function bareStatusSentence(status) {
815
+ if (status === 401 || status === 403) {
816
+ return "The server would not let this request through \u2014 your session may have expired, or this account may not have access here. Sign in again, and if it repeats, ask an administrator.";
817
+ }
818
+ if (status === 404) {
819
+ return "The server has nothing at that address. Reload the page; if it repeats, report it \u2014 a client asking for something that no longer exists is a defect, not your mistake.";
820
+ }
821
+ if (status === 429) {
822
+ return "The server is rate-limiting this request. Wait a few seconds and try again.";
823
+ }
824
+ if (status >= 500) {
825
+ return "The server failed while answering this, and sent no explanation. Try again in a moment; if it repeats, report it with the request id above.";
826
+ }
827
+ return "The server refused this request and sent no reason with it \u2014 the missing reason is itself a defect worth reporting. Reload the page and try once more; if it repeats, report it with the request id above.";
1025
828
  }
1026
- function readLiveRunRequestId(error) {
1027
- return readLiveRunRejoin(error)?.liveRequestId ?? null;
829
+ function honestTransportMessage(raw, status) {
830
+ if (raw && !isBareTransportCode(raw)) return raw;
831
+ return bareStatusSentence(status ?? 0);
1028
832
  }
1029
- function isResumeConflict(error) {
1030
- return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT;
833
+ var SENSITIVE_HEADER_NAME = /authorization|cookie|token|api[-_]?key/i;
834
+ function buildSafeRequestLog(headers, body) {
835
+ const safeHeaders = Object.fromEntries(
836
+ Object.entries(headers).map(([name, value]) => [
837
+ name,
838
+ SENSITIVE_HEADER_NAME.test(name) ? "[REDACTED]" : value
839
+ ])
840
+ );
841
+ const bodyMetadata = Array.isArray(body) ? { type: "array", itemCount: body.length } : body && typeof body === "object" ? { type: "object", keys: Object.keys(body) } : { type: body === null ? "null" : typeof body };
842
+ return { headers: safeHeaders, body: bodyMetadata };
1031
843
  }
1032
- function isLiveStreamUnavailable(error) {
1033
- return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_LIVE_STREAM_UNAVAILABLE;
844
+ function redactUrlForRequestLog(url) {
845
+ try {
846
+ const parsed = new URL(url);
847
+ for (const key of parsed.searchParams.keys()) {
848
+ parsed.searchParams.set(key, "[REDACTED]");
849
+ }
850
+ return parsed.toString();
851
+ } catch {
852
+ const queryIndex = url.indexOf("?");
853
+ return queryIndex === -1 ? url : `${url.slice(0, queryIndex)}?[REDACTED]`;
854
+ }
1034
855
  }
1035
- function runtimeOperationRejoinPath(requestId) {
1036
- return `/runtime/operations/${encodePathSegment(requestId)}/rejoin`;
856
+ function shouldReportMatrxCallError(status, expectedErrorStatuses) {
857
+ return status == null || !expectedErrorStatuses?.includes(status);
1037
858
  }
1038
- function streamErrorText(eventOrPayload) {
1039
- if (!isRecord3(eventOrPayload)) return null;
1040
- const payload = typeof eventOrPayload.event === "string" && isRecord3(eventOrPayload.data) ? eventOrPayload.data : eventOrPayload;
1041
- return nonBlank(payload.user_message) ?? nonBlank(payload.message);
859
+ function parseMatrxNdjsonResponse(response, signal, hooks = {}) {
860
+ const ids = {
861
+ requestId: response.headers.get("X-Request-ID"),
862
+ conversationId: response.headers.get("X-Conversation-ID")
863
+ };
864
+ async function* events() {
865
+ if (!response.body) {
866
+ throw new BackendApiError({
867
+ code: "internal_error",
868
+ detail: "Response has no body",
869
+ userMessage: "No response received from server"
870
+ });
871
+ }
872
+ try {
873
+ for await (const envelope of readMatrxNdjsonStream(response.body, {
874
+ ...signal ? { signal } : {},
875
+ ...hooks.onMalformedLine ? { onMalformedLine: hooks.onMalformedLine } : {},
876
+ ...hooks.onUnknownEnvelope ? { onUnknownEnvelope: hooks.onUnknownEnvelope } : {}
877
+ })) {
878
+ hooks.onEvent?.(envelope, ids);
879
+ yield envelope;
880
+ }
881
+ } catch (error) {
882
+ if (signal?.aborted || error instanceof Error && error.name === "AbortError") {
883
+ return;
884
+ }
885
+ const transportError = error instanceof BackendApiError ? error : new StreamTransportError({
886
+ detail: error instanceof Error ? error.message : "The response stream ended unexpectedly.",
887
+ details: error,
888
+ ...ids.requestId ? { requestId: ids.requestId } : {}
889
+ });
890
+ hooks.onTransportError?.(transportError, ids);
891
+ throw transportError;
892
+ }
893
+ }
894
+ return { events: events(), ...ids };
1042
895
  }
1043
- async function openMatrxStream(transport, path, options = {}) {
1044
- const response = await requestStream(transport, path, {
1045
- method: "POST",
1046
- ...options.body !== void 0 ? { body: options.body } : {},
1047
- ...options.signal ? { signal: options.signal } : {}
1048
- });
1049
- return toRunHandle(response, options);
896
+ var DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
897
+ var DEFAULT_JSON_TOTAL_TIMEOUT_MS = 3e4;
898
+ async function httpErrorFrom(response) {
899
+ const serverDetail = await response.json().catch(() => void 0);
900
+ return {
901
+ type: response.status >= 400 && response.status < 500 ? "validation_error" : "http_error",
902
+ message: extractMatrxErrorMessage(serverDetail) ?? bareStatusSentence(response.status),
903
+ status: response.status,
904
+ serverDetail
905
+ };
1050
906
  }
1051
- function withRunOrganization(transport, organizationId) {
1052
- const orgId = requireOrganizationContext(organizationId);
907
+ async function executeMatrxCall(request, hooks = {}) {
908
+ const upper = request.method.toUpperCase();
909
+ const sendsBody = request.stream || upper !== "GET" && upper !== "HEAD";
910
+ const { response } = await fetchWithMatrxProtocolFallback(
911
+ request.url,
912
+ {
913
+ method: request.method,
914
+ headers: request.headers,
915
+ ...sendsBody ? { body: JSON.stringify(request.body) } : {}
916
+ },
917
+ {
918
+ ...request.signal ? { signal: request.signal } : {},
919
+ connectTimeoutMs: request.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
920
+ totalTimeoutMs: request.stream ? null : request.totalTimeoutMs === void 0 ? DEFAULT_JSON_TOTAL_TIMEOUT_MS : request.totalTimeoutMs,
921
+ throwOnHttpError: false,
922
+ ...hooks.onProtocolDowngrade ? { onDowngrade: hooks.onProtocolDowngrade } : {}
923
+ }
924
+ );
925
+ const requestId = response.headers.get("X-Request-ID");
926
+ const conversationId = response.headers.get("X-Conversation-ID");
927
+ const idFields = {
928
+ ...requestId !== null ? { requestId } : {},
929
+ ...conversationId !== null ? { conversationId } : {}
930
+ };
931
+ if (!response.ok) {
932
+ const error = await httpErrorFrom(response);
933
+ if (request.stream) request.onStreamError?.(error);
934
+ return { ...idFields, error };
935
+ }
936
+ if (!request.stream) {
937
+ const data = response.status === 204 ? void 0 : await response.json();
938
+ return { data, ...idFields };
939
+ }
940
+ if (request.consumeStream) {
941
+ request.onStreamStart?.(requestId, conversationId);
942
+ await request.consumeStream(response, { requestId, conversationId });
943
+ request.onStreamComplete?.(requestId, conversationId);
944
+ return idFields;
945
+ }
946
+ const parse = hooks.parseStream ?? ((res, signal) => parseMatrxNdjsonResponse(res, signal));
947
+ const parsed = parse(response, request.signal);
948
+ request.onStreamStart?.(parsed.requestId, parsed.conversationId);
949
+ for await (const event of parsed.events) {
950
+ request.onStreamEvent?.(event);
951
+ }
952
+ request.onStreamComplete?.(parsed.requestId, parsed.conversationId);
1053
953
  return {
1054
- fetch: (path, init) => transport.fetch(path, {
1055
- ...init,
1056
- headers: { ...init.headers, "X-Organization-Id": orgId }
1057
- })
954
+ ...parsed.requestId !== null ? { requestId: parsed.requestId } : {},
955
+ ...parsed.conversationId !== null ? { conversationId: parsed.conversationId } : {}
1058
956
  };
1059
957
  }
1060
- async function followWorkflowRunEvents(transport, runId, options) {
1061
- const headers = { Accept: "text/event-stream" };
1062
- if (options.lastEventId) headers["Last-Event-ID"] = String(options.lastEventId);
1063
- let response;
1064
- try {
1065
- response = await requestStream(
1066
- transport,
1067
- `/runs/${encodePathSegment(runId)}/events/stream`,
1068
- {
1069
- method: "GET",
1070
- headers,
1071
- ...options.signal ? { signal: options.signal } : {}
1072
- }
958
+
959
+ // matrx/client.ts
960
+ function classifyStatus(status) {
961
+ return status >= 400 && status < 500 ? "validation_error" : "http_error";
962
+ }
963
+ function normalizeMatrxError(err) {
964
+ if (err instanceof OrganizationContextError) {
965
+ return {
966
+ type: "validation_error",
967
+ message: err.message,
968
+ code: err.code,
969
+ name: err.name,
970
+ ...err.stack !== void 0 ? { stack: err.stack } : {}
971
+ };
972
+ }
973
+ if (err instanceof DOMException && err.name === "AbortError") {
974
+ return { type: "abort_error", message: "Request was cancelled." };
975
+ }
976
+ if (err instanceof MatrxApiError) {
977
+ return {
978
+ type: classifyStatus(err.status),
979
+ message: honestTransportMessage(err.message, err.status),
980
+ status: err.status,
981
+ serverDetail: err.serverDetail,
982
+ ...err.code ? { code: err.code } : {},
983
+ name: err.name
984
+ };
985
+ }
986
+ if (err instanceof BackendApiError) {
987
+ return {
988
+ type: "network_error",
989
+ message: honestTransportMessage(
990
+ err.detail || err.userMessage,
991
+ err.status ?? void 0
992
+ ),
993
+ code: err.code,
994
+ name: err.name,
995
+ ...err.status !== null ? { status: err.status } : {},
996
+ ...err.details !== null ? { serverDetail: err.details } : {}
997
+ };
998
+ }
999
+ if (isNetError2(err)) {
1000
+ if (err.code === "aborted") {
1001
+ return { type: "abort_error", message: err.message };
1002
+ }
1003
+ if (err.code === "http") {
1004
+ const status = err.status ?? 0;
1005
+ return {
1006
+ type: classifyStatus(status),
1007
+ message: honestTransportMessage(err.message, status),
1008
+ status
1009
+ };
1010
+ }
1011
+ return { type: "network_error", message: err.message };
1012
+ }
1013
+ if (err instanceof Error) {
1014
+ const httpMatch = err.message.match(/HTTP (\d+):\s*(.*)/);
1015
+ if (httpMatch) {
1016
+ const status = parseInt(httpMatch[1], 10);
1017
+ return {
1018
+ type: classifyStatus(status),
1019
+ message: honestTransportMessage(httpMatch[2] ?? err.message, status),
1020
+ status
1021
+ };
1022
+ }
1023
+ if (isBareTransportCode(err.message)) {
1024
+ const status = Number.parseInt(err.message.replace(/\D+/g, ""), 10);
1025
+ return {
1026
+ type: classifyStatus(status),
1027
+ message: bareStatusSentence(status),
1028
+ status
1029
+ };
1030
+ }
1031
+ return { type: "network_error", message: err.message };
1032
+ }
1033
+ return { type: "unknown", message: extractErrorMessage(err) };
1034
+ }
1035
+ var PRODUCTION_CONNECT_TIMEOUT_MS = 15e3;
1036
+ function stripContentType(headers) {
1037
+ return Object.fromEntries(
1038
+ Object.entries(headers).filter(
1039
+ ([name]) => name.toLowerCase() !== "content-type"
1040
+ )
1041
+ );
1042
+ }
1043
+ function createMatrxTransport(options) {
1044
+ const {
1045
+ baseUrl,
1046
+ resolveTarget,
1047
+ credentials,
1048
+ organizationId,
1049
+ aiApiVersion,
1050
+ connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,
1051
+ totalTimeoutMs = null,
1052
+ expectedErrorStatuses,
1053
+ diagnostics,
1054
+ source = "matrxTransport"
1055
+ } = options;
1056
+ if (baseUrl === void 0 === (resolveTarget === void 0)) {
1057
+ throw new Error(
1058
+ "createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`."
1073
1059
  );
1074
- } catch (error) {
1075
- if (options.signal?.aborted) return { ended: false };
1076
- throw error;
1077
1060
  }
1078
- try {
1079
- for await (const frame of readMatrxSseStream(
1080
- response.body
1081
- )) {
1082
- if (frame.event === "end") return { ended: true };
1083
- if (frame.data === null) continue;
1084
- let parsed;
1061
+ const resolve = resolveTarget ?? (() => ({ baseUrl }));
1062
+ const resolveVersion = () => {
1063
+ if (aiApiVersion === void 0) return MATRX_AI_API_VERSION_DEFAULT;
1064
+ return typeof aiApiVersion === "function" ? aiApiVersion() : aiApiVersion;
1065
+ };
1066
+ const resolveOrganizationId = () => {
1067
+ if (organizationId === void 0) return null;
1068
+ const candidate = typeof organizationId === "function" ? organizationId() : organizationId;
1069
+ return requireOrganizationContext(candidate);
1070
+ };
1071
+ return {
1072
+ async fetch(path, init) {
1073
+ const target = resolve();
1074
+ const versionedPath = applyAiApiVersion(path, resolveVersion());
1075
+ const url = `${target.baseUrl}${versionedPath}`;
1076
+ const credentialHeaders = credentials ? credentialToHeaders(await credentials.get()) : {};
1077
+ let headers = {
1078
+ ...init.headers,
1079
+ ...stripContentType(credentialHeaders),
1080
+ ...stripContentType(target.policyHeaders ?? {})
1081
+ };
1082
+ const orgId = resolveOrganizationId();
1083
+ if (orgId !== null) {
1084
+ headers = applyOrganizationContextHeader(headers, orgId);
1085
+ }
1086
+ const info = {
1087
+ url,
1088
+ method: init.method,
1089
+ path,
1090
+ channel: target.channel ?? "default",
1091
+ source
1092
+ };
1093
+ diagnostics?.onRequest?.(info);
1094
+ let response;
1085
1095
  try {
1086
- parsed = JSON.parse(frame.data);
1087
- } catch {
1088
- continue;
1096
+ ({ response } = await fetchWithMatrxProtocolFallback(
1097
+ url,
1098
+ {
1099
+ method: init.method,
1100
+ headers,
1101
+ ...init.body !== void 0 ? { body: init.body } : {}
1102
+ },
1103
+ {
1104
+ ...init.signal ? { signal: init.signal } : {},
1105
+ connectTimeoutMs,
1106
+ totalTimeoutMs,
1107
+ throwOnHttpError: false,
1108
+ ...diagnostics?.onProtocolDowngrade ? { onDowngrade: diagnostics.onProtocolDowngrade } : {}
1109
+ }
1110
+ ));
1111
+ } catch (err) {
1112
+ diagnostics?.onError?.(normalizeMatrxError(err), info);
1113
+ throw err;
1089
1114
  }
1090
- if (!isRecord3(parsed)) continue;
1091
- options.onEvent(parsed, parsed.event === "node_stream" ? null : frame.seq);
1115
+ if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {
1116
+ const serverDetail = await response.clone().json().catch(() => void 0);
1117
+ const code = extractMatrxErrorCode(serverDetail);
1118
+ diagnostics?.onError?.(
1119
+ {
1120
+ type: classifyStatus(response.status),
1121
+ message: extractMatrxErrorMessage(serverDetail) ?? `HTTP ${response.status}`,
1122
+ status: response.status,
1123
+ serverDetail,
1124
+ ...code ? { code } : {}
1125
+ },
1126
+ info
1127
+ );
1128
+ }
1129
+ return response;
1092
1130
  }
1093
- } catch (error) {
1094
- if (!options.signal?.aborted) throw error;
1095
- }
1096
- return { ended: false };
1131
+ };
1097
1132
  }
1098
- async function drain(handle, onEnvelope) {
1099
- for await (const envelope of handle.events) onEnvelope(envelope);
1133
+
1134
+ // matrx/conversation.ts
1135
+ function mintMatrxConversationId() {
1136
+ return crypto.randomUUID();
1137
+ }
1138
+ function newStoredConversationStart(conversationId) {
1139
+ return {
1140
+ conversation_id: conversationId ?? mintMatrxConversationId(),
1141
+ is_new: true,
1142
+ store: true
1143
+ };
1144
+ }
1145
+ function continueStoredConversationStart(conversationId) {
1146
+ return { conversation_id: conversationId, is_new: false, store: true };
1100
1147
  }
1101
- async function resumeOrRejoin(transport, resumeCall, options) {
1102
- const {
1103
- onEnvelope,
1104
- organizationId,
1105
- onRejoin,
1106
- onOperationEvent,
1107
- onWorkflowEvent,
1108
- follow,
1109
- ...rest
1110
- } = options;
1111
- const bound = organizationId ? withRunOrganization(transport, organizationId) : transport;
1112
- const streamOptions = {
1113
- ...rest,
1114
- ...options.signal ? { signal: options.signal } : {}
1148
+ function newEphemeralConversationStart(conversationId) {
1149
+ return {
1150
+ conversation_id: conversationId ?? mintMatrxConversationId(),
1151
+ is_new: true,
1152
+ store: false
1115
1153
  };
1116
- const signalOnly = options.signal ? { signal: options.signal } : {};
1117
- let rejoin;
1118
- try {
1119
- const handle = await resumeCall(bound, streamOptions);
1120
- await drain(handle, onEnvelope);
1121
- return { kind: "resumed", requestId: handle.requestId };
1122
- } catch (error) {
1123
- const target = readLiveRunRejoin(error);
1124
- if (!target) {
1125
- if (error instanceof MatrxApiError && isResumeConflict(error)) {
1126
- return { kind: "resume_conflict", error };
1127
- }
1128
- throw error;
1154
+ }
1155
+ function continueEphemeralConversationStart(conversationId, priorMessages) {
1156
+ return {
1157
+ conversation_id: conversationId,
1158
+ is_new: false,
1159
+ store: false,
1160
+ prior_messages: priorMessages
1161
+ };
1162
+ }
1163
+
1164
+ // matrx/internal.ts
1165
+ function encodePathSegment(value) {
1166
+ return encodeURIComponent(value);
1167
+ }
1168
+ function buildQuery(params) {
1169
+ const search = new URLSearchParams();
1170
+ for (const [key, value] of Object.entries(params)) {
1171
+ if (value === void 0) continue;
1172
+ if (Array.isArray(value)) {
1173
+ for (const entry of value) search.append(key, entry);
1174
+ } else {
1175
+ search.append(key, String(value));
1129
1176
  }
1130
- rejoin = target;
1131
1177
  }
1132
- onRejoin?.(rejoin);
1133
- let unavailable;
1178
+ const encoded = search.toString();
1179
+ return encoded ? `?${encoded}` : "";
1180
+ }
1181
+ async function readServerDetail(response) {
1134
1182
  try {
1135
- const handle = await openMatrxStream(bound, rejoin.rejoinPath, {
1136
- ...streamOptions,
1137
- // The rejoin routes take no body model; the reference client posts {}.
1138
- body: {}
1139
- });
1140
- await drain(handle, onEnvelope);
1141
- return { kind: "rejoined", rejoin };
1142
- } catch (error) {
1143
- const read = readLiveStreamUnavailable(error);
1144
- if (!read) throw error;
1145
- unavailable = read;
1183
+ return await response.json();
1184
+ } catch {
1185
+ return void 0;
1146
1186
  }
1147
- return followUnavailableRejoin(bound, rejoin, unavailable, {
1148
- ...signalOnly,
1149
- ...onOperationEvent ? { onOperationEvent } : {},
1150
- ...onWorkflowEvent ? { onWorkflowEvent } : {},
1151
- ...follow ? { follow } : {}
1152
- });
1153
1187
  }
1154
- function readLiveStreamUnavailable(error) {
1155
- if (!isLiveStreamUnavailable(error)) return null;
1156
- return { runId: firstString(detailCandidates(bodyOf(error)), "run_id") };
1188
+ async function throwApiError(path, response) {
1189
+ throw new MatrxApiError({
1190
+ status: response.status,
1191
+ path,
1192
+ serverDetail: await readServerDetail(response)
1193
+ });
1157
1194
  }
1158
- async function followUnavailableRejoin(transport, rejoin, unavailable, options = {}) {
1159
- const bound = options.organizationId ? withRunOrganization(transport, options.organizationId) : transport;
1160
- const signalOnly = options.signal ? { signal: options.signal } : {};
1161
- const runId = unavailable.runId ?? rejoin.runId;
1162
- if (runId && rejoin.rejoinPath.startsWith("/runtime/operations/")) {
1163
- const result2 = await followWorkflowRunEvents(bound, runId, {
1164
- ...signalOnly,
1165
- onEvent: (event, seq) => options.onWorkflowEvent?.(event, seq)
1166
- });
1167
- return { kind: "followed_workflow", rejoin, runId, ended: result2.ended };
1168
- }
1169
- const liveRequestId = rejoin.liveRequestId;
1170
- const statusView = liveRequestId ? await getRuntimeOperationStatus(bound, liveRequestId, signalOnly) : null;
1171
- const operation = statusView?.operations.find((op) => !op.is_terminal) ?? statusView?.operations[0] ?? null;
1172
- if (!operation) {
1173
- return { kind: "followed", rejoin, executionId: null, ended: false, status: null };
1174
- }
1175
- if (operation.is_terminal) {
1176
- return {
1177
- kind: "followed",
1178
- rejoin,
1179
- executionId: operation.execution_id,
1180
- ended: true,
1181
- status: operation.status
1182
- };
1183
- }
1184
- const result = await followRuntimeOperationToEnd(bound, operation.execution_id, {
1185
- ...options.follow,
1186
- lastEventSeq: operation.last_event_seq,
1187
- ...signalOnly,
1188
- onEvent: (event) => options.onOperationEvent?.(event)
1195
+ async function requestJson(transport, path, options) {
1196
+ const hasBody = options.method !== "GET" && options.body !== void 0;
1197
+ const response = await transport.fetch(path, {
1198
+ method: options.method,
1199
+ headers: hasBody ? { "Content-Type": "application/json" } : {},
1200
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
1201
+ ...options.signal ? { signal: options.signal } : {}
1189
1202
  });
1203
+ if (!response.ok) return throwApiError(path, response);
1204
+ return await response.json();
1205
+ }
1206
+ function toRunHandle(response, options) {
1190
1207
  return {
1191
- kind: "followed",
1192
- rejoin,
1193
- executionId: operation.execution_id,
1194
- ended: result.ended,
1195
- status: result.status
1208
+ requestId: response.headers.get("X-Request-ID"),
1209
+ conversationId: response.headers.get("X-Conversation-ID"),
1210
+ events: readMatrxNdjsonStream(response.body, {
1211
+ ...options.signal ? { signal: options.signal } : {},
1212
+ ...options.maxReadAhead !== void 0 ? { maxReadAhead: options.maxReadAhead } : {},
1213
+ ...options.onMalformedLine ? { onMalformedLine: options.onMalformedLine } : {},
1214
+ ...options.onUnknownEnvelope ? { onUnknownEnvelope: options.onUnknownEnvelope } : {},
1215
+ ...options.onValidEnvelope ? { onValidEnvelope: options.onValidEnvelope } : {}
1216
+ }),
1217
+ response
1196
1218
  };
1197
1219
  }
1198
- async function attempt(run) {
1199
- try {
1200
- await run();
1201
- return { reloaded: true };
1202
- } catch (error) {
1203
- return { reloaded: false, error };
1220
+ async function requestStream(transport, path, options) {
1221
+ const hasBody = options.method !== "GET" && options.body !== void 0;
1222
+ const response = await transport.fetch(path, {
1223
+ method: options.method,
1224
+ headers: {
1225
+ ...hasBody ? { "Content-Type": "application/json" } : {},
1226
+ ...options.headers
1227
+ },
1228
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
1229
+ ...options.signal ? { signal: options.signal } : {}
1230
+ });
1231
+ if (!response.ok) return throwApiError(path, response);
1232
+ if (!response.body) {
1233
+ throw new MatrxApiError({
1234
+ status: response.status,
1235
+ path,
1236
+ serverDetail: { code: "missing_response_body" },
1237
+ message: "The streaming response carried no body."
1238
+ });
1204
1239
  }
1240
+ return response;
1205
1241
  }
1206
- async function settleRunPickup(pickup, options) {
1207
- switch (pickup.kind) {
1208
- case "resumed":
1209
- case "rejoined":
1210
- return { state: "streamed" };
1211
- case "resume_conflict":
1212
- return { state: "retry" };
1213
- case "followed_workflow":
1214
- case "followed": {
1215
- const status = pickup.kind === "followed" ? pickup.status : null;
1216
- const nothingLive = pickup.kind === "followed" && pickup.executionId === null;
1217
- if (pickup.ended || nothingLive) {
1218
- return { state: "settled", status, ...await attempt(options.reloadSavedTurn) };
1242
+
1243
+ // stream/sse.ts
1244
+ var FRAME_SEPARATOR = /\r\n\r\n|\n\n|\r\r/;
1245
+ var LINE_SEPARATOR = /\r\n|\n|\r/;
1246
+ function parseMatrxSseFrame(frame) {
1247
+ let event = "message";
1248
+ let id = null;
1249
+ const dataLines = [];
1250
+ let sawData = false;
1251
+ for (const line of frame.split(LINE_SEPARATOR)) {
1252
+ if (line.startsWith(":")) continue;
1253
+ if (line.startsWith("event:")) event = line.slice(6).trim();
1254
+ else if (line.startsWith("data:")) {
1255
+ sawData = true;
1256
+ dataLines.push(line.slice(5).replace(/^ /, ""));
1257
+ } else if (line.startsWith("id:")) id = line.slice(3).trim();
1258
+ }
1259
+ const seqCandidate = id !== null && id !== "" ? Number(id) : NaN;
1260
+ const seq = Number.isSafeInteger(seqCandidate) && seqCandidate >= 0 ? seqCandidate : null;
1261
+ return { event, id, seq, data: sawData ? dataLines.join("\n") : null };
1262
+ }
1263
+ function createMatrxSseFramer() {
1264
+ let buffer = "";
1265
+ return {
1266
+ push(chunk) {
1267
+ buffer += chunk;
1268
+ const frames = [];
1269
+ for (; ; ) {
1270
+ const sep = FRAME_SEPARATOR.exec(buffer);
1271
+ if (sep === null) break;
1272
+ const frame = buffer.slice(0, sep.index);
1273
+ buffer = buffer.slice(sep.index + sep[0].length);
1274
+ frames.push(parseMatrxSseFrame(frame));
1219
1275
  }
1220
- return {
1221
- state: "still_running",
1222
- ...await attempt(options.onStillRunning ?? options.reloadSavedTurn)
1223
- };
1276
+ return frames;
1277
+ },
1278
+ flush() {
1279
+ const rest = buffer;
1280
+ buffer = "";
1281
+ return { incomplete: rest.length > 0 ? rest : null };
1224
1282
  }
1225
- }
1283
+ };
1226
1284
  }
1227
-
1228
- // matrx/run.ts
1229
- async function streamCall(transport, path, body, options) {
1230
- const response = await requestStream(transport, path, {
1231
- method: "POST",
1232
- // This client IS the streaming path — `stream: true` always, last so a
1233
- // caller-supplied value can never flip the response off NDJSON.
1234
- body: { ...body, stream: true },
1235
- ...options.signal ? { signal: options.signal } : {}
1236
- });
1237
- return toRunHandle(response, options);
1285
+ async function* readMatrxSseStream(stream, options = {}) {
1286
+ const reader = stream.getReader();
1287
+ const decoder = new TextDecoder();
1288
+ const framer = createMatrxSseFramer();
1289
+ try {
1290
+ for (; ; ) {
1291
+ const { value, done } = await reader.read();
1292
+ if (done) break;
1293
+ const frames = framer.push(decoder.decode(value, { stream: true }));
1294
+ for (const frame of frames) yield frame;
1295
+ }
1296
+ const tail = framer.push(decoder.decode());
1297
+ for (const frame of tail) yield frame;
1298
+ const { incomplete } = framer.flush();
1299
+ if (incomplete !== null) options.onIncomplete?.(incomplete);
1300
+ } finally {
1301
+ reader.releaseLock();
1302
+ }
1238
1303
  }
1239
- function startAgentRun(transport, agentId, request, options = {}) {
1240
- return streamCall(
1241
- transport,
1242
- `/ai/agents/${encodePathSegment(agentId)}`,
1243
- request,
1244
- options
1245
- );
1304
+
1305
+ // matrx/operations.ts
1306
+ var TERMINAL_MATRX_RUNTIME_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
1307
+ var RUNTIME_STATUSES = /* @__PURE__ */ new Set([
1308
+ "pending",
1309
+ "running",
1310
+ "paused",
1311
+ "waiting_input",
1312
+ "completed",
1313
+ "failed",
1314
+ "cancelled"
1315
+ ]);
1316
+ async function getRuntimeOperationStatus(transport, requestId, options = {}) {
1317
+ try {
1318
+ return await requestJson(
1319
+ transport,
1320
+ `/runtime/operations/${encodePathSegment(requestId)}`,
1321
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1322
+ );
1323
+ } catch (error) {
1324
+ if (error instanceof MatrxApiError && error.status === 404) return null;
1325
+ throw error;
1326
+ }
1246
1327
  }
1247
- function startMandateRun(transport, mandateKey, request, options = {}) {
1248
- return streamCall(
1249
- transport,
1250
- `/ai/mandates/${encodePathSegment(mandateKey)}`,
1251
- request,
1252
- options
1328
+ async function getRuntimeOperationsByLink(transport, linkKind, linkId, options = {}) {
1329
+ const query = buildQuery(
1330
+ options.limit !== void 0 ? { limit: options.limit } : {}
1253
1331
  );
1332
+ try {
1333
+ return await requestJson(
1334
+ transport,
1335
+ `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,
1336
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1337
+ );
1338
+ } catch (error) {
1339
+ if (error instanceof MatrxApiError && error.status === 404) return null;
1340
+ throw error;
1341
+ }
1254
1342
  }
1255
- function continueAgentConversation(transport, conversationId, request, options = {}) {
1256
- return streamCall(
1343
+ function listRuntimeOperationEvents(transport, executionId, options = {}) {
1344
+ const query = buildQuery({
1345
+ ...options.afterSeq !== void 0 ? { after_seq: options.afterSeq } : {},
1346
+ ...options.limit !== void 0 ? { limit: options.limit } : {},
1347
+ ...options.kinds !== void 0 ? { kind: options.kinds } : {}
1348
+ });
1349
+ return requestJson(
1257
1350
  transport,
1258
- `/ai/conversations/${encodePathSegment(conversationId)}`,
1259
- request,
1260
- options
1351
+ `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,
1352
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1261
1353
  );
1262
1354
  }
1263
- function resumeAgentConversation(transport, conversationId, request = {}, options = {}) {
1264
- return streamCall(
1265
- transport,
1266
- `/ai/conversations/${encodePathSegment(conversationId)}/resume`,
1267
- request,
1268
- options
1269
- );
1355
+ function parseEndStatus(data) {
1356
+ if (data === null) return null;
1357
+ try {
1358
+ const parsed = JSON.parse(data);
1359
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.status === "string") {
1360
+ const status = parsed.status;
1361
+ return RUNTIME_STATUSES.has(status) ? status : null;
1362
+ }
1363
+ } catch {
1364
+ }
1365
+ return null;
1270
1366
  }
1271
- function cancelAgentRun(transport, requestId, options = {}) {
1272
- const query = buildQuery(
1273
- options.mode === "interrupt" ? { mode: "interrupt" } : {}
1274
- );
1275
- return requestJson(
1367
+ async function* followRuntimeOperationEvents(transport, executionId, options = {}) {
1368
+ let cursor = options.lastEventSeq ?? 0;
1369
+ const headers = { Accept: "text/event-stream" };
1370
+ if (cursor > 0) headers["Last-Event-ID"] = String(cursor);
1371
+ const response = await requestStream(
1276
1372
  transport,
1277
- `/ai/cancel/${encodePathSegment(requestId)}${query}`,
1373
+ `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,
1278
1374
  {
1279
- method: "POST",
1375
+ method: "GET",
1376
+ headers,
1280
1377
  ...options.signal ? { signal: options.signal } : {}
1281
1378
  }
1282
1379
  );
1283
- }
1284
- var MatrxRunError = class extends Error {
1285
- name = "MatrxRunError";
1286
- /** The verbatim `error` event payload, when one fired. */
1287
- errorPayload;
1288
- /** The `user_request` completion status (`"failed"` | `"cancelled"`), when that was the trigger. */
1289
- completionStatus;
1290
- /** Text streamed before the failure — partial content never vanishes. */
1291
- partialText;
1292
- constructor(args) {
1293
- super(args.message);
1294
- this.errorPayload = args.errorPayload ?? null;
1295
- this.completionStatus = args.completionStatus ?? null;
1296
- this.partialText = args.partialText ?? "";
1297
- }
1298
- };
1299
- function isRecord4(value) {
1300
- return typeof value === "object" && value !== null && !Array.isArray(value);
1301
- }
1302
- function stringField(value, key) {
1303
- if (!isRecord4(value)) return null;
1304
- const field = value[key];
1305
- return typeof field === "string" && field ? field : null;
1306
- }
1307
- async function runAgentToCompletion(transport, agentId, request, options = {}) {
1308
- const handle = await startAgentRun(transport, agentId, request, options);
1309
- let text = "";
1310
- let completion = null;
1311
- let failure = null;
1312
- for await (const envelope of handle.events) {
1313
- options.onEvent?.(envelope);
1314
- if (envelope.event === "chunk") {
1315
- const chunk = stringField(envelope.data, "text");
1316
- if (chunk !== null) {
1317
- text += chunk;
1318
- options.onChunk?.(text);
1319
- }
1320
- continue;
1380
+ const frames = readMatrxSseStream(
1381
+ response.body,
1382
+ options.onIncomplete ? { onIncomplete: options.onIncomplete } : {}
1383
+ );
1384
+ for await (const frame of frames) {
1385
+ if (frame.event === "end") {
1386
+ yield { type: "end", status: parseEndStatus(frame.data), cursor };
1387
+ return;
1321
1388
  }
1322
- if (envelope.event === "error" && failure === null) {
1323
- const payload = isRecord4(envelope.data) ? envelope.data : null;
1324
- failure = new MatrxRunError({
1325
- message: streamErrorText(payload) ?? "The agent run failed",
1326
- errorPayload: payload,
1327
- partialText: text
1328
- });
1389
+ if (frame.event === "execution_event" && frame.data !== null) {
1390
+ let event;
1391
+ try {
1392
+ event = JSON.parse(frame.data);
1393
+ } catch (error) {
1394
+ options.onMalformedFrame?.(frame, error);
1395
+ yield { type: "liveness", cursor };
1396
+ continue;
1397
+ }
1398
+ if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;
1399
+ yield { type: "event", event, seq: frame.seq, cursor };
1329
1400
  continue;
1330
1401
  }
1331
- if (envelope.event !== "completion" || !isRecord4(envelope.data)) continue;
1332
- if (envelope.data.operation !== "user_request") continue;
1333
- completion = envelope.data;
1334
- const status = envelope.data.status;
1335
- if ((status === "failed" || status === "cancelled") && failure === null) {
1336
- const result = isRecord4(envelope.data.result) ? envelope.data.result : null;
1337
- failure = new MatrxRunError({
1338
- message: stringField(result, "error") ?? stringField(result, "user_message") ?? `The agent run ${status}`,
1339
- completionStatus: status,
1340
- partialText: text
1341
- });
1342
- }
1343
- }
1344
- if (failure) throw failure;
1345
- if (!text && completion) {
1346
- const result = completion.result;
1347
- const output = stringField(result, "output");
1348
- if (output !== null) text = output;
1349
- }
1350
- return {
1351
- text,
1352
- requestId: handle.requestId,
1353
- conversationId: handle.conversationId,
1354
- completion
1355
- };
1356
- }
1357
-
1358
- // matrx/provider-failures.ts
1359
- var clip = (value, max) => value.length > max ? value.slice(0, max) : value;
1360
- function providerSessionFailureBody(failure) {
1361
- const message = failure.message?.trim() || failure.error_type?.trim() || (failure.status_code ? `HTTP ${failure.status_code}` : "provider session failed");
1362
- const body = {
1363
- provider: failure.provider,
1364
- message: clip(message, 4e3)
1365
- };
1366
- if (failure.model?.trim()) body.model = clip(failure.model.trim(), 200);
1367
- if (failure.error_type?.trim()) body.error_type = clip(failure.error_type.trim(), 200);
1368
- if (typeof failure.status_code === "number" && Number.isInteger(failure.status_code) && failure.status_code >= 100 && failure.status_code <= 599) {
1369
- body.status_code = failure.status_code;
1402
+ yield { type: "liveness", cursor };
1370
1403
  }
1371
- return body;
1372
1404
  }
1373
- async function reportProviderSessionFailure(transport, failure, options = {}) {
1374
- try {
1375
- return await requestJson(
1376
- transport,
1377
- "/broker/provider-failures",
1378
- {
1379
- method: "POST",
1380
- body: providerSessionFailureBody(failure),
1381
- ...options.signal ? { signal: options.signal } : {}
1405
+ async function followRuntimeOperationToEnd(transport, executionId, options) {
1406
+ const stallTimeoutMs = options.stallTimeoutMs ?? 45e3;
1407
+ const reconnectLimit = options.reconnectLimit ?? 60;
1408
+ const reconnectDelayMs = options.reconnectDelayMs ?? 2e3;
1409
+ const outer = options.signal;
1410
+ let cursor = options.lastEventSeq ?? 0;
1411
+ let failures = 0;
1412
+ while (!outer?.aborted && failures < reconnectLimit) {
1413
+ const attempt2 = new AbortController();
1414
+ const onOuterAbort = () => attempt2.abort();
1415
+ outer?.addEventListener("abort", onOuterAbort, { once: true });
1416
+ let stallTimer = null;
1417
+ const armStall = () => {
1418
+ if (stallTimer !== null) clearTimeout(stallTimer);
1419
+ stallTimer = setTimeout(() => attempt2.abort(), stallTimeoutMs);
1420
+ };
1421
+ try {
1422
+ const items = followRuntimeOperationEvents(transport, executionId, {
1423
+ lastEventSeq: cursor,
1424
+ signal: attempt2.signal,
1425
+ ...options.onMalformedFrame ? { onMalformedFrame: options.onMalformedFrame } : {}
1426
+ });
1427
+ armStall();
1428
+ for await (const item of items) {
1429
+ armStall();
1430
+ failures = 0;
1431
+ cursor = item.cursor;
1432
+ if (item.type === "end") {
1433
+ return { ended: true, status: item.status };
1434
+ }
1435
+ if (item.type === "event") {
1436
+ options.onEvent(item.event, item.seq);
1437
+ }
1382
1438
  }
1383
- );
1384
- } catch (error) {
1385
- console.warn(
1386
- `[provider-failures] could not report a ${failure.provider} session failure`,
1387
- error
1388
- );
1389
- return null;
1439
+ failures += 1;
1440
+ } catch (error) {
1441
+ if (outer?.aborted) break;
1442
+ failures += 1;
1443
+ options.onAttemptError?.(error);
1444
+ } finally {
1445
+ if (stallTimer !== null) clearTimeout(stallTimer);
1446
+ outer?.removeEventListener("abort", onOuterAbort);
1447
+ }
1448
+ if (!outer?.aborted && failures < reconnectLimit) {
1449
+ await new Promise((r) => setTimeout(r, reconnectDelayMs));
1450
+ }
1390
1451
  }
1452
+ return { ended: false, status: null };
1391
1453
  }
1392
-
1393
- // matrx/tools.ts
1394
- function submitAgentToolResults(transport, conversationId, results, options = {}) {
1395
- return requestJson(
1454
+ async function rejoinRuntimeOperation(transport, requestId, options = {}) {
1455
+ const response = await requestStream(
1396
1456
  transport,
1397
- `/ai/conversations/${encodePathSegment(conversationId)}/tool_results`,
1457
+ `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,
1398
1458
  {
1399
1459
  method: "POST",
1400
- body: {
1401
- results,
1402
- ...options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1403
- },
1460
+ // The route takes no body model; the reference client posts an empty
1461
+ // JSON object. Match it so proxies see an ordinary JSON POST.
1462
+ body: {},
1404
1463
  ...options.signal ? { signal: options.signal } : {}
1405
1464
  }
1406
1465
  );
1466
+ return toRunHandle(response, options);
1407
1467
  }
1408
- function listConversationPendingToolCalls(transport, conversationId, options = {}) {
1409
- return requestJson(
1410
- transport,
1411
- `/ai/conversations/${encodePathSegment(conversationId)}/pending_calls`,
1412
- {
1413
- method: "GET",
1414
- ...options.signal ? { signal: options.signal } : {}
1415
- }
1416
- );
1468
+
1469
+ // matrx/rejoin.ts
1470
+ var MATRX_RUN_IN_PROGRESS = "run_in_progress";
1471
+ var MATRX_RESUME_CONFLICT = "resume_conflict";
1472
+ var MATRX_LIVE_STREAM_UNAVAILABLE = "live_stream_unavailable";
1473
+ function isRecord3(value) {
1474
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1417
1475
  }
1418
- function listUserPendingToolCalls(transport, options = {}) {
1419
- const query = buildQuery(
1420
- options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1421
- );
1422
- return requestJson(
1423
- transport,
1424
- `/ai/user/pending_calls${query}`,
1425
- {
1426
- method: "GET",
1427
- ...options.signal ? { signal: options.signal } : {}
1428
- }
1429
- );
1476
+ function nonBlank(value) {
1477
+ return typeof value === "string" && value.trim() ? value : null;
1430
1478
  }
1431
-
1432
- // matrx/backend-errors.ts
1433
- var BackendApiError = class extends Error {
1434
- /** Machine-readable error code */
1435
- code;
1436
- /** Developer-facing detail */
1437
- detail;
1438
- /** Safe to display directly in the UI */
1439
- userMessage;
1440
- /** Extra info (validation errors, etc.) */
1441
- details;
1442
- /** Unique request ID for support/debugging */
1443
- requestId;
1444
- /** HTTP status code (if from an HTTP response) */
1445
- status;
1446
- constructor(data) {
1447
- super(data.userMessage);
1448
- this.name = "BackendApiError";
1449
- this.code = data.code;
1450
- this.detail = data.detail;
1451
- this.userMessage = data.userMessage;
1452
- this.details = data.details ?? null;
1453
- this.requestId = data.requestId ?? "";
1454
- this.status = data.status ?? null;
1479
+ function detailCandidates(body) {
1480
+ if (!isRecord3(body)) return [];
1481
+ const out = [];
1482
+ if (isRecord3(body.detail)) out.push(body.detail);
1483
+ if (isRecord3(body.details)) out.push(body.details);
1484
+ out.push(body);
1485
+ return out;
1486
+ }
1487
+ function statusOf(error) {
1488
+ if (!isRecord3(error) && !(error instanceof Error)) return null;
1489
+ const status = error.status;
1490
+ return typeof status === "number" ? status : null;
1491
+ }
1492
+ function bodyOf(error) {
1493
+ if (!isRecord3(error) && !(error instanceof Error)) return void 0;
1494
+ return error.serverDetail;
1495
+ }
1496
+ function readMatrxErrorCode(error) {
1497
+ for (const candidate of detailCandidates(bodyOf(error))) {
1498
+ const code = nonBlank(candidate.code);
1499
+ if (code) return code;
1455
1500
  }
1456
- /** Convert to the wire format for logging */
1457
- toJSON() {
1458
- return {
1459
- error: this.code,
1460
- message: this.detail,
1461
- user_message: this.userMessage,
1462
- details: this.details,
1463
- request_id: this.requestId
1464
- };
1501
+ for (const candidate of detailCandidates(bodyOf(error))) {
1502
+ const code = nonBlank(candidate.error);
1503
+ if (code) return code;
1465
1504
  }
1466
- };
1467
- var StreamTransportError = class extends BackendApiError {
1468
- /** Always true — reattach and let the server settle the outcome. */
1469
- resumable = true;
1470
- constructor(data) {
1471
- super({
1472
- code: "stream_transport_lost",
1473
- detail: data.detail,
1474
- userMessage: "The connection dropped. Your run is still going on the server \u2014 reconnecting to it now.",
1475
- details: data.details ?? null,
1476
- ...data.requestId !== void 0 ? { requestId: data.requestId } : {}
1477
- });
1478
- this.name = "StreamTransportError";
1505
+ return null;
1506
+ }
1507
+ var PREFIXED_ERROR_CODES = [
1508
+ "resume_conflict",
1509
+ "not_resumable",
1510
+ "outstanding_delegated_calls",
1511
+ "user_request_not_found",
1512
+ "run_in_flight"
1513
+ ];
1514
+ function readMatrxErrorCodeFromMessage(message) {
1515
+ if (typeof message !== "string") return null;
1516
+ for (const code of PREFIXED_ERROR_CODES) {
1517
+ if (message.startsWith(`${code}:`)) return code;
1479
1518
  }
1480
- };
1481
- function isStreamTransportLost(error) {
1482
- if (error instanceof StreamTransportError) return true;
1483
- if (error instanceof BackendApiError) {
1484
- return error.code === "stream_transport_lost";
1519
+ return null;
1520
+ }
1521
+ async function readConversationOrganizationId(conversationId, read, onUnreadable) {
1522
+ try {
1523
+ const value = await read(conversationId);
1524
+ if (typeof value === "string" && value.trim()) return value;
1525
+ onUnreadable?.(null);
1526
+ } catch (error) {
1527
+ onUnreadable?.(error instanceof Error ? error.message : String(error));
1485
1528
  }
1486
- if (error && typeof error === "object") {
1487
- const code = error.code;
1488
- if (code === "stream_transport_lost") return true;
1489
- const errorType = error.error_type;
1490
- if (errorType === "transport_lost") return true;
1529
+ return null;
1530
+ }
1531
+ function firstString(candidates, key) {
1532
+ for (const candidate of candidates) {
1533
+ const value = nonBlank(candidate[key]);
1534
+ if (value) return value.trim();
1491
1535
  }
1492
- return false;
1536
+ return null;
1493
1537
  }
1494
- async function parseHttpError(response) {
1495
- const status = response.status;
1496
- let body = null;
1538
+ function readLiveRunRejoin(error) {
1539
+ if (statusOf(error) !== 409) return null;
1540
+ const candidates = detailCandidates(bodyOf(error));
1541
+ if (candidates.length === 0) return null;
1542
+ const code = readMatrxErrorCode(error);
1543
+ if (code !== MATRX_RUN_IN_PROGRESS) return null;
1544
+ const liveRequestId = firstString(candidates, "live_request_id");
1545
+ const pathFromBody = firstString(candidates, "rejoin_path");
1546
+ const rejoinPath = pathFromBody && pathFromBody.startsWith("/") ? pathFromBody : liveRequestId ? runtimeOperationRejoinPath(liveRequestId) : null;
1547
+ if (!rejoinPath) return null;
1548
+ const root = bodyOf(error);
1549
+ return {
1550
+ liveRequestId,
1551
+ rejoinPath,
1552
+ runId: firstString(candidates, "run_id"),
1553
+ code,
1554
+ body: isRecord3(root) ? root : {}
1555
+ };
1556
+ }
1557
+ function readLiveRunRequestId(error) {
1558
+ return readLiveRunRejoin(error)?.liveRequestId ?? null;
1559
+ }
1560
+ function isResumeConflict(error) {
1561
+ return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT;
1562
+ }
1563
+ function isLiveStreamUnavailable(error) {
1564
+ return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_LIVE_STREAM_UNAVAILABLE;
1565
+ }
1566
+ function runtimeOperationRejoinPath(requestId) {
1567
+ return `/runtime/operations/${encodePathSegment(requestId)}/rejoin`;
1568
+ }
1569
+ function streamErrorText(eventOrPayload) {
1570
+ if (!isRecord3(eventOrPayload)) return null;
1571
+ const payload = typeof eventOrPayload.event === "string" && isRecord3(eventOrPayload.data) ? eventOrPayload.data : eventOrPayload;
1572
+ return nonBlank(payload.user_message) ?? nonBlank(payload.message);
1573
+ }
1574
+ async function openMatrxStream(transport, path, options = {}) {
1575
+ const response = await requestStream(transport, path, {
1576
+ method: "POST",
1577
+ ...options.body !== void 0 ? { body: options.body } : {},
1578
+ ...options.signal ? { signal: options.signal } : {}
1579
+ });
1580
+ return toRunHandle(response, options);
1581
+ }
1582
+ function withRunOrganization(transport, organizationId) {
1583
+ const orgId = requireOrganizationContext(organizationId);
1584
+ return {
1585
+ fetch: (path, init) => transport.fetch(path, {
1586
+ ...init,
1587
+ headers: { ...init.headers, "X-Organization-Id": orgId }
1588
+ })
1589
+ };
1590
+ }
1591
+ async function followWorkflowRunEvents(transport, runId, options) {
1592
+ const headers = { Accept: "text/event-stream" };
1593
+ if (options.lastEventId) headers["Last-Event-ID"] = String(options.lastEventId);
1594
+ let response;
1497
1595
  try {
1498
- body = await response.json();
1499
- } catch {
1500
- try {
1501
- const text = await response.text();
1502
- return new BackendApiError({
1503
- code: statusToCode(status),
1504
- detail: text || `HTTP ${status}`,
1505
- userMessage: text || `Request failed (${status})`,
1506
- status
1507
- });
1508
- } catch {
1509
- return new BackendApiError({
1510
- code: statusToCode(status),
1511
- detail: `HTTP ${status}`,
1512
- userMessage: `Request failed (${status})`,
1513
- status
1514
- });
1596
+ response = await requestStream(
1597
+ transport,
1598
+ `/runs/${encodePathSegment(runId)}/events/stream`,
1599
+ {
1600
+ method: "GET",
1601
+ headers,
1602
+ ...options.signal ? { signal: options.signal } : {}
1603
+ }
1604
+ );
1605
+ } catch (error) {
1606
+ if (options.signal?.aborted) return { ended: false };
1607
+ throw error;
1608
+ }
1609
+ try {
1610
+ for await (const frame of readMatrxSseStream(
1611
+ response.body
1612
+ )) {
1613
+ if (frame.event === "end") return { ended: true };
1614
+ if (frame.data === null) continue;
1615
+ let parsed;
1616
+ try {
1617
+ parsed = JSON.parse(frame.data);
1618
+ } catch {
1619
+ continue;
1620
+ }
1621
+ if (!isRecord3(parsed)) continue;
1622
+ options.onEvent(parsed, parsed.event === "node_stream" ? null : frame.seq);
1515
1623
  }
1624
+ } catch (error) {
1625
+ if (!options.signal?.aborted) throw error;
1516
1626
  }
1517
- return parseHttpErrorBody(body, status);
1627
+ return { ended: false };
1518
1628
  }
1519
- function parseHttpErrorBody(body, status) {
1520
- if (!body) {
1521
- return new BackendApiError({
1522
- code: statusToCode(status),
1523
- detail: `HTTP ${status}`,
1524
- userMessage: `Request failed (${status})`,
1525
- status
1526
- });
1629
+ async function drain(handle, onEnvelope) {
1630
+ for await (const envelope of handle.events) onEnvelope(envelope);
1631
+ }
1632
+ async function resumeOrRejoin(transport, resumeCall, options) {
1633
+ const {
1634
+ onEnvelope,
1635
+ organizationId,
1636
+ onRejoin,
1637
+ onOperationEvent,
1638
+ onWorkflowEvent,
1639
+ follow,
1640
+ ...rest
1641
+ } = options;
1642
+ const bound = organizationId ? withRunOrganization(transport, organizationId) : transport;
1643
+ const streamOptions = {
1644
+ ...rest,
1645
+ ...options.signal ? { signal: options.signal } : {}
1646
+ };
1647
+ const signalOnly = options.signal ? { signal: options.signal } : {};
1648
+ let rejoin;
1649
+ try {
1650
+ const handle = await resumeCall(bound, streamOptions);
1651
+ await drain(handle, onEnvelope);
1652
+ return { kind: "resumed", requestId: handle.requestId };
1653
+ } catch (error) {
1654
+ const target = readLiveRunRejoin(error);
1655
+ if (!target) {
1656
+ if (error instanceof MatrxApiError && isResumeConflict(error)) {
1657
+ return { kind: "resume_conflict", error };
1658
+ }
1659
+ throw error;
1660
+ }
1661
+ rejoin = target;
1527
1662
  }
1528
- if (typeof body.error === "string" && typeof body.user_message === "string") {
1529
- return new BackendApiError({
1530
- code: body.error,
1531
- detail: body.message || `HTTP ${status}`,
1532
- userMessage: body.user_message,
1533
- details: body.details ?? null,
1534
- requestId: typeof body.request_id === "string" ? body.request_id : void 0,
1535
- status
1663
+ onRejoin?.(rejoin);
1664
+ let unavailable;
1665
+ try {
1666
+ const handle = await openMatrxStream(bound, rejoin.rejoinPath, {
1667
+ ...streamOptions,
1668
+ // The rejoin routes take no body model; the reference client posts {}.
1669
+ body: {}
1536
1670
  });
1671
+ await drain(handle, onEnvelope);
1672
+ return { kind: "rejoined", rejoin };
1673
+ } catch (error) {
1674
+ const read = readLiveStreamUnavailable(error);
1675
+ if (!read) throw error;
1676
+ unavailable = read;
1537
1677
  }
1538
- if (typeof body.error === "object" && body.error !== null) {
1539
- const errorObj = body.error;
1540
- return new BackendApiError({
1541
- code: errorObj.type || errorObj.error || statusToCode(status),
1542
- detail: errorObj.message || `HTTP ${status}`,
1543
- userMessage: errorObj.user_message || errorObj.user_visible_message || errorObj.message || `Request failed (${status})`,
1544
- details: errorObj.details ?? null,
1545
- requestId: typeof errorObj.request_id === "string" ? errorObj.request_id : void 0,
1546
- status
1678
+ return followUnavailableRejoin(bound, rejoin, unavailable, {
1679
+ ...signalOnly,
1680
+ ...onOperationEvent ? { onOperationEvent } : {},
1681
+ ...onWorkflowEvent ? { onWorkflowEvent } : {},
1682
+ ...follow ? { follow } : {}
1683
+ });
1684
+ }
1685
+ function readLiveStreamUnavailable(error) {
1686
+ if (!isLiveStreamUnavailable(error)) return null;
1687
+ return { runId: firstString(detailCandidates(bodyOf(error)), "run_id") };
1688
+ }
1689
+ async function followUnavailableRejoin(transport, rejoin, unavailable, options = {}) {
1690
+ const bound = options.organizationId ? withRunOrganization(transport, options.organizationId) : transport;
1691
+ const signalOnly = options.signal ? { signal: options.signal } : {};
1692
+ const runId = unavailable.runId ?? rejoin.runId;
1693
+ if (runId && rejoin.rejoinPath.startsWith("/runtime/operations/")) {
1694
+ const result2 = await followWorkflowRunEvents(bound, runId, {
1695
+ ...signalOnly,
1696
+ onEvent: (event, seq) => options.onWorkflowEvent?.(event, seq)
1547
1697
  });
1698
+ return { kind: "followed_workflow", rejoin, runId, ended: result2.ended };
1548
1699
  }
1549
- if (Array.isArray(body.detail)) {
1550
- const first = body.detail[0];
1551
- const firstMsg = first && typeof first.msg === "string" ? first.msg : null;
1552
- return new BackendApiError({
1553
- code: statusToCode(status),
1554
- detail: firstMsg ? `Validation error: ${firstMsg}` : JSON.stringify(body.detail),
1555
- userMessage: firstMsg || `Request failed (${status})`,
1556
- details: body.detail,
1557
- status
1558
- });
1700
+ const liveRequestId = rejoin.liveRequestId;
1701
+ const statusView = liveRequestId ? await getRuntimeOperationStatus(bound, liveRequestId, signalOnly) : null;
1702
+ const operation = statusView?.operations.find((op) => !op.is_terminal) ?? statusView?.operations[0] ?? null;
1703
+ if (!operation) {
1704
+ return { kind: "followed", rejoin, executionId: null, ended: false, status: null };
1559
1705
  }
1560
- if (typeof body.detail === "object" && body.detail !== null && !Array.isArray(body.detail)) {
1561
- const d = body.detail;
1562
- const code = typeof d.code === "string" && d.code || typeof d.error === "string" && d.error || statusToCode(status);
1563
- const message = typeof d.message === "string" && d.message || typeof d.detail === "string" && d.detail || `HTTP ${status}`;
1564
- return new BackendApiError({
1565
- code,
1566
- detail: message,
1567
- userMessage: typeof d.user_message === "string" && d.user_message || typeof d.user_visible_message === "string" && d.user_visible_message || message,
1568
- details: d.details ?? d,
1569
- requestId: typeof d.request_id === "string" ? d.request_id : void 0,
1570
- status
1571
- });
1706
+ if (operation.is_terminal) {
1707
+ return {
1708
+ kind: "followed",
1709
+ rejoin,
1710
+ executionId: operation.execution_id,
1711
+ ended: true,
1712
+ status: operation.status
1713
+ };
1572
1714
  }
1573
- return new BackendApiError({
1574
- code: typeof body.error === "string" ? body.error : statusToCode(status),
1575
- detail: body.message || body.detail || body.error || `HTTP ${status}`,
1576
- userMessage: body.user_message || body.user_visible_message || body.message || body.detail || `Request failed (${status})`,
1577
- details: body.details ?? null,
1578
- requestId: typeof body.request_id === "string" ? body.request_id : void 0,
1579
- status
1715
+ const result = await followRuntimeOperationToEnd(bound, operation.execution_id, {
1716
+ ...options.follow,
1717
+ lastEventSeq: operation.last_event_seq,
1718
+ ...signalOnly,
1719
+ onEvent: (event) => options.onOperationEvent?.(event)
1580
1720
  });
1721
+ return {
1722
+ kind: "followed",
1723
+ rejoin,
1724
+ executionId: operation.execution_id,
1725
+ ended: result.ended,
1726
+ status: result.status
1727
+ };
1581
1728
  }
1582
- function parseCallApiError(error) {
1583
- const status = error.status ?? 500;
1584
- const body = error.serverDetail && typeof error.serverDetail === "object" && !Array.isArray(error.serverDetail) ? error.serverDetail : { message: error.message };
1585
- return parseHttpErrorBody(body, status);
1729
+ async function attempt(run) {
1730
+ try {
1731
+ await run();
1732
+ return { reloaded: true };
1733
+ } catch (error) {
1734
+ return { reloaded: false, error };
1735
+ }
1586
1736
  }
1587
- function parseStreamError(data) {
1588
- if (!data || typeof data !== "object") {
1589
- return new BackendApiError({
1590
- code: "internal_error",
1591
- detail: typeof data === "string" ? data : "Unknown streaming error",
1592
- userMessage: typeof data === "string" ? data : "Something went wrong"
1593
- });
1737
+ async function settleRunPickup(pickup, options) {
1738
+ switch (pickup.kind) {
1739
+ case "resumed":
1740
+ case "rejoined":
1741
+ return { state: "streamed" };
1742
+ case "resume_conflict":
1743
+ return { state: "retry" };
1744
+ case "followed_workflow":
1745
+ case "followed": {
1746
+ const status = pickup.kind === "followed" ? pickup.status : null;
1747
+ const nothingLive = pickup.kind === "followed" && pickup.executionId === null;
1748
+ if (pickup.ended || nothingLive) {
1749
+ return { state: "settled", status, ...await attempt(options.reloadSavedTurn) };
1750
+ }
1751
+ return {
1752
+ state: "still_running",
1753
+ ...await attempt(options.onStillRunning ?? options.reloadSavedTurn)
1754
+ };
1755
+ }
1594
1756
  }
1595
- const obj = data;
1596
- const details = typeof obj.details === "object" && obj.details !== null ? obj.details : null;
1597
- return new BackendApiError({
1598
- code: obj.code || obj.error_type || obj.error || "internal_error",
1599
- detail: obj.message || "Streaming error",
1600
- userMessage: obj.user_message || obj.message || "Something went wrong",
1601
- details,
1602
- requestId: typeof obj.request_id === "string" ? obj.request_id : typeof details?.request_id === "string" ? details.request_id : void 0
1757
+ }
1758
+
1759
+ // matrx/run.ts
1760
+ async function streamCall(transport, path, body, options) {
1761
+ const response = await requestStream(transport, path, {
1762
+ method: "POST",
1763
+ // This client IS the streaming path — `stream: true` always, last so a
1764
+ // caller-supplied value can never flip the response off NDJSON.
1765
+ body: { ...body, stream: true },
1766
+ ...options.signal ? { signal: options.signal } : {}
1603
1767
  });
1768
+ return toRunHandle(response, options);
1604
1769
  }
1605
- function parsePersistedBackendError(data, requestId = "") {
1606
- if (!data || typeof data !== "object") return null;
1607
- const error = data;
1608
- const failures = Array.isArray(error.failures) ? error.failures : [];
1609
- const firstSpecific = failures.find(
1610
- (failure) => typeof failure === "object" && failure !== null && typeof failure.message === "string"
1770
+ function startAgentRun(transport, agentId, request, options = {}) {
1771
+ return streamCall(
1772
+ transport,
1773
+ `/ai/agents/${encodePathSegment(agentId)}`,
1774
+ request,
1775
+ options
1776
+ );
1777
+ }
1778
+ function startMandateRun(transport, mandateKey, request, options = {}) {
1779
+ return streamCall(
1780
+ transport,
1781
+ `/ai/mandates/${encodePathSegment(mandateKey)}`,
1782
+ request,
1783
+ options
1784
+ );
1785
+ }
1786
+ function continueAgentConversation(transport, conversationId, request, options = {}) {
1787
+ return streamCall(
1788
+ transport,
1789
+ `/ai/conversations/${encodePathSegment(conversationId)}`,
1790
+ request,
1791
+ options
1792
+ );
1793
+ }
1794
+ function resumeAgentConversation(transport, conversationId, request = {}, options = {}) {
1795
+ return streamCall(
1796
+ transport,
1797
+ `/ai/conversations/${encodePathSegment(conversationId)}/resume`,
1798
+ request,
1799
+ options
1611
1800
  );
1612
- const summary = typeof error.message === "string" ? error.message : "Backend operation failed";
1613
- const specific = typeof firstSpecific?.message === "string" ? firstSpecific.message : summary;
1614
- const detail = specific === summary ? summary : `${summary}: ${specific}`;
1615
- const code = typeof error.type === "string" ? error.type : "internal_error";
1616
- const userMessage = typeof error.user_message === "string" && error.user_message ? error.user_message : detail;
1617
- return new BackendApiError({
1618
- code,
1619
- detail,
1620
- userMessage,
1621
- details: error,
1622
- requestId
1623
- });
1624
1801
  }
1625
- var GENERIC_MESSAGE_PATTERNS = [
1626
- /failed unexpectedly/i,
1627
- /^\s*something went wrong/i,
1628
- /please try again(\s+later)?\.?\s*$/i,
1629
- /^\s*request failed\b/i,
1630
- /^\s*unknown (streaming )?error/i,
1631
- /^\s*internal server error\.?\s*$/i
1632
- ];
1633
- function isGenericUserMessage(message) {
1634
- const value = (message ?? "").trim();
1635
- if (!value) return true;
1636
- return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));
1802
+ function cancelAgentRun(transport, requestId, options = {}) {
1803
+ const query = buildQuery(
1804
+ options.mode === "interrupt" ? { mode: "interrupt" } : {}
1805
+ );
1806
+ return requestJson(
1807
+ transport,
1808
+ `/ai/cancel/${encodePathSegment(requestId)}${query}`,
1809
+ {
1810
+ method: "POST",
1811
+ ...options.signal ? { signal: options.signal } : {}
1812
+ }
1813
+ );
1637
1814
  }
1638
- function unwrapUpstreamError(message) {
1639
- const start = message.indexOf("{");
1640
- const end = message.lastIndexOf("}");
1641
- if (start < 0 || end <= start) return null;
1642
- let parsed;
1643
- try {
1644
- parsed = JSON.parse(message.slice(start, end + 1));
1645
- } catch {
1646
- return null;
1815
+ var MatrxRunError = class extends Error {
1816
+ name = "MatrxRunError";
1817
+ /** The verbatim `error` event payload, when one fired. */
1818
+ errorPayload;
1819
+ /** The `user_request` completion status (`"failed"` | `"cancelled"`), when that was the trigger. */
1820
+ completionStatus;
1821
+ /** Text streamed before the failure — partial content never vanishes. */
1822
+ partialText;
1823
+ constructor(args) {
1824
+ super(args.message);
1825
+ this.errorPayload = args.errorPayload ?? null;
1826
+ this.completionStatus = args.completionStatus ?? null;
1827
+ this.partialText = args.partialText ?? "";
1647
1828
  }
1648
- if (typeof parsed !== "object" || parsed === null) return null;
1649
- const body = parsed;
1650
- const inner = typeof body.message === "string" && body.message || typeof body.detail === "string" && body.detail || "";
1651
- if (!inner) return null;
1652
- const statusMatch = /\bHTTP (\d{3})\b/.exec(message.slice(0, start));
1653
- return {
1654
- message: inner,
1655
- code: typeof body.error_type === "string" && body.error_type || typeof body.error === "string" && body.error || null,
1656
- userMessage: typeof body.user_message === "string" ? body.user_message : null,
1657
- requestId: typeof body.request_id === "string" ? body.request_id : null,
1658
- status: statusMatch ? Number(statusMatch[1]) : null
1659
- };
1829
+ };
1830
+ function isRecord4(value) {
1831
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1660
1832
  }
1661
- function describeBackendFailure(error) {
1662
- const chain = [];
1663
- let code = "internal_error";
1664
- let requestId = "";
1665
- let status = null;
1666
- let userFacing = null;
1667
- if (error instanceof BackendApiError) {
1668
- code = error.code;
1669
- requestId = error.requestId;
1670
- status = error.status;
1671
- userFacing = error.userMessage;
1672
- if (error.detail) chain.push(error.detail);
1673
- if (error.userMessage && error.userMessage !== error.detail) {
1674
- chain.push(error.userMessage);
1833
+ function stringField(value, key) {
1834
+ if (!isRecord4(value)) return null;
1835
+ const field = value[key];
1836
+ return typeof field === "string" && field ? field : null;
1837
+ }
1838
+ async function runAgentToCompletion(transport, agentId, request, options = {}) {
1839
+ const handle = await startAgentRun(transport, agentId, request, options);
1840
+ let text = "";
1841
+ let completion = null;
1842
+ let failure = null;
1843
+ for await (const envelope of handle.events) {
1844
+ options.onEvent?.(envelope);
1845
+ if (envelope.event === "chunk") {
1846
+ const chunk = stringField(envelope.data, "text");
1847
+ if (chunk !== null) {
1848
+ text += chunk;
1849
+ options.onChunk?.(text);
1850
+ }
1851
+ continue;
1852
+ }
1853
+ if (envelope.event === "error" && failure === null) {
1854
+ const payload = isRecord4(envelope.data) ? envelope.data : null;
1855
+ failure = new MatrxRunError({
1856
+ message: streamErrorText(payload) ?? "The agent run failed",
1857
+ errorPayload: payload,
1858
+ partialText: text
1859
+ });
1860
+ continue;
1861
+ }
1862
+ if (envelope.event !== "completion" || !isRecord4(envelope.data)) continue;
1863
+ if (envelope.data.operation !== "user_request") continue;
1864
+ completion = envelope.data;
1865
+ const status = envelope.data.status;
1866
+ if ((status === "failed" || status === "cancelled") && failure === null) {
1867
+ const result = isRecord4(envelope.data.result) ? envelope.data.result : null;
1868
+ failure = new MatrxRunError({
1869
+ message: stringField(result, "error") ?? stringField(result, "user_message") ?? `The agent run ${status}`,
1870
+ completionStatus: status,
1871
+ partialText: text
1872
+ });
1675
1873
  }
1676
- } else if (error instanceof Error) {
1677
- chain.push(error.message);
1678
- userFacing = error.message;
1679
- } else if (typeof error === "string") {
1680
- chain.push(error);
1681
- userFacing = error;
1682
- } else {
1683
- chain.push("Unknown error");
1684
1874
  }
1685
- for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {
1686
- const upstream = unwrapUpstreamError(chain[cursor] ?? "");
1687
- if (!upstream || chain.includes(upstream.message)) continue;
1688
- chain.push(upstream.message);
1689
- if (upstream.code) code = upstream.code;
1690
- if (upstream.requestId) requestId = upstream.requestId;
1691
- if (upstream.status !== null) status = upstream.status;
1875
+ if (failure) throw failure;
1876
+ if (!text && completion) {
1877
+ const result = completion.result;
1878
+ const output = stringField(result, "output");
1879
+ if (output !== null) text = output;
1692
1880
  }
1693
- const specific = [...chain].reverse().find((message) => !isGenericUserMessage(message));
1694
- const cause = specific ?? chain[0] ?? "Unknown error";
1695
- const headlineWasGeneric = isGenericUserMessage(userFacing);
1696
1881
  return {
1697
- code,
1698
- cause,
1699
- headline: headlineWasGeneric ? cause : userFacing ?? cause,
1700
- headlineWasGeneric,
1701
- chain,
1702
- requestId,
1703
- status
1882
+ text,
1883
+ requestId: handle.requestId,
1884
+ conversationId: handle.conversationId,
1885
+ completion
1704
1886
  };
1705
1887
  }
1706
- function getUserMessage(error) {
1707
- if (error instanceof BackendApiError) {
1708
- return error.userMessage;
1709
- }
1710
- if (error instanceof Error) {
1711
- return error.message;
1712
- }
1713
- if (typeof error === "string") {
1714
- return error;
1715
- }
1716
- if (typeof error === "object" && error !== null && typeof error.message === "string" && error.message) {
1717
- return error.message;
1888
+
1889
+ // matrx/provider-failures.ts
1890
+ var clip = (value, max) => value.length > max ? value.slice(0, max) : value;
1891
+ function providerSessionFailureBody(failure) {
1892
+ const message = failure.message?.trim() || failure.error_type?.trim() || (failure.status_code ? `HTTP ${failure.status_code}` : "provider session failed");
1893
+ const body = {
1894
+ provider: failure.provider,
1895
+ message: clip(message, 4e3)
1896
+ };
1897
+ if (failure.model?.trim()) body.model = clip(failure.model.trim(), 200);
1898
+ if (failure.error_type?.trim()) body.error_type = clip(failure.error_type.trim(), 200);
1899
+ if (typeof failure.status_code === "number" && Number.isInteger(failure.status_code) && failure.status_code >= 100 && failure.status_code <= 599) {
1900
+ body.status_code = failure.status_code;
1718
1901
  }
1719
- return "Something went wrong";
1902
+ return body;
1720
1903
  }
1721
- function statusToCode(status) {
1722
- switch (status) {
1723
- case 401:
1724
- return "auth_required";
1725
- case 403:
1726
- return "admin_required";
1727
- case 404:
1728
- return "not_found";
1729
- case 422:
1730
- return "validation_error";
1731
- default:
1732
- return "internal_error";
1904
+ async function reportProviderSessionFailure(transport, failure, options = {}) {
1905
+ try {
1906
+ return await requestJson(
1907
+ transport,
1908
+ "/broker/provider-failures",
1909
+ {
1910
+ method: "POST",
1911
+ body: providerSessionFailureBody(failure),
1912
+ ...options.signal ? { signal: options.signal } : {}
1913
+ }
1914
+ );
1915
+ } catch (error) {
1916
+ console.warn(
1917
+ `[provider-failures] could not report a ${failure.provider} session failure`,
1918
+ error
1919
+ );
1920
+ return null;
1733
1921
  }
1734
1922
  }
1735
1923
 
1924
+ // matrx/tools.ts
1925
+ function submitAgentToolResults(transport, conversationId, results, options = {}) {
1926
+ return requestJson(
1927
+ transport,
1928
+ `/ai/conversations/${encodePathSegment(conversationId)}/tool_results`,
1929
+ {
1930
+ method: "POST",
1931
+ body: {
1932
+ results,
1933
+ ...options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1934
+ },
1935
+ ...options.signal ? { signal: options.signal } : {}
1936
+ }
1937
+ );
1938
+ }
1939
+ function listConversationPendingToolCalls(transport, conversationId, options = {}) {
1940
+ return requestJson(
1941
+ transport,
1942
+ `/ai/conversations/${encodePathSegment(conversationId)}/pending_calls`,
1943
+ {
1944
+ method: "GET",
1945
+ ...options.signal ? { signal: options.signal } : {}
1946
+ }
1947
+ );
1948
+ }
1949
+ function listUserPendingToolCalls(transport, options = {}) {
1950
+ const query = buildQuery(
1951
+ options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1952
+ );
1953
+ return requestJson(
1954
+ transport,
1955
+ `/ai/user/pending_calls${query}`,
1956
+ {
1957
+ method: "GET",
1958
+ ...options.signal ? { signal: options.signal } : {}
1959
+ }
1960
+ );
1961
+ }
1962
+
1736
1963
  // matrx/endpoints.ts
1737
1964
  var ENDPOINTS = {
1738
1965
  /** AI endpoints — chat, agents, conversations */
@@ -2363,6 +2590,7 @@ export {
2363
2590
  MATRX_LIVE_STREAM_UNAVAILABLE,
2364
2591
  MATRX_RESUME_CONFLICT,
2365
2592
  MATRX_RUN_IN_PROGRESS,
2593
+ MATRX_UI_ONLY_BODY_FIELDS,
2366
2594
  MatrxApiError,
2367
2595
  MatrxRunError,
2368
2596
  OrganizationContextError,
@@ -2378,6 +2606,10 @@ export {
2378
2606
  applyOrganizationContextHeader,
2379
2607
  assertOrganizationMatchesOperation,
2380
2608
  assertQueryOrganizationMatchesContext,
2609
+ bareStatusSentence,
2610
+ buildMatrxRequestBody,
2611
+ buildMatrxRequestUrl,
2612
+ buildSafeRequestLog,
2381
2613
  cancelAgentRun,
2382
2614
  continueAgentConversation,
2383
2615
  continueEphemeralConversationStart,
@@ -2385,6 +2617,7 @@ export {
2385
2617
  createMatrxTransport,
2386
2618
  createOrganizationOperation,
2387
2619
  describeBackendFailure,
2620
+ executeMatrxCall,
2388
2621
  extractMatrxErrorCode,
2389
2622
  extractMatrxErrorMessage,
2390
2623
  fetchWithMatrxProtocolFallback,
@@ -2395,6 +2628,8 @@ export {
2395
2628
  getRuntimeOperationStatus,
2396
2629
  getRuntimeOperationsByLink,
2397
2630
  getUserMessage,
2631
+ honestTransportMessage,
2632
+ isBareTransportCode,
2398
2633
  isCoveredAiPath,
2399
2634
  isGenericUserMessage,
2400
2635
  isJobOutputKind,
@@ -2414,6 +2649,7 @@ export {
2414
2649
  parseCallApiError,
2415
2650
  parseHttpError,
2416
2651
  parseHttpErrorBody,
2652
+ parseMatrxNdjsonResponse,
2417
2653
  parsePersistedBackendError,
2418
2654
  parseStreamError,
2419
2655
  providerSessionFailureBody,
@@ -2423,6 +2659,7 @@ export {
2423
2659
  readLiveStreamUnavailable,
2424
2660
  readMatrxErrorCode,
2425
2661
  readMatrxErrorCodeFromMessage,
2662
+ redactUrlForRequestLog,
2426
2663
  rejoinRuntimeOperation,
2427
2664
  reportProviderSessionFailure,
2428
2665
  requireOrganizationContext,
@@ -2437,6 +2674,7 @@ export {
2437
2674
  runWaitTimeoutMessage,
2438
2675
  runtimeOperationRejoinPath,
2439
2676
  settleRunPickup,
2677
+ shouldReportMatrxCallError,
2440
2678
  startAgentRun,
2441
2679
  startMandateRun,
2442
2680
  streamErrorText,