@ai-matrx/agents 0.35.0 → 0.37.0

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