@ai-matrx/agents 0.36.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 (49) hide show
  1. package/CHANGELOG.md +10 -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-DCtN66RS.d.cts → keys.generated-klzs6I1T.d.cts} +4 -4
  21. package/dist/{keys.generated-DCtN66RS.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.d.cts +1 -100929
  35. package/dist/message-parts/index.d.ts +1 -100929
  36. package/dist/portable/mcp.cjs +99 -11
  37. package/dist/portable/mcp.cjs.map +1 -1
  38. package/dist/portable/mcp.js +99 -11
  39. package/dist/portable/mcp.js.map +1 -1
  40. package/dist/react/index.cjs +145 -57
  41. package/dist/react/index.cjs.map +1 -1
  42. package/dist/react/index.d.cts +3 -1
  43. package/dist/react/index.d.ts +3 -1
  44. package/dist/react/index.js +145 -57
  45. package/dist/react/index.js.map +1 -1
  46. package/mandates/snapshots/keys.0.37.0.json +653 -0
  47. package/package.json +2 -2
  48. package/dist/{operations-BT5kKMHl.d.cts → operations-DqOe2zob.d.cts} +34 -34
  49. package/dist/{operations-BT5kKMHl.d.ts → operations-DqOe2zob.d.ts} +34 -34
package/dist/index.cjs CHANGED
@@ -31,6 +31,7 @@ __export(index_exports, {
31
31
  MATRX_LIVE_STREAM_UNAVAILABLE: () => MATRX_LIVE_STREAM_UNAVAILABLE,
32
32
  MATRX_RESUME_CONFLICT: () => MATRX_RESUME_CONFLICT,
33
33
  MATRX_RUN_IN_PROGRESS: () => MATRX_RUN_IN_PROGRESS,
34
+ MATRX_UI_ONLY_BODY_FIELDS: () => MATRX_UI_ONLY_BODY_FIELDS,
34
35
  MatrxApiError: () => MatrxApiError,
35
36
  MatrxRunError: () => MatrxRunError,
36
37
  OrganizationContextError: () => OrganizationContextError,
@@ -48,6 +49,10 @@ __export(index_exports, {
48
49
  applyOrganizationContextHeader: () => applyOrganizationContextHeader,
49
50
  assertOrganizationMatchesOperation: () => assertOrganizationMatchesOperation,
50
51
  assertQueryOrganizationMatchesContext: () => assertQueryOrganizationMatchesContext,
52
+ bareStatusSentence: () => bareStatusSentence,
53
+ buildMatrxRequestBody: () => buildMatrxRequestBody,
54
+ buildMatrxRequestUrl: () => buildMatrxRequestUrl,
55
+ buildSafeRequestLog: () => buildSafeRequestLog,
51
56
  cancelAgentRun: () => cancelAgentRun,
52
57
  continueAgentConversation: () => continueAgentConversation,
53
58
  continueEphemeralConversationStart: () => continueEphemeralConversationStart,
@@ -60,6 +65,7 @@ __export(index_exports, {
60
65
  createWorkflowNodeProjection: () => createWorkflowNodeProjection,
61
66
  deriveRunInputs: () => deriveRunInputs,
62
67
  describeBackendFailure: () => describeBackendFailure,
68
+ executeMatrxCall: () => executeMatrxCall,
63
69
  extractMatrxErrorCode: () => extractMatrxErrorCode,
64
70
  extractMatrxErrorMessage: () => extractMatrxErrorMessage,
65
71
  fetchWithMatrxProtocolFallback: () => fetchWithMatrxProtocolFallback,
@@ -70,7 +76,9 @@ __export(index_exports, {
70
76
  getRuntimeOperationStatus: () => getRuntimeOperationStatus,
71
77
  getRuntimeOperationsByLink: () => getRuntimeOperationsByLink,
72
78
  getUserMessage: () => getUserMessage,
79
+ honestTransportMessage: () => honestTransportMessage,
73
80
  imageRoleVerdict: () => imageRoleVerdict,
81
+ isBareTransportCode: () => isBareTransportCode,
74
82
  isControlVariable: () => isControlVariable,
75
83
  isCoveredAiPath: () => isCoveredAiPath,
76
84
  isGenericUserMessage: () => isGenericUserMessage,
@@ -97,6 +105,7 @@ __export(index_exports, {
97
105
  parseCallApiError: () => parseCallApiError,
98
106
  parseHttpError: () => parseHttpError,
99
107
  parseHttpErrorBody: () => parseHttpErrorBody,
108
+ parseMatrxNdjsonResponse: () => parseMatrxNdjsonResponse,
100
109
  parseMatrxSseFrame: () => parseMatrxSseFrame,
101
110
  parsePersistedBackendError: () => parsePersistedBackendError,
102
111
  parseStreamError: () => parseStreamError,
@@ -115,6 +124,7 @@ __export(index_exports, {
115
124
  readMatrxErrorCodeFromMessage: () => readMatrxErrorCodeFromMessage,
116
125
  readMatrxNdjsonStream: () => readMatrxNdjsonStream,
117
126
  readMatrxSseStream: () => readMatrxSseStream,
127
+ redactUrlForRequestLog: () => redactUrlForRequestLog,
118
128
  referenceRoleCaption: () => referenceRoleCaption,
119
129
  rejoinRuntimeOperation: () => rejoinRuntimeOperation,
120
130
  reportProviderSessionFailure: () => reportProviderSessionFailure,
@@ -132,6 +142,7 @@ __export(index_exports, {
132
142
  runWaitTimeoutMessage: () => runWaitTimeoutMessage,
133
143
  runtimeOperationRejoinPath: () => runtimeOperationRejoinPath,
134
144
  settleRunPickup: () => settleRunPickup,
145
+ shouldReportMatrxCallError: () => shouldReportMatrxCallError,
135
146
  startAgentRun: () => startAgentRun,
136
147
  startMandateRun: () => startMandateRun,
137
148
  streamErrorText: () => streamErrorText,
@@ -1009,1384 +1020,1611 @@ function extractMatrxErrorCode(serverDetail) {
1009
1020
  // matrx/client.ts
1010
1021
  var import_data = require("@ai-matrx/data");
1011
1022
  var import_data2 = require("@ai-matrx/data");
1023
+ var import_net2 = require("@ai-matrx/data/net");
1012
1024
 
1013
- // matrx/org-context.ts
1014
- var import_uuid = require("@ai-matrx/kit/uuid");
1015
- var OrganizationContextError = class extends Error {
1025
+ // matrx/backend-errors.ts
1026
+ var BackendApiError = class extends Error {
1027
+ /** Machine-readable error code */
1016
1028
  code;
1017
- constructor(code, message) {
1018
- super(message);
1019
- this.name = "OrganizationContextError";
1020
- this.code = code;
1021
- }
1022
- };
1023
- function createOrganizationOperation(organizationId) {
1024
- const organization_id = requireOrganizationContext(organizationId);
1025
- return Object.freeze({ organization_id });
1026
- }
1027
- function assertOrganizationMatchesOperation(operation, organizationId) {
1028
- if (operation.organization_id !== requireOrganizationContext(organizationId)) {
1029
- throw new OrganizationContextError(
1030
- "organization_context_mismatch",
1031
- "Organization ID must match the request operation."
1032
- );
1033
- }
1034
- }
1035
- function requireOrganizationContext(selectedOrganizationId, overrideOrganizationId) {
1036
- const candidate = overrideOrganizationId ?? selectedOrganizationId;
1037
- if (typeof candidate !== "string" || candidate.trim().length === 0) {
1038
- throw new OrganizationContextError(
1039
- "organization_context_required",
1040
- "Select an organization before sending this request."
1041
- );
1029
+ /** Developer-facing detail */
1030
+ detail;
1031
+ /** Safe to display directly in the UI */
1032
+ userMessage;
1033
+ /** Extra info (validation errors, etc.) */
1034
+ details;
1035
+ /** Unique request ID for support/debugging */
1036
+ requestId;
1037
+ /** HTTP status code (if from an HTTP response) */
1038
+ status;
1039
+ constructor(data) {
1040
+ super(data.userMessage);
1041
+ this.name = "BackendApiError";
1042
+ this.code = data.code;
1043
+ this.detail = data.detail;
1044
+ this.userMessage = data.userMessage;
1045
+ this.details = data.details ?? null;
1046
+ this.requestId = data.requestId ?? "";
1047
+ this.status = data.status ?? null;
1042
1048
  }
1043
- const normalized = candidate.trim();
1044
- if (!(0, import_uuid.isUuidShape)(normalized)) {
1045
- throw new OrganizationContextError(
1046
- "organization_context_invalid",
1047
- "The selected organization ID is invalid."
1048
- );
1049
+ /** Convert to the wire format for logging */
1050
+ toJSON() {
1051
+ return {
1052
+ error: this.code,
1053
+ message: this.detail,
1054
+ user_message: this.userMessage,
1055
+ details: this.details,
1056
+ request_id: this.requestId
1057
+ };
1049
1058
  }
1050
- return normalized.toLowerCase();
1051
- }
1052
- function applyOrganizationContextHeader(headers, organizationId) {
1053
- const normalizedOrganizationId = requireOrganizationContext(organizationId);
1054
- for (const [name, value] of Object.entries(headers)) {
1055
- if (name.toLowerCase() === "x-organization-id" && value.trim().toLowerCase() !== normalizedOrganizationId) {
1056
- throw new OrganizationContextError(
1057
- "organization_context_mismatch",
1058
- "X-Organization-Id must match the request context organization."
1059
- );
1060
- }
1059
+ };
1060
+ var StreamTransportError = class extends BackendApiError {
1061
+ /** Always true — reattach and let the server settle the outcome. */
1062
+ resumable = true;
1063
+ constructor(data) {
1064
+ super({
1065
+ code: "stream_transport_lost",
1066
+ detail: data.detail,
1067
+ userMessage: "The connection dropped. Your run is still going on the server \u2014 reconnecting to it now.",
1068
+ details: data.details ?? null,
1069
+ ...data.requestId !== void 0 ? { requestId: data.requestId } : {}
1070
+ });
1071
+ this.name = "StreamTransportError";
1061
1072
  }
1062
- const withoutOrganizationHeader = Object.fromEntries(
1063
- Object.entries(headers).filter(
1064
- ([name]) => name.toLowerCase() !== "x-organization-id"
1065
- )
1066
- );
1067
- return {
1068
- ...withoutOrganizationHeader,
1069
- "X-Organization-Id": normalizedOrganizationId
1070
- };
1071
- }
1072
- function assertQueryOrganizationMatchesContext(queryParams, organizationId) {
1073
- if (!queryParams || queryParams.organization_id === void 0) return;
1074
- const queryOrganizationId = requireOrganizationContext(
1075
- String(queryParams.organization_id)
1076
- );
1077
- if (queryOrganizationId !== requireOrganizationContext(organizationId)) {
1078
- throw new OrganizationContextError(
1079
- "organization_context_mismatch",
1080
- "Query organization_id must match the request context organization."
1081
- );
1073
+ };
1074
+ function isStreamTransportLost(error) {
1075
+ if (error instanceof StreamTransportError) return true;
1076
+ if (error instanceof BackendApiError) {
1077
+ return error.code === "stream_transport_lost";
1082
1078
  }
1083
- }
1084
-
1085
- // matrx/protocol.ts
1086
- var import_net = require("@ai-matrx/data/net");
1087
- var MATRX_AI_API_VERSION_DEFAULT = "v2";
1088
- var V2_COVERED_AI_PATH_TEMPLATES = [
1089
- "/ai/manual",
1090
- "/ai/chat",
1091
- "/ai/agents/{agent_id}",
1092
- "/ai/agent/{agent_id}",
1093
- // singular alias — behaves identically
1094
- "/ai/conversations/{conversation_id}",
1095
- "/ai/conversation/{conversation_id}",
1096
- // singular alias
1097
- "/ai/prompts/{prompt_id}",
1098
- // saved-prompt execution
1099
- "/ai/mandates/{mandate_key}"
1100
- // THE MANDATE DOOR — `/v2` sibling exists (routers/v2.py)
1101
- ];
1102
- function toV2Path(path) {
1103
- const withLead = path.startsWith("/") ? path : `/${path}`;
1104
- if (withLead.startsWith("/api/")) {
1105
- const rest = withLead.slice("/api".length);
1106
- return rest.startsWith("/v2/") ? withLead : `/api/v2${rest}`;
1079
+ if (error && typeof error === "object") {
1080
+ const code = error.code;
1081
+ if (code === "stream_transport_lost") return true;
1082
+ const errorType = error.error_type;
1083
+ if (errorType === "transport_lost") return true;
1107
1084
  }
1108
- return withLead.startsWith("/v2/") ? withLead : `/v2${withLead}`;
1109
- }
1110
- var COVERED_INTERPOLATED_PATH = [
1111
- /^(?:\/api)?\/ai\/manual$/,
1112
- /^(?:\/api)?\/ai\/chat$/,
1113
- /^(?:\/api)?\/ai\/agents\/[^/]+$/,
1114
- /^(?:\/api)?\/ai\/agent\/[^/]+$/,
1115
- /^(?:\/api)?\/ai\/conversations\/[^/]+$/,
1116
- /^(?:\/api)?\/ai\/conversation\/[^/]+$/,
1117
- /^(?:\/api)?\/ai\/prompts\/[^/]+$/,
1118
- // `/warm` etc. deliberately don't match
1119
- /^(?:\/api)?\/ai\/mandates\/[^/]+$/
1120
- // the mandate door
1121
- ];
1122
- function isCoveredAiPath(path) {
1123
- return COVERED_INTERPOLATED_PATH.some((re) => re.test(path));
1124
- }
1125
- function isV2Path(pathOrUrl) {
1126
- return /\/v2\/ai\//.test(pathOrUrl);
1127
- }
1128
- function toV1FallbackUrl(url) {
1129
- return url.replace(/\/v2\/ai\//, "/ai/");
1130
- }
1131
- function applyAiApiVersion(path, version) {
1132
- if (version !== "v2") return path;
1133
- return isCoveredAiPath(path) ? toV2Path(path) : path;
1085
+ return false;
1134
1086
  }
1135
- async function fetchWithMatrxProtocolFallback(url, init, opts = {}) {
1136
- const { onDowngrade, ...fetchOpts } = opts;
1137
- const logDowngrade = (reason, status2) => {
1138
- console.warn(
1139
- `[matrx] ai_v2_downgrade \u2192 retrying on v1. v2=${url} reason=${reason}`
1140
- );
1141
- onDowngrade?.({ url, reason, ...status2 !== void 0 ? { status: status2 } : {} });
1142
- };
1143
- if (!isV2Path(url)) return (0, import_net.resilientFetch)(url, init, fetchOpts);
1144
- let result;
1087
+ async function parseHttpError(response) {
1088
+ const status = response.status;
1089
+ let body = null;
1145
1090
  try {
1146
- result = await (0, import_net.resilientFetch)(url, init, fetchOpts);
1147
- } catch (err) {
1148
- const isAbort = (0, import_net.isNetError)(err) && err.code === "aborted" || err instanceof Error && err.name === "AbortError";
1149
- if (isAbort) throw err;
1150
- logDowngrade(String(err));
1151
- return (0, import_net.resilientFetch)(toV1FallbackUrl(url), init, fetchOpts);
1152
- }
1153
- const status = result.response.status;
1154
- if (status === 404 || status === 405 || status >= 500) {
1155
- logDowngrade(`HTTP ${status}`, status);
1156
- return (0, import_net.resilientFetch)(toV1FallbackUrl(url), init, fetchOpts);
1157
- }
1158
- return result;
1159
- }
1160
- function aiVersionPathOverrides(version) {
1161
- if (version !== "v2") return {};
1162
- const map = {};
1163
- for (const template of V2_COVERED_AI_PATH_TEMPLATES) {
1164
- map[template] = toV2Path(template);
1091
+ body = await response.json();
1092
+ } catch {
1093
+ try {
1094
+ const text = await response.text();
1095
+ return new BackendApiError({
1096
+ code: statusToCode(status),
1097
+ detail: text || `HTTP ${status}`,
1098
+ userMessage: text || `Request failed (${status})`,
1099
+ status
1100
+ });
1101
+ } catch {
1102
+ return new BackendApiError({
1103
+ code: statusToCode(status),
1104
+ detail: `HTTP ${status}`,
1105
+ userMessage: `Request failed (${status})`,
1106
+ status
1107
+ });
1108
+ }
1165
1109
  }
1166
- return map;
1167
- }
1168
-
1169
- // matrx/client.ts
1170
- function classifyStatus(status) {
1171
- return status >= 400 && status < 500 ? "validation_error" : "http_error";
1110
+ return parseHttpErrorBody(body, status);
1172
1111
  }
1173
- function normalizeMatrxError(err) {
1174
- if (err instanceof MatrxApiError) {
1175
- return {
1176
- type: classifyStatus(err.status),
1177
- message: err.message,
1178
- status: err.status,
1179
- serverDetail: err.serverDetail,
1180
- ...err.code ? { code: err.code } : {},
1181
- name: err.name
1182
- };
1112
+ function parseHttpErrorBody(body, status) {
1113
+ if (!body) {
1114
+ return new BackendApiError({
1115
+ code: statusToCode(status),
1116
+ detail: `HTTP ${status}`,
1117
+ userMessage: `Request failed (${status})`,
1118
+ status
1119
+ });
1183
1120
  }
1184
- if (err instanceof OrganizationContextError) {
1185
- return {
1186
- type: "validation_error",
1187
- message: err.message,
1188
- code: err.code,
1189
- name: err.name,
1190
- ...err.stack !== void 0 ? { stack: err.stack } : {}
1191
- };
1121
+ if (typeof body.error === "string" && typeof body.user_message === "string") {
1122
+ return new BackendApiError({
1123
+ code: body.error,
1124
+ detail: body.message || `HTTP ${status}`,
1125
+ userMessage: body.user_message,
1126
+ details: body.details ?? null,
1127
+ requestId: typeof body.request_id === "string" ? body.request_id : void 0,
1128
+ status
1129
+ });
1192
1130
  }
1193
- if (err instanceof DOMException && err.name === "AbortError") {
1194
- return { type: "abort_error", message: "Request was cancelled." };
1131
+ if (typeof body.error === "object" && body.error !== null) {
1132
+ const errorObj = body.error;
1133
+ return new BackendApiError({
1134
+ code: errorObj.type || errorObj.error || statusToCode(status),
1135
+ detail: errorObj.message || `HTTP ${status}`,
1136
+ userMessage: errorObj.user_message || errorObj.user_visible_message || errorObj.message || `Request failed (${status})`,
1137
+ details: errorObj.details ?? null,
1138
+ requestId: typeof errorObj.request_id === "string" ? errorObj.request_id : void 0,
1139
+ status
1140
+ });
1195
1141
  }
1196
- if ((0, import_data.isNetError)(err)) {
1197
- if (err.code === "aborted") {
1198
- return { type: "abort_error", message: err.message };
1199
- }
1200
- if (err.code === "http") {
1201
- const status = err.status ?? 0;
1202
- return { type: classifyStatus(status), message: err.message, status };
1203
- }
1204
- return { type: "network_error", message: err.message };
1142
+ if (Array.isArray(body.detail)) {
1143
+ const first = body.detail[0];
1144
+ const firstMsg = first && typeof first.msg === "string" ? first.msg : null;
1145
+ return new BackendApiError({
1146
+ code: statusToCode(status),
1147
+ detail: firstMsg ? `Validation error: ${firstMsg}` : JSON.stringify(body.detail),
1148
+ userMessage: firstMsg || `Request failed (${status})`,
1149
+ details: body.detail,
1150
+ status
1151
+ });
1205
1152
  }
1206
- if (err instanceof Error) {
1207
- const httpMatch = err.message.match(/HTTP (\d+):\s*(.*)/);
1208
- if (httpMatch) {
1209
- const status = parseInt(httpMatch[1], 10);
1210
- return {
1211
- type: classifyStatus(status),
1212
- message: httpMatch[2] ?? err.message,
1213
- status
1214
- };
1215
- }
1216
- return { type: "network_error", message: err.message };
1153
+ if (typeof body.detail === "object" && body.detail !== null && !Array.isArray(body.detail)) {
1154
+ const d = body.detail;
1155
+ const code = typeof d.code === "string" && d.code || typeof d.error === "string" && d.error || statusToCode(status);
1156
+ const message = typeof d.message === "string" && d.message || typeof d.detail === "string" && d.detail || `HTTP ${status}`;
1157
+ return new BackendApiError({
1158
+ code,
1159
+ detail: message,
1160
+ userMessage: typeof d.user_message === "string" && d.user_message || typeof d.user_visible_message === "string" && d.user_visible_message || message,
1161
+ details: d.details ?? d,
1162
+ requestId: typeof d.request_id === "string" ? d.request_id : void 0,
1163
+ status
1164
+ });
1217
1165
  }
1218
- return { type: "unknown", message: String(err ?? "Unknown error") };
1166
+ return new BackendApiError({
1167
+ code: typeof body.error === "string" ? body.error : statusToCode(status),
1168
+ detail: body.message || body.detail || body.error || `HTTP ${status}`,
1169
+ userMessage: body.user_message || body.user_visible_message || body.message || body.detail || `Request failed (${status})`,
1170
+ details: body.details ?? null,
1171
+ requestId: typeof body.request_id === "string" ? body.request_id : void 0,
1172
+ status
1173
+ });
1219
1174
  }
1220
- var PRODUCTION_CONNECT_TIMEOUT_MS = 15e3;
1221
- function stripContentType(headers) {
1222
- return Object.fromEntries(
1223
- Object.entries(headers).filter(
1224
- ([name]) => name.toLowerCase() !== "content-type"
1225
- )
1226
- );
1175
+ function parseCallApiError(error) {
1176
+ const status = error.status ?? 500;
1177
+ const body = error.serverDetail && typeof error.serverDetail === "object" && !Array.isArray(error.serverDetail) ? error.serverDetail : { message: error.message };
1178
+ return parseHttpErrorBody(body, status);
1227
1179
  }
1228
- function createMatrxTransport(options) {
1229
- const {
1230
- baseUrl,
1231
- resolveTarget,
1232
- credentials,
1233
- organizationId,
1234
- aiApiVersion,
1235
- connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,
1236
- totalTimeoutMs = null,
1237
- expectedErrorStatuses,
1238
- diagnostics,
1239
- source = "matrxTransport"
1240
- } = options;
1241
- if (baseUrl === void 0 === (resolveTarget === void 0)) {
1242
- throw new Error(
1243
- "createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`."
1244
- );
1180
+ function parseStreamError(data) {
1181
+ if (!data || typeof data !== "object") {
1182
+ return new BackendApiError({
1183
+ code: "internal_error",
1184
+ detail: typeof data === "string" ? data : "Unknown streaming error",
1185
+ userMessage: typeof data === "string" ? data : "Something went wrong"
1186
+ });
1245
1187
  }
1246
- const resolve = resolveTarget ?? (() => ({ baseUrl }));
1247
- const resolveVersion = () => {
1248
- if (aiApiVersion === void 0) return MATRX_AI_API_VERSION_DEFAULT;
1249
- return typeof aiApiVersion === "function" ? aiApiVersion() : aiApiVersion;
1250
- };
1251
- const resolveOrganizationId = () => {
1252
- if (organizationId === void 0) return null;
1253
- const candidate = typeof organizationId === "function" ? organizationId() : organizationId;
1254
- return requireOrganizationContext(candidate);
1255
- };
1256
- return {
1257
- async fetch(path, init) {
1258
- const target = resolve();
1259
- const versionedPath = applyAiApiVersion(path, resolveVersion());
1260
- const url = `${target.baseUrl}${versionedPath}`;
1261
- const credentialHeaders = credentials ? (0, import_data2.credentialToHeaders)(await credentials.get()) : {};
1262
- let headers = {
1263
- ...init.headers,
1264
- ...stripContentType(credentialHeaders),
1265
- ...stripContentType(target.policyHeaders ?? {})
1266
- };
1267
- const orgId = resolveOrganizationId();
1268
- if (orgId !== null) {
1269
- headers = applyOrganizationContextHeader(headers, orgId);
1270
- }
1271
- const info = {
1272
- url,
1273
- method: init.method,
1274
- path,
1275
- channel: target.channel ?? "default",
1276
- source
1277
- };
1278
- diagnostics?.onRequest?.(info);
1279
- let response;
1280
- try {
1281
- ({ response } = await fetchWithMatrxProtocolFallback(
1282
- url,
1283
- {
1284
- method: init.method,
1285
- headers,
1286
- ...init.body !== void 0 ? { body: init.body } : {}
1287
- },
1288
- {
1289
- ...init.signal ? { signal: init.signal } : {},
1290
- connectTimeoutMs,
1291
- totalTimeoutMs,
1292
- throwOnHttpError: false,
1293
- ...diagnostics?.onProtocolDowngrade ? { onDowngrade: diagnostics.onProtocolDowngrade } : {}
1294
- }
1295
- ));
1296
- } catch (err) {
1297
- diagnostics?.onError?.(normalizeMatrxError(err), info);
1298
- throw err;
1299
- }
1300
- if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {
1301
- const serverDetail = await response.clone().json().catch(() => void 0);
1302
- const code = extractMatrxErrorCode(serverDetail);
1303
- diagnostics?.onError?.(
1304
- {
1305
- type: classifyStatus(response.status),
1306
- message: extractMatrxErrorMessage(serverDetail) ?? `HTTP ${response.status}`,
1307
- status: response.status,
1308
- serverDetail,
1309
- ...code ? { code } : {}
1310
- },
1311
- info
1312
- );
1313
- }
1314
- return response;
1315
- }
1316
- };
1317
- }
1318
-
1319
- // matrx/conversation.ts
1320
- function mintMatrxConversationId() {
1321
- return crypto.randomUUID();
1322
- }
1323
- function newStoredConversationStart(conversationId) {
1324
- return {
1325
- conversation_id: conversationId ?? mintMatrxConversationId(),
1326
- is_new: true,
1327
- store: true
1328
- };
1188
+ const obj = data;
1189
+ const details = typeof obj.details === "object" && obj.details !== null ? obj.details : null;
1190
+ return new BackendApiError({
1191
+ code: obj.code || obj.error_type || obj.error || "internal_error",
1192
+ detail: obj.message || "Streaming error",
1193
+ userMessage: obj.user_message || obj.message || "Something went wrong",
1194
+ details,
1195
+ requestId: typeof obj.request_id === "string" ? obj.request_id : typeof details?.request_id === "string" ? details.request_id : void 0
1196
+ });
1329
1197
  }
1330
- function continueStoredConversationStart(conversationId) {
1331
- return { conversation_id: conversationId, is_new: false, store: true };
1198
+ function parsePersistedBackendError(data, requestId = "") {
1199
+ if (!data || typeof data !== "object") return null;
1200
+ const error = data;
1201
+ const failures = Array.isArray(error.failures) ? error.failures : [];
1202
+ const firstSpecific = failures.find(
1203
+ (failure) => typeof failure === "object" && failure !== null && typeof failure.message === "string"
1204
+ );
1205
+ const summary = typeof error.message === "string" ? error.message : "Backend operation failed";
1206
+ const specific = typeof firstSpecific?.message === "string" ? firstSpecific.message : summary;
1207
+ const detail = specific === summary ? summary : `${summary}: ${specific}`;
1208
+ const code = typeof error.type === "string" ? error.type : "internal_error";
1209
+ const userMessage = typeof error.user_message === "string" && error.user_message ? error.user_message : detail;
1210
+ return new BackendApiError({
1211
+ code,
1212
+ detail,
1213
+ userMessage,
1214
+ details: error,
1215
+ requestId
1216
+ });
1332
1217
  }
1333
- function newEphemeralConversationStart(conversationId) {
1334
- return {
1335
- conversation_id: conversationId ?? mintMatrxConversationId(),
1336
- is_new: true,
1337
- store: false
1338
- };
1218
+ var GENERIC_MESSAGE_PATTERNS = [
1219
+ /failed unexpectedly/i,
1220
+ /^\s*something went wrong/i,
1221
+ /please try again(\s+later)?\.?\s*$/i,
1222
+ /^\s*request failed\b/i,
1223
+ /^\s*unknown (streaming )?error/i,
1224
+ /^\s*internal server error\.?\s*$/i
1225
+ ];
1226
+ function isGenericUserMessage(message) {
1227
+ const value = (message ?? "").trim();
1228
+ if (!value) return true;
1229
+ return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));
1339
1230
  }
1340
- function continueEphemeralConversationStart(conversationId, priorMessages) {
1231
+ function unwrapUpstreamError(message) {
1232
+ const start = message.indexOf("{");
1233
+ const end = message.lastIndexOf("}");
1234
+ if (start < 0 || end <= start) return null;
1235
+ let parsed;
1236
+ try {
1237
+ parsed = JSON.parse(message.slice(start, end + 1));
1238
+ } catch {
1239
+ return null;
1240
+ }
1241
+ if (typeof parsed !== "object" || parsed === null) return null;
1242
+ const body = parsed;
1243
+ const inner = typeof body.message === "string" && body.message || typeof body.detail === "string" && body.detail || "";
1244
+ if (!inner) return null;
1245
+ const statusMatch = /\bHTTP (\d{3})\b/.exec(message.slice(0, start));
1341
1246
  return {
1342
- conversation_id: conversationId,
1343
- is_new: false,
1344
- store: false,
1345
- prior_messages: priorMessages
1247
+ message: inner,
1248
+ code: typeof body.error_type === "string" && body.error_type || typeof body.error === "string" && body.error || null,
1249
+ userMessage: typeof body.user_message === "string" ? body.user_message : null,
1250
+ requestId: typeof body.request_id === "string" ? body.request_id : null,
1251
+ status: statusMatch ? Number(statusMatch[1]) : null
1346
1252
  };
1347
1253
  }
1348
-
1349
- // matrx/internal.ts
1350
- function encodePathSegment(value) {
1351
- return encodeURIComponent(value);
1352
- }
1353
- function buildQuery(params) {
1354
- const search = new URLSearchParams();
1355
- for (const [key, value] of Object.entries(params)) {
1356
- if (value === void 0) continue;
1357
- if (Array.isArray(value)) {
1358
- for (const entry of value) search.append(key, entry);
1359
- } else {
1360
- search.append(key, String(value));
1254
+ function describeBackendFailure(error) {
1255
+ const chain = [];
1256
+ let code = "internal_error";
1257
+ let requestId = "";
1258
+ let status = null;
1259
+ let userFacing = null;
1260
+ if (error instanceof BackendApiError) {
1261
+ code = error.code;
1262
+ requestId = error.requestId;
1263
+ status = error.status;
1264
+ userFacing = error.userMessage;
1265
+ if (error.detail) chain.push(error.detail);
1266
+ if (error.userMessage && error.userMessage !== error.detail) {
1267
+ chain.push(error.userMessage);
1361
1268
  }
1269
+ } else if (error instanceof Error) {
1270
+ chain.push(error.message);
1271
+ userFacing = error.message;
1272
+ } else if (typeof error === "string") {
1273
+ chain.push(error);
1274
+ userFacing = error;
1275
+ } else {
1276
+ chain.push("Unknown error");
1362
1277
  }
1363
- const encoded = search.toString();
1364
- return encoded ? `?${encoded}` : "";
1365
- }
1366
- async function readServerDetail(response) {
1367
- try {
1368
- return await response.json();
1369
- } catch {
1370
- return void 0;
1278
+ for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {
1279
+ const upstream = unwrapUpstreamError(chain[cursor] ?? "");
1280
+ if (!upstream || chain.includes(upstream.message)) continue;
1281
+ chain.push(upstream.message);
1282
+ if (upstream.code) code = upstream.code;
1283
+ if (upstream.requestId) requestId = upstream.requestId;
1284
+ if (upstream.status !== null) status = upstream.status;
1371
1285
  }
1372
- }
1373
- async function throwApiError(path, response) {
1374
- throw new MatrxApiError({
1375
- status: response.status,
1376
- path,
1377
- serverDetail: await readServerDetail(response)
1378
- });
1379
- }
1380
- async function requestJson(transport, path, options) {
1381
- const hasBody = options.method !== "GET" && options.body !== void 0;
1382
- const response = await transport.fetch(path, {
1383
- method: options.method,
1384
- headers: hasBody ? { "Content-Type": "application/json" } : {},
1385
- ...hasBody ? { body: JSON.stringify(options.body) } : {},
1386
- ...options.signal ? { signal: options.signal } : {}
1387
- });
1388
- if (!response.ok) return throwApiError(path, response);
1389
- return await response.json();
1390
- }
1391
- function toRunHandle(response, options) {
1286
+ const specific = [...chain].reverse().find((message) => !isGenericUserMessage(message));
1287
+ const cause = specific ?? chain[0] ?? "Unknown error";
1288
+ const headlineWasGeneric = isGenericUserMessage(userFacing);
1392
1289
  return {
1393
- requestId: response.headers.get("X-Request-ID"),
1394
- conversationId: response.headers.get("X-Conversation-ID"),
1395
- events: readMatrxNdjsonStream(response.body, {
1396
- ...options.signal ? { signal: options.signal } : {},
1397
- ...options.maxReadAhead !== void 0 ? { maxReadAhead: options.maxReadAhead } : {},
1398
- ...options.onMalformedLine ? { onMalformedLine: options.onMalformedLine } : {},
1399
- ...options.onUnknownEnvelope ? { onUnknownEnvelope: options.onUnknownEnvelope } : {},
1400
- ...options.onValidEnvelope ? { onValidEnvelope: options.onValidEnvelope } : {}
1401
- }),
1402
- response
1290
+ code,
1291
+ cause,
1292
+ headline: headlineWasGeneric ? cause : userFacing ?? cause,
1293
+ headlineWasGeneric,
1294
+ chain,
1295
+ requestId,
1296
+ status
1403
1297
  };
1404
1298
  }
1405
- async function requestStream(transport, path, options) {
1406
- const hasBody = options.method !== "GET" && options.body !== void 0;
1407
- const response = await transport.fetch(path, {
1408
- method: options.method,
1409
- headers: {
1410
- ...hasBody ? { "Content-Type": "application/json" } : {},
1411
- ...options.headers
1412
- },
1413
- ...hasBody ? { body: JSON.stringify(options.body) } : {},
1414
- ...options.signal ? { signal: options.signal } : {}
1415
- });
1416
- if (!response.ok) return throwApiError(path, response);
1417
- if (!response.body) {
1418
- throw new MatrxApiError({
1419
- status: response.status,
1420
- path,
1421
- serverDetail: { code: "missing_response_body" },
1422
- message: "The streaming response carried no body."
1423
- });
1299
+ function getUserMessage(error) {
1300
+ if (error instanceof BackendApiError) {
1301
+ return error.userMessage;
1302
+ }
1303
+ if (error instanceof Error) {
1304
+ return error.message;
1305
+ }
1306
+ if (typeof error === "string") {
1307
+ return error;
1308
+ }
1309
+ if (typeof error === "object" && error !== null && typeof error.message === "string" && error.message) {
1310
+ return error.message;
1311
+ }
1312
+ return "Something went wrong";
1313
+ }
1314
+ function statusToCode(status) {
1315
+ switch (status) {
1316
+ case 401:
1317
+ return "auth_required";
1318
+ case 403:
1319
+ return "admin_required";
1320
+ case 404:
1321
+ return "not_found";
1322
+ case 422:
1323
+ return "validation_error";
1324
+ default:
1325
+ return "internal_error";
1424
1326
  }
1425
- return response;
1426
1327
  }
1427
1328
 
1428
- // matrx/operations.ts
1429
- var TERMINAL_MATRX_RUNTIME_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
1430
- var RUNTIME_STATUSES = /* @__PURE__ */ new Set([
1431
- "pending",
1432
- "running",
1433
- "paused",
1434
- "waiting_input",
1435
- "completed",
1436
- "failed",
1437
- "cancelled"
1438
- ]);
1439
- async function getRuntimeOperationStatus(transport, requestId, options = {}) {
1440
- try {
1441
- return await requestJson(
1442
- transport,
1443
- `/runtime/operations/${encodePathSegment(requestId)}`,
1444
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1329
+ // matrx/org-context.ts
1330
+ var import_uuid = require("@ai-matrx/kit/uuid");
1331
+ var OrganizationContextError = class extends Error {
1332
+ code;
1333
+ constructor(code, message) {
1334
+ super(message);
1335
+ this.name = "OrganizationContextError";
1336
+ this.code = code;
1337
+ }
1338
+ };
1339
+ function createOrganizationOperation(organizationId) {
1340
+ const organization_id = requireOrganizationContext(organizationId);
1341
+ return Object.freeze({ organization_id });
1342
+ }
1343
+ function assertOrganizationMatchesOperation(operation, organizationId) {
1344
+ if (operation.organization_id !== requireOrganizationContext(organizationId)) {
1345
+ throw new OrganizationContextError(
1346
+ "organization_context_mismatch",
1347
+ "Organization ID must match the request operation."
1445
1348
  );
1446
- } catch (error) {
1447
- if (error instanceof MatrxApiError && error.status === 404) return null;
1448
- throw error;
1449
1349
  }
1450
1350
  }
1451
- async function getRuntimeOperationsByLink(transport, linkKind, linkId, options = {}) {
1452
- const query = buildQuery(
1453
- options.limit !== void 0 ? { limit: options.limit } : {}
1454
- );
1455
- try {
1456
- return await requestJson(
1457
- transport,
1458
- `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,
1459
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1351
+ function requireOrganizationContext(selectedOrganizationId, overrideOrganizationId) {
1352
+ const candidate = overrideOrganizationId ?? selectedOrganizationId;
1353
+ if (typeof candidate !== "string" || candidate.trim().length === 0) {
1354
+ throw new OrganizationContextError(
1355
+ "organization_context_required",
1356
+ "Select an organization before sending this request."
1357
+ );
1358
+ }
1359
+ const normalized = candidate.trim();
1360
+ if (!(0, import_uuid.isUuidShape)(normalized)) {
1361
+ throw new OrganizationContextError(
1362
+ "organization_context_invalid",
1363
+ "The selected organization ID is invalid."
1460
1364
  );
1461
- } catch (error) {
1462
- if (error instanceof MatrxApiError && error.status === 404) return null;
1463
- throw error;
1464
1365
  }
1366
+ return normalized.toLowerCase();
1465
1367
  }
1466
- function listRuntimeOperationEvents(transport, executionId, options = {}) {
1467
- const query = buildQuery({
1468
- ...options.afterSeq !== void 0 ? { after_seq: options.afterSeq } : {},
1469
- ...options.limit !== void 0 ? { limit: options.limit } : {},
1470
- ...options.kinds !== void 0 ? { kind: options.kinds } : {}
1471
- });
1472
- return requestJson(
1473
- transport,
1474
- `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,
1475
- { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1476
- );
1477
- }
1478
- function parseEndStatus(data) {
1479
- if (data === null) return null;
1480
- try {
1481
- const parsed = JSON.parse(data);
1482
- if (typeof parsed === "object" && parsed !== null && typeof parsed.status === "string") {
1483
- const status = parsed.status;
1484
- return RUNTIME_STATUSES.has(status) ? status : null;
1368
+ function applyOrganizationContextHeader(headers, organizationId) {
1369
+ const normalizedOrganizationId = requireOrganizationContext(organizationId);
1370
+ for (const [name, value] of Object.entries(headers)) {
1371
+ if (name.toLowerCase() === "x-organization-id" && value.trim().toLowerCase() !== normalizedOrganizationId) {
1372
+ throw new OrganizationContextError(
1373
+ "organization_context_mismatch",
1374
+ "X-Organization-Id must match the request context organization."
1375
+ );
1485
1376
  }
1486
- } catch {
1487
1377
  }
1488
- return null;
1489
- }
1490
- async function* followRuntimeOperationEvents(transport, executionId, options = {}) {
1491
- let cursor = options.lastEventSeq ?? 0;
1492
- const headers = { Accept: "text/event-stream" };
1493
- if (cursor > 0) headers["Last-Event-ID"] = String(cursor);
1494
- const response = await requestStream(
1495
- transport,
1496
- `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,
1497
- {
1498
- method: "GET",
1499
- headers,
1500
- ...options.signal ? { signal: options.signal } : {}
1501
- }
1378
+ const withoutOrganizationHeader = Object.fromEntries(
1379
+ Object.entries(headers).filter(
1380
+ ([name]) => name.toLowerCase() !== "x-organization-id"
1381
+ )
1502
1382
  );
1503
- const frames = readMatrxSseStream(
1504
- response.body,
1505
- options.onIncomplete ? { onIncomplete: options.onIncomplete } : {}
1383
+ return {
1384
+ ...withoutOrganizationHeader,
1385
+ "X-Organization-Id": normalizedOrganizationId
1386
+ };
1387
+ }
1388
+ function assertQueryOrganizationMatchesContext(queryParams, organizationId) {
1389
+ if (!queryParams || queryParams.organization_id === void 0) return;
1390
+ const queryOrganizationId = requireOrganizationContext(
1391
+ String(queryParams.organization_id)
1506
1392
  );
1507
- for await (const frame of frames) {
1508
- if (frame.event === "end") {
1509
- yield { type: "end", status: parseEndStatus(frame.data), cursor };
1510
- return;
1511
- }
1512
- if (frame.event === "execution_event" && frame.data !== null) {
1513
- let event;
1514
- try {
1515
- event = JSON.parse(frame.data);
1516
- } catch (error) {
1517
- options.onMalformedFrame?.(frame, error);
1518
- yield { type: "liveness", cursor };
1519
- continue;
1520
- }
1521
- if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;
1522
- yield { type: "event", event, seq: frame.seq, cursor };
1523
- continue;
1524
- }
1525
- yield { type: "liveness", cursor };
1393
+ if (queryOrganizationId !== requireOrganizationContext(organizationId)) {
1394
+ throw new OrganizationContextError(
1395
+ "organization_context_mismatch",
1396
+ "Query organization_id must match the request context organization."
1397
+ );
1526
1398
  }
1527
1399
  }
1528
- async function followRuntimeOperationToEnd(transport, executionId, options) {
1529
- const stallTimeoutMs = options.stallTimeoutMs ?? 45e3;
1530
- const reconnectLimit = options.reconnectLimit ?? 60;
1531
- const reconnectDelayMs = options.reconnectDelayMs ?? 2e3;
1532
- const outer = options.signal;
1533
- let cursor = options.lastEventSeq ?? 0;
1534
- let failures = 0;
1535
- while (!outer?.aborted && failures < reconnectLimit) {
1536
- const attempt2 = new AbortController();
1537
- const onOuterAbort = () => attempt2.abort();
1538
- outer?.addEventListener("abort", onOuterAbort, { once: true });
1539
- let stallTimer = null;
1540
- const armStall = () => {
1541
- if (stallTimer !== null) clearTimeout(stallTimer);
1542
- stallTimer = setTimeout(() => attempt2.abort(), stallTimeoutMs);
1543
- };
1544
- try {
1545
- const items = followRuntimeOperationEvents(transport, executionId, {
1546
- lastEventSeq: cursor,
1547
- signal: attempt2.signal,
1548
- ...options.onMalformedFrame ? { onMalformedFrame: options.onMalformedFrame } : {}
1549
- });
1550
- armStall();
1551
- for await (const item of items) {
1552
- armStall();
1553
- failures = 0;
1554
- cursor = item.cursor;
1555
- if (item.type === "end") {
1556
- return { ended: true, status: item.status };
1557
- }
1558
- if (item.type === "event") {
1559
- options.onEvent(item.event, item.seq);
1560
- }
1561
- }
1562
- failures += 1;
1563
- } catch (error) {
1564
- if (outer?.aborted) break;
1565
- failures += 1;
1566
- options.onAttemptError?.(error);
1567
- } finally {
1568
- if (stallTimer !== null) clearTimeout(stallTimer);
1569
- outer?.removeEventListener("abort", onOuterAbort);
1570
- }
1571
- if (!outer?.aborted && failures < reconnectLimit) {
1572
- await new Promise((r) => setTimeout(r, reconnectDelayMs));
1573
- }
1400
+
1401
+ // matrx/protocol.ts
1402
+ var import_net = require("@ai-matrx/data/net");
1403
+ var MATRX_AI_API_VERSION_DEFAULT = "v2";
1404
+ var V2_COVERED_AI_PATH_TEMPLATES = [
1405
+ "/ai/manual",
1406
+ "/ai/chat",
1407
+ "/ai/agents/{agent_id}",
1408
+ "/ai/agent/{agent_id}",
1409
+ // singular alias — behaves identically
1410
+ "/ai/conversations/{conversation_id}",
1411
+ "/ai/conversation/{conversation_id}",
1412
+ // singular alias
1413
+ "/ai/prompts/{prompt_id}",
1414
+ // saved-prompt execution
1415
+ "/ai/mandates/{mandate_key}"
1416
+ // THE MANDATE DOOR — `/v2` sibling exists (routers/v2.py)
1417
+ ];
1418
+ function toV2Path(path) {
1419
+ const withLead = path.startsWith("/") ? path : `/${path}`;
1420
+ if (withLead.startsWith("/api/")) {
1421
+ const rest = withLead.slice("/api".length);
1422
+ return rest.startsWith("/v2/") ? withLead : `/api/v2${rest}`;
1574
1423
  }
1575
- return { ended: false, status: null };
1424
+ return withLead.startsWith("/v2/") ? withLead : `/v2${withLead}`;
1576
1425
  }
1577
- async function rejoinRuntimeOperation(transport, requestId, options = {}) {
1578
- const response = await requestStream(
1579
- transport,
1580
- `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,
1581
- {
1582
- method: "POST",
1583
- // The route takes no body model; the reference client posts an empty
1584
- // JSON object. Match it so proxies see an ordinary JSON POST.
1585
- body: {},
1586
- ...options.signal ? { signal: options.signal } : {}
1587
- }
1588
- );
1589
- return toRunHandle(response, options);
1426
+ var COVERED_INTERPOLATED_PATH = [
1427
+ /^(?:\/api)?\/ai\/manual$/,
1428
+ /^(?:\/api)?\/ai\/chat$/,
1429
+ /^(?:\/api)?\/ai\/agents\/[^/]+$/,
1430
+ /^(?:\/api)?\/ai\/agent\/[^/]+$/,
1431
+ /^(?:\/api)?\/ai\/conversations\/[^/]+$/,
1432
+ /^(?:\/api)?\/ai\/conversation\/[^/]+$/,
1433
+ /^(?:\/api)?\/ai\/prompts\/[^/]+$/,
1434
+ // `/warm` etc. deliberately don't match
1435
+ /^(?:\/api)?\/ai\/mandates\/[^/]+$/
1436
+ // the mandate door
1437
+ ];
1438
+ function isCoveredAiPath(path) {
1439
+ return COVERED_INTERPOLATED_PATH.some((re) => re.test(path));
1590
1440
  }
1591
-
1592
- // matrx/rejoin.ts
1593
- var MATRX_RUN_IN_PROGRESS = "run_in_progress";
1594
- var MATRX_RESUME_CONFLICT = "resume_conflict";
1595
- var MATRX_LIVE_STREAM_UNAVAILABLE = "live_stream_unavailable";
1596
- function isRecord4(value) {
1597
- return typeof value === "object" && value !== null && !Array.isArray(value);
1441
+ function isV2Path(pathOrUrl) {
1442
+ return /\/v2\/ai\//.test(pathOrUrl);
1598
1443
  }
1599
- function nonBlank(value) {
1600
- return typeof value === "string" && value.trim() ? value : null;
1444
+ function toV1FallbackUrl(url) {
1445
+ return url.replace(/\/v2\/ai\//, "/ai/");
1601
1446
  }
1602
- function detailCandidates(body) {
1603
- if (!isRecord4(body)) return [];
1604
- const out = [];
1605
- if (isRecord4(body.detail)) out.push(body.detail);
1606
- if (isRecord4(body.details)) out.push(body.details);
1607
- out.push(body);
1608
- return out;
1447
+ function applyAiApiVersion(path, version) {
1448
+ if (version !== "v2") return path;
1449
+ return isCoveredAiPath(path) ? toV2Path(path) : path;
1609
1450
  }
1610
- function statusOf(error) {
1611
- if (!isRecord4(error) && !(error instanceof Error)) return null;
1612
- const status = error.status;
1613
- return typeof status === "number" ? status : null;
1451
+ async function fetchWithMatrxProtocolFallback(url, init, opts = {}) {
1452
+ const { onDowngrade, ...fetchOpts } = opts;
1453
+ const logDowngrade = (reason, status2) => {
1454
+ console.warn(
1455
+ `[matrx] ai_v2_downgrade \u2192 retrying on v1. v2=${url} reason=${reason}`
1456
+ );
1457
+ onDowngrade?.({ url, reason, ...status2 !== void 0 ? { status: status2 } : {} });
1458
+ };
1459
+ if (!isV2Path(url)) return (0, import_net.resilientFetch)(url, init, fetchOpts);
1460
+ let result;
1461
+ try {
1462
+ result = await (0, import_net.resilientFetch)(url, init, fetchOpts);
1463
+ } catch (err) {
1464
+ const isAbort = (0, import_net.isNetError)(err) && err.code === "aborted" || err instanceof Error && err.name === "AbortError";
1465
+ if (isAbort) throw err;
1466
+ logDowngrade(String(err));
1467
+ return (0, import_net.resilientFetch)(toV1FallbackUrl(url), init, fetchOpts);
1468
+ }
1469
+ const status = result.response.status;
1470
+ if (status === 404 || status === 405 || status >= 500) {
1471
+ logDowngrade(`HTTP ${status}`, status);
1472
+ return (0, import_net.resilientFetch)(toV1FallbackUrl(url), init, fetchOpts);
1473
+ }
1474
+ return result;
1475
+ }
1476
+ function aiVersionPathOverrides(version) {
1477
+ if (version !== "v2") return {};
1478
+ const map = {};
1479
+ for (const template of V2_COVERED_AI_PATH_TEMPLATES) {
1480
+ map[template] = toV2Path(template);
1481
+ }
1482
+ return map;
1483
+ }
1484
+
1485
+ // matrx/call.ts
1486
+ function buildMatrxRequestUrl(baseUrl, pathTemplate, pathParams, queryParams) {
1487
+ let resolvedPath = pathTemplate;
1488
+ if (pathParams) {
1489
+ for (const [key, value] of Object.entries(pathParams)) {
1490
+ resolvedPath = resolvedPath.replace(`{${key}}`, encodeURIComponent(value));
1491
+ }
1492
+ }
1493
+ const fullPath = resolvedPath.startsWith("/api/") ? resolvedPath.slice(4) : resolvedPath;
1494
+ const url = `${baseUrl}${fullPath}`;
1495
+ if (queryParams && Object.keys(queryParams).length > 0) {
1496
+ const qs = new URLSearchParams(
1497
+ Object.fromEntries(
1498
+ Object.entries(queryParams).map(([k, v]) => [k, String(v)])
1499
+ )
1500
+ ).toString();
1501
+ return `${url}?${qs}`;
1502
+ }
1503
+ return url;
1504
+ }
1505
+ var MATRX_UI_ONLY_BODY_FIELDS = /* @__PURE__ */ new Set([
1506
+ "youtube_videos",
1507
+ "file_urls",
1508
+ "image_urls"
1509
+ ]);
1510
+ function buildMatrxRequestBody(body, scope) {
1511
+ const raw = body ?? {};
1512
+ const base = {};
1513
+ for (const [key, value] of Object.entries(raw)) {
1514
+ if (!MATRX_UI_ONLY_BODY_FIELDS.has(key)) base[key] = value;
1515
+ }
1516
+ if (base.organization_id === null || typeof base.organization_id === "string" && base.organization_id.trim() === "") {
1517
+ delete base.organization_id;
1518
+ }
1519
+ const bodyOrganizationId = base.organization_id;
1520
+ if (scope.organization_id !== void 0 && bodyOrganizationId !== void 0 && (typeof bodyOrganizationId !== "string" || bodyOrganizationId.trim() !== scope.organization_id)) {
1521
+ throw new OrganizationContextError(
1522
+ "organization_context_mismatch",
1523
+ "Request body organization_id must match the request context organization."
1524
+ );
1525
+ }
1526
+ if (bodyOrganizationId !== void 0 && scope.organization_id !== void 0) {
1527
+ base.organization_id = scope.organization_id;
1528
+ }
1529
+ const scopeFields = {
1530
+ ...scope.organization_id !== void 0 ? { organization_id: scope.organization_id } : {}
1531
+ };
1532
+ if (scope.project_id !== void 0) scopeFields.project_id = scope.project_id;
1533
+ if (scope.task_id !== void 0) scopeFields.task_id = scope.task_id;
1534
+ return { ...scopeFields, ...base };
1614
1535
  }
1615
- function bodyOf(error) {
1616
- if (!isRecord4(error) && !(error instanceof Error)) return void 0;
1617
- return error.serverDetail;
1536
+ var BARE_TRANSPORT_CODE = /^\s*(?:HTTP|HTTP\s*Error|Status(?:\s*Code)?)?\s*[:\-]?\s*\d{3}\s*[.:!]?\s*$/i;
1537
+ function isBareTransportCode(text) {
1538
+ if (!text) return false;
1539
+ return BARE_TRANSPORT_CODE.test(text);
1618
1540
  }
1619
- function readMatrxErrorCode(error) {
1620
- for (const candidate of detailCandidates(bodyOf(error))) {
1621
- const code = nonBlank(candidate.code);
1622
- if (code) return code;
1541
+ function bareStatusSentence(status) {
1542
+ if (status === 401 || status === 403) {
1543
+ 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.";
1623
1544
  }
1624
- for (const candidate of detailCandidates(bodyOf(error))) {
1625
- const code = nonBlank(candidate.error);
1626
- if (code) return code;
1545
+ if (status === 404) {
1546
+ 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.";
1627
1547
  }
1628
- return null;
1629
- }
1630
- var PREFIXED_ERROR_CODES = [
1631
- "resume_conflict",
1632
- "not_resumable",
1633
- "outstanding_delegated_calls",
1634
- "user_request_not_found",
1635
- "run_in_flight"
1636
- ];
1637
- function readMatrxErrorCodeFromMessage(message) {
1638
- if (typeof message !== "string") return null;
1639
- for (const code of PREFIXED_ERROR_CODES) {
1640
- if (message.startsWith(`${code}:`)) return code;
1548
+ if (status === 429) {
1549
+ return "The server is rate-limiting this request. Wait a few seconds and try again.";
1641
1550
  }
1642
- return null;
1551
+ if (status >= 500) {
1552
+ 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.";
1553
+ }
1554
+ 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.";
1643
1555
  }
1644
- async function readConversationOrganizationId(conversationId, read, onUnreadable) {
1556
+ function honestTransportMessage(raw, status) {
1557
+ if (raw && !isBareTransportCode(raw)) return raw;
1558
+ return bareStatusSentence(status ?? 0);
1559
+ }
1560
+ var SENSITIVE_HEADER_NAME = /authorization|cookie|token|api[-_]?key/i;
1561
+ function buildSafeRequestLog(headers, body) {
1562
+ const safeHeaders = Object.fromEntries(
1563
+ Object.entries(headers).map(([name, value]) => [
1564
+ name,
1565
+ SENSITIVE_HEADER_NAME.test(name) ? "[REDACTED]" : value
1566
+ ])
1567
+ );
1568
+ 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 };
1569
+ return { headers: safeHeaders, body: bodyMetadata };
1570
+ }
1571
+ function redactUrlForRequestLog(url) {
1645
1572
  try {
1646
- const value = await read(conversationId);
1647
- if (typeof value === "string" && value.trim()) return value;
1648
- onUnreadable?.(null);
1649
- } catch (error) {
1650
- onUnreadable?.(error instanceof Error ? error.message : String(error));
1573
+ const parsed = new URL(url);
1574
+ for (const key of parsed.searchParams.keys()) {
1575
+ parsed.searchParams.set(key, "[REDACTED]");
1576
+ }
1577
+ return parsed.toString();
1578
+ } catch {
1579
+ const queryIndex = url.indexOf("?");
1580
+ return queryIndex === -1 ? url : `${url.slice(0, queryIndex)}?[REDACTED]`;
1651
1581
  }
1652
- return null;
1653
1582
  }
1654
- function firstString(candidates, key) {
1655
- for (const candidate of candidates) {
1656
- const value = nonBlank(candidate[key]);
1657
- if (value) return value.trim();
1583
+ function shouldReportMatrxCallError(status, expectedErrorStatuses) {
1584
+ return status == null || !expectedErrorStatuses?.includes(status);
1585
+ }
1586
+ function parseMatrxNdjsonResponse(response, signal, hooks = {}) {
1587
+ const ids = {
1588
+ requestId: response.headers.get("X-Request-ID"),
1589
+ conversationId: response.headers.get("X-Conversation-ID")
1590
+ };
1591
+ async function* events() {
1592
+ if (!response.body) {
1593
+ throw new BackendApiError({
1594
+ code: "internal_error",
1595
+ detail: "Response has no body",
1596
+ userMessage: "No response received from server"
1597
+ });
1598
+ }
1599
+ try {
1600
+ for await (const envelope of readMatrxNdjsonStream(response.body, {
1601
+ ...signal ? { signal } : {},
1602
+ ...hooks.onMalformedLine ? { onMalformedLine: hooks.onMalformedLine } : {},
1603
+ ...hooks.onUnknownEnvelope ? { onUnknownEnvelope: hooks.onUnknownEnvelope } : {}
1604
+ })) {
1605
+ hooks.onEvent?.(envelope, ids);
1606
+ yield envelope;
1607
+ }
1608
+ } catch (error) {
1609
+ if (signal?.aborted || error instanceof Error && error.name === "AbortError") {
1610
+ return;
1611
+ }
1612
+ const transportError = error instanceof BackendApiError ? error : new StreamTransportError({
1613
+ detail: error instanceof Error ? error.message : "The response stream ended unexpectedly.",
1614
+ details: error,
1615
+ ...ids.requestId ? { requestId: ids.requestId } : {}
1616
+ });
1617
+ hooks.onTransportError?.(transportError, ids);
1618
+ throw transportError;
1619
+ }
1658
1620
  }
1659
- return null;
1621
+ return { events: events(), ...ids };
1660
1622
  }
1661
- function readLiveRunRejoin(error) {
1662
- if (statusOf(error) !== 409) return null;
1663
- const candidates = detailCandidates(bodyOf(error));
1664
- if (candidates.length === 0) return null;
1665
- const code = readMatrxErrorCode(error);
1666
- if (code !== MATRX_RUN_IN_PROGRESS) return null;
1667
- const liveRequestId = firstString(candidates, "live_request_id");
1668
- const pathFromBody = firstString(candidates, "rejoin_path");
1669
- const rejoinPath = pathFromBody && pathFromBody.startsWith("/") ? pathFromBody : liveRequestId ? runtimeOperationRejoinPath(liveRequestId) : null;
1670
- if (!rejoinPath) return null;
1671
- const root = bodyOf(error);
1623
+ var DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
1624
+ var DEFAULT_JSON_TOTAL_TIMEOUT_MS = 3e4;
1625
+ async function httpErrorFrom(response) {
1626
+ const serverDetail = await response.json().catch(() => void 0);
1672
1627
  return {
1673
- liveRequestId,
1674
- rejoinPath,
1675
- runId: firstString(candidates, "run_id"),
1676
- code,
1677
- body: isRecord4(root) ? root : {}
1628
+ type: response.status >= 400 && response.status < 500 ? "validation_error" : "http_error",
1629
+ message: extractMatrxErrorMessage(serverDetail) ?? bareStatusSentence(response.status),
1630
+ status: response.status,
1631
+ serverDetail
1678
1632
  };
1679
1633
  }
1680
- function readLiveRunRequestId(error) {
1681
- return readLiveRunRejoin(error)?.liveRequestId ?? null;
1682
- }
1683
- function isResumeConflict(error) {
1684
- return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT;
1634
+ async function executeMatrxCall(request, hooks = {}) {
1635
+ const upper = request.method.toUpperCase();
1636
+ const sendsBody = request.stream || upper !== "GET" && upper !== "HEAD";
1637
+ const { response } = await fetchWithMatrxProtocolFallback(
1638
+ request.url,
1639
+ {
1640
+ method: request.method,
1641
+ headers: request.headers,
1642
+ ...sendsBody ? { body: JSON.stringify(request.body) } : {}
1643
+ },
1644
+ {
1645
+ ...request.signal ? { signal: request.signal } : {},
1646
+ connectTimeoutMs: request.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
1647
+ totalTimeoutMs: request.stream ? null : request.totalTimeoutMs === void 0 ? DEFAULT_JSON_TOTAL_TIMEOUT_MS : request.totalTimeoutMs,
1648
+ throwOnHttpError: false,
1649
+ ...hooks.onProtocolDowngrade ? { onDowngrade: hooks.onProtocolDowngrade } : {}
1650
+ }
1651
+ );
1652
+ const requestId = response.headers.get("X-Request-ID");
1653
+ const conversationId = response.headers.get("X-Conversation-ID");
1654
+ const idFields = {
1655
+ ...requestId !== null ? { requestId } : {},
1656
+ ...conversationId !== null ? { conversationId } : {}
1657
+ };
1658
+ if (!response.ok) {
1659
+ const error = await httpErrorFrom(response);
1660
+ if (request.stream) request.onStreamError?.(error);
1661
+ return { ...idFields, error };
1662
+ }
1663
+ if (!request.stream) {
1664
+ const data = response.status === 204 ? void 0 : await response.json();
1665
+ return { data, ...idFields };
1666
+ }
1667
+ if (request.consumeStream) {
1668
+ request.onStreamStart?.(requestId, conversationId);
1669
+ await request.consumeStream(response, { requestId, conversationId });
1670
+ request.onStreamComplete?.(requestId, conversationId);
1671
+ return idFields;
1672
+ }
1673
+ const parse = hooks.parseStream ?? ((res, signal) => parseMatrxNdjsonResponse(res, signal));
1674
+ const parsed = parse(response, request.signal);
1675
+ request.onStreamStart?.(parsed.requestId, parsed.conversationId);
1676
+ for await (const event of parsed.events) {
1677
+ request.onStreamEvent?.(event);
1678
+ }
1679
+ request.onStreamComplete?.(parsed.requestId, parsed.conversationId);
1680
+ return {
1681
+ ...parsed.requestId !== null ? { requestId: parsed.requestId } : {},
1682
+ ...parsed.conversationId !== null ? { conversationId: parsed.conversationId } : {}
1683
+ };
1685
1684
  }
1686
- function isLiveStreamUnavailable(error) {
1687
- return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_LIVE_STREAM_UNAVAILABLE;
1685
+
1686
+ // matrx/client.ts
1687
+ function classifyStatus(status) {
1688
+ return status >= 400 && status < 500 ? "validation_error" : "http_error";
1688
1689
  }
1689
- function runtimeOperationRejoinPath(requestId) {
1690
- return `/runtime/operations/${encodePathSegment(requestId)}/rejoin`;
1690
+ function normalizeMatrxError(err) {
1691
+ if (err instanceof OrganizationContextError) {
1692
+ return {
1693
+ type: "validation_error",
1694
+ message: err.message,
1695
+ code: err.code,
1696
+ name: err.name,
1697
+ ...err.stack !== void 0 ? { stack: err.stack } : {}
1698
+ };
1699
+ }
1700
+ if (err instanceof DOMException && err.name === "AbortError") {
1701
+ return { type: "abort_error", message: "Request was cancelled." };
1702
+ }
1703
+ if (err instanceof MatrxApiError) {
1704
+ return {
1705
+ type: classifyStatus(err.status),
1706
+ message: honestTransportMessage(err.message, err.status),
1707
+ status: err.status,
1708
+ serverDetail: err.serverDetail,
1709
+ ...err.code ? { code: err.code } : {},
1710
+ name: err.name
1711
+ };
1712
+ }
1713
+ if (err instanceof BackendApiError) {
1714
+ return {
1715
+ type: "network_error",
1716
+ message: honestTransportMessage(
1717
+ err.detail || err.userMessage,
1718
+ err.status ?? void 0
1719
+ ),
1720
+ code: err.code,
1721
+ name: err.name,
1722
+ ...err.status !== null ? { status: err.status } : {},
1723
+ ...err.details !== null ? { serverDetail: err.details } : {}
1724
+ };
1725
+ }
1726
+ if ((0, import_data.isNetError)(err)) {
1727
+ if (err.code === "aborted") {
1728
+ return { type: "abort_error", message: err.message };
1729
+ }
1730
+ if (err.code === "http") {
1731
+ const status = err.status ?? 0;
1732
+ return {
1733
+ type: classifyStatus(status),
1734
+ message: honestTransportMessage(err.message, status),
1735
+ status
1736
+ };
1737
+ }
1738
+ return { type: "network_error", message: err.message };
1739
+ }
1740
+ if (err instanceof Error) {
1741
+ const httpMatch = err.message.match(/HTTP (\d+):\s*(.*)/);
1742
+ if (httpMatch) {
1743
+ const status = parseInt(httpMatch[1], 10);
1744
+ return {
1745
+ type: classifyStatus(status),
1746
+ message: honestTransportMessage(httpMatch[2] ?? err.message, status),
1747
+ status
1748
+ };
1749
+ }
1750
+ if (isBareTransportCode(err.message)) {
1751
+ const status = Number.parseInt(err.message.replace(/\D+/g, ""), 10);
1752
+ return {
1753
+ type: classifyStatus(status),
1754
+ message: bareStatusSentence(status),
1755
+ status
1756
+ };
1757
+ }
1758
+ return { type: "network_error", message: err.message };
1759
+ }
1760
+ return { type: "unknown", message: (0, import_net2.extractErrorMessage)(err) };
1691
1761
  }
1692
- function streamErrorText(eventOrPayload) {
1693
- if (!isRecord4(eventOrPayload)) return null;
1694
- const payload = typeof eventOrPayload.event === "string" && isRecord4(eventOrPayload.data) ? eventOrPayload.data : eventOrPayload;
1695
- return nonBlank(payload.user_message) ?? nonBlank(payload.message);
1762
+ var PRODUCTION_CONNECT_TIMEOUT_MS = 15e3;
1763
+ function stripContentType(headers) {
1764
+ return Object.fromEntries(
1765
+ Object.entries(headers).filter(
1766
+ ([name]) => name.toLowerCase() !== "content-type"
1767
+ )
1768
+ );
1696
1769
  }
1697
- async function openMatrxStream(transport, path, options = {}) {
1698
- const response = await requestStream(transport, path, {
1699
- method: "POST",
1700
- ...options.body !== void 0 ? { body: options.body } : {},
1701
- ...options.signal ? { signal: options.signal } : {}
1702
- });
1703
- return toRunHandle(response, options);
1770
+ function createMatrxTransport(options) {
1771
+ const {
1772
+ baseUrl,
1773
+ resolveTarget,
1774
+ credentials,
1775
+ organizationId,
1776
+ aiApiVersion,
1777
+ connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,
1778
+ totalTimeoutMs = null,
1779
+ expectedErrorStatuses,
1780
+ diagnostics,
1781
+ source = "matrxTransport"
1782
+ } = options;
1783
+ if (baseUrl === void 0 === (resolveTarget === void 0)) {
1784
+ throw new Error(
1785
+ "createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`."
1786
+ );
1787
+ }
1788
+ const resolve = resolveTarget ?? (() => ({ baseUrl }));
1789
+ const resolveVersion = () => {
1790
+ if (aiApiVersion === void 0) return MATRX_AI_API_VERSION_DEFAULT;
1791
+ return typeof aiApiVersion === "function" ? aiApiVersion() : aiApiVersion;
1792
+ };
1793
+ const resolveOrganizationId = () => {
1794
+ if (organizationId === void 0) return null;
1795
+ const candidate = typeof organizationId === "function" ? organizationId() : organizationId;
1796
+ return requireOrganizationContext(candidate);
1797
+ };
1798
+ return {
1799
+ async fetch(path, init) {
1800
+ const target = resolve();
1801
+ const versionedPath = applyAiApiVersion(path, resolveVersion());
1802
+ const url = `${target.baseUrl}${versionedPath}`;
1803
+ const credentialHeaders = credentials ? (0, import_data2.credentialToHeaders)(await credentials.get()) : {};
1804
+ let headers = {
1805
+ ...init.headers,
1806
+ ...stripContentType(credentialHeaders),
1807
+ ...stripContentType(target.policyHeaders ?? {})
1808
+ };
1809
+ const orgId = resolveOrganizationId();
1810
+ if (orgId !== null) {
1811
+ headers = applyOrganizationContextHeader(headers, orgId);
1812
+ }
1813
+ const info = {
1814
+ url,
1815
+ method: init.method,
1816
+ path,
1817
+ channel: target.channel ?? "default",
1818
+ source
1819
+ };
1820
+ diagnostics?.onRequest?.(info);
1821
+ let response;
1822
+ try {
1823
+ ({ response } = await fetchWithMatrxProtocolFallback(
1824
+ url,
1825
+ {
1826
+ method: init.method,
1827
+ headers,
1828
+ ...init.body !== void 0 ? { body: init.body } : {}
1829
+ },
1830
+ {
1831
+ ...init.signal ? { signal: init.signal } : {},
1832
+ connectTimeoutMs,
1833
+ totalTimeoutMs,
1834
+ throwOnHttpError: false,
1835
+ ...diagnostics?.onProtocolDowngrade ? { onDowngrade: diagnostics.onProtocolDowngrade } : {}
1836
+ }
1837
+ ));
1838
+ } catch (err) {
1839
+ diagnostics?.onError?.(normalizeMatrxError(err), info);
1840
+ throw err;
1841
+ }
1842
+ if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {
1843
+ const serverDetail = await response.clone().json().catch(() => void 0);
1844
+ const code = extractMatrxErrorCode(serverDetail);
1845
+ diagnostics?.onError?.(
1846
+ {
1847
+ type: classifyStatus(response.status),
1848
+ message: extractMatrxErrorMessage(serverDetail) ?? `HTTP ${response.status}`,
1849
+ status: response.status,
1850
+ serverDetail,
1851
+ ...code ? { code } : {}
1852
+ },
1853
+ info
1854
+ );
1855
+ }
1856
+ return response;
1857
+ }
1858
+ };
1704
1859
  }
1705
- function withRunOrganization(transport, organizationId) {
1706
- const orgId = requireOrganizationContext(organizationId);
1860
+
1861
+ // matrx/conversation.ts
1862
+ function mintMatrxConversationId() {
1863
+ return crypto.randomUUID();
1864
+ }
1865
+ function newStoredConversationStart(conversationId) {
1707
1866
  return {
1708
- fetch: (path, init) => transport.fetch(path, {
1709
- ...init,
1710
- headers: { ...init.headers, "X-Organization-Id": orgId }
1711
- })
1867
+ conversation_id: conversationId ?? mintMatrxConversationId(),
1868
+ is_new: true,
1869
+ store: true
1712
1870
  };
1713
1871
  }
1714
- async function followWorkflowRunEvents(transport, runId, options) {
1715
- const headers = { Accept: "text/event-stream" };
1716
- if (options.lastEventId) headers["Last-Event-ID"] = String(options.lastEventId);
1717
- let response;
1718
- try {
1719
- response = await requestStream(
1720
- transport,
1721
- `/runs/${encodePathSegment(runId)}/events/stream`,
1722
- {
1723
- method: "GET",
1724
- headers,
1725
- ...options.signal ? { signal: options.signal } : {}
1726
- }
1727
- );
1728
- } catch (error) {
1729
- if (options.signal?.aborted) return { ended: false };
1730
- throw error;
1731
- }
1732
- try {
1733
- for await (const frame of readMatrxSseStream(
1734
- response.body
1735
- )) {
1736
- if (frame.event === "end") return { ended: true };
1737
- if (frame.data === null) continue;
1738
- let parsed;
1739
- try {
1740
- parsed = JSON.parse(frame.data);
1741
- } catch {
1742
- continue;
1743
- }
1744
- if (!isRecord4(parsed)) continue;
1745
- options.onEvent(parsed, parsed.event === "node_stream" ? null : frame.seq);
1746
- }
1747
- } catch (error) {
1748
- if (!options.signal?.aborted) throw error;
1749
- }
1750
- return { ended: false };
1872
+ function continueStoredConversationStart(conversationId) {
1873
+ return { conversation_id: conversationId, is_new: false, store: true };
1751
1874
  }
1752
- async function drain(handle, onEnvelope) {
1753
- for await (const envelope of handle.events) onEnvelope(envelope);
1875
+ function newEphemeralConversationStart(conversationId) {
1876
+ return {
1877
+ conversation_id: conversationId ?? mintMatrxConversationId(),
1878
+ is_new: true,
1879
+ store: false
1880
+ };
1754
1881
  }
1755
- async function resumeOrRejoin(transport, resumeCall, options) {
1756
- const {
1757
- onEnvelope,
1758
- organizationId,
1759
- onRejoin,
1760
- onOperationEvent,
1761
- onWorkflowEvent,
1762
- follow,
1763
- ...rest
1764
- } = options;
1765
- const bound = organizationId ? withRunOrganization(transport, organizationId) : transport;
1766
- const streamOptions = {
1767
- ...rest,
1768
- ...options.signal ? { signal: options.signal } : {}
1882
+ function continueEphemeralConversationStart(conversationId, priorMessages) {
1883
+ return {
1884
+ conversation_id: conversationId,
1885
+ is_new: false,
1886
+ store: false,
1887
+ prior_messages: priorMessages
1769
1888
  };
1770
- const signalOnly = options.signal ? { signal: options.signal } : {};
1771
- let rejoin;
1772
- try {
1773
- const handle = await resumeCall(bound, streamOptions);
1774
- await drain(handle, onEnvelope);
1775
- return { kind: "resumed", requestId: handle.requestId };
1776
- } catch (error) {
1777
- const target = readLiveRunRejoin(error);
1778
- if (!target) {
1779
- if (error instanceof MatrxApiError && isResumeConflict(error)) {
1780
- return { kind: "resume_conflict", error };
1781
- }
1782
- throw error;
1889
+ }
1890
+
1891
+ // matrx/internal.ts
1892
+ function encodePathSegment(value) {
1893
+ return encodeURIComponent(value);
1894
+ }
1895
+ function buildQuery(params) {
1896
+ const search = new URLSearchParams();
1897
+ for (const [key, value] of Object.entries(params)) {
1898
+ if (value === void 0) continue;
1899
+ if (Array.isArray(value)) {
1900
+ for (const entry of value) search.append(key, entry);
1901
+ } else {
1902
+ search.append(key, String(value));
1783
1903
  }
1784
- rejoin = target;
1785
1904
  }
1786
- onRejoin?.(rejoin);
1787
- let unavailable;
1905
+ const encoded = search.toString();
1906
+ return encoded ? `?${encoded}` : "";
1907
+ }
1908
+ async function readServerDetail(response) {
1788
1909
  try {
1789
- const handle = await openMatrxStream(bound, rejoin.rejoinPath, {
1790
- ...streamOptions,
1791
- // The rejoin routes take no body model; the reference client posts {}.
1792
- body: {}
1793
- });
1794
- await drain(handle, onEnvelope);
1795
- return { kind: "rejoined", rejoin };
1796
- } catch (error) {
1797
- const read = readLiveStreamUnavailable(error);
1798
- if (!read) throw error;
1799
- unavailable = read;
1910
+ return await response.json();
1911
+ } catch {
1912
+ return void 0;
1800
1913
  }
1801
- return followUnavailableRejoin(bound, rejoin, unavailable, {
1802
- ...signalOnly,
1803
- ...onOperationEvent ? { onOperationEvent } : {},
1804
- ...onWorkflowEvent ? { onWorkflowEvent } : {},
1805
- ...follow ? { follow } : {}
1806
- });
1807
1914
  }
1808
- function readLiveStreamUnavailable(error) {
1809
- if (!isLiveStreamUnavailable(error)) return null;
1810
- return { runId: firstString(detailCandidates(bodyOf(error)), "run_id") };
1915
+ async function throwApiError(path, response) {
1916
+ throw new MatrxApiError({
1917
+ status: response.status,
1918
+ path,
1919
+ serverDetail: await readServerDetail(response)
1920
+ });
1811
1921
  }
1812
- async function followUnavailableRejoin(transport, rejoin, unavailable, options = {}) {
1813
- const bound = options.organizationId ? withRunOrganization(transport, options.organizationId) : transport;
1814
- const signalOnly = options.signal ? { signal: options.signal } : {};
1815
- const runId = unavailable.runId ?? rejoin.runId;
1816
- if (runId && rejoin.rejoinPath.startsWith("/runtime/operations/")) {
1817
- const result2 = await followWorkflowRunEvents(bound, runId, {
1818
- ...signalOnly,
1819
- onEvent: (event, seq) => options.onWorkflowEvent?.(event, seq)
1820
- });
1821
- return { kind: "followed_workflow", rejoin, runId, ended: result2.ended };
1822
- }
1823
- const liveRequestId = rejoin.liveRequestId;
1824
- const statusView = liveRequestId ? await getRuntimeOperationStatus(bound, liveRequestId, signalOnly) : null;
1825
- const operation = statusView?.operations.find((op) => !op.is_terminal) ?? statusView?.operations[0] ?? null;
1826
- if (!operation) {
1827
- return { kind: "followed", rejoin, executionId: null, ended: false, status: null };
1828
- }
1829
- if (operation.is_terminal) {
1830
- return {
1831
- kind: "followed",
1832
- rejoin,
1833
- executionId: operation.execution_id,
1834
- ended: true,
1835
- status: operation.status
1836
- };
1837
- }
1838
- const result = await followRuntimeOperationToEnd(bound, operation.execution_id, {
1839
- ...options.follow,
1840
- lastEventSeq: operation.last_event_seq,
1841
- ...signalOnly,
1842
- onEvent: (event) => options.onOperationEvent?.(event)
1922
+ async function requestJson(transport, path, options) {
1923
+ const hasBody = options.method !== "GET" && options.body !== void 0;
1924
+ const response = await transport.fetch(path, {
1925
+ method: options.method,
1926
+ headers: hasBody ? { "Content-Type": "application/json" } : {},
1927
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
1928
+ ...options.signal ? { signal: options.signal } : {}
1843
1929
  });
1930
+ if (!response.ok) return throwApiError(path, response);
1931
+ return await response.json();
1932
+ }
1933
+ function toRunHandle(response, options) {
1844
1934
  return {
1845
- kind: "followed",
1846
- rejoin,
1847
- executionId: operation.execution_id,
1848
- ended: result.ended,
1849
- status: result.status
1935
+ requestId: response.headers.get("X-Request-ID"),
1936
+ conversationId: response.headers.get("X-Conversation-ID"),
1937
+ events: readMatrxNdjsonStream(response.body, {
1938
+ ...options.signal ? { signal: options.signal } : {},
1939
+ ...options.maxReadAhead !== void 0 ? { maxReadAhead: options.maxReadAhead } : {},
1940
+ ...options.onMalformedLine ? { onMalformedLine: options.onMalformedLine } : {},
1941
+ ...options.onUnknownEnvelope ? { onUnknownEnvelope: options.onUnknownEnvelope } : {},
1942
+ ...options.onValidEnvelope ? { onValidEnvelope: options.onValidEnvelope } : {}
1943
+ }),
1944
+ response
1850
1945
  };
1851
1946
  }
1852
- async function attempt(run) {
1853
- try {
1854
- await run();
1855
- return { reloaded: true };
1856
- } catch (error) {
1857
- return { reloaded: false, error };
1858
- }
1859
- }
1860
- async function settleRunPickup(pickup, options) {
1861
- switch (pickup.kind) {
1862
- case "resumed":
1863
- case "rejoined":
1864
- return { state: "streamed" };
1865
- case "resume_conflict":
1866
- return { state: "retry" };
1867
- case "followed_workflow":
1868
- case "followed": {
1869
- const status = pickup.kind === "followed" ? pickup.status : null;
1870
- const nothingLive = pickup.kind === "followed" && pickup.executionId === null;
1871
- if (pickup.ended || nothingLive) {
1872
- return { state: "settled", status, ...await attempt(options.reloadSavedTurn) };
1873
- }
1874
- return {
1875
- state: "still_running",
1876
- ...await attempt(options.onStillRunning ?? options.reloadSavedTurn)
1877
- };
1878
- }
1879
- }
1880
- }
1881
-
1882
- // matrx/run.ts
1883
- async function streamCall(transport, path, body, options) {
1884
- const response = await requestStream(transport, path, {
1885
- method: "POST",
1886
- // This client IS the streaming path — `stream: true` always, last so a
1887
- // caller-supplied value can never flip the response off NDJSON.
1888
- body: { ...body, stream: true },
1947
+ async function requestStream(transport, path, options) {
1948
+ const hasBody = options.method !== "GET" && options.body !== void 0;
1949
+ const response = await transport.fetch(path, {
1950
+ method: options.method,
1951
+ headers: {
1952
+ ...hasBody ? { "Content-Type": "application/json" } : {},
1953
+ ...options.headers
1954
+ },
1955
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
1889
1956
  ...options.signal ? { signal: options.signal } : {}
1890
1957
  });
1891
- return toRunHandle(response, options);
1958
+ if (!response.ok) return throwApiError(path, response);
1959
+ if (!response.body) {
1960
+ throw new MatrxApiError({
1961
+ status: response.status,
1962
+ path,
1963
+ serverDetail: { code: "missing_response_body" },
1964
+ message: "The streaming response carried no body."
1965
+ });
1966
+ }
1967
+ return response;
1892
1968
  }
1893
- function startAgentRun(transport, agentId, request, options = {}) {
1894
- return streamCall(
1895
- transport,
1896
- `/ai/agents/${encodePathSegment(agentId)}`,
1897
- request,
1898
- options
1899
- );
1969
+
1970
+ // matrx/operations.ts
1971
+ var TERMINAL_MATRX_RUNTIME_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
1972
+ var RUNTIME_STATUSES = /* @__PURE__ */ new Set([
1973
+ "pending",
1974
+ "running",
1975
+ "paused",
1976
+ "waiting_input",
1977
+ "completed",
1978
+ "failed",
1979
+ "cancelled"
1980
+ ]);
1981
+ async function getRuntimeOperationStatus(transport, requestId, options = {}) {
1982
+ try {
1983
+ return await requestJson(
1984
+ transport,
1985
+ `/runtime/operations/${encodePathSegment(requestId)}`,
1986
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1987
+ );
1988
+ } catch (error) {
1989
+ if (error instanceof MatrxApiError && error.status === 404) return null;
1990
+ throw error;
1991
+ }
1900
1992
  }
1901
- function startMandateRun(transport, mandateKey, request, options = {}) {
1902
- return streamCall(
1903
- transport,
1904
- `/ai/mandates/${encodePathSegment(mandateKey)}`,
1905
- request,
1906
- options
1993
+ async function getRuntimeOperationsByLink(transport, linkKind, linkId, options = {}) {
1994
+ const query = buildQuery(
1995
+ options.limit !== void 0 ? { limit: options.limit } : {}
1907
1996
  );
1997
+ try {
1998
+ return await requestJson(
1999
+ transport,
2000
+ `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,
2001
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
2002
+ );
2003
+ } catch (error) {
2004
+ if (error instanceof MatrxApiError && error.status === 404) return null;
2005
+ throw error;
2006
+ }
1908
2007
  }
1909
- function continueAgentConversation(transport, conversationId, request, options = {}) {
1910
- return streamCall(
2008
+ function listRuntimeOperationEvents(transport, executionId, options = {}) {
2009
+ const query = buildQuery({
2010
+ ...options.afterSeq !== void 0 ? { after_seq: options.afterSeq } : {},
2011
+ ...options.limit !== void 0 ? { limit: options.limit } : {},
2012
+ ...options.kinds !== void 0 ? { kind: options.kinds } : {}
2013
+ });
2014
+ return requestJson(
1911
2015
  transport,
1912
- `/ai/conversations/${encodePathSegment(conversationId)}`,
1913
- request,
1914
- options
2016
+ `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,
2017
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1915
2018
  );
1916
2019
  }
1917
- function resumeAgentConversation(transport, conversationId, request = {}, options = {}) {
1918
- return streamCall(
1919
- transport,
1920
- `/ai/conversations/${encodePathSegment(conversationId)}/resume`,
1921
- request,
1922
- options
1923
- );
2020
+ function parseEndStatus(data) {
2021
+ if (data === null) return null;
2022
+ try {
2023
+ const parsed = JSON.parse(data);
2024
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.status === "string") {
2025
+ const status = parsed.status;
2026
+ return RUNTIME_STATUSES.has(status) ? status : null;
2027
+ }
2028
+ } catch {
2029
+ }
2030
+ return null;
1924
2031
  }
1925
- function cancelAgentRun(transport, requestId, options = {}) {
1926
- const query = buildQuery(
1927
- options.mode === "interrupt" ? { mode: "interrupt" } : {}
1928
- );
1929
- return requestJson(
2032
+ async function* followRuntimeOperationEvents(transport, executionId, options = {}) {
2033
+ let cursor = options.lastEventSeq ?? 0;
2034
+ const headers = { Accept: "text/event-stream" };
2035
+ if (cursor > 0) headers["Last-Event-ID"] = String(cursor);
2036
+ const response = await requestStream(
1930
2037
  transport,
1931
- `/ai/cancel/${encodePathSegment(requestId)}${query}`,
2038
+ `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,
1932
2039
  {
1933
- method: "POST",
2040
+ method: "GET",
2041
+ headers,
1934
2042
  ...options.signal ? { signal: options.signal } : {}
1935
2043
  }
1936
2044
  );
1937
- }
1938
- var MatrxRunError = class extends Error {
1939
- name = "MatrxRunError";
1940
- /** The verbatim `error` event payload, when one fired. */
1941
- errorPayload;
1942
- /** The `user_request` completion status (`"failed"` | `"cancelled"`), when that was the trigger. */
1943
- completionStatus;
1944
- /** Text streamed before the failure — partial content never vanishes. */
1945
- partialText;
1946
- constructor(args) {
1947
- super(args.message);
1948
- this.errorPayload = args.errorPayload ?? null;
1949
- this.completionStatus = args.completionStatus ?? null;
1950
- this.partialText = args.partialText ?? "";
1951
- }
1952
- };
1953
- function isRecord5(value) {
1954
- return typeof value === "object" && value !== null && !Array.isArray(value);
1955
- }
1956
- function stringField(value, key) {
1957
- if (!isRecord5(value)) return null;
1958
- const field = value[key];
1959
- return typeof field === "string" && field ? field : null;
1960
- }
1961
- async function runAgentToCompletion(transport, agentId, request, options = {}) {
1962
- const handle = await startAgentRun(transport, agentId, request, options);
1963
- let text = "";
1964
- let completion = null;
1965
- let failure = null;
1966
- for await (const envelope of handle.events) {
1967
- options.onEvent?.(envelope);
1968
- if (envelope.event === "chunk") {
1969
- const chunk = stringField(envelope.data, "text");
1970
- if (chunk !== null) {
1971
- text += chunk;
1972
- options.onChunk?.(text);
1973
- }
1974
- continue;
2045
+ const frames = readMatrxSseStream(
2046
+ response.body,
2047
+ options.onIncomplete ? { onIncomplete: options.onIncomplete } : {}
2048
+ );
2049
+ for await (const frame of frames) {
2050
+ if (frame.event === "end") {
2051
+ yield { type: "end", status: parseEndStatus(frame.data), cursor };
2052
+ return;
1975
2053
  }
1976
- if (envelope.event === "error" && failure === null) {
1977
- const payload = isRecord5(envelope.data) ? envelope.data : null;
1978
- failure = new MatrxRunError({
1979
- message: streamErrorText(payload) ?? "The agent run failed",
1980
- errorPayload: payload,
1981
- partialText: text
1982
- });
2054
+ if (frame.event === "execution_event" && frame.data !== null) {
2055
+ let event;
2056
+ try {
2057
+ event = JSON.parse(frame.data);
2058
+ } catch (error) {
2059
+ options.onMalformedFrame?.(frame, error);
2060
+ yield { type: "liveness", cursor };
2061
+ continue;
2062
+ }
2063
+ if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;
2064
+ yield { type: "event", event, seq: frame.seq, cursor };
1983
2065
  continue;
1984
2066
  }
1985
- if (envelope.event !== "completion" || !isRecord5(envelope.data)) continue;
1986
- if (envelope.data.operation !== "user_request") continue;
1987
- completion = envelope.data;
1988
- const status = envelope.data.status;
1989
- if ((status === "failed" || status === "cancelled") && failure === null) {
1990
- const result = isRecord5(envelope.data.result) ? envelope.data.result : null;
1991
- failure = new MatrxRunError({
1992
- message: stringField(result, "error") ?? stringField(result, "user_message") ?? `The agent run ${status}`,
1993
- completionStatus: status,
1994
- partialText: text
1995
- });
1996
- }
1997
- }
1998
- if (failure) throw failure;
1999
- if (!text && completion) {
2000
- const result = completion.result;
2001
- const output = stringField(result, "output");
2002
- if (output !== null) text = output;
2003
- }
2004
- return {
2005
- text,
2006
- requestId: handle.requestId,
2007
- conversationId: handle.conversationId,
2008
- completion
2009
- };
2010
- }
2011
-
2012
- // matrx/provider-failures.ts
2013
- var clip = (value, max) => value.length > max ? value.slice(0, max) : value;
2014
- function providerSessionFailureBody(failure) {
2015
- const message = failure.message?.trim() || failure.error_type?.trim() || (failure.status_code ? `HTTP ${failure.status_code}` : "provider session failed");
2016
- const body = {
2017
- provider: failure.provider,
2018
- message: clip(message, 4e3)
2019
- };
2020
- if (failure.model?.trim()) body.model = clip(failure.model.trim(), 200);
2021
- if (failure.error_type?.trim()) body.error_type = clip(failure.error_type.trim(), 200);
2022
- if (typeof failure.status_code === "number" && Number.isInteger(failure.status_code) && failure.status_code >= 100 && failure.status_code <= 599) {
2023
- body.status_code = failure.status_code;
2067
+ yield { type: "liveness", cursor };
2024
2068
  }
2025
- return body;
2026
2069
  }
2027
- async function reportProviderSessionFailure(transport, failure, options = {}) {
2028
- try {
2029
- return await requestJson(
2030
- transport,
2031
- "/broker/provider-failures",
2032
- {
2033
- method: "POST",
2034
- body: providerSessionFailureBody(failure),
2035
- ...options.signal ? { signal: options.signal } : {}
2070
+ async function followRuntimeOperationToEnd(transport, executionId, options) {
2071
+ const stallTimeoutMs = options.stallTimeoutMs ?? 45e3;
2072
+ const reconnectLimit = options.reconnectLimit ?? 60;
2073
+ const reconnectDelayMs = options.reconnectDelayMs ?? 2e3;
2074
+ const outer = options.signal;
2075
+ let cursor = options.lastEventSeq ?? 0;
2076
+ let failures = 0;
2077
+ while (!outer?.aborted && failures < reconnectLimit) {
2078
+ const attempt2 = new AbortController();
2079
+ const onOuterAbort = () => attempt2.abort();
2080
+ outer?.addEventListener("abort", onOuterAbort, { once: true });
2081
+ let stallTimer = null;
2082
+ const armStall = () => {
2083
+ if (stallTimer !== null) clearTimeout(stallTimer);
2084
+ stallTimer = setTimeout(() => attempt2.abort(), stallTimeoutMs);
2085
+ };
2086
+ try {
2087
+ const items = followRuntimeOperationEvents(transport, executionId, {
2088
+ lastEventSeq: cursor,
2089
+ signal: attempt2.signal,
2090
+ ...options.onMalformedFrame ? { onMalformedFrame: options.onMalformedFrame } : {}
2091
+ });
2092
+ armStall();
2093
+ for await (const item of items) {
2094
+ armStall();
2095
+ failures = 0;
2096
+ cursor = item.cursor;
2097
+ if (item.type === "end") {
2098
+ return { ended: true, status: item.status };
2099
+ }
2100
+ if (item.type === "event") {
2101
+ options.onEvent(item.event, item.seq);
2102
+ }
2036
2103
  }
2037
- );
2038
- } catch (error) {
2039
- console.warn(
2040
- `[provider-failures] could not report a ${failure.provider} session failure`,
2041
- error
2042
- );
2043
- return null;
2104
+ failures += 1;
2105
+ } catch (error) {
2106
+ if (outer?.aborted) break;
2107
+ failures += 1;
2108
+ options.onAttemptError?.(error);
2109
+ } finally {
2110
+ if (stallTimer !== null) clearTimeout(stallTimer);
2111
+ outer?.removeEventListener("abort", onOuterAbort);
2112
+ }
2113
+ if (!outer?.aborted && failures < reconnectLimit) {
2114
+ await new Promise((r) => setTimeout(r, reconnectDelayMs));
2115
+ }
2044
2116
  }
2117
+ return { ended: false, status: null };
2045
2118
  }
2046
-
2047
- // matrx/tools.ts
2048
- function submitAgentToolResults(transport, conversationId, results, options = {}) {
2049
- return requestJson(
2119
+ async function rejoinRuntimeOperation(transport, requestId, options = {}) {
2120
+ const response = await requestStream(
2050
2121
  transport,
2051
- `/ai/conversations/${encodePathSegment(conversationId)}/tool_results`,
2122
+ `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,
2052
2123
  {
2053
2124
  method: "POST",
2054
- body: {
2055
- results,
2056
- ...options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
2057
- },
2125
+ // The route takes no body model; the reference client posts an empty
2126
+ // JSON object. Match it so proxies see an ordinary JSON POST.
2127
+ body: {},
2058
2128
  ...options.signal ? { signal: options.signal } : {}
2059
2129
  }
2060
2130
  );
2131
+ return toRunHandle(response, options);
2061
2132
  }
2062
- function listConversationPendingToolCalls(transport, conversationId, options = {}) {
2063
- return requestJson(
2064
- transport,
2065
- `/ai/conversations/${encodePathSegment(conversationId)}/pending_calls`,
2066
- {
2067
- method: "GET",
2068
- ...options.signal ? { signal: options.signal } : {}
2069
- }
2070
- );
2133
+
2134
+ // matrx/rejoin.ts
2135
+ var MATRX_RUN_IN_PROGRESS = "run_in_progress";
2136
+ var MATRX_RESUME_CONFLICT = "resume_conflict";
2137
+ var MATRX_LIVE_STREAM_UNAVAILABLE = "live_stream_unavailable";
2138
+ function isRecord4(value) {
2139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2071
2140
  }
2072
- function listUserPendingToolCalls(transport, options = {}) {
2073
- const query = buildQuery(
2074
- options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
2075
- );
2076
- return requestJson(
2077
- transport,
2078
- `/ai/user/pending_calls${query}`,
2079
- {
2080
- method: "GET",
2081
- ...options.signal ? { signal: options.signal } : {}
2082
- }
2083
- );
2141
+ function nonBlank(value) {
2142
+ return typeof value === "string" && value.trim() ? value : null;
2084
2143
  }
2085
-
2086
- // matrx/backend-errors.ts
2087
- var BackendApiError = class extends Error {
2088
- /** Machine-readable error code */
2089
- code;
2090
- /** Developer-facing detail */
2091
- detail;
2092
- /** Safe to display directly in the UI */
2093
- userMessage;
2094
- /** Extra info (validation errors, etc.) */
2095
- details;
2096
- /** Unique request ID for support/debugging */
2097
- requestId;
2098
- /** HTTP status code (if from an HTTP response) */
2099
- status;
2100
- constructor(data) {
2101
- super(data.userMessage);
2102
- this.name = "BackendApiError";
2103
- this.code = data.code;
2104
- this.detail = data.detail;
2105
- this.userMessage = data.userMessage;
2106
- this.details = data.details ?? null;
2107
- this.requestId = data.requestId ?? "";
2108
- this.status = data.status ?? null;
2144
+ function detailCandidates(body) {
2145
+ if (!isRecord4(body)) return [];
2146
+ const out = [];
2147
+ if (isRecord4(body.detail)) out.push(body.detail);
2148
+ if (isRecord4(body.details)) out.push(body.details);
2149
+ out.push(body);
2150
+ return out;
2151
+ }
2152
+ function statusOf(error) {
2153
+ if (!isRecord4(error) && !(error instanceof Error)) return null;
2154
+ const status = error.status;
2155
+ return typeof status === "number" ? status : null;
2156
+ }
2157
+ function bodyOf(error) {
2158
+ if (!isRecord4(error) && !(error instanceof Error)) return void 0;
2159
+ return error.serverDetail;
2160
+ }
2161
+ function readMatrxErrorCode(error) {
2162
+ for (const candidate of detailCandidates(bodyOf(error))) {
2163
+ const code = nonBlank(candidate.code);
2164
+ if (code) return code;
2109
2165
  }
2110
- /** Convert to the wire format for logging */
2111
- toJSON() {
2112
- return {
2113
- error: this.code,
2114
- message: this.detail,
2115
- user_message: this.userMessage,
2116
- details: this.details,
2117
- request_id: this.requestId
2118
- };
2166
+ for (const candidate of detailCandidates(bodyOf(error))) {
2167
+ const code = nonBlank(candidate.error);
2168
+ if (code) return code;
2119
2169
  }
2120
- };
2121
- var StreamTransportError = class extends BackendApiError {
2122
- /** Always true — reattach and let the server settle the outcome. */
2123
- resumable = true;
2124
- constructor(data) {
2125
- super({
2126
- code: "stream_transport_lost",
2127
- detail: data.detail,
2128
- userMessage: "The connection dropped. Your run is still going on the server \u2014 reconnecting to it now.",
2129
- details: data.details ?? null,
2130
- ...data.requestId !== void 0 ? { requestId: data.requestId } : {}
2131
- });
2132
- this.name = "StreamTransportError";
2170
+ return null;
2171
+ }
2172
+ var PREFIXED_ERROR_CODES = [
2173
+ "resume_conflict",
2174
+ "not_resumable",
2175
+ "outstanding_delegated_calls",
2176
+ "user_request_not_found",
2177
+ "run_in_flight"
2178
+ ];
2179
+ function readMatrxErrorCodeFromMessage(message) {
2180
+ if (typeof message !== "string") return null;
2181
+ for (const code of PREFIXED_ERROR_CODES) {
2182
+ if (message.startsWith(`${code}:`)) return code;
2133
2183
  }
2134
- };
2135
- function isStreamTransportLost(error) {
2136
- if (error instanceof StreamTransportError) return true;
2137
- if (error instanceof BackendApiError) {
2138
- return error.code === "stream_transport_lost";
2184
+ return null;
2185
+ }
2186
+ async function readConversationOrganizationId(conversationId, read, onUnreadable) {
2187
+ try {
2188
+ const value = await read(conversationId);
2189
+ if (typeof value === "string" && value.trim()) return value;
2190
+ onUnreadable?.(null);
2191
+ } catch (error) {
2192
+ onUnreadable?.(error instanceof Error ? error.message : String(error));
2139
2193
  }
2140
- if (error && typeof error === "object") {
2141
- const code = error.code;
2142
- if (code === "stream_transport_lost") return true;
2143
- const errorType = error.error_type;
2144
- if (errorType === "transport_lost") return true;
2194
+ return null;
2195
+ }
2196
+ function firstString(candidates, key) {
2197
+ for (const candidate of candidates) {
2198
+ const value = nonBlank(candidate[key]);
2199
+ if (value) return value.trim();
2145
2200
  }
2146
- return false;
2201
+ return null;
2147
2202
  }
2148
- async function parseHttpError(response) {
2149
- const status = response.status;
2150
- let body = null;
2203
+ function readLiveRunRejoin(error) {
2204
+ if (statusOf(error) !== 409) return null;
2205
+ const candidates = detailCandidates(bodyOf(error));
2206
+ if (candidates.length === 0) return null;
2207
+ const code = readMatrxErrorCode(error);
2208
+ if (code !== MATRX_RUN_IN_PROGRESS) return null;
2209
+ const liveRequestId = firstString(candidates, "live_request_id");
2210
+ const pathFromBody = firstString(candidates, "rejoin_path");
2211
+ const rejoinPath = pathFromBody && pathFromBody.startsWith("/") ? pathFromBody : liveRequestId ? runtimeOperationRejoinPath(liveRequestId) : null;
2212
+ if (!rejoinPath) return null;
2213
+ const root = bodyOf(error);
2214
+ return {
2215
+ liveRequestId,
2216
+ rejoinPath,
2217
+ runId: firstString(candidates, "run_id"),
2218
+ code,
2219
+ body: isRecord4(root) ? root : {}
2220
+ };
2221
+ }
2222
+ function readLiveRunRequestId(error) {
2223
+ return readLiveRunRejoin(error)?.liveRequestId ?? null;
2224
+ }
2225
+ function isResumeConflict(error) {
2226
+ return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT;
2227
+ }
2228
+ function isLiveStreamUnavailable(error) {
2229
+ return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_LIVE_STREAM_UNAVAILABLE;
2230
+ }
2231
+ function runtimeOperationRejoinPath(requestId) {
2232
+ return `/runtime/operations/${encodePathSegment(requestId)}/rejoin`;
2233
+ }
2234
+ function streamErrorText(eventOrPayload) {
2235
+ if (!isRecord4(eventOrPayload)) return null;
2236
+ const payload = typeof eventOrPayload.event === "string" && isRecord4(eventOrPayload.data) ? eventOrPayload.data : eventOrPayload;
2237
+ return nonBlank(payload.user_message) ?? nonBlank(payload.message);
2238
+ }
2239
+ async function openMatrxStream(transport, path, options = {}) {
2240
+ const response = await requestStream(transport, path, {
2241
+ method: "POST",
2242
+ ...options.body !== void 0 ? { body: options.body } : {},
2243
+ ...options.signal ? { signal: options.signal } : {}
2244
+ });
2245
+ return toRunHandle(response, options);
2246
+ }
2247
+ function withRunOrganization(transport, organizationId) {
2248
+ const orgId = requireOrganizationContext(organizationId);
2249
+ return {
2250
+ fetch: (path, init) => transport.fetch(path, {
2251
+ ...init,
2252
+ headers: { ...init.headers, "X-Organization-Id": orgId }
2253
+ })
2254
+ };
2255
+ }
2256
+ async function followWorkflowRunEvents(transport, runId, options) {
2257
+ const headers = { Accept: "text/event-stream" };
2258
+ if (options.lastEventId) headers["Last-Event-ID"] = String(options.lastEventId);
2259
+ let response;
2151
2260
  try {
2152
- body = await response.json();
2153
- } catch {
2154
- try {
2155
- const text = await response.text();
2156
- return new BackendApiError({
2157
- code: statusToCode(status),
2158
- detail: text || `HTTP ${status}`,
2159
- userMessage: text || `Request failed (${status})`,
2160
- status
2161
- });
2162
- } catch {
2163
- return new BackendApiError({
2164
- code: statusToCode(status),
2165
- detail: `HTTP ${status}`,
2166
- userMessage: `Request failed (${status})`,
2167
- status
2168
- });
2261
+ response = await requestStream(
2262
+ transport,
2263
+ `/runs/${encodePathSegment(runId)}/events/stream`,
2264
+ {
2265
+ method: "GET",
2266
+ headers,
2267
+ ...options.signal ? { signal: options.signal } : {}
2268
+ }
2269
+ );
2270
+ } catch (error) {
2271
+ if (options.signal?.aborted) return { ended: false };
2272
+ throw error;
2273
+ }
2274
+ try {
2275
+ for await (const frame of readMatrxSseStream(
2276
+ response.body
2277
+ )) {
2278
+ if (frame.event === "end") return { ended: true };
2279
+ if (frame.data === null) continue;
2280
+ let parsed;
2281
+ try {
2282
+ parsed = JSON.parse(frame.data);
2283
+ } catch {
2284
+ continue;
2285
+ }
2286
+ if (!isRecord4(parsed)) continue;
2287
+ options.onEvent(parsed, parsed.event === "node_stream" ? null : frame.seq);
2169
2288
  }
2289
+ } catch (error) {
2290
+ if (!options.signal?.aborted) throw error;
2170
2291
  }
2171
- return parseHttpErrorBody(body, status);
2292
+ return { ended: false };
2172
2293
  }
2173
- function parseHttpErrorBody(body, status) {
2174
- if (!body) {
2175
- return new BackendApiError({
2176
- code: statusToCode(status),
2177
- detail: `HTTP ${status}`,
2178
- userMessage: `Request failed (${status})`,
2179
- status
2180
- });
2294
+ async function drain(handle, onEnvelope) {
2295
+ for await (const envelope of handle.events) onEnvelope(envelope);
2296
+ }
2297
+ async function resumeOrRejoin(transport, resumeCall, options) {
2298
+ const {
2299
+ onEnvelope,
2300
+ organizationId,
2301
+ onRejoin,
2302
+ onOperationEvent,
2303
+ onWorkflowEvent,
2304
+ follow,
2305
+ ...rest
2306
+ } = options;
2307
+ const bound = organizationId ? withRunOrganization(transport, organizationId) : transport;
2308
+ const streamOptions = {
2309
+ ...rest,
2310
+ ...options.signal ? { signal: options.signal } : {}
2311
+ };
2312
+ const signalOnly = options.signal ? { signal: options.signal } : {};
2313
+ let rejoin;
2314
+ try {
2315
+ const handle = await resumeCall(bound, streamOptions);
2316
+ await drain(handle, onEnvelope);
2317
+ return { kind: "resumed", requestId: handle.requestId };
2318
+ } catch (error) {
2319
+ const target = readLiveRunRejoin(error);
2320
+ if (!target) {
2321
+ if (error instanceof MatrxApiError && isResumeConflict(error)) {
2322
+ return { kind: "resume_conflict", error };
2323
+ }
2324
+ throw error;
2325
+ }
2326
+ rejoin = target;
2181
2327
  }
2182
- if (typeof body.error === "string" && typeof body.user_message === "string") {
2183
- return new BackendApiError({
2184
- code: body.error,
2185
- detail: body.message || `HTTP ${status}`,
2186
- userMessage: body.user_message,
2187
- details: body.details ?? null,
2188
- requestId: typeof body.request_id === "string" ? body.request_id : void 0,
2189
- status
2328
+ onRejoin?.(rejoin);
2329
+ let unavailable;
2330
+ try {
2331
+ const handle = await openMatrxStream(bound, rejoin.rejoinPath, {
2332
+ ...streamOptions,
2333
+ // The rejoin routes take no body model; the reference client posts {}.
2334
+ body: {}
2190
2335
  });
2336
+ await drain(handle, onEnvelope);
2337
+ return { kind: "rejoined", rejoin };
2338
+ } catch (error) {
2339
+ const read = readLiveStreamUnavailable(error);
2340
+ if (!read) throw error;
2341
+ unavailable = read;
2191
2342
  }
2192
- if (typeof body.error === "object" && body.error !== null) {
2193
- const errorObj = body.error;
2194
- return new BackendApiError({
2195
- code: errorObj.type || errorObj.error || statusToCode(status),
2196
- detail: errorObj.message || `HTTP ${status}`,
2197
- userMessage: errorObj.user_message || errorObj.user_visible_message || errorObj.message || `Request failed (${status})`,
2198
- details: errorObj.details ?? null,
2199
- requestId: typeof errorObj.request_id === "string" ? errorObj.request_id : void 0,
2200
- status
2343
+ return followUnavailableRejoin(bound, rejoin, unavailable, {
2344
+ ...signalOnly,
2345
+ ...onOperationEvent ? { onOperationEvent } : {},
2346
+ ...onWorkflowEvent ? { onWorkflowEvent } : {},
2347
+ ...follow ? { follow } : {}
2348
+ });
2349
+ }
2350
+ function readLiveStreamUnavailable(error) {
2351
+ if (!isLiveStreamUnavailable(error)) return null;
2352
+ return { runId: firstString(detailCandidates(bodyOf(error)), "run_id") };
2353
+ }
2354
+ async function followUnavailableRejoin(transport, rejoin, unavailable, options = {}) {
2355
+ const bound = options.organizationId ? withRunOrganization(transport, options.organizationId) : transport;
2356
+ const signalOnly = options.signal ? { signal: options.signal } : {};
2357
+ const runId = unavailable.runId ?? rejoin.runId;
2358
+ if (runId && rejoin.rejoinPath.startsWith("/runtime/operations/")) {
2359
+ const result2 = await followWorkflowRunEvents(bound, runId, {
2360
+ ...signalOnly,
2361
+ onEvent: (event, seq) => options.onWorkflowEvent?.(event, seq)
2201
2362
  });
2363
+ return { kind: "followed_workflow", rejoin, runId, ended: result2.ended };
2202
2364
  }
2203
- if (Array.isArray(body.detail)) {
2204
- const first = body.detail[0];
2205
- const firstMsg = first && typeof first.msg === "string" ? first.msg : null;
2206
- return new BackendApiError({
2207
- code: statusToCode(status),
2208
- detail: firstMsg ? `Validation error: ${firstMsg}` : JSON.stringify(body.detail),
2209
- userMessage: firstMsg || `Request failed (${status})`,
2210
- details: body.detail,
2211
- status
2212
- });
2365
+ const liveRequestId = rejoin.liveRequestId;
2366
+ const statusView = liveRequestId ? await getRuntimeOperationStatus(bound, liveRequestId, signalOnly) : null;
2367
+ const operation = statusView?.operations.find((op) => !op.is_terminal) ?? statusView?.operations[0] ?? null;
2368
+ if (!operation) {
2369
+ return { kind: "followed", rejoin, executionId: null, ended: false, status: null };
2213
2370
  }
2214
- if (typeof body.detail === "object" && body.detail !== null && !Array.isArray(body.detail)) {
2215
- const d = body.detail;
2216
- const code = typeof d.code === "string" && d.code || typeof d.error === "string" && d.error || statusToCode(status);
2217
- const message = typeof d.message === "string" && d.message || typeof d.detail === "string" && d.detail || `HTTP ${status}`;
2218
- return new BackendApiError({
2219
- code,
2220
- detail: message,
2221
- userMessage: typeof d.user_message === "string" && d.user_message || typeof d.user_visible_message === "string" && d.user_visible_message || message,
2222
- details: d.details ?? d,
2223
- requestId: typeof d.request_id === "string" ? d.request_id : void 0,
2224
- status
2225
- });
2371
+ if (operation.is_terminal) {
2372
+ return {
2373
+ kind: "followed",
2374
+ rejoin,
2375
+ executionId: operation.execution_id,
2376
+ ended: true,
2377
+ status: operation.status
2378
+ };
2226
2379
  }
2227
- return new BackendApiError({
2228
- code: typeof body.error === "string" ? body.error : statusToCode(status),
2229
- detail: body.message || body.detail || body.error || `HTTP ${status}`,
2230
- userMessage: body.user_message || body.user_visible_message || body.message || body.detail || `Request failed (${status})`,
2231
- details: body.details ?? null,
2232
- requestId: typeof body.request_id === "string" ? body.request_id : void 0,
2233
- status
2380
+ const result = await followRuntimeOperationToEnd(bound, operation.execution_id, {
2381
+ ...options.follow,
2382
+ lastEventSeq: operation.last_event_seq,
2383
+ ...signalOnly,
2384
+ onEvent: (event) => options.onOperationEvent?.(event)
2234
2385
  });
2386
+ return {
2387
+ kind: "followed",
2388
+ rejoin,
2389
+ executionId: operation.execution_id,
2390
+ ended: result.ended,
2391
+ status: result.status
2392
+ };
2235
2393
  }
2236
- function parseCallApiError(error) {
2237
- const status = error.status ?? 500;
2238
- const body = error.serverDetail && typeof error.serverDetail === "object" && !Array.isArray(error.serverDetail) ? error.serverDetail : { message: error.message };
2239
- return parseHttpErrorBody(body, status);
2394
+ async function attempt(run) {
2395
+ try {
2396
+ await run();
2397
+ return { reloaded: true };
2398
+ } catch (error) {
2399
+ return { reloaded: false, error };
2400
+ }
2240
2401
  }
2241
- function parseStreamError(data) {
2242
- if (!data || typeof data !== "object") {
2243
- return new BackendApiError({
2244
- code: "internal_error",
2245
- detail: typeof data === "string" ? data : "Unknown streaming error",
2246
- userMessage: typeof data === "string" ? data : "Something went wrong"
2247
- });
2402
+ async function settleRunPickup(pickup, options) {
2403
+ switch (pickup.kind) {
2404
+ case "resumed":
2405
+ case "rejoined":
2406
+ return { state: "streamed" };
2407
+ case "resume_conflict":
2408
+ return { state: "retry" };
2409
+ case "followed_workflow":
2410
+ case "followed": {
2411
+ const status = pickup.kind === "followed" ? pickup.status : null;
2412
+ const nothingLive = pickup.kind === "followed" && pickup.executionId === null;
2413
+ if (pickup.ended || nothingLive) {
2414
+ return { state: "settled", status, ...await attempt(options.reloadSavedTurn) };
2415
+ }
2416
+ return {
2417
+ state: "still_running",
2418
+ ...await attempt(options.onStillRunning ?? options.reloadSavedTurn)
2419
+ };
2420
+ }
2248
2421
  }
2249
- const obj = data;
2250
- const details = typeof obj.details === "object" && obj.details !== null ? obj.details : null;
2251
- return new BackendApiError({
2252
- code: obj.code || obj.error_type || obj.error || "internal_error",
2253
- detail: obj.message || "Streaming error",
2254
- userMessage: obj.user_message || obj.message || "Something went wrong",
2255
- details,
2256
- requestId: typeof obj.request_id === "string" ? obj.request_id : typeof details?.request_id === "string" ? details.request_id : void 0
2422
+ }
2423
+
2424
+ // matrx/run.ts
2425
+ async function streamCall(transport, path, body, options) {
2426
+ const response = await requestStream(transport, path, {
2427
+ method: "POST",
2428
+ // This client IS the streaming path — `stream: true` always, last so a
2429
+ // caller-supplied value can never flip the response off NDJSON.
2430
+ body: { ...body, stream: true },
2431
+ ...options.signal ? { signal: options.signal } : {}
2257
2432
  });
2433
+ return toRunHandle(response, options);
2258
2434
  }
2259
- function parsePersistedBackendError(data, requestId = "") {
2260
- if (!data || typeof data !== "object") return null;
2261
- const error = data;
2262
- const failures = Array.isArray(error.failures) ? error.failures : [];
2263
- const firstSpecific = failures.find(
2264
- (failure) => typeof failure === "object" && failure !== null && typeof failure.message === "string"
2435
+ function startAgentRun(transport, agentId, request, options = {}) {
2436
+ return streamCall(
2437
+ transport,
2438
+ `/ai/agents/${encodePathSegment(agentId)}`,
2439
+ request,
2440
+ options
2441
+ );
2442
+ }
2443
+ function startMandateRun(transport, mandateKey, request, options = {}) {
2444
+ return streamCall(
2445
+ transport,
2446
+ `/ai/mandates/${encodePathSegment(mandateKey)}`,
2447
+ request,
2448
+ options
2449
+ );
2450
+ }
2451
+ function continueAgentConversation(transport, conversationId, request, options = {}) {
2452
+ return streamCall(
2453
+ transport,
2454
+ `/ai/conversations/${encodePathSegment(conversationId)}`,
2455
+ request,
2456
+ options
2457
+ );
2458
+ }
2459
+ function resumeAgentConversation(transport, conversationId, request = {}, options = {}) {
2460
+ return streamCall(
2461
+ transport,
2462
+ `/ai/conversations/${encodePathSegment(conversationId)}/resume`,
2463
+ request,
2464
+ options
2265
2465
  );
2266
- const summary = typeof error.message === "string" ? error.message : "Backend operation failed";
2267
- const specific = typeof firstSpecific?.message === "string" ? firstSpecific.message : summary;
2268
- const detail = specific === summary ? summary : `${summary}: ${specific}`;
2269
- const code = typeof error.type === "string" ? error.type : "internal_error";
2270
- const userMessage = typeof error.user_message === "string" && error.user_message ? error.user_message : detail;
2271
- return new BackendApiError({
2272
- code,
2273
- detail,
2274
- userMessage,
2275
- details: error,
2276
- requestId
2277
- });
2278
2466
  }
2279
- var GENERIC_MESSAGE_PATTERNS = [
2280
- /failed unexpectedly/i,
2281
- /^\s*something went wrong/i,
2282
- /please try again(\s+later)?\.?\s*$/i,
2283
- /^\s*request failed\b/i,
2284
- /^\s*unknown (streaming )?error/i,
2285
- /^\s*internal server error\.?\s*$/i
2286
- ];
2287
- function isGenericUserMessage(message) {
2288
- const value = (message ?? "").trim();
2289
- if (!value) return true;
2290
- return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));
2467
+ function cancelAgentRun(transport, requestId, options = {}) {
2468
+ const query = buildQuery(
2469
+ options.mode === "interrupt" ? { mode: "interrupt" } : {}
2470
+ );
2471
+ return requestJson(
2472
+ transport,
2473
+ `/ai/cancel/${encodePathSegment(requestId)}${query}`,
2474
+ {
2475
+ method: "POST",
2476
+ ...options.signal ? { signal: options.signal } : {}
2477
+ }
2478
+ );
2291
2479
  }
2292
- function unwrapUpstreamError(message) {
2293
- const start = message.indexOf("{");
2294
- const end = message.lastIndexOf("}");
2295
- if (start < 0 || end <= start) return null;
2296
- let parsed;
2297
- try {
2298
- parsed = JSON.parse(message.slice(start, end + 1));
2299
- } catch {
2300
- return null;
2480
+ var MatrxRunError = class extends Error {
2481
+ name = "MatrxRunError";
2482
+ /** The verbatim `error` event payload, when one fired. */
2483
+ errorPayload;
2484
+ /** The `user_request` completion status (`"failed"` | `"cancelled"`), when that was the trigger. */
2485
+ completionStatus;
2486
+ /** Text streamed before the failure — partial content never vanishes. */
2487
+ partialText;
2488
+ constructor(args) {
2489
+ super(args.message);
2490
+ this.errorPayload = args.errorPayload ?? null;
2491
+ this.completionStatus = args.completionStatus ?? null;
2492
+ this.partialText = args.partialText ?? "";
2301
2493
  }
2302
- if (typeof parsed !== "object" || parsed === null) return null;
2303
- const body = parsed;
2304
- const inner = typeof body.message === "string" && body.message || typeof body.detail === "string" && body.detail || "";
2305
- if (!inner) return null;
2306
- const statusMatch = /\bHTTP (\d{3})\b/.exec(message.slice(0, start));
2307
- return {
2308
- message: inner,
2309
- code: typeof body.error_type === "string" && body.error_type || typeof body.error === "string" && body.error || null,
2310
- userMessage: typeof body.user_message === "string" ? body.user_message : null,
2311
- requestId: typeof body.request_id === "string" ? body.request_id : null,
2312
- status: statusMatch ? Number(statusMatch[1]) : null
2313
- };
2494
+ };
2495
+ function isRecord5(value) {
2496
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2314
2497
  }
2315
- function describeBackendFailure(error) {
2316
- const chain = [];
2317
- let code = "internal_error";
2318
- let requestId = "";
2319
- let status = null;
2320
- let userFacing = null;
2321
- if (error instanceof BackendApiError) {
2322
- code = error.code;
2323
- requestId = error.requestId;
2324
- status = error.status;
2325
- userFacing = error.userMessage;
2326
- if (error.detail) chain.push(error.detail);
2327
- if (error.userMessage && error.userMessage !== error.detail) {
2328
- chain.push(error.userMessage);
2498
+ function stringField(value, key) {
2499
+ if (!isRecord5(value)) return null;
2500
+ const field = value[key];
2501
+ return typeof field === "string" && field ? field : null;
2502
+ }
2503
+ async function runAgentToCompletion(transport, agentId, request, options = {}) {
2504
+ const handle = await startAgentRun(transport, agentId, request, options);
2505
+ let text = "";
2506
+ let completion = null;
2507
+ let failure = null;
2508
+ for await (const envelope of handle.events) {
2509
+ options.onEvent?.(envelope);
2510
+ if (envelope.event === "chunk") {
2511
+ const chunk = stringField(envelope.data, "text");
2512
+ if (chunk !== null) {
2513
+ text += chunk;
2514
+ options.onChunk?.(text);
2515
+ }
2516
+ continue;
2517
+ }
2518
+ if (envelope.event === "error" && failure === null) {
2519
+ const payload = isRecord5(envelope.data) ? envelope.data : null;
2520
+ failure = new MatrxRunError({
2521
+ message: streamErrorText(payload) ?? "The agent run failed",
2522
+ errorPayload: payload,
2523
+ partialText: text
2524
+ });
2525
+ continue;
2526
+ }
2527
+ if (envelope.event !== "completion" || !isRecord5(envelope.data)) continue;
2528
+ if (envelope.data.operation !== "user_request") continue;
2529
+ completion = envelope.data;
2530
+ const status = envelope.data.status;
2531
+ if ((status === "failed" || status === "cancelled") && failure === null) {
2532
+ const result = isRecord5(envelope.data.result) ? envelope.data.result : null;
2533
+ failure = new MatrxRunError({
2534
+ message: stringField(result, "error") ?? stringField(result, "user_message") ?? `The agent run ${status}`,
2535
+ completionStatus: status,
2536
+ partialText: text
2537
+ });
2329
2538
  }
2330
- } else if (error instanceof Error) {
2331
- chain.push(error.message);
2332
- userFacing = error.message;
2333
- } else if (typeof error === "string") {
2334
- chain.push(error);
2335
- userFacing = error;
2336
- } else {
2337
- chain.push("Unknown error");
2338
2539
  }
2339
- for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {
2340
- const upstream = unwrapUpstreamError(chain[cursor] ?? "");
2341
- if (!upstream || chain.includes(upstream.message)) continue;
2342
- chain.push(upstream.message);
2343
- if (upstream.code) code = upstream.code;
2344
- if (upstream.requestId) requestId = upstream.requestId;
2345
- if (upstream.status !== null) status = upstream.status;
2540
+ if (failure) throw failure;
2541
+ if (!text && completion) {
2542
+ const result = completion.result;
2543
+ const output = stringField(result, "output");
2544
+ if (output !== null) text = output;
2346
2545
  }
2347
- const specific = [...chain].reverse().find((message) => !isGenericUserMessage(message));
2348
- const cause = specific ?? chain[0] ?? "Unknown error";
2349
- const headlineWasGeneric = isGenericUserMessage(userFacing);
2350
2546
  return {
2351
- code,
2352
- cause,
2353
- headline: headlineWasGeneric ? cause : userFacing ?? cause,
2354
- headlineWasGeneric,
2355
- chain,
2356
- requestId,
2357
- status
2547
+ text,
2548
+ requestId: handle.requestId,
2549
+ conversationId: handle.conversationId,
2550
+ completion
2358
2551
  };
2359
2552
  }
2360
- function getUserMessage(error) {
2361
- if (error instanceof BackendApiError) {
2362
- return error.userMessage;
2363
- }
2364
- if (error instanceof Error) {
2365
- return error.message;
2366
- }
2367
- if (typeof error === "string") {
2368
- return error;
2369
- }
2370
- if (typeof error === "object" && error !== null && typeof error.message === "string" && error.message) {
2371
- return error.message;
2553
+
2554
+ // matrx/provider-failures.ts
2555
+ var clip = (value, max) => value.length > max ? value.slice(0, max) : value;
2556
+ function providerSessionFailureBody(failure) {
2557
+ const message = failure.message?.trim() || failure.error_type?.trim() || (failure.status_code ? `HTTP ${failure.status_code}` : "provider session failed");
2558
+ const body = {
2559
+ provider: failure.provider,
2560
+ message: clip(message, 4e3)
2561
+ };
2562
+ if (failure.model?.trim()) body.model = clip(failure.model.trim(), 200);
2563
+ if (failure.error_type?.trim()) body.error_type = clip(failure.error_type.trim(), 200);
2564
+ if (typeof failure.status_code === "number" && Number.isInteger(failure.status_code) && failure.status_code >= 100 && failure.status_code <= 599) {
2565
+ body.status_code = failure.status_code;
2372
2566
  }
2373
- return "Something went wrong";
2567
+ return body;
2374
2568
  }
2375
- function statusToCode(status) {
2376
- switch (status) {
2377
- case 401:
2378
- return "auth_required";
2379
- case 403:
2380
- return "admin_required";
2381
- case 404:
2382
- return "not_found";
2383
- case 422:
2384
- return "validation_error";
2385
- default:
2386
- return "internal_error";
2569
+ async function reportProviderSessionFailure(transport, failure, options = {}) {
2570
+ try {
2571
+ return await requestJson(
2572
+ transport,
2573
+ "/broker/provider-failures",
2574
+ {
2575
+ method: "POST",
2576
+ body: providerSessionFailureBody(failure),
2577
+ ...options.signal ? { signal: options.signal } : {}
2578
+ }
2579
+ );
2580
+ } catch (error) {
2581
+ console.warn(
2582
+ `[provider-failures] could not report a ${failure.provider} session failure`,
2583
+ error
2584
+ );
2585
+ return null;
2387
2586
  }
2388
2587
  }
2389
2588
 
2589
+ // matrx/tools.ts
2590
+ function submitAgentToolResults(transport, conversationId, results, options = {}) {
2591
+ return requestJson(
2592
+ transport,
2593
+ `/ai/conversations/${encodePathSegment(conversationId)}/tool_results`,
2594
+ {
2595
+ method: "POST",
2596
+ body: {
2597
+ results,
2598
+ ...options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
2599
+ },
2600
+ ...options.signal ? { signal: options.signal } : {}
2601
+ }
2602
+ );
2603
+ }
2604
+ function listConversationPendingToolCalls(transport, conversationId, options = {}) {
2605
+ return requestJson(
2606
+ transport,
2607
+ `/ai/conversations/${encodePathSegment(conversationId)}/pending_calls`,
2608
+ {
2609
+ method: "GET",
2610
+ ...options.signal ? { signal: options.signal } : {}
2611
+ }
2612
+ );
2613
+ }
2614
+ function listUserPendingToolCalls(transport, options = {}) {
2615
+ const query = buildQuery(
2616
+ options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
2617
+ );
2618
+ return requestJson(
2619
+ transport,
2620
+ `/ai/user/pending_calls${query}`,
2621
+ {
2622
+ method: "GET",
2623
+ ...options.signal ? { signal: options.signal } : {}
2624
+ }
2625
+ );
2626
+ }
2627
+
2390
2628
  // matrx/endpoints.ts
2391
2629
  var ENDPOINTS = {
2392
2630
  /** AI endpoints — chat, agents, conversations */