@providerprotocol/ai 0.0.34 → 0.0.36

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 (136) hide show
  1. package/README.md +546 -3
  2. package/dist/anthropic/index.d.ts +2 -1
  3. package/dist/anthropic/index.js +151 -145
  4. package/dist/anthropic/index.js.map +1 -1
  5. package/dist/cerebras/index.d.ts +392 -0
  6. package/dist/cerebras/index.js +648 -0
  7. package/dist/cerebras/index.js.map +1 -0
  8. package/dist/chunk-2YXFLRQ6.js +49 -0
  9. package/dist/chunk-2YXFLRQ6.js.map +1 -0
  10. package/dist/chunk-4OGB7JZA.js +157 -0
  11. package/dist/chunk-4OGB7JZA.js.map +1 -0
  12. package/dist/chunk-4RX4VQCB.js +31 -0
  13. package/dist/chunk-4RX4VQCB.js.map +1 -0
  14. package/dist/chunk-5IWHCXKN.js +30 -0
  15. package/dist/chunk-5IWHCXKN.js.map +1 -0
  16. package/dist/{chunk-3C7O2RNO.js → chunk-A2IM7PGT.js} +6 -4
  17. package/dist/{chunk-3C7O2RNO.js.map → chunk-A2IM7PGT.js.map} +1 -1
  18. package/dist/{chunk-3D6XGGVG.js → chunk-ARVM24K2.js} +2 -2
  19. package/dist/{chunk-4J6OFUKX.js → chunk-AY55T37A.js} +70 -162
  20. package/dist/chunk-AY55T37A.js.map +1 -0
  21. package/dist/{chunk-ILR2D5PN.js → chunk-BRP5XJ6Q.js} +2 -86
  22. package/dist/chunk-BRP5XJ6Q.js.map +1 -0
  23. package/dist/chunk-C4JP64VW.js +298 -0
  24. package/dist/chunk-C4JP64VW.js.map +1 -0
  25. package/dist/chunk-COS4ON4G.js +111 -0
  26. package/dist/chunk-COS4ON4G.js.map +1 -0
  27. package/dist/chunk-CRP6Y7NF.js +31 -0
  28. package/dist/chunk-CRP6Y7NF.js.map +1 -0
  29. package/dist/chunk-EPB3GQNL.js +118 -0
  30. package/dist/chunk-EPB3GQNL.js.map +1 -0
  31. package/dist/chunk-ETBFOLQN.js +34 -0
  32. package/dist/chunk-ETBFOLQN.js.map +1 -0
  33. package/dist/chunk-I53CI6ZZ.js +142 -0
  34. package/dist/chunk-I53CI6ZZ.js.map +1 -0
  35. package/dist/chunk-IDZOVWP3.js +29 -0
  36. package/dist/chunk-IDZOVWP3.js.map +1 -0
  37. package/dist/chunk-JA3UZALR.js +88 -0
  38. package/dist/chunk-JA3UZALR.js.map +1 -0
  39. package/dist/{chunk-WAKD3OO5.js → chunk-N5DX5JW3.js} +31 -31
  40. package/dist/chunk-N5DX5JW3.js.map +1 -0
  41. package/dist/chunk-OIEWDFQU.js +97 -0
  42. package/dist/chunk-OIEWDFQU.js.map +1 -0
  43. package/dist/{chunk-TOJCZMVU.js → chunk-PMK5LZ5Z.js} +40 -40
  44. package/dist/chunk-PMK5LZ5Z.js.map +1 -0
  45. package/dist/{chunk-6S222DHN.js → chunk-RJGTRQ47.js} +20 -1
  46. package/dist/chunk-RJGTRQ47.js.map +1 -0
  47. package/dist/chunk-UFFJDYCE.js +94 -0
  48. package/dist/chunk-UFFJDYCE.js.map +1 -0
  49. package/dist/chunk-VGKZIGVI.js +222 -0
  50. package/dist/chunk-VGKZIGVI.js.map +1 -0
  51. package/dist/{chunk-KUPF5KHT.js → chunk-Y5H7C5J4.js} +2 -2
  52. package/dist/{embedding-D2BYIehX.d.ts → embedding-BXA72PlJ.d.ts} +1 -1
  53. package/dist/google/index.d.ts +2 -1
  54. package/dist/google/index.js +202 -199
  55. package/dist/google/index.js.map +1 -1
  56. package/dist/groq/index.d.ts +410 -0
  57. package/dist/groq/index.js +649 -0
  58. package/dist/groq/index.js.map +1 -0
  59. package/dist/http/index.d.ts +3 -2
  60. package/dist/http/index.js +5 -4
  61. package/dist/image-stream-CCgwB7ve.d.ts +11 -0
  62. package/dist/index.d.ts +8 -118
  63. package/dist/index.js +520 -769
  64. package/dist/index.js.map +1 -1
  65. package/dist/{llm-BQJZj3cD.d.ts → llm-ByUFPcFH.d.ts} +12 -1632
  66. package/dist/middleware/logging/index.d.ts +76 -0
  67. package/dist/middleware/logging/index.js +74 -0
  68. package/dist/middleware/logging/index.js.map +1 -0
  69. package/dist/middleware/parsed-object/index.d.ts +45 -0
  70. package/dist/middleware/parsed-object/index.js +73 -0
  71. package/dist/middleware/parsed-object/index.js.map +1 -0
  72. package/dist/middleware/pubsub/index.d.ts +97 -0
  73. package/dist/middleware/pubsub/index.js +160 -0
  74. package/dist/middleware/pubsub/index.js.map +1 -0
  75. package/dist/middleware/pubsub/server/express/index.d.ts +66 -0
  76. package/dist/middleware/pubsub/server/express/index.js +11 -0
  77. package/dist/middleware/pubsub/server/express/index.js.map +1 -0
  78. package/dist/middleware/pubsub/server/fastify/index.d.ts +67 -0
  79. package/dist/middleware/pubsub/server/fastify/index.js +11 -0
  80. package/dist/middleware/pubsub/server/fastify/index.js.map +1 -0
  81. package/dist/middleware/pubsub/server/h3/index.d.ts +70 -0
  82. package/dist/middleware/pubsub/server/h3/index.js +11 -0
  83. package/dist/middleware/pubsub/server/h3/index.js.map +1 -0
  84. package/dist/middleware/pubsub/server/index.d.ts +78 -0
  85. package/dist/middleware/pubsub/server/index.js +34 -0
  86. package/dist/middleware/pubsub/server/index.js.map +1 -0
  87. package/dist/middleware/pubsub/server/webapi/index.d.ts +63 -0
  88. package/dist/middleware/pubsub/server/webapi/index.js +11 -0
  89. package/dist/middleware/pubsub/server/webapi/index.js.map +1 -0
  90. package/dist/ollama/index.d.ts +2 -1
  91. package/dist/ollama/index.js +48 -45
  92. package/dist/ollama/index.js.map +1 -1
  93. package/dist/openai/index.d.ts +2 -1
  94. package/dist/openai/index.js +319 -313
  95. package/dist/openai/index.js.map +1 -1
  96. package/dist/openrouter/index.d.ts +2 -1
  97. package/dist/openrouter/index.js +381 -385
  98. package/dist/openrouter/index.js.map +1 -1
  99. package/dist/proxy/index.d.ts +10 -914
  100. package/dist/proxy/index.js +275 -1007
  101. package/dist/proxy/index.js.map +1 -1
  102. package/dist/proxy/server/express/index.d.ts +161 -0
  103. package/dist/proxy/server/express/index.js +24 -0
  104. package/dist/proxy/server/express/index.js.map +1 -0
  105. package/dist/proxy/server/fastify/index.d.ts +162 -0
  106. package/dist/proxy/server/fastify/index.js +24 -0
  107. package/dist/proxy/server/fastify/index.js.map +1 -0
  108. package/dist/proxy/server/h3/index.d.ts +189 -0
  109. package/dist/proxy/server/h3/index.js +28 -0
  110. package/dist/proxy/server/h3/index.js.map +1 -0
  111. package/dist/proxy/server/index.d.ts +151 -0
  112. package/dist/proxy/server/index.js +48 -0
  113. package/dist/proxy/server/index.js.map +1 -0
  114. package/dist/proxy/server/webapi/index.d.ts +278 -0
  115. package/dist/proxy/server/webapi/index.js +32 -0
  116. package/dist/proxy/server/webapi/index.js.map +1 -0
  117. package/dist/responses/index.d.ts +650 -0
  118. package/dist/responses/index.js +930 -0
  119. package/dist/responses/index.js.map +1 -0
  120. package/dist/{retry-8Ch-WWgX.d.ts → retry-BDMo4AVu.d.ts} +1 -1
  121. package/dist/stream-S7nwQRqM.d.ts +1643 -0
  122. package/dist/types-CE4B7pno.d.ts +96 -0
  123. package/dist/utils/index.d.ts +53 -0
  124. package/dist/utils/index.js +7 -0
  125. package/dist/utils/index.js.map +1 -0
  126. package/dist/xai/index.d.ts +2 -1
  127. package/dist/xai/index.js +310 -310
  128. package/dist/xai/index.js.map +1 -1
  129. package/package.json +82 -4
  130. package/dist/chunk-4J6OFUKX.js.map +0 -1
  131. package/dist/chunk-6S222DHN.js.map +0 -1
  132. package/dist/chunk-ILR2D5PN.js.map +0 -1
  133. package/dist/chunk-TOJCZMVU.js.map +0 -1
  134. package/dist/chunk-WAKD3OO5.js.map +0 -1
  135. /package/dist/{chunk-3D6XGGVG.js.map → chunk-ARVM24K2.js.map} +0 -0
  136. /package/dist/{chunk-KUPF5KHT.js.map → chunk-Y5H7C5J4.js.map} +0 -0
@@ -1,107 +1,7 @@
1
- // src/types/errors.ts
2
- var ErrorCode = {
3
- /** API key is invalid or expired */
4
- AuthenticationFailed: "AUTHENTICATION_FAILED",
5
- /** Rate limit exceeded, retry after delay */
6
- RateLimited: "RATE_LIMITED",
7
- /** Input exceeds model's context window */
8
- ContextLengthExceeded: "CONTEXT_LENGTH_EXCEEDED",
9
- /** Requested model does not exist */
10
- ModelNotFound: "MODEL_NOT_FOUND",
11
- /** Request parameters are malformed */
12
- InvalidRequest: "INVALID_REQUEST",
13
- /** Provider returned an unexpected response format */
14
- InvalidResponse: "INVALID_RESPONSE",
15
- /** Content was blocked by safety filters */
16
- ContentFiltered: "CONTENT_FILTERED",
17
- /** Account quota or credits exhausted */
18
- QuotaExceeded: "QUOTA_EXCEEDED",
19
- /** Provider-specific error not covered by other codes */
20
- ProviderError: "PROVIDER_ERROR",
21
- /** Network connectivity issue */
22
- NetworkError: "NETWORK_ERROR",
23
- /** Request exceeded timeout limit */
24
- Timeout: "TIMEOUT",
25
- /** Request was cancelled via AbortSignal */
26
- Cancelled: "CANCELLED"
27
- };
28
- var ModalityType = {
29
- /** Large language model for text generation */
30
- LLM: "llm",
31
- /** Text/image embedding model */
32
- Embedding: "embedding",
33
- /** Image generation model */
34
- Image: "image",
35
- /** Audio processing/generation model */
36
- Audio: "audio",
37
- /** Video processing/generation model */
38
- Video: "video"
39
- };
40
- var UPPError = class _UPPError extends Error {
41
- /** Normalized error code for programmatic handling */
42
- code;
43
- /** Name of the provider that generated the error */
44
- provider;
45
- /** The modality that was being used when the error occurred */
46
- modality;
47
- /** HTTP status code from the provider's response, if available */
48
- statusCode;
49
- /** The original error that caused this UPPError, if wrapping another error */
50
- cause;
51
- /** Error class name, always 'UPPError' */
52
- name = "UPPError";
53
- /**
54
- * Creates a new UPPError instance.
55
- *
56
- * @param message - Human-readable error description
57
- * @param code - Normalized error code for programmatic handling
58
- * @param provider - Name of the provider that generated the error
59
- * @param modality - The modality that was being used
60
- * @param statusCode - HTTP status code from the provider's response
61
- * @param cause - The original error being wrapped
62
- */
63
- constructor(message, code, provider, modality, statusCode, cause) {
64
- super(message);
65
- this.code = code;
66
- this.provider = provider;
67
- this.modality = modality;
68
- this.statusCode = statusCode;
69
- this.cause = cause;
70
- if (Error.captureStackTrace) {
71
- Error.captureStackTrace(this, _UPPError);
72
- }
73
- }
74
- /**
75
- * Creates a string representation of the error.
76
- *
77
- * @returns Formatted error string including code, message, provider, and modality
78
- */
79
- toString() {
80
- let str = `UPPError [${this.code}]: ${this.message}`;
81
- str += ` (provider: ${this.provider}, modality: ${this.modality}`;
82
- if (this.statusCode) {
83
- str += `, status: ${this.statusCode}`;
84
- }
85
- str += ")";
86
- return str;
87
- }
88
- /**
89
- * Converts the error to a JSON-serializable object.
90
- *
91
- * @returns Plain object representation suitable for logging or transmission
92
- */
93
- toJSON() {
94
- return {
95
- name: this.name,
96
- message: this.message,
97
- code: this.code,
98
- provider: this.provider,
99
- modality: this.modality,
100
- statusCode: this.statusCode,
101
- cause: this.cause?.message
102
- };
103
- }
104
- };
1
+ import {
2
+ ErrorCode,
3
+ UPPError
4
+ } from "./chunk-COS4ON4G.js";
105
5
 
106
6
  // src/utils/error.ts
107
7
  function toError(value) {
@@ -119,6 +19,16 @@ function toError(value) {
119
19
  }
120
20
  return new Error(String(value));
121
21
  }
22
+ function isCancelledError(value) {
23
+ if (value instanceof UPPError) {
24
+ return value.code === ErrorCode.Cancelled;
25
+ }
26
+ if (value && typeof value === "object" && "name" in value) {
27
+ const name = value.name;
28
+ return name === "AbortError";
29
+ }
30
+ return false;
31
+ }
122
32
 
123
33
  // src/http/errors.ts
124
34
  function statusToErrorCode(status) {
@@ -217,6 +127,60 @@ function warnInsecureUrl(url, provider) {
217
127
  function hasFork(strategy) {
218
128
  return !!strategy && typeof strategy.fork === "function";
219
129
  }
130
+ function sleep(ms) {
131
+ return new Promise((resolve) => setTimeout(resolve, ms));
132
+ }
133
+ function parseRetryAfter(headerValue, maxSeconds) {
134
+ if (!headerValue) {
135
+ return null;
136
+ }
137
+ const seconds = parseInt(headerValue, 10);
138
+ if (!Number.isNaN(seconds)) {
139
+ return Math.min(maxSeconds, Math.max(0, seconds));
140
+ }
141
+ const dateMillis = Date.parse(headerValue);
142
+ if (Number.isNaN(dateMillis)) {
143
+ return null;
144
+ }
145
+ const deltaMs = dateMillis - Date.now();
146
+ if (deltaMs <= 0) {
147
+ return 0;
148
+ }
149
+ const deltaSeconds = Math.ceil(deltaMs / 1e3);
150
+ return Math.min(maxSeconds, Math.max(0, deltaSeconds));
151
+ }
152
+ async function fetchWithTimeout(fetchFn, url, init, timeout, provider, modality) {
153
+ const existingSignal = init.signal;
154
+ if (existingSignal?.aborted) {
155
+ throw cancelledError(provider, modality);
156
+ }
157
+ const controller = new AbortController();
158
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
159
+ const onAbort = () => controller.abort();
160
+ if (existingSignal) {
161
+ existingSignal.addEventListener("abort", onAbort, { once: true });
162
+ }
163
+ try {
164
+ const response = await fetchFn(url, {
165
+ ...init,
166
+ signal: controller.signal
167
+ });
168
+ return response;
169
+ } catch (error) {
170
+ if (toError(error).name === "AbortError") {
171
+ if (existingSignal?.aborted) {
172
+ throw cancelledError(provider, modality);
173
+ }
174
+ throw timeoutError(timeout, provider, modality);
175
+ }
176
+ throw error;
177
+ } finally {
178
+ clearTimeout(timeoutId);
179
+ if (existingSignal) {
180
+ existingSignal.removeEventListener("abort", onAbort);
181
+ }
182
+ }
183
+ }
220
184
  async function doFetch(url, init, config, provider, modality) {
221
185
  const fetchFn = config.fetch ?? fetch;
222
186
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
@@ -287,41 +251,6 @@ async function doFetch(url, init, config, provider, modality) {
287
251
  return response;
288
252
  }
289
253
  }
290
- async function fetchWithTimeout(fetchFn, url, init, timeout, provider, modality) {
291
- const existingSignal = init.signal;
292
- if (existingSignal?.aborted) {
293
- throw cancelledError(provider, modality);
294
- }
295
- const controller = new AbortController();
296
- const timeoutId = setTimeout(() => controller.abort(), timeout);
297
- const onAbort = () => controller.abort();
298
- if (existingSignal) {
299
- existingSignal.addEventListener("abort", onAbort, { once: true });
300
- }
301
- try {
302
- const response = await fetchFn(url, {
303
- ...init,
304
- signal: controller.signal
305
- });
306
- return response;
307
- } catch (error) {
308
- if (toError(error).name === "AbortError") {
309
- if (existingSignal?.aborted) {
310
- throw cancelledError(provider, modality);
311
- }
312
- throw timeoutError(timeout, provider, modality);
313
- }
314
- throw error;
315
- } finally {
316
- clearTimeout(timeoutId);
317
- if (existingSignal) {
318
- existingSignal.removeEventListener("abort", onAbort);
319
- }
320
- }
321
- }
322
- function sleep(ms) {
323
- return new Promise((resolve) => setTimeout(resolve, ms));
324
- }
325
254
  async function doStreamFetch(url, init, config, provider, modality) {
326
255
  const fetchFn = config.fetch ?? fetch;
327
256
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
@@ -350,31 +279,10 @@ async function doStreamFetch(url, init, config, provider, modality) {
350
279
  throw networkError(toError(error), provider, modality);
351
280
  }
352
281
  }
353
- function parseRetryAfter(headerValue, maxSeconds) {
354
- if (!headerValue) {
355
- return null;
356
- }
357
- const seconds = parseInt(headerValue, 10);
358
- if (!Number.isNaN(seconds)) {
359
- return Math.min(maxSeconds, Math.max(0, seconds));
360
- }
361
- const dateMillis = Date.parse(headerValue);
362
- if (Number.isNaN(dateMillis)) {
363
- return null;
364
- }
365
- const deltaMs = dateMillis - Date.now();
366
- if (deltaMs <= 0) {
367
- return 0;
368
- }
369
- const deltaSeconds = Math.ceil(deltaMs / 1e3);
370
- return Math.min(maxSeconds, Math.max(0, deltaSeconds));
371
- }
372
282
 
373
283
  export {
374
- ErrorCode,
375
- ModalityType,
376
- UPPError,
377
284
  toError,
285
+ isCancelledError,
378
286
  statusToErrorCode,
379
287
  normalizeHttpError,
380
288
  networkError,
@@ -384,4 +292,4 @@ export {
384
292
  doFetch,
385
293
  doStreamFetch
386
294
  };
387
- //# sourceMappingURL=chunk-4J6OFUKX.js.map
295
+ //# sourceMappingURL=chunk-AY55T37A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/error.ts","../src/http/errors.ts","../src/http/fetch.ts"],"sourcesContent":["/**\n * @fileoverview Error normalization utilities.\n *\n * @module utils/error\n */\n\nimport { ErrorCode, UPPError } from '../types/errors.ts';\n\n/**\n * Converts an unknown thrown value into an Error instance.\n *\n * @param value - Unknown error value\n * @returns An Error instance\n */\nexport function toError(value: unknown): Error {\n if (value instanceof Error) {\n return value;\n }\n if (typeof value === 'string') {\n return new Error(value);\n }\n if (typeof value === 'object' && value !== null && 'message' in value) {\n const message = (value as { message?: unknown }).message;\n if (typeof message === 'string') {\n return new Error(message);\n }\n }\n return new Error(String(value));\n}\n\n/**\n * Checks whether an error represents a cancellation.\n *\n * @param value - Unknown error value\n * @returns True if the error indicates cancellation\n */\nexport function isCancelledError(value: unknown): boolean {\n if (value instanceof UPPError) {\n return value.code === ErrorCode.Cancelled;\n }\n\n if (value && typeof value === 'object' && 'name' in value) {\n const name = (value as { name?: unknown }).name;\n return name === 'AbortError';\n }\n\n return false;\n}\n","/**\n * HTTP error handling and normalization utilities.\n * @module http/errors\n */\n\nimport {\n UPPError,\n ErrorCode,\n type Modality,\n} from '../types/errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/**\n * Maps HTTP status codes to standardized UPP error codes.\n *\n * This function provides consistent error categorization across all providers:\n * - 400 -> INVALID_REQUEST (bad request format or parameters)\n * - 401, 403 -> AUTHENTICATION_FAILED (invalid or missing credentials)\n * - 404 -> MODEL_NOT_FOUND (requested model does not exist)\n * - 408 -> TIMEOUT (request timed out)\n * - 413 -> CONTEXT_LENGTH_EXCEEDED (input too long)\n * - 429 -> RATE_LIMITED (too many requests)\n * - 5xx -> PROVIDER_ERROR (server-side issues)\n *\n * @param status - HTTP status code from the response\n * @returns The corresponding UPP ErrorCode\n *\n * @example\n * ```typescript\n * const errorCode = statusToErrorCode(429);\n * // Returns 'RATE_LIMITED'\n *\n * const serverError = statusToErrorCode(503);\n * // Returns 'PROVIDER_ERROR'\n * ```\n */\nexport function statusToErrorCode(status: number): ErrorCode {\n switch (status) {\n case 400:\n return ErrorCode.InvalidRequest;\n case 402:\n return ErrorCode.QuotaExceeded;\n case 401:\n case 403:\n return ErrorCode.AuthenticationFailed;\n case 404:\n return ErrorCode.ModelNotFound;\n case 408:\n return ErrorCode.Timeout;\n case 409:\n return ErrorCode.InvalidRequest;\n case 422:\n return ErrorCode.InvalidRequest;\n case 413:\n return ErrorCode.ContextLengthExceeded;\n case 451:\n return ErrorCode.ContentFiltered;\n case 429:\n return ErrorCode.RateLimited;\n case 500:\n case 502:\n case 503:\n case 504:\n return ErrorCode.ProviderError;\n default:\n return ErrorCode.ProviderError;\n }\n}\n\n/**\n * Normalizes HTTP error responses into standardized UPPError objects.\n *\n * This function performs several operations:\n * 1. Maps the HTTP status code to an appropriate ErrorCode\n * 2. Attempts to extract a meaningful error message from the response body\n * 3. Handles various provider-specific error response formats\n *\n * Supported error message formats:\n * - `{ error: { message: \"...\" } }` (OpenAI, Anthropic)\n * - `{ message: \"...\" }` (simple format)\n * - `{ error: { error: { message: \"...\" } } }` (nested format)\n * - `{ detail: \"...\" }` (FastAPI style)\n * - Plain text body (if under 200 characters)\n *\n * @param response - The HTTP Response object with non-2xx status\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with normalized code and message\n *\n * @example\n * ```typescript\n * if (!response.ok) {\n * const error = await normalizeHttpError(response, 'openai', 'llm');\n * // error.code might be 'RATE_LIMITED' for 429\n * // error.message contains provider's error message\n * throw error;\n * }\n * ```\n */\nexport async function normalizeHttpError(\n response: Response,\n provider: string,\n modality: Modality\n): Promise<UPPError> {\n const code = statusToErrorCode(response.status);\n let message = `HTTP ${response.status}: ${response.statusText}`;\n let bodyReadError: Error | undefined;\n\n try {\n const body = await response.text();\n if (body) {\n try {\n const json = JSON.parse(body);\n const extractedMessage =\n json.error?.message ||\n json.message ||\n json.error?.error?.message ||\n json.detail;\n\n if (extractedMessage) {\n message = extractedMessage;\n }\n } catch {\n if (body.length < 200) {\n message = body;\n }\n }\n }\n } catch (error) {\n bodyReadError = toError(error);\n }\n\n return new UPPError(message, code, provider, modality, response.status, bodyReadError);\n}\n\n/**\n * Creates a UPPError for network failures (DNS, connection, etc.).\n *\n * Use this when the request fails before receiving any HTTP response,\n * such as DNS resolution failures, connection refused, or network unreachable.\n *\n * @param error - The underlying Error that caused the failure\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with NETWORK_ERROR code and the original error attached\n */\nexport function networkError(\n error: Error,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Network error: ${error.message}`,\n ErrorCode.NetworkError,\n provider,\n modality,\n undefined,\n error\n );\n}\n\n/**\n * Creates a UPPError for request timeout.\n *\n * Use this when the request exceeds the configured timeout duration\n * and is aborted by the AbortController.\n *\n * @param timeout - The timeout duration in milliseconds that was exceeded\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with TIMEOUT code\n */\nexport function timeoutError(\n timeout: number,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Request timed out after ${timeout}ms`,\n ErrorCode.Timeout,\n provider,\n modality\n );\n}\n\n/**\n * Creates a UPPError for user-initiated request cancellation.\n *\n * Use this when the request is aborted via a user-provided AbortSignal,\n * distinct from timeout-based cancellation.\n *\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with CANCELLED code\n */\nexport function cancelledError(provider: string, modality: Modality): UPPError {\n return new UPPError(\n 'Request was cancelled',\n ErrorCode.Cancelled,\n provider,\n modality\n );\n}\n","/**\n * HTTP fetch utilities with retry, timeout, and error normalization.\n * @module http/fetch\n */\n\nimport type { ProviderConfig, RetryStrategy } from '../types/provider.ts';\nimport type { Modality } from '../types/errors.ts';\nimport { UPPError } from '../types/errors.ts';\nimport {\n normalizeHttpError,\n networkError,\n timeoutError,\n cancelledError,\n} from './errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/** Default request timeout in milliseconds (2 minutes). */\nconst DEFAULT_TIMEOUT = 120000;\nconst MAX_RETRY_AFTER_SECONDS = 3600;\n\n/**\n * Warns when a non-TLS URL is used with a provider.\n * Only warns in non-production, excludes localhost for local development.\n */\nexport function warnInsecureUrl(url: string, provider: string): void {\n if (\n process.env.NODE_ENV !== 'production' &&\n url.startsWith('http://') &&\n !url.includes('localhost') &&\n !url.includes('127.0.0.1') &&\n !url.includes('[::1]')\n ) {\n console.warn(\n `[UPP] Provider \"${provider}\" using non-TLS URL: ${url}. ` +\n 'API keys may be exposed to network interception.'\n );\n }\n}\n\ntype ForkableRetryStrategy = RetryStrategy & {\n fork: () => RetryStrategy | undefined;\n};\n\nfunction hasFork(strategy: RetryStrategy | undefined): strategy is ForkableRetryStrategy {\n return !!strategy && typeof (strategy as { fork?: unknown }).fork === 'function';\n}\n\n/**\n * Delays execution for a specified duration.\n *\n * @param ms - Duration to sleep in milliseconds\n * @returns Promise that resolves after the specified delay\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Parses Retry-After header values into seconds.\n *\n * Supports both delta-seconds and HTTP-date formats.\n */\nfunction parseRetryAfter(headerValue: string | null, maxSeconds: number): number | null {\n if (!headerValue) {\n return null;\n }\n\n const seconds = parseInt(headerValue, 10);\n if (!Number.isNaN(seconds)) {\n return Math.min(maxSeconds, Math.max(0, seconds));\n }\n\n const dateMillis = Date.parse(headerValue);\n if (Number.isNaN(dateMillis)) {\n return null;\n }\n\n const deltaMs = dateMillis - Date.now();\n if (deltaMs <= 0) {\n return 0;\n }\n\n const deltaSeconds = Math.ceil(deltaMs / 1000);\n return Math.min(maxSeconds, Math.max(0, deltaSeconds));\n}\n\n/**\n * Executes a fetch request with configurable timeout.\n *\n * Creates an AbortController to cancel the request if it exceeds the timeout.\n * Properly handles both user-provided abort signals and timeout-based cancellation,\n * throwing appropriate error types for each case.\n *\n * @param fetchFn - The fetch function to use (allows custom implementations)\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param timeout - Maximum time in milliseconds before aborting\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response from the fetch call\n *\n * @throws {UPPError} TIMEOUT - When the timeout is exceeded\n * @throws {UPPError} CANCELLED - When cancelled via user-provided signal\n * @throws {Error} Network errors are passed through unchanged\n */\nasync function fetchWithTimeout(\n fetchFn: typeof fetch,\n url: string,\n init: RequestInit,\n timeout: number,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const existingSignal = init.signal;\n\n // Check if already aborted before starting\n if (existingSignal?.aborted) {\n throw cancelledError(provider, modality);\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const onAbort = () => controller.abort();\n if (existingSignal) {\n existingSignal.addEventListener('abort', onAbort, { once: true });\n }\n\n try {\n const response = await fetchFn(url, {\n ...init,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n if (toError(error).name === 'AbortError') {\n if (existingSignal?.aborted) {\n throw cancelledError(provider, modality);\n }\n throw timeoutError(timeout, provider, modality);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n if (existingSignal) {\n existingSignal.removeEventListener('abort', onAbort);\n }\n }\n}\n\n/**\n * Executes an HTTP fetch request with automatic retry, timeout handling, and error normalization.\n *\n * This function wraps the standard fetch API with additional capabilities:\n * - Configurable timeout with automatic request cancellation (per attempt)\n * - Retry strategy support (exponential backoff, linear, token bucket, etc.)\n * - Pre-request delay support for rate limiting strategies\n * - Automatic Retry-After header parsing and handling\n * - Error normalization to UPPError format\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options (method, headers, body, etc.)\n * @param config - Provider configuration containing fetch customization, timeout, and retry strategy\n * @param provider - Provider identifier for error context (e.g., 'openai', 'anthropic')\n * @param modality - Request modality for error context (e.g., 'llm', 'embedding', 'image')\n * @returns The successful Response object\n *\n * @throws {UPPError} RATE_LIMITED - When rate limited and retries exhausted\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n * @throws {UPPError} Various codes based on HTTP status (see statusToErrorCode)\n *\n * @example\n * ```typescript\n * const response = await doFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [] })\n * },\n * { timeout: 30000, retryStrategy: new ExponentialBackoff() },\n * 'openai',\n * 'llm'\n * );\n * ```\n */\nexport async function doFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const baseStrategy = config.retryStrategy;\n const strategy = hasFork(baseStrategy) ? baseStrategy.fork() : baseStrategy;\n\n // Warn about potential security issue with non-TLS URLs\n warnInsecureUrl(url, provider);\n\n let attempt = 0;\n\n while (true) {\n attempt++;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n let response: Response;\n try {\n response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n } catch (error) {\n if (error instanceof UPPError) {\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n throw error;\n }\n\n const uppError = networkError(toError(error), provider, modality);\n\n if (strategy) {\n const delay = await strategy.onRetry(uppError, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n\n throw uppError;\n }\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, provider, modality);\n\n const retryAfterSeconds = parseRetryAfter(\n response.headers.get('Retry-After'),\n config.retryAfterMaxSeconds ?? MAX_RETRY_AFTER_SECONDS\n );\n if (retryAfterSeconds !== null && strategy && 'setRetryAfter' in strategy) {\n (strategy as { setRetryAfter: (s: number) => void }).setRetryAfter(\n retryAfterSeconds\n );\n }\n\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n\n throw error;\n }\n\n strategy?.reset?.();\n\n return response;\n }\n}\n\n/**\n * Executes an HTTP fetch request for streaming responses.\n *\n * Unlike {@link doFetch}, this function returns the response immediately without\n * checking the HTTP status. This is necessary for Server-Sent Events (SSE) and\n * other streaming protocols where error information may be embedded in the stream.\n *\n * The caller is responsible for:\n * - Checking response.ok and handling HTTP errors\n * - Parsing the response stream (e.g., using parseSSEStream)\n * - Handling stream-specific error conditions\n *\n * Retries are not performed for streaming requests since partial data may have\n * already been consumed by the caller.\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param config - Provider configuration containing fetch customization and timeout\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response object (may have non-2xx status)\n *\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n *\n * @example\n * ```typescript\n * const response = await doStreamFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [], stream: true })\n * },\n * { timeout: 120000 },\n * 'openai',\n * 'llm'\n * );\n *\n * if (!response.ok) {\n * throw await normalizeHttpError(response, 'openai', 'llm');\n * }\n *\n * for await (const event of parseSSEStream(response.body!)) {\n * console.log(event);\n * }\n * ```\n */\nexport async function doStreamFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const baseStrategy = config.retryStrategy;\n const strategy = hasFork(baseStrategy) ? baseStrategy.fork() : baseStrategy;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n try {\n const response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n return response;\n } catch (error) {\n if (error instanceof UPPError) {\n throw error;\n }\n throw networkError(toError(error), provider, modality);\n }\n}\n"],"mappings":"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","names":[]}
@@ -163,90 +163,7 @@ function isToolResultMessage(msg) {
163
163
  return msg.type === MessageRole.ToolResult;
164
164
  }
165
165
 
166
- // src/core/provider-handlers.ts
167
- function isHandlerResolver(value) {
168
- return value !== void 0 && "handlers" in value && "getMode" in value;
169
- }
170
- var providerHandlers = /* @__PURE__ */ new WeakMap();
171
- function registerProviderHandlers(provider, handlers) {
172
- providerHandlers.set(provider, handlers);
173
- }
174
- function getProviderHandlers(provider) {
175
- return providerHandlers.get(provider);
176
- }
177
- function resolveLLMHandler(provider, options) {
178
- const handlers = getProviderHandlers(provider);
179
- const resolver = handlers?.llmResolver;
180
- if (resolver) {
181
- const mode = resolver.getMode(options);
182
- return resolver.handlers[mode] ?? handlers?.llm;
183
- }
184
- return handlers?.llm;
185
- }
186
- function resolveEmbeddingHandler(provider) {
187
- const handlers = getProviderHandlers(provider);
188
- return handlers?.embedding;
189
- }
190
- function resolveImageHandler(provider) {
191
- const handlers = getProviderHandlers(provider);
192
- return handlers?.image;
193
- }
194
-
195
- // src/core/provider.ts
196
- function createProvider(options) {
197
- const llmInput = options.handlers.llm;
198
- const hasResolver = isHandlerResolver(llmInput);
199
- const defaultLLMHandler = hasResolver ? llmInput.handlers[llmInput.defaultMode] : llmInput;
200
- if (hasResolver && !defaultLLMHandler) {
201
- throw new Error(
202
- `Provider '${options.name}' LLM resolver defaultMode '${llmInput.defaultMode}' has no handler`
203
- );
204
- }
205
- const fn = function(modelId, modelOptions) {
206
- if (options.createModelReference) {
207
- return options.createModelReference(modelId, modelOptions, provider);
208
- }
209
- return { modelId, provider, options: modelOptions };
210
- };
211
- Object.defineProperties(fn, {
212
- name: {
213
- value: options.name,
214
- writable: false,
215
- configurable: true
216
- },
217
- version: {
218
- value: options.version,
219
- writable: false,
220
- configurable: true
221
- }
222
- });
223
- const provider = fn;
224
- if (hasResolver) {
225
- for (const handler of Object.values(llmInput.handlers)) {
226
- handler._setProvider?.(provider);
227
- }
228
- } else if (defaultLLMHandler?._setProvider) {
229
- defaultLLMHandler._setProvider(provider);
230
- }
231
- if (options.handlers.embedding?._setProvider) {
232
- options.handlers.embedding._setProvider(provider);
233
- }
234
- if (options.handlers.image?._setProvider) {
235
- options.handlers.image._setProvider(provider);
236
- }
237
- registerProviderHandlers(provider, {
238
- llm: defaultLLMHandler,
239
- embedding: options.handlers.embedding,
240
- image: options.handlers.image,
241
- ...hasResolver ? { llmResolver: llmInput } : {}
242
- });
243
- return provider;
244
- }
245
-
246
166
  export {
247
- resolveLLMHandler,
248
- resolveEmbeddingHandler,
249
- resolveImageHandler,
250
167
  generateId,
251
168
  MessageRole,
252
169
  Message,
@@ -255,7 +172,6 @@ export {
255
172
  ToolResultMessage,
256
173
  isUserMessage,
257
174
  isAssistantMessage,
258
- isToolResultMessage,
259
- createProvider
175
+ isToolResultMessage
260
176
  };
261
- //# sourceMappingURL=chunk-ILR2D5PN.js.map
177
+ //# sourceMappingURL=chunk-BRP5XJ6Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/id.ts","../src/types/messages.ts"],"sourcesContent":["/**\n * @fileoverview ID generation utilities for the Universal Provider Protocol.\n *\n * Provides functions for generating unique identifiers used throughout UPP,\n * including message IDs, tool call IDs, and other internal references.\n *\n * @module utils/id\n */\n\n/**\n * Generates a unique UUID v4 identifier.\n *\n * Uses the native `crypto.randomUUID()` when available for cryptographically\n * secure randomness. Falls back to a Math.random-based implementation for\n * environments without Web Crypto API support.\n *\n * @returns A UUID v4 string in the format `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`\n *\n * @example\n * ```typescript\n * const messageId = generateId();\n * // => \"f47ac10b-58cc-4372-a567-0e02b2c3d479\"\n * ```\n */\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Generates a short alphanumeric identifier.\n *\n * Creates a 12-character random string using alphanumeric characters (a-z, A-Z, 0-9).\n * Useful for tool call IDs and other cases where a full UUID is not required.\n *\n * @param prefix - Optional prefix to prepend to the generated ID\n * @returns A string containing the prefix followed by 12 random alphanumeric characters\n *\n * @example\n * ```typescript\n * const toolCallId = generateShortId('call_');\n * // => \"call_aB3xY9mK2pQr\"\n *\n * const simpleId = generateShortId();\n * // => \"Tz4wN8vL1sHj\"\n * ```\n */\nexport function generateShortId(prefix = ''): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let result = prefix;\n for (let i = 0; i < 12; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n","/**\n * @fileoverview Message types for conversation history.\n *\n * Defines the message classes used to represent conversation turns\n * between users and assistants, including support for multimodal\n * content and tool calls.\n *\n * @module types/messages\n */\n\nimport { generateId } from '../utils/id.ts';\nimport type {\n ContentBlock,\n TextBlock,\n ReasoningBlock,\n ImageBlock,\n DocumentBlock,\n AudioBlock,\n VideoBlock,\n UserContent,\n AssistantContent,\n} from './content.ts';\nimport type { ToolCall, ToolResult } from './tool.ts';\n\n/**\n * Message serialized to JSON format.\n * Picks common fields from Message, converts timestamp to string.\n */\nexport type MessageJSON = Pick<Message, 'id' | 'type' | 'metadata'> & {\n timestamp: string;\n content: ContentBlock[];\n toolCalls?: ToolCall[];\n results?: ToolResult[];\n};\n\n/**\n * Message role/type constants.\n *\n * Use these constants instead of raw strings for type-safe message handling:\n *\n * @example\n * ```typescript\n * import { MessageRole, isUserMessage } from 'upp';\n *\n * if (message.type === MessageRole.User) {\n * console.log('User said:', message.text);\n * } else if (message.type === MessageRole.Assistant) {\n * console.log('Assistant replied:', message.text);\n * }\n * ```\n */\nexport const MessageRole = {\n /** User message */\n User: 'user',\n /** Assistant/model response */\n Assistant: 'assistant',\n /** Tool execution result */\n ToolResult: 'tool_result',\n} as const;\n\n/**\n * Message type discriminator union.\n *\n * This type is derived from {@link MessageRole} constants. The name `MessageType`\n * is kept for backward compatibility; `MessageRole` works as both the const\n * object and this type.\n */\nexport type MessageType = (typeof MessageRole)[keyof typeof MessageRole];\n\n/**\n * Type alias for MessageType, allowing `MessageRole` to work as both const and type.\n */\nexport type MessageRole = MessageType;\n\n/**\n * Provider-namespaced metadata for messages.\n *\n * Each provider can attach its own metadata under its namespace,\n * preventing conflicts between different providers.\n *\n * @example\n * ```typescript\n * const metadata: MessageMetadata = {\n * openai: { model: 'gpt-4', finishReason: 'stop' },\n * anthropic: { model: 'claude-3', stopReason: 'end_turn' }\n * };\n * ```\n */\nexport interface MessageMetadata {\n [provider: string]: Record<string, unknown> | undefined;\n}\n\n/**\n * Options for constructing messages.\n */\nexport interface MessageOptions {\n /** Custom message ID (auto-generated if not provided) */\n id?: string;\n\n /** Provider-specific metadata */\n metadata?: MessageMetadata;\n}\n\n/**\n * Abstract base class for all message types.\n *\n * Provides common functionality for user, assistant, and tool result\n * messages, including content accessors and metadata handling.\n *\n * @example\n * ```typescript\n * // Access text content from any message\n * const text = message.text;\n *\n * // Access images\n * const images = message.images;\n * ```\n */\nexport abstract class Message {\n /** Unique message identifier */\n readonly id: string;\n\n /** Timestamp when the message was created */\n readonly timestamp: Date;\n\n /** Provider-specific metadata, namespaced by provider name */\n readonly metadata?: MessageMetadata;\n\n /** Message type discriminator (implemented by subclasses) */\n abstract readonly type: MessageType;\n\n /**\n * Returns the content blocks for this message.\n * Implemented by subclasses to provide type-specific content.\n */\n protected abstract getContent(): ContentBlock[];\n\n /**\n * Creates a new message instance.\n *\n * @param options - Optional message ID and metadata\n */\n constructor(options?: MessageOptions) {\n this.id = options?.id ?? generateId();\n this.timestamp = new Date();\n this.metadata = options?.metadata;\n }\n\n /**\n * Concatenated text content from all text blocks.\n * Blocks are joined with double newlines.\n */\n get text(): string {\n return this.getContent()\n .filter((block): block is TextBlock => block.type === 'text')\n .map((block) => block.text)\n .join('\\n\\n');\n }\n\n /**\n * All image content blocks in this message.\n */\n get images(): ImageBlock[] {\n return this.getContent().filter((block): block is ImageBlock => block.type === 'image');\n }\n\n /**\n * All document content blocks in this message.\n */\n get documents(): DocumentBlock[] {\n return this.getContent().filter((block): block is DocumentBlock => block.type === 'document');\n }\n\n /**\n * All audio content blocks in this message.\n */\n get audio(): AudioBlock[] {\n return this.getContent().filter((block): block is AudioBlock => block.type === 'audio');\n }\n\n /**\n * All video content blocks in this message.\n */\n get video(): VideoBlock[] {\n return this.getContent().filter((block): block is VideoBlock => block.type === 'video');\n }\n\n /**\n * All reasoning/thinking content blocks in this message.\n * Available when using extended thinking models.\n */\n get reasoning(): ReasoningBlock[] {\n return this.getContent().filter((block): block is ReasoningBlock => block.type === 'reasoning');\n }\n}\n\n/**\n * User input message.\n *\n * Represents a message from the user, which can contain text and/or\n * multimodal content like images, audio, or video.\n *\n * @example\n * ```typescript\n * // Simple text message\n * const msg = new UserMessage('Hello, world!');\n *\n * // Multimodal message\n * const msg = new UserMessage([\n * { type: 'text', text: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', url: '...' }, mimeType: 'image/png' }\n * ]);\n * ```\n */\nexport class UserMessage extends Message {\n /** Message type discriminator */\n readonly type = 'user' as const;\n\n /** Content blocks in this message */\n readonly content: UserContent[];\n\n /**\n * Creates a new user message.\n *\n * @param content - String (converted to TextBlock) or array of content blocks\n * @param options - Optional message ID and metadata\n */\n constructor(content: string | UserContent[], options?: MessageOptions) {\n super(options);\n if (typeof content === 'string') {\n this.content = [{ type: 'text', text: content }];\n } else {\n this.content = content;\n }\n }\n\n protected getContent(): ContentBlock[] {\n return this.content;\n }\n}\n\n/**\n * Assistant response message.\n *\n * Represents a response from the AI assistant, which may contain\n * text, media content, and/or tool call requests.\n *\n * @example\n * ```typescript\n * // Simple text response\n * const msg = new AssistantMessage('Hello! How can I help?');\n *\n * // Response with tool calls\n * const msg = new AssistantMessage(\n * 'Let me check the weather...',\n * [{ toolCallId: 'call_1', toolName: 'get_weather', arguments: { location: 'NYC' } }]\n * );\n * ```\n */\nexport class AssistantMessage extends Message {\n /** Message type discriminator */\n readonly type = 'assistant' as const;\n\n /** Content blocks in this message */\n readonly content: AssistantContent[];\n\n /** Tool calls requested by the model (if any) */\n readonly toolCalls?: ToolCall[];\n\n /**\n * Creates a new assistant message.\n *\n * @param content - String (converted to TextBlock) or array of content blocks\n * @param toolCalls - Tool calls requested by the model\n * @param options - Optional message ID and metadata\n */\n constructor(\n content: string | AssistantContent[],\n toolCalls?: ToolCall[],\n options?: MessageOptions\n ) {\n super(options);\n if (typeof content === 'string') {\n this.content = [{ type: 'text', text: content }];\n } else {\n this.content = content;\n }\n this.toolCalls = toolCalls;\n }\n\n protected getContent(): ContentBlock[] {\n return this.content;\n }\n\n /**\n * Whether this message contains tool call requests.\n */\n get hasToolCalls(): boolean {\n return this.toolCalls !== undefined && this.toolCalls.length > 0;\n }\n}\n\n/**\n * Tool execution result message.\n *\n * Contains the results of executing one or more tool calls,\n * sent back to the model for further processing.\n *\n * @example\n * ```typescript\n * const msg = new ToolResultMessage([\n * { toolCallId: 'call_1', result: { temperature: 72, conditions: 'sunny' } },\n * { toolCallId: 'call_2', result: 'File not found', isError: true }\n * ]);\n * ```\n */\nexport class ToolResultMessage extends Message {\n /** Message type discriminator */\n readonly type = 'tool_result' as const;\n\n /** Results from tool executions */\n readonly results: ToolResult[];\n\n /**\n * Creates a new tool result message.\n *\n * @param results - Array of tool execution results\n * @param options - Optional message ID and metadata\n */\n constructor(results: ToolResult[], options?: MessageOptions) {\n super(options);\n this.results = results;\n }\n\n protected getContent(): ContentBlock[] {\n return this.results.map((result) => ({\n type: 'text' as const,\n text:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n }\n}\n\n/**\n * Type guard for UserMessage.\n *\n * @param msg - The message to check\n * @returns True if the message is a UserMessage\n *\n * @example\n * ```typescript\n * if (isUserMessage(msg)) {\n * console.log('User said:', msg.text);\n * }\n * ```\n */\nexport function isUserMessage(msg: Message): msg is UserMessage {\n return msg.type === MessageRole.User;\n}\n\n/**\n * Type guard for AssistantMessage.\n *\n * @param msg - The message to check\n * @returns True if the message is an AssistantMessage\n *\n * @example\n * ```typescript\n * if (isAssistantMessage(msg)) {\n * console.log('Assistant said:', msg.text);\n * if (msg.hasToolCalls) {\n * console.log('Tool calls:', msg.toolCalls);\n * }\n * }\n * ```\n */\nexport function isAssistantMessage(msg: Message): msg is AssistantMessage {\n return msg.type === MessageRole.Assistant;\n}\n\n/**\n * Type guard for ToolResultMessage.\n *\n * @param msg - The message to check\n * @returns True if the message is a ToolResultMessage\n *\n * @example\n * ```typescript\n * if (isToolResultMessage(msg)) {\n * for (const result of msg.results) {\n * console.log(`Tool ${result.toolCallId}:`, result.result);\n * }\n * }\n * ```\n */\nexport function isToolResultMessage(msg: Message): msg is ToolResultMessage {\n return msg.type === MessageRole.ToolResult;\n}\n"],"mappings":";AAwBO,SAAS,aAAqB;AACnC,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;;;ACiBO,IAAM,cAAc;AAAA;AAAA,EAEzB,MAAM;AAAA;AAAA,EAEN,WAAW;AAAA;AAAA,EAEX,YAAY;AACd;AA4DO,IAAe,UAAf,MAAuB;AAAA;AAAA,EAEnB;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBT,YAAY,SAA0B;AACpC,SAAK,KAAK,SAAS,MAAM,WAAW;AACpC,SAAK,YAAY,oBAAI,KAAK;AAC1B,SAAK,WAAW,SAAS;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAe;AACjB,WAAO,KAAK,WAAW,EACpB,OAAO,CAAC,UAA8B,MAAM,SAAS,MAAM,EAC3D,IAAI,CAAC,UAAU,MAAM,IAAI,EACzB,KAAK,MAAM;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAuB;AACzB,WAAO,KAAK,WAAW,EAAE,OAAO,CAAC,UAA+B,MAAM,SAAS,OAAO;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,WAAW,EAAE,OAAO,CAAC,UAAkC,MAAM,SAAS,UAAU;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAsB;AACxB,WAAO,KAAK,WAAW,EAAE,OAAO,CAAC,UAA+B,MAAM,SAAS,OAAO;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAsB;AACxB,WAAO,KAAK,WAAW,EAAE,OAAO,CAAC,UAA+B,MAAM,SAAS,OAAO;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,YAA8B;AAChC,WAAO,KAAK,WAAW,EAAE,OAAO,CAAC,UAAmC,MAAM,SAAS,WAAW;AAAA,EAChG;AACF;AAoBO,IAAM,cAAN,cAA0B,QAAQ;AAAA;AAAA,EAE9B,OAAO;AAAA;AAAA,EAGP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,YAAY,SAAiC,SAA0B;AACrE,UAAM,OAAO;AACb,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,UAAU,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,IACjD,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAEU,aAA6B;AACrC,WAAO,KAAK;AAAA,EACd;AACF;AAoBO,IAAM,mBAAN,cAA+B,QAAQ;AAAA;AAAA,EAEnC,OAAO;AAAA;AAAA,EAGP;AAAA;AAAA,EAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,YACE,SACA,WACA,SACA;AACA,UAAM,OAAO;AACb,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,UAAU,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,IACjD,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEU,aAA6B;AACrC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,eAAwB;AAC1B,WAAO,KAAK,cAAc,UAAa,KAAK,UAAU,SAAS;AAAA,EACjE;AACF;AAgBO,IAAM,oBAAN,cAAgC,QAAQ;AAAA;AAAA,EAEpC,OAAO;AAAA;AAAA,EAGP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,YAAY,SAAuB,SAA0B;AAC3D,UAAM,OAAO;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAEU,aAA6B;AACrC,WAAO,KAAK,QAAQ,IAAI,CAAC,YAAY;AAAA,MACnC,MAAM;AAAA,MACN,MACE,OAAO,OAAO,WAAW,WACrB,OAAO,SACP,KAAK,UAAU,OAAO,MAAM;AAAA,IACpC,EAAE;AAAA,EACJ;AACF;AAeO,SAAS,cAAc,KAAkC;AAC9D,SAAO,IAAI,SAAS,YAAY;AAClC;AAkBO,SAAS,mBAAmB,KAAuC;AACxE,SAAO,IAAI,SAAS,YAAY;AAClC;AAiBO,SAAS,oBAAoB,KAAwC;AAC1E,SAAO,IAAI,SAAS,YAAY;AAClC;","names":[]}