@xisui-pa/chatserver 1.0.0 → 1.1.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.
@@ -43,8 +43,8 @@ var __webpack_unused_export__;
43
43
 
44
44
  // Keep matching config schema defaults and bounds in sync when changing them here.
45
45
  __webpack_unused_export__ = ({ value: true });
46
- __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_TTS_INPUT_CHARS = exports.MAX_VOICE_PADDING_MS = exports.MIN_VOICE_PADDING_MS = exports.AUDIO_CLEANUP_RETRY_DELAY_MS = exports.AUDIO_CLEANUP_RETRIES = exports.AUDIO_CONVERSION_STDERR_CHARS = exports.AUDIO_CONVERSION_TIMEOUT_MS = exports.MAX_TRANSCRIPT_CHARS = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_ENCODED_AUDIO_BYTES = exports.MAX_AUDIO_BYTES = __webpack_unused_export__ = exports.MAX_IMAGE_BYTES = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = void 0;
47
- __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_WEB_TEXT_CHARS = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_SETUP_CHOICES = exports.MODEL_DISCOVERY_TIMEOUT_MS = __webpack_unused_export__ = exports.MODEL_RETRY_DELAY_MS = exports.MAX_MODEL_RETRIES = exports.DEFAULT_MODEL_TIMEOUT_MS = exports.MAX_TIMER_MS = void 0;
46
+ exports.MAX_TIMER_MS = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_TTS_INPUT_CHARS = exports.MAX_VOICE_PADDING_MS = exports.MIN_VOICE_PADDING_MS = exports.AUDIO_CLEANUP_RETRY_DELAY_MS = exports.AUDIO_CLEANUP_RETRIES = exports.AUDIO_CONVERSION_STDERR_CHARS = exports.AUDIO_CONVERSION_TIMEOUT_MS = exports.MAX_TRANSCRIPT_CHARS = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_ENCODED_AUDIO_BYTES = exports.MAX_AUDIO_BYTES = __webpack_unused_export__ = exports.MAX_IMAGE_BYTES = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = void 0;
47
+ __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_WEB_TEXT_CHARS = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.MAX_SETUP_CHOICES = exports.MODEL_DISCOVERY_TIMEOUT_MS = __webpack_unused_export__ = exports.MODEL_RETRY_DELAY_MS = exports.MAX_MODEL_RETRIES = exports.DEFAULT_MODEL_TIMEOUT_MS = void 0;
48
48
  // Model input and conversation snapshots.
49
49
  /**
50
50
  * Budget calculations with the current values:
@@ -110,8 +110,8 @@ __webpack_unused_export__ = 20;
110
110
  __webpack_unused_export__ = 50;
111
111
  /** Maximum supporting source IDs per proposed memory note. */
112
112
  __webpack_unused_export__ = 64;
113
- /** Maximum characters in a stored memory ID or cited source ID. */
114
- __webpack_unused_export__ = 128;
113
+ /** Maximum characters in memory, source, and cached image IDs; accommodates encoded WeChat IDs. */
114
+ __webpack_unused_export__ = 1_024;
115
115
  // Images, voice input, and local audio conversion.
116
116
  /** Default maximum generated image width in pixels. */
117
117
  __webpack_unused_export__ = 1024;
@@ -155,17 +155,15 @@ exports.MIN_VOICE_PADDING_MS = 500;
155
155
  exports.MAX_VOICE_PADDING_MS = 2_000;
156
156
  /** Maximum characters submitted to a speech synthesis endpoint. */
157
157
  exports.MAX_TTS_INPUT_CHARS = 4_096;
158
- /** Maximum wait for a WeChat voice job to confirm delivery. */
158
+ /** Maximum wait for one WeChat text send or media job to confirm delivery. */
159
159
  __webpack_unused_export__ = 180_000;
160
- /** Interval between status checks of a submitted WeChat voice job. */
161
- __webpack_unused_export__ = 1_000;
162
- /** Deadline for a best-effort voice-job cancellation after failure or shutdown. */
160
+ /** Deadline for a best-effort send or job cancellation after failure or shutdown. */
163
161
  __webpack_unused_export__ = 5_000;
164
162
  // Model requests, pacing, and transport defaults.
165
- /** Largest duration supported by Node timers; a platform bound. */
166
- exports.MAX_TIMER_MS = 2_147_483_647;
163
+ /** Maximum configured timeout or timer delay: one hour. */
164
+ exports.MAX_TIMER_MS = 60 * 60 * 1_000;
167
165
  /** Default provider deadline for generation and transcription requests. */
168
- exports.DEFAULT_MODEL_TIMEOUT_MS = 180_000;
166
+ exports.DEFAULT_MODEL_TIMEOUT_MS = 300_000;
169
167
  /** Retries after the initial attempt of a complete model generation. */
170
168
  exports.MAX_MODEL_RETRIES = 3;
171
169
  /** Delay between complete model-generation retries. */
@@ -184,18 +182,30 @@ __webpack_unused_export__ = 180_000;
184
182
  __webpack_unused_export__ = 2;
185
183
  /** Initial delay for pending-media retries; doubles on each retry. */
186
184
  __webpack_unused_export__ = 30_000;
187
- /** Default delay after a completed WeChat poll. */
188
- __webpack_unused_export__ = 30_000;
189
- /** Default deadline for each WeChat REST request. */
185
+ /** Default base delay before reconnecting an interrupted WeChat event stream. */
186
+ __webpack_unused_export__ = 1_000;
187
+ /** Default deadline for each WeChat REST request and SSE connect/idle wait. */
190
188
  __webpack_unused_export__ = 30_000;
191
- /** Default maximum timestamp gap for adjacent same-sender bundles. */
192
- __webpack_unused_export__ = 20_000;
193
- /** Maximum exponent used by WeChat polling failure backoff. */
194
- __webpack_unused_export__ = 6;
195
- /** Chat rows requested per WeChat API page. */
189
+ /** Maximum wait for a WeChat stream reader to acknowledge cancellation. */
196
190
  __webpack_unused_export__ = 100;
197
- /** Message rows requested per WeChat API page. */
198
- __webpack_unused_export__ = 50;
191
+ /** Maximum exponent used by WeChat event-stream reconnect backoff. */
192
+ __webpack_unused_export__ = 6;
193
+ /** Maximum cached WeChat contact and group-member display names. */
194
+ __webpack_unused_export__ = 1_000;
195
+ /** Lifetime of a successfully resolved WeChat display name. */
196
+ __webpack_unused_export__ = 10 * 60_000;
197
+ /** Lifetime of a failed or empty WeChat display-name lookup. */
198
+ __webpack_unused_export__ = 60_000;
199
+ /** Largest signed 64-bit event cursor accepted from the WeChat daemon. */
200
+ __webpack_unused_export__ = 9223372036854775807n;
201
+ /** Maximum characters buffered for one unterminated SSE line. */
202
+ __webpack_unused_export__ = 1_048_576;
203
+ /** Maximum characters accepted in one SSE data payload. */
204
+ __webpack_unused_export__ = 1_048_576;
205
+ /** Maximum bytes read from a rejected SSE response body. */
206
+ __webpack_unused_export__ = 16_384;
207
+ /** Recent live message IDs retained for replay deduplication; cursors are the primary guard. */
208
+ __webpack_unused_export__ = 2_000;
199
209
  /** Default interval between ignore-file reload checks; zero disables caching. */
200
210
  __webpack_unused_export__ = 1_000;
201
211
  // Tool execution and web content.
@@ -247,6 +257,7 @@ __webpack_unused_export__ = 100;
247
257
  var __webpack_unused_export__;
248
258
 
249
259
  __webpack_unused_export__ = ({ value: true });
260
+ __webpack_unused_export__ = audioExtension;
250
261
  exports.audioFile = audioFile;
251
262
  const limit_1 = __webpack_require__(9258);
252
263
  const media_1 = __webpack_require__(2358);
@@ -255,12 +266,21 @@ const formats = {
255
266
  wav: 'audio/wav', ogg: 'audio/ogg', flac: 'audio/flac',
256
267
  m4a: 'audio/mp4', mp4: 'audio/mp4', webm: 'audio/webm'
257
268
  };
269
+ /** Maps an upstream audio MIME type to the extension used for STT uploads. */
270
+ function audioExtension(mimeType) {
271
+ const type = mimeType.split(';')[0].trim().toLowerCase();
272
+ const extension = Object.keys(formats).find((key) => formats[key] === type);
273
+ if (extension === undefined) {
274
+ throw new media_1.MediaUnavailableError(`Unsupported voice MIME type "${mimeType}"; agent-wechat must provide decoded WAV or another supported audio format.`);
275
+ }
276
+ return extension;
277
+ }
258
278
  /** Decodes supported upstream audio without writing files or attempting local conversion. */
259
279
  function audioFile(data, format, id) {
260
280
  const extension = format.toLowerCase();
261
281
  const mimeType = formats[extension];
262
282
  if (!Object.hasOwn(formats, extension)) {
263
- throw new media_1.MediaUnavailableError(`Unsupported voice format "${format}"; agent-wechat must convert SILK to MP3.`);
283
+ throw new media_1.MediaUnavailableError(`Unsupported voice format "${format}"; agent-wechat must provide decoded WAV or another supported audio format.`);
264
284
  }
265
285
  if (!data || data.length > limit_1.MAX_ENCODED_AUDIO_BYTES || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data)) {
266
286
  throw new media_1.MediaUnavailableError(`Voice data must be valid base64 no larger than ${limit_1.MAX_AUDIO_BYTES / (1024 * 1024)} MiB.`);
@@ -1239,6 +1259,7 @@ const limit_1 = __webpack_require__(9258);
1239
1259
  const node_fs_1 = __webpack_require__(3024);
1240
1260
  const node_path_1 = __importDefault(__webpack_require__(6760));
1241
1261
  const responses_client_1 = __webpack_require__(102);
1262
+ const type_guards_1 = __webpack_require__(3012);
1242
1263
  /**
1243
1264
  * Name of the file that holds model configurations inside the config folder.
1244
1265
  */
@@ -1259,28 +1280,22 @@ exports.ModelStoreError = ModelStoreError;
1259
1280
  function getModelsPath(configDir) {
1260
1281
  return node_path_1.default.join(configDir, exports.MODELS_FILE_NAME);
1261
1282
  }
1262
- /**
1263
- * Returns true for plain JSON objects (not null, arrays, or primitives).
1264
- */
1265
- function isPlainObject(value) {
1266
- return typeof value === 'object' && value !== null && !Array.isArray(value);
1267
- }
1268
1283
  /**
1269
1284
  * Validates one model entry.
1270
1285
  */
1271
1286
  function parseModelEntry(value, where, seen) {
1272
- if (!isPlainObject(value)) {
1287
+ if (!(0, type_guards_1.isRecord)(value)) {
1273
1288
  throw new ModelStoreError(`${where} must be an object.`);
1274
1289
  }
1275
- if (typeof value.id !== 'string' || value.id.trim() === '') {
1290
+ if (!(0, type_guards_1.isString)(value.id) || value.id.trim() === '') {
1276
1291
  throw new ModelStoreError(`${where}: "id" must be a non-empty string.`);
1277
1292
  }
1278
1293
  const id = value.id.trim();
1279
1294
  const name = value.name ?? id;
1280
- if (typeof name !== 'string' || name.trim() === '') {
1295
+ if (!(0, type_guards_1.isString)(name) || name.trim() === '') {
1281
1296
  throw new ModelStoreError(`${where}: "name" must be a non-empty string.`);
1282
1297
  }
1283
- if (value.supportsImageToolResults !== undefined && typeof value.supportsImageToolResults !== 'boolean') {
1298
+ if (value.supportsImageToolResults !== undefined && !(0, type_guards_1.isBoolean)(value.supportsImageToolResults)) {
1284
1299
  throw new ModelStoreError(`${where}: "supportsImageToolResults" must be a boolean.`);
1285
1300
  }
1286
1301
  const entry = {
@@ -1298,14 +1313,14 @@ function parseModelEntry(value, where, seen) {
1298
1313
  */
1299
1314
  function parseProvider(value, index, modelsPath) {
1300
1315
  const where = `${modelsPath}: providers[${index}]`;
1301
- if (!isPlainObject(value)) {
1316
+ if (!(0, type_guards_1.isRecord)(value)) {
1302
1317
  throw new ModelStoreError(`${where} must be an object.`);
1303
1318
  }
1304
- if (typeof value.provider !== 'string' || value.provider.trim() === '') {
1319
+ if (!(0, type_guards_1.isString)(value.provider) || value.provider.trim() === '') {
1305
1320
  throw new ModelStoreError(`${where}: "provider" must be a non-empty string.`);
1306
1321
  }
1307
1322
  const baseUrl = value.baseUrl;
1308
- if (typeof baseUrl !== 'string' || baseUrl.trim() === '') {
1323
+ if (!(0, type_guards_1.isString)(baseUrl) || baseUrl.trim() === '') {
1309
1324
  throw new ModelStoreError(`${where}: "baseUrl" must be a non-empty string.`);
1310
1325
  }
1311
1326
  if (!Array.isArray(value.models)) {
@@ -1348,10 +1363,10 @@ async function loadModels(configDir) {
1348
1363
  catch (error) {
1349
1364
  throw new ModelStoreError(`Invalid JSON in ${modelsPath}: ${error.message}`);
1350
1365
  }
1351
- if (!isPlainObject(parsed) || !Array.isArray(parsed.providers)) {
1366
+ if (!(0, type_guards_1.isRecord)(parsed) || !Array.isArray(parsed.providers)) {
1352
1367
  throw new ModelStoreError(`${modelsPath} must contain an object with a "providers" array.`);
1353
1368
  }
1354
- if (parsed.$schema !== undefined && typeof parsed.$schema !== 'string') {
1369
+ if (parsed.$schema !== undefined && !(0, type_guards_1.isString)(parsed.$schema)) {
1355
1370
  throw new ModelStoreError(`${modelsPath}: "$schema" must be a string.`);
1356
1371
  }
1357
1372
  const seen = new Set();
@@ -1524,9 +1539,10 @@ __webpack_unused_export__ = extractOutputText;
1524
1539
  __webpack_unused_export__ = extractCitations;
1525
1540
  exports.createResponse = createResponse;
1526
1541
  const limit_1 = __webpack_require__(9258);
1542
+ const type_guards_1 = __webpack_require__(3012);
1527
1543
  /** Validates durations against Node's timer range, including programmatic callers. */
1528
1544
  function resolveModelTimeoutMs(value = limit_1.DEFAULT_MODEL_TIMEOUT_MS) {
1529
- if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1 || value > limit_1.MAX_TIMER_MS) {
1545
+ if (!(0, type_guards_1.isNumber)(value) || !Number.isSafeInteger(value) || value < 1 || value > limit_1.MAX_TIMER_MS) {
1530
1546
  throw new Error(`Model timeout must be an integer from 1 to ${limit_1.MAX_TIMER_MS} milliseconds.`);
1531
1547
  }
1532
1548
  return value;
@@ -1545,12 +1561,6 @@ class ResponsesApiError extends Error {
1545
1561
  }
1546
1562
  }
1547
1563
  exports.ResponsesApiError = ResponsesApiError;
1548
- /**
1549
- * Returns true for plain JSON objects (not null, arrays, or primitives).
1550
- */
1551
- function isRecord(value) {
1552
- return typeof value === 'object' && value !== null && !Array.isArray(value);
1553
- }
1554
1564
  /**
1555
1565
  * Builds the endpoint used for Responses API calls.
1556
1566
  */
@@ -1564,10 +1574,10 @@ function responsesEndpoint(baseUrl) {
1564
1574
  * back to a top-level `output_text` string for compatible servers that use it.
1565
1575
  */
1566
1576
  function extractOutputText(payload) {
1567
- if (!isRecord(payload)) {
1577
+ if (!(0, type_guards_1.isRecord)(payload)) {
1568
1578
  return '';
1569
1579
  }
1570
- if (typeof payload.output_text === 'string') {
1580
+ if ((0, type_guards_1.isString)(payload.output_text)) {
1571
1581
  return payload.output_text.trim();
1572
1582
  }
1573
1583
  if (!Array.isArray(payload.output)) {
@@ -1575,11 +1585,11 @@ function extractOutputText(payload) {
1575
1585
  }
1576
1586
  const parts = [];
1577
1587
  for (const item of payload.output) {
1578
- if (!isRecord(item) || !Array.isArray(item.content)) {
1588
+ if (!(0, type_guards_1.isRecord)(item) || !Array.isArray(item.content)) {
1579
1589
  continue;
1580
1590
  }
1581
1591
  for (const content of item.content) {
1582
- if (isRecord(content) && content.type === 'output_text' && typeof content.text === 'string') {
1592
+ if ((0, type_guards_1.isRecord)(content) && content.type === 'output_text' && (0, type_guards_1.isString)(content.text)) {
1583
1593
  parts.push(content.text);
1584
1594
  }
1585
1595
  }
@@ -1588,20 +1598,20 @@ function extractOutputText(payload) {
1588
1598
  }
1589
1599
  /** Retains URL citations even when generated text uses provider-specific markers. */
1590
1600
  function extractCitations(payload) {
1591
- if (!isRecord(payload) || !Array.isArray(payload.output)) {
1601
+ if (!(0, type_guards_1.isRecord)(payload) || !Array.isArray(payload.output)) {
1592
1602
  return [];
1593
1603
  }
1594
1604
  const urls = new Set();
1595
1605
  for (const item of payload.output) {
1596
- if (!isRecord(item) || item.type !== 'message' || !Array.isArray(item.content)) {
1606
+ if (!(0, type_guards_1.isRecord)(item) || item.type !== 'message' || !Array.isArray(item.content)) {
1597
1607
  continue;
1598
1608
  }
1599
1609
  for (const content of item.content) {
1600
- if (!isRecord(content) || content.type !== 'output_text' || !Array.isArray(content.annotations)) {
1610
+ if (!(0, type_guards_1.isRecord)(content) || content.type !== 'output_text' || !Array.isArray(content.annotations)) {
1601
1611
  continue;
1602
1612
  }
1603
1613
  for (const annotation of content.annotations) {
1604
- if (!isRecord(annotation) || annotation.type !== 'url_citation' || typeof annotation.url !== 'string') {
1614
+ if (!(0, type_guards_1.isRecord)(annotation) || annotation.type !== 'url_citation' || !(0, type_guards_1.isString)(annotation.url)) {
1605
1615
  continue;
1606
1616
  }
1607
1617
  try {
@@ -1676,11 +1686,11 @@ async function createResponse(request) {
1676
1686
  catch (error) {
1677
1687
  throw new ResponsesApiError(`Response from ${url} was not valid JSON: ${error.message}`, response.status, text);
1678
1688
  }
1679
- if (isRecord(parsed) && ((parsed.error !== undefined && parsed.error !== null) || parsed.status === 'failed' || parsed.status === 'incomplete')) {
1689
+ if ((0, type_guards_1.isRecord)(parsed) && ((parsed.error !== undefined && parsed.error !== null) || parsed.status === 'failed' || parsed.status === 'incomplete')) {
1680
1690
  throw new ResponsesApiError(`Response from ${url} did not complete successfully.`, response.status);
1681
1691
  }
1682
1692
  const result = { outputText: extractOutputText(parsed), citations: extractCitations(parsed), raw: parsed };
1683
- if (isRecord(parsed) && typeof parsed.id === 'string') {
1693
+ if ((0, type_guards_1.isRecord)(parsed) && (0, type_guards_1.isString)(parsed.id)) {
1684
1694
  result.id = parsed.id;
1685
1695
  }
1686
1696
  return result;
@@ -1754,6 +1764,7 @@ exports.setupInteractive = setupInteractive;
1754
1764
  exports.setupModelId = setupModelId;
1755
1765
  const limit_1 = __webpack_require__(9258);
1756
1766
  const cli_support_1 = __webpack_require__(8256);
1767
+ const type_guards_1 = __webpack_require__(3012);
1757
1768
  /** Reads an optional nonempty, trimmed command-line value. */
1758
1769
  function setupValue(cli, key, flag) {
1759
1770
  const value = (0, cli_support_1.optionalValue)(cli, key)?.trim();
@@ -1864,10 +1875,10 @@ async function setupModelId(provider, apiKey) {
1864
1875
  throw new Error(`HTTP ${response.status}`);
1865
1876
  }
1866
1877
  const body = await response.json();
1867
- if (typeof body !== 'object' || body === null || !('data' in body) || !Array.isArray(body.data)) {
1878
+ if (!(0, type_guards_1.isObject)(body) || !('data' in body) || !Array.isArray(body.data)) {
1868
1879
  throw new Error('Unsupported model list');
1869
1880
  }
1870
- ids = [...new Set(body.data.flatMap((model) => typeof model === 'object' && model !== null && 'id' in model && typeof model.id === 'string' && model.id.trim()
1881
+ ids = [...new Set(body.data.flatMap((model) => (0, type_guards_1.isObject)(model) && 'id' in model && (0, type_guards_1.isString)(model.id) && model.id.trim()
1871
1882
  ? [model.id.trim()] : []))].sort();
1872
1883
  }
1873
1884
  catch {
@@ -2071,6 +2082,7 @@ const command_support_1 = __webpack_require__(2327);
2071
2082
  const speech_client_1 = __webpack_require__(5314);
2072
2083
  const voice_synthesizer_1 = __webpack_require__(7393);
2073
2084
  const retry_1 = __webpack_require__(7718);
2085
+ const type_guards_1 = __webpack_require__(3012);
2074
2086
  const DEFAULT_SPEECH_TEST_TEXT = 'Hello! This is a speech synthesis test.';
2075
2087
  exports.TEST_TTS_HELP = `Usage: chatserver-models test-tts --provider <name> --name <name> [options]
2076
2088
 
@@ -2106,7 +2118,7 @@ function speechOptions(encoded) {
2106
2118
  throw new cli_support_1.CliError('--options must contain a valid JSON object.');
2107
2119
  }
2108
2120
  }
2109
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
2121
+ if (!(0, type_guards_1.isRecord)(value)) {
2110
2122
  throw new cli_support_1.CliError('--options must contain a JSON object.');
2111
2123
  }
2112
2124
  for (const key of ['model', 'input', 'response_format', 'stream_format']) {
@@ -2264,6 +2276,7 @@ __webpack_unused_export__ = ({ value: true });
2264
2276
  exports.createTranscription = createTranscription;
2265
2277
  const limit_1 = __webpack_require__(9258);
2266
2278
  const responses_client_1 = __webpack_require__(102);
2279
+ const type_guards_1 = __webpack_require__(3012);
2267
2280
  /** Calls an OpenAI-compatible audio/transcriptions endpoint using native multipart upload. */
2268
2281
  async function createTranscription(request) {
2269
2282
  request.signal?.throwIfAborted();
@@ -2301,8 +2314,8 @@ async function createTranscription(request) {
2301
2314
  }
2302
2315
  throw new responses_client_1.ResponsesApiError('Transcription model returned invalid JSON.', response.status);
2303
2316
  }
2304
- if (typeof result !== 'object' || result === null || Array.isArray(result) ||
2305
- !('text' in result) || typeof result.text !== 'string' || result.text.length > limit_1.MAX_TRANSCRIPT_CHARS) {
2317
+ if (!(0, type_guards_1.isRecord)(result) ||
2318
+ !('text' in result) || !(0, type_guards_1.isString)(result.text) || result.text.length > limit_1.MAX_TRANSCRIPT_CHARS) {
2306
2319
  throw new responses_client_1.ResponsesApiError(`Transcription model must return a text string of at most ${limit_1.MAX_TRANSCRIPT_CHARS} characters.`, response.status);
2307
2320
  }
2308
2321
  return result.text.trim();
@@ -2576,6 +2589,50 @@ class VoiceSynthesizer {
2576
2589
  __webpack_unused_export__ = VoiceSynthesizer;
2577
2590
 
2578
2591
 
2592
+ /***/ },
2593
+
2594
+ /***/ 3012
2595
+ (__unused_webpack_module, exports) {
2596
+
2597
+ var __webpack_unused_export__;
2598
+
2599
+ /**
2600
+ * Shared runtime type guards for values that arrive as untrusted data, such as
2601
+ * parsed JSON, provider responses, and configuration files.
2602
+ */
2603
+ __webpack_unused_export__ = ({ value: true });
2604
+ exports.isString = isString;
2605
+ exports.isNumber = isNumber;
2606
+ exports.isBoolean = isBoolean;
2607
+ exports.isObject = isObject;
2608
+ exports.isRecord = isRecord;
2609
+ __webpack_unused_export__ = isFunction;
2610
+ /** Returns true when `value` is a string. */
2611
+ function isString(value) {
2612
+ return typeof value === 'string';
2613
+ }
2614
+ /** Returns true when `value` is a number, including `NaN` and infinities. */
2615
+ function isNumber(value) {
2616
+ return typeof value === 'number';
2617
+ }
2618
+ /** Returns true when `value` is a boolean. */
2619
+ function isBoolean(value) {
2620
+ return typeof value === 'boolean';
2621
+ }
2622
+ /** Returns true for any non-null object, including arrays, dates, and maps. */
2623
+ function isObject(value) {
2624
+ return typeof value === 'object' && value !== null;
2625
+ }
2626
+ /** Returns true for non-null objects that are not arrays; functions are rejected. */
2627
+ function isRecord(value) {
2628
+ return isObject(value) && !Array.isArray(value);
2629
+ }
2630
+ /** Returns true for functions, typed as callable so callers need no assertions. */
2631
+ function isFunction(value) {
2632
+ return typeof value === 'function';
2633
+ }
2634
+
2635
+
2579
2636
  /***/ },
2580
2637
 
2581
2638
  /***/ 1421