librechat-data-provider 0.8.402 → 0.8.404

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 (109) hide show
  1. package/dist/index.es.js +1 -1
  2. package/dist/index.es.js.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/react-query/index.es.js +1 -1
  6. package/dist/react-query/index.es.js.map +1 -1
  7. package/dist/types/accessPermissions.d.ts +744 -0
  8. package/dist/types/actions.d.ts +118 -0
  9. package/dist/types/api-endpoints.d.ts +150 -0
  10. package/dist/types/artifacts.d.ts +97 -0
  11. package/dist/types/azure.d.ts +22 -0
  12. package/dist/types/bedrock.d.ts +1220 -0
  13. package/dist/types/config.d.ts +14849 -0
  14. package/dist/types/config.spec.d.ts +1 -0
  15. package/dist/types/createPayload.d.ts +5 -0
  16. package/dist/types/data-service.d.ts +287 -0
  17. package/dist/types/feedback.d.ts +36 -0
  18. package/dist/types/file-config.d.ts +263 -0
  19. package/dist/types/file-config.spec.d.ts +1 -0
  20. package/dist/types/generate.d.ts +597 -0
  21. package/dist/types/headers-helpers.d.ts +2 -0
  22. package/{src/index.ts → dist/types/index.d.ts} +0 -15
  23. package/dist/types/keys.d.ts +92 -0
  24. package/dist/types/mcp.d.ts +2760 -0
  25. package/dist/types/messages.d.ts +10 -0
  26. package/dist/types/models.d.ts +1547 -0
  27. package/dist/types/parameterSettings.d.ts +69 -0
  28. package/dist/types/parsers.d.ts +110 -0
  29. package/dist/types/permissions.d.ts +522 -0
  30. package/dist/types/react-query/react-query-service.d.ts +85 -0
  31. package/dist/types/request.d.ts +25 -0
  32. package/dist/types/roles.d.ts +554 -0
  33. package/dist/types/roles.spec.d.ts +1 -0
  34. package/dist/types/schemas.d.ts +5110 -0
  35. package/dist/types/schemas.spec.d.ts +1 -0
  36. package/dist/types/types/agents.d.ts +433 -0
  37. package/dist/types/types/assistants.d.ts +547 -0
  38. package/dist/types/types/files.d.ts +172 -0
  39. package/dist/types/types/graph.d.ts +135 -0
  40. package/{src/types/mcpServers.ts → dist/types/types/mcpServers.d.ts} +12 -18
  41. package/dist/types/types/mutations.d.ts +209 -0
  42. package/dist/types/types/queries.d.ts +169 -0
  43. package/dist/types/types/runs.d.ts +36 -0
  44. package/dist/types/types/web.d.ts +520 -0
  45. package/dist/types/types.d.ts +503 -0
  46. package/dist/types/utils.d.ts +12 -0
  47. package/package.json +5 -1
  48. package/babel.config.js +0 -4
  49. package/check_updates.sh +0 -52
  50. package/jest.config.js +0 -19
  51. package/react-query/package-lock.json +0 -292
  52. package/react-query/package.json +0 -10
  53. package/rollup.config.js +0 -74
  54. package/server-rollup.config.js +0 -40
  55. package/specs/actions.spec.ts +0 -2533
  56. package/specs/api-endpoints-subdir.spec.ts +0 -140
  57. package/specs/api-endpoints.spec.ts +0 -74
  58. package/specs/azure.spec.ts +0 -844
  59. package/specs/bedrock.spec.ts +0 -862
  60. package/specs/filetypes.spec.ts +0 -175
  61. package/specs/generate.spec.ts +0 -770
  62. package/specs/headers-helpers.spec.ts +0 -24
  63. package/specs/mcp.spec.ts +0 -147
  64. package/specs/openapiSpecs.ts +0 -524
  65. package/specs/parsers.spec.ts +0 -601
  66. package/specs/request-interceptor.spec.ts +0 -304
  67. package/specs/utils.spec.ts +0 -196
  68. package/src/accessPermissions.ts +0 -346
  69. package/src/actions.ts +0 -813
  70. package/src/api-endpoints.ts +0 -440
  71. package/src/artifacts.ts +0 -3104
  72. package/src/azure.ts +0 -328
  73. package/src/bedrock.ts +0 -425
  74. package/src/config.spec.ts +0 -315
  75. package/src/config.ts +0 -2006
  76. package/src/createPayload.ts +0 -46
  77. package/src/data-service.ts +0 -1087
  78. package/src/feedback.ts +0 -141
  79. package/src/file-config.spec.ts +0 -1248
  80. package/src/file-config.ts +0 -764
  81. package/src/generate.ts +0 -634
  82. package/src/headers-helpers.ts +0 -13
  83. package/src/keys.ts +0 -99
  84. package/src/mcp.ts +0 -271
  85. package/src/messages.ts +0 -50
  86. package/src/models.ts +0 -69
  87. package/src/parameterSettings.ts +0 -1111
  88. package/src/parsers.ts +0 -563
  89. package/src/permissions.ts +0 -188
  90. package/src/react-query/react-query-service.ts +0 -566
  91. package/src/request.ts +0 -171
  92. package/src/roles.spec.ts +0 -132
  93. package/src/roles.ts +0 -225
  94. package/src/schemas.spec.ts +0 -355
  95. package/src/schemas.ts +0 -1234
  96. package/src/types/agents.ts +0 -470
  97. package/src/types/assistants.ts +0 -654
  98. package/src/types/files.ts +0 -191
  99. package/src/types/graph.ts +0 -145
  100. package/src/types/mutations.ts +0 -422
  101. package/src/types/queries.ts +0 -208
  102. package/src/types/runs.ts +0 -40
  103. package/src/types/web.ts +0 -588
  104. package/src/types.ts +0 -676
  105. package/src/utils.ts +0 -85
  106. package/tsconfig.json +0 -28
  107. package/tsconfig.spec.json +0 -10
  108. /package/{src/react-query/index.ts → dist/types/react-query/index.d.ts} +0 -0
  109. /package/{src/types/index.ts → dist/types/types/index.d.ts} +0 -0
package/src/parsers.ts DELETED
@@ -1,563 +0,0 @@
1
- import dayjs from 'dayjs';
2
- import type { ZodIssue } from 'zod';
3
- import type * as a from './types/assistants';
4
- import type * as s from './schemas';
5
- import type * as t from './types';
6
- import { ContentTypes } from './types/runs';
7
- import {
8
- openAISchema,
9
- googleSchema,
10
- EModelEndpoint,
11
- anthropicSchema,
12
- assistantSchema,
13
- // agentsSchema,
14
- compactAgentsSchema,
15
- compactGoogleSchema,
16
- compactAssistantSchema,
17
- } from './schemas';
18
- import { bedrockInputSchema } from './bedrock';
19
- import { alternateName } from './config';
20
-
21
- type EndpointSchema =
22
- | typeof openAISchema
23
- | typeof googleSchema
24
- | typeof anthropicSchema
25
- | typeof assistantSchema
26
- | typeof compactAgentsSchema
27
- | typeof bedrockInputSchema;
28
-
29
- export type EndpointSchemaKey = EModelEndpoint;
30
-
31
- const endpointSchemas: Record<EndpointSchemaKey, EndpointSchema> = {
32
- [EModelEndpoint.openAI]: openAISchema,
33
- [EModelEndpoint.azureOpenAI]: openAISchema,
34
- [EModelEndpoint.custom]: openAISchema,
35
- [EModelEndpoint.google]: googleSchema,
36
- [EModelEndpoint.anthropic]: anthropicSchema,
37
- [EModelEndpoint.assistants]: assistantSchema,
38
- [EModelEndpoint.azureAssistants]: assistantSchema,
39
- [EModelEndpoint.agents]: compactAgentsSchema,
40
- [EModelEndpoint.bedrock]: bedrockInputSchema,
41
- };
42
-
43
- // const schemaCreators: Record<EModelEndpoint, (customSchema: DefaultSchemaValues) => EndpointSchema> = {
44
- // [EModelEndpoint.google]: createGoogleSchema,
45
- // };
46
-
47
- /** Get the enabled endpoints from the `ENDPOINTS` environment variable */
48
- export function getEnabledEndpoints() {
49
- const defaultEndpoints: string[] = [
50
- EModelEndpoint.openAI,
51
- EModelEndpoint.agents,
52
- EModelEndpoint.assistants,
53
- EModelEndpoint.azureAssistants,
54
- EModelEndpoint.azureOpenAI,
55
- EModelEndpoint.google,
56
- EModelEndpoint.anthropic,
57
- EModelEndpoint.bedrock,
58
- ];
59
-
60
- const endpointsEnv = process.env.ENDPOINTS ?? '';
61
- let enabledEndpoints = defaultEndpoints;
62
- if (endpointsEnv) {
63
- enabledEndpoints = endpointsEnv
64
- .split(',')
65
- .filter((endpoint) => endpoint.trim())
66
- .map((endpoint) => endpoint.trim());
67
- }
68
- return enabledEndpoints;
69
- }
70
-
71
- /** Orders an existing EndpointsConfig object based on enabled endpoint/custom ordering */
72
- export function orderEndpointsConfig(endpointsConfig: t.TEndpointsConfig) {
73
- if (!endpointsConfig) {
74
- return {};
75
- }
76
- const enabledEndpoints = getEnabledEndpoints();
77
- const endpointKeys = Object.keys(endpointsConfig);
78
- const defaultCustomIndex = enabledEndpoints.indexOf(EModelEndpoint.custom);
79
- return endpointKeys.reduce(
80
- (accumulatedConfig: Record<string, t.TConfig | null | undefined>, currentEndpointKey) => {
81
- const isCustom = !(currentEndpointKey in EModelEndpoint);
82
- const isEnabled = enabledEndpoints.includes(currentEndpointKey);
83
- if (!isEnabled && !isCustom) {
84
- return accumulatedConfig;
85
- }
86
-
87
- const index = enabledEndpoints.indexOf(currentEndpointKey);
88
-
89
- if (isCustom) {
90
- accumulatedConfig[currentEndpointKey] = {
91
- order: defaultCustomIndex >= 0 ? defaultCustomIndex : 9999,
92
- ...(endpointsConfig[currentEndpointKey] as Omit<t.TConfig, 'order'> & { order?: number }),
93
- };
94
- } else if (endpointsConfig[currentEndpointKey]) {
95
- accumulatedConfig[currentEndpointKey] = {
96
- ...endpointsConfig[currentEndpointKey],
97
- order: index,
98
- };
99
- }
100
- return accumulatedConfig;
101
- },
102
- {},
103
- );
104
- }
105
-
106
- /** Converts an array of Zod issues into a string. */
107
- export function errorsToString(errors: ZodIssue[]) {
108
- return errors
109
- .map((error) => {
110
- const field = error.path.join('.');
111
- const message = error.message;
112
-
113
- return `${field}: ${message}`;
114
- })
115
- .join(' ');
116
- }
117
-
118
- export function getFirstDefinedValue(possibleValues: string[]) {
119
- let returnValue;
120
- for (const value of possibleValues) {
121
- if (value) {
122
- returnValue = value;
123
- break;
124
- }
125
- }
126
- return returnValue;
127
- }
128
-
129
- export function getNonEmptyValue(possibleValues: string[]) {
130
- for (const value of possibleValues) {
131
- if (value && value.trim() !== '') {
132
- return value;
133
- }
134
- }
135
- return undefined;
136
- }
137
-
138
- export type TPossibleValues = {
139
- models: string[];
140
- };
141
-
142
- export const parseConvo = ({
143
- endpoint,
144
- endpointType,
145
- conversation,
146
- possibleValues,
147
- defaultParamsEndpoint,
148
- }: {
149
- endpoint: EndpointSchemaKey;
150
- endpointType?: EndpointSchemaKey | null;
151
- conversation: Partial<s.TConversation | s.TPreset> | null;
152
- possibleValues?: TPossibleValues;
153
- defaultParamsEndpoint?: string | null;
154
- }) => {
155
- let schema = endpointSchemas[endpoint] as EndpointSchema | undefined;
156
-
157
- if (!schema && !endpointType) {
158
- throw new Error(`Unknown endpoint: ${endpoint}`);
159
- } else if (!schema) {
160
- const overrideSchema = defaultParamsEndpoint
161
- ? endpointSchemas[defaultParamsEndpoint as EndpointSchemaKey]
162
- : undefined;
163
- schema = overrideSchema ?? (endpointType ? endpointSchemas[endpointType] : undefined);
164
- }
165
-
166
- const convo = schema?.parse(conversation) as s.TConversation | undefined;
167
- const { models } = possibleValues ?? {};
168
-
169
- if (models && convo) {
170
- convo.model = getFirstDefinedValue(models) ?? convo.model;
171
- }
172
-
173
- return convo;
174
- };
175
-
176
- /** Match GPT followed by digit, optional decimal, and optional suffix
177
- *
178
- * Examples: gpt-4, gpt-4o, gpt-4.5, gpt-5a, etc. */
179
- const extractGPTVersion = (modelStr: string): string => {
180
- const gptMatch = modelStr.match(/gpt-(\d+(?:\.\d+)?)([a-z])?/i);
181
- if (gptMatch) {
182
- const version = gptMatch[1];
183
- const suffix = gptMatch[2] || '';
184
- return `GPT-${version}${suffix}`;
185
- }
186
- return '';
187
- };
188
-
189
- /** Match omni models (o1, o3, etc.), "o" followed by a digit, possibly with decimal */
190
- const extractOmniVersion = (modelStr: string): string => {
191
- const omniMatch = modelStr.match(/\bo(\d+(?:\.\d+)?)\b/i);
192
- if (omniMatch) {
193
- const version = omniMatch[1];
194
- return `o${version}`;
195
- }
196
- return '';
197
- };
198
-
199
- export const getResponseSender = (endpointOption: Partial<t.TEndpointOption>): string => {
200
- const {
201
- model: _m,
202
- endpoint: _e,
203
- endpointType,
204
- modelDisplayLabel: _mdl,
205
- chatGptLabel: _cgl,
206
- modelLabel: _ml,
207
- } = endpointOption;
208
-
209
- const endpoint = _e as EModelEndpoint;
210
-
211
- const model = _m ?? '';
212
- const modelDisplayLabel = _mdl ?? '';
213
- const chatGptLabel = _cgl ?? '';
214
- const modelLabel = _ml ?? '';
215
- if (
216
- [EModelEndpoint.openAI, EModelEndpoint.bedrock, EModelEndpoint.azureOpenAI].includes(endpoint)
217
- ) {
218
- if (modelLabel) {
219
- return modelLabel;
220
- } else if (chatGptLabel) {
221
- // @deprecated - prefer modelLabel
222
- return chatGptLabel;
223
- } else if (model && extractOmniVersion(model)) {
224
- return extractOmniVersion(model);
225
- } else if (model && (model.includes('mistral') || model.includes('codestral'))) {
226
- return 'Mistral';
227
- } else if (model && model.includes('deepseek')) {
228
- return 'Deepseek';
229
- } else if (model && model.includes('kimi')) {
230
- return 'Kimi';
231
- } else if (model && model.includes('moonshot')) {
232
- return 'Moonshot';
233
- } else if (model && model.includes('gpt-')) {
234
- const gptVersion = extractGPTVersion(model);
235
- return gptVersion || 'GPT';
236
- }
237
- return (alternateName[endpoint] as string | undefined) ?? 'AI';
238
- }
239
-
240
- if (endpoint === EModelEndpoint.anthropic) {
241
- return modelLabel || 'Claude';
242
- }
243
-
244
- if (endpoint === EModelEndpoint.bedrock) {
245
- return modelLabel || alternateName[endpoint];
246
- }
247
-
248
- if (endpoint === EModelEndpoint.google) {
249
- if (modelLabel) {
250
- return modelLabel;
251
- } else if (model?.toLowerCase().includes('gemma') === true) {
252
- return 'Gemma';
253
- }
254
-
255
- return 'Gemini';
256
- }
257
-
258
- if (endpoint === EModelEndpoint.custom || endpointType === EModelEndpoint.custom) {
259
- if (modelLabel) {
260
- return modelLabel;
261
- } else if (chatGptLabel) {
262
- // @deprecated - prefer modelLabel
263
- return chatGptLabel;
264
- } else if (model && extractOmniVersion(model)) {
265
- return extractOmniVersion(model);
266
- } else if (model && (model.includes('mistral') || model.includes('codestral'))) {
267
- return 'Mistral';
268
- } else if (model && model.includes('deepseek')) {
269
- return 'Deepseek';
270
- } else if (model && model.includes('kimi')) {
271
- return 'Kimi';
272
- } else if (model && model.includes('moonshot')) {
273
- return 'Moonshot';
274
- } else if (model && model.includes('gpt-')) {
275
- const gptVersion = extractGPTVersion(model);
276
- return gptVersion || 'GPT';
277
- } else if (modelDisplayLabel) {
278
- return modelDisplayLabel;
279
- }
280
-
281
- return 'AI';
282
- }
283
-
284
- return '';
285
- };
286
-
287
- type CompactEndpointSchema =
288
- | typeof openAISchema
289
- | typeof compactAssistantSchema
290
- | typeof compactAgentsSchema
291
- | typeof compactGoogleSchema
292
- | typeof anthropicSchema
293
- | typeof bedrockInputSchema;
294
-
295
- const compactEndpointSchemas: Record<EndpointSchemaKey, CompactEndpointSchema> = {
296
- [EModelEndpoint.openAI]: openAISchema,
297
- [EModelEndpoint.azureOpenAI]: openAISchema,
298
- [EModelEndpoint.custom]: openAISchema,
299
- [EModelEndpoint.assistants]: compactAssistantSchema,
300
- [EModelEndpoint.azureAssistants]: compactAssistantSchema,
301
- [EModelEndpoint.agents]: compactAgentsSchema,
302
- [EModelEndpoint.google]: compactGoogleSchema,
303
- [EModelEndpoint.bedrock]: bedrockInputSchema,
304
- [EModelEndpoint.anthropic]: anthropicSchema,
305
- };
306
-
307
- export const parseCompactConvo = ({
308
- endpoint,
309
- endpointType,
310
- conversation,
311
- possibleValues,
312
- defaultParamsEndpoint,
313
- }: {
314
- endpoint?: EndpointSchemaKey;
315
- endpointType?: EndpointSchemaKey | null;
316
- conversation: Partial<s.TConversation | s.TPreset>;
317
- possibleValues?: TPossibleValues;
318
- defaultParamsEndpoint?: string | null;
319
- }): Omit<s.TConversation, 'iconURL'> | null => {
320
- if (!endpoint) {
321
- throw new Error(`undefined endpoint: ${endpoint}`);
322
- }
323
-
324
- let schema = compactEndpointSchemas[endpoint] as CompactEndpointSchema | undefined;
325
-
326
- if (!schema && !endpointType) {
327
- throw new Error(`Unknown endpoint: ${endpoint}`);
328
- } else if (!schema) {
329
- const overrideSchema = defaultParamsEndpoint
330
- ? compactEndpointSchemas[defaultParamsEndpoint as EndpointSchemaKey]
331
- : undefined;
332
- schema = overrideSchema ?? (endpointType ? compactEndpointSchemas[endpointType] : undefined);
333
- }
334
-
335
- if (!schema) {
336
- throw new Error(`Unknown endpointType: ${endpointType}`);
337
- }
338
-
339
- // Strip iconURL from input before parsing - it should only be derived server-side
340
- // from model spec configuration, not accepted from client requests
341
- const { iconURL: _clientIconURL, ...conversationWithoutIconURL } = conversation;
342
-
343
- const convo = schema.parse(conversationWithoutIconURL) as s.TConversation | null;
344
- const { models } = possibleValues ?? {};
345
-
346
- if (models && convo) {
347
- convo.model = getFirstDefinedValue(models) ?? convo.model;
348
- }
349
-
350
- return convo;
351
- };
352
-
353
- export function parseTextParts(
354
- contentParts: Array<a.TMessageContentParts | undefined>,
355
- skipReasoning: boolean = false,
356
- ): string {
357
- let result = '';
358
-
359
- for (const part of contentParts) {
360
- if (!part?.type) {
361
- continue;
362
- }
363
- if (part.type === ContentTypes.TEXT) {
364
- const textValue = (typeof part.text === 'string' ? part.text : part.text?.value) || '';
365
-
366
- if (
367
- result.length > 0 &&
368
- textValue.length > 0 &&
369
- result[result.length - 1] !== ' ' &&
370
- textValue[0] !== ' '
371
- ) {
372
- result += ' ';
373
- }
374
- result += textValue;
375
- } else if (part.type === ContentTypes.THINK && !skipReasoning) {
376
- const textValue = typeof part.think === 'string' ? part.think : '';
377
- if (
378
- result.length > 0 &&
379
- textValue.length > 0 &&
380
- result[result.length - 1] !== ' ' &&
381
- textValue[0] !== ' '
382
- ) {
383
- result += ' ';
384
- }
385
- result += textValue;
386
- }
387
- }
388
-
389
- return result;
390
- }
391
-
392
- export const SEPARATORS = ['.', '?', '!', '۔', '。', '‥', ';', '¡', '¿', '\n', '```'];
393
-
394
- export function findLastSeparatorIndex(text: string, separators = SEPARATORS): number {
395
- let lastIndex = -1;
396
- for (const separator of separators) {
397
- const index = text.lastIndexOf(separator);
398
- if (index > lastIndex) {
399
- lastIndex = index;
400
- }
401
- }
402
- return lastIndex;
403
- }
404
-
405
- export function replaceSpecialVars({ text, user }: { text: string; user?: t.TUser | null }) {
406
- let result = text;
407
- if (!result) {
408
- return result;
409
- }
410
-
411
- const now = dayjs();
412
- const weekdayName = now.format('dddd');
413
-
414
- const currentDate = now.format('YYYY-MM-DD');
415
- result = result.replace(/{{current_date}}/gi, `${currentDate} (${weekdayName})`);
416
-
417
- const currentDatetime = now.format('YYYY-MM-DD HH:mm:ss Z');
418
- result = result.replace(/{{current_datetime}}/gi, `${currentDatetime} (${weekdayName})`);
419
-
420
- const isoDatetime = now.toISOString();
421
- result = result.replace(/{{iso_datetime}}/gi, isoDatetime);
422
-
423
- if (user && user.name) {
424
- result = result.replace(/{{current_user}}/gi, user.name);
425
- }
426
-
427
- return result;
428
- }
429
-
430
- /**
431
- * Parsed ephemeral agent ID result
432
- */
433
- export type ParsedEphemeralAgentId = {
434
- endpoint: string;
435
- model: string;
436
- sender?: string;
437
- index?: number;
438
- };
439
-
440
- /**
441
- * Encodes an ephemeral agent ID from endpoint, model, optional sender, and optional index.
442
- * Uses __ to replace : (reserved in graph node names) and ___ to separate sender.
443
- *
444
- * Format: endpoint__model___sender or endpoint__model___sender____index (if index provided)
445
- *
446
- * @example
447
- * encodeEphemeralAgentId({ endpoint: 'openAI', model: 'gpt-4o', sender: 'GPT-4o' })
448
- * // => 'openAI__gpt-4o___GPT-4o'
449
- *
450
- * @example
451
- * encodeEphemeralAgentId({ endpoint: 'openAI', model: 'gpt-4o', sender: 'GPT-4o', index: 1 })
452
- * // => 'openAI__gpt-4o___GPT-4o____1'
453
- */
454
- export function encodeEphemeralAgentId({
455
- endpoint,
456
- model,
457
- sender,
458
- index,
459
- }: {
460
- endpoint: string;
461
- model: string;
462
- sender?: string;
463
- index?: number;
464
- }): string {
465
- const base = `${endpoint}:${model}`.replace(/:/g, '__');
466
- let result = base;
467
- if (sender) {
468
- // Use ___ as separator before sender to distinguish from __ in model names
469
- result = `${base}___${sender.replace(/:/g, '__')}`;
470
- }
471
- if (index != null) {
472
- // Use ____ (4 underscores) as separator for index
473
- result = `${result}____${index}`;
474
- }
475
- return result;
476
- }
477
-
478
- /**
479
- * Parses an ephemeral agent ID back into its components.
480
- * Returns undefined if the ID doesn't match the expected format.
481
- *
482
- * Format: endpoint__model___sender or endpoint__model___sender____index
483
- * - ____ (4 underscores) separates optional index suffix
484
- * - ___ (triple underscore) separates model from optional sender
485
- * - __ (double underscore) replaces : in endpoint/model names
486
- *
487
- * @example
488
- * parseEphemeralAgentId('openAI__gpt-4o___GPT-4o')
489
- * // => { endpoint: 'openAI', model: 'gpt-4o', sender: 'GPT-4o' }
490
- *
491
- * @example
492
- * parseEphemeralAgentId('openAI__gpt-4o___GPT-4o____1')
493
- * // => { endpoint: 'openAI', model: 'gpt-4o', sender: 'GPT-4o', index: 1 }
494
- */
495
- export function parseEphemeralAgentId(agentId: string): ParsedEphemeralAgentId | undefined {
496
- if (!agentId.includes('__')) {
497
- return undefined;
498
- }
499
-
500
- // First check for index suffix (separated by ____)
501
- let index: number | undefined;
502
- let workingId = agentId;
503
- if (agentId.includes('____')) {
504
- const lastIndexSep = agentId.lastIndexOf('____');
505
- const indexStr = agentId.slice(lastIndexSep + 4);
506
- const parsedIndex = parseInt(indexStr, 10);
507
- if (!isNaN(parsedIndex)) {
508
- index = parsedIndex;
509
- workingId = agentId.slice(0, lastIndexSep);
510
- }
511
- }
512
-
513
- // Check for sender (separated by ___)
514
- let sender: string | undefined;
515
- let mainPart = workingId;
516
- if (workingId.includes('___')) {
517
- const [before, after] = workingId.split('___');
518
- mainPart = before;
519
- // Restore colons in sender if any
520
- sender = after?.replace(/__/g, ':');
521
- }
522
-
523
- const [endpoint, ...modelParts] = mainPart.split('__');
524
- if (!endpoint || modelParts.length === 0) {
525
- return undefined;
526
- }
527
- // Restore colons in model name (model names can contain colons like claude-3:opus)
528
- const model = modelParts.join(':');
529
- return { endpoint, model, sender, index };
530
- }
531
-
532
- /**
533
- * Checks if an agent ID represents an ephemeral (non-saved) agent.
534
- * Real agent IDs always start with "agent_", so anything else is ephemeral.
535
- */
536
- export function isEphemeralAgentId(agentId: string | null | undefined): boolean {
537
- return !agentId?.startsWith('agent_');
538
- }
539
-
540
- /**
541
- * Strips the index suffix (____N) from an agent ID if present.
542
- * Works with both ephemeral and real agent IDs.
543
- *
544
- * @example
545
- * stripAgentIdSuffix('agent_abc123____1') // => 'agent_abc123'
546
- * stripAgentIdSuffix('openAI__gpt-4o___GPT-4o____1') // => 'openAI__gpt-4o___GPT-4o'
547
- * stripAgentIdSuffix('agent_abc123') // => 'agent_abc123' (unchanged)
548
- */
549
- export function stripAgentIdSuffix(agentId: string): string {
550
- return agentId.replace(/____\d+$/, '');
551
- }
552
-
553
- /**
554
- * Appends an index suffix (____N) to an agent ID.
555
- * Used to distinguish parallel agents with the same base ID.
556
- *
557
- * @example
558
- * appendAgentIdSuffix('agent_abc123', 1) // => 'agent_abc123____1'
559
- * appendAgentIdSuffix('openAI__gpt-4o___GPT-4o', 1) // => 'openAI__gpt-4o___GPT-4o____1'
560
- */
561
- export function appendAgentIdSuffix(agentId: string, index: number): string {
562
- return `${agentId}____${index}`;
563
- }