@ai-matrx/agents 0.35.0 → 0.37.0

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