@elizaos/core 1.5.1 → 1.5.2

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 (88) hide show
  1. package/dist/browser/index.browser.js +120 -120
  2. package/dist/browser/index.browser.js.map +5 -21
  3. package/dist/browser/index.d.ts +3 -1
  4. package/dist/index.d.ts +2 -3
  5. package/dist/index.js +1 -5
  6. package/dist/node/index.d.ts +3 -1
  7. package/package.json +10 -4
  8. package/src/__tests__/action-chaining-simple.test.ts +203 -0
  9. package/src/__tests__/actions.test.ts +218 -0
  10. package/src/__tests__/buffer.test.ts +337 -0
  11. package/src/__tests__/character-validation.test.ts +309 -0
  12. package/src/__tests__/database.test.ts +750 -0
  13. package/src/__tests__/entities.test.ts +727 -0
  14. package/src/__tests__/env.test.ts +23 -0
  15. package/src/__tests__/environment.test.ts +285 -0
  16. package/src/__tests__/logger-browser-node.test.ts +716 -0
  17. package/src/__tests__/logger.test.ts +403 -0
  18. package/src/__tests__/messages.test.ts +196 -0
  19. package/src/__tests__/mockCharacter.ts +544 -0
  20. package/src/__tests__/parsing.test.ts +58 -0
  21. package/src/__tests__/prompts.test.ts +159 -0
  22. package/src/__tests__/roles.test.ts +331 -0
  23. package/src/__tests__/runtime-embedding.test.ts +343 -0
  24. package/src/__tests__/runtime.test.ts +978 -0
  25. package/src/__tests__/search.test.ts +15 -0
  26. package/src/__tests__/services-by-type.test.ts +204 -0
  27. package/src/__tests__/services.test.ts +136 -0
  28. package/src/__tests__/settings.test.ts +810 -0
  29. package/src/__tests__/utils.test.ts +1105 -0
  30. package/src/__tests__/uuid.test.ts +94 -0
  31. package/src/actions.ts +122 -0
  32. package/src/database.ts +579 -0
  33. package/src/entities.ts +406 -0
  34. package/src/index.browser.ts +48 -0
  35. package/src/index.node.ts +39 -0
  36. package/src/index.ts +50 -0
  37. package/src/logger.ts +527 -0
  38. package/src/prompts.ts +243 -0
  39. package/src/roles.ts +85 -0
  40. package/src/runtime.ts +2514 -0
  41. package/src/schemas/character.ts +149 -0
  42. package/src/search.ts +1543 -0
  43. package/src/sentry/instrument.browser.ts +65 -0
  44. package/src/sentry/instrument.node.ts +57 -0
  45. package/src/sentry/instrument.ts +82 -0
  46. package/src/services.ts +105 -0
  47. package/src/settings.ts +409 -0
  48. package/src/test_resources/constants.ts +12 -0
  49. package/src/test_resources/testSetup.ts +21 -0
  50. package/src/test_resources/types.ts +22 -0
  51. package/src/types/agent.ts +112 -0
  52. package/src/types/browser.ts +145 -0
  53. package/src/types/components.ts +184 -0
  54. package/src/types/database.ts +348 -0
  55. package/src/types/email.ts +162 -0
  56. package/src/types/environment.ts +129 -0
  57. package/src/types/events.ts +249 -0
  58. package/src/types/index.ts +29 -0
  59. package/src/types/knowledge.ts +65 -0
  60. package/src/types/lp.ts +124 -0
  61. package/src/types/memory.ts +228 -0
  62. package/src/types/message.ts +233 -0
  63. package/src/types/messaging.ts +57 -0
  64. package/src/types/model.ts +359 -0
  65. package/src/types/pdf.ts +77 -0
  66. package/src/types/plugin.ts +78 -0
  67. package/src/types/post.ts +271 -0
  68. package/src/types/primitives.ts +97 -0
  69. package/src/types/runtime.ts +190 -0
  70. package/src/types/service.ts +198 -0
  71. package/src/types/settings.ts +30 -0
  72. package/src/types/state.ts +60 -0
  73. package/src/types/task.ts +72 -0
  74. package/src/types/tee.ts +107 -0
  75. package/src/types/testing.ts +30 -0
  76. package/src/types/token.ts +96 -0
  77. package/src/types/transcription.ts +133 -0
  78. package/src/types/video.ts +108 -0
  79. package/src/types/wallet.ts +56 -0
  80. package/src/types/web-search.ts +146 -0
  81. package/src/utils/__tests__/buffer.test.ts +80 -0
  82. package/src/utils/__tests__/environment.test.ts +58 -0
  83. package/src/utils/__tests__/stringToUuid.test.ts +88 -0
  84. package/src/utils/buffer.ts +312 -0
  85. package/src/utils/environment.ts +316 -0
  86. package/src/utils/server-health.ts +117 -0
  87. package/src/utils.ts +1076 -0
  88. package/dist/tsconfig.build.tsbuildinfo +0 -1
@@ -0,0 +1,406 @@
1
+ import { stringToUuid } from './index';
2
+ import { logger } from './logger';
3
+ import { composePrompt, parseKeyValueXml } from './utils';
4
+ import {
5
+ type Entity,
6
+ type IAgentRuntime,
7
+ type Memory,
8
+ ModelType,
9
+ type Relationship,
10
+ type State,
11
+ type UUID,
12
+ } from './types';
13
+
14
+ /**
15
+ * Template for resolving entity name within a conversation context.
16
+ *
17
+ * @type {string}
18
+ */
19
+ /**
20
+ * Entity Resolution Template for resolving entity names based on context and recent messages.
21
+ *
22
+ * Contains placeholders for message sender, agent, entities in the room, and recent messages.
23
+ * Provides instructions for analyzing the context and resolving entity references.
24
+ *
25
+ * @return {string} entityResolutionTemplate - The template for resolving entity names with detailed instructions.
26
+ */
27
+ const entityResolutionTemplate = `# Task: Resolve Entity Name
28
+ Message Sender: {{senderName}} (ID: {{senderId}})
29
+ Agent: {{agentName}} (ID: {{agentId}})
30
+
31
+ # Entities in Room:
32
+ {{#if entitiesInRoom}}
33
+ {{entitiesInRoom}}
34
+ {{/if}}
35
+
36
+ {{recentMessages}}
37
+
38
+ # Instructions:
39
+ 1. Analyze the context to identify which entity is being referenced
40
+ 2. Consider special references like "me" (the message sender) or "you" (agent the message is directed to)
41
+ 3. Look for usernames/handles in standard formats (e.g. @username, user#1234)
42
+ 4. Consider context from recent messages for pronouns and references
43
+ 5. If multiple matches exist, use context to disambiguate
44
+ 6. Consider recent interactions and relationship strength when resolving ambiguity
45
+
46
+ Do NOT include any thinking, reasoning, or <think> sections in your response.
47
+ Go directly to the XML response format without any preamble or explanation.
48
+
49
+ Return an XML response with:
50
+ <response>
51
+ <entityId>exact-id-if-known-otherwise-null</entityId>
52
+ <type>EXACT_MATCH | USERNAME_MATCH | NAME_MATCH | RELATIONSHIP_MATCH | AMBIGUOUS | UNKNOWN</type>
53
+ <matches>
54
+ <match>
55
+ <name>matched-name</name>
56
+ <reason>why this entity matches</reason>
57
+ </match>
58
+ </matches>
59
+ </response>
60
+
61
+ IMPORTANT: Your response must ONLY contain the <response></response> XML block above. Do not include any text, thinking, or reasoning before or after this XML block. Start your response immediately with <response> and end with </response>.`;
62
+
63
+ /**
64
+ * Get recent interactions between a source entity and candidate entities in a specific room.
65
+ *
66
+ * @param {IAgentRuntime} runtime - The runtime context for the agent.
67
+ * @param {UUID} sourceEntityId - The ID of the source entity initiating interactions.
68
+ * @param {Entity[]} candidateEntities - The list of candidate entities to evaluate interactions with.
69
+ * @param {UUID} roomId - The ID of the room where interactions are taking place.
70
+ * @param {Relationship[]} relationships - The relationships between the entities involved.
71
+ * @returns {Promise<{ entity: Entity; interactions: Memory[]; count: number }[]>} - An array of objects containing the entity, recent interactions, and interaction count.
72
+ */
73
+ async function getRecentInteractions(
74
+ runtime: IAgentRuntime,
75
+ sourceEntityId: UUID,
76
+ candidateEntities: Entity[],
77
+ roomId: UUID,
78
+ relationships: Relationship[]
79
+ ): Promise<{ entity: Entity; interactions: Memory[]; count: number }[]> {
80
+ const results: Array<{ entity: Entity; interactions: Memory[]; count: number }> = [];
81
+
82
+ // Get recent messages from the room - just for context
83
+ const recentMessages = await runtime.getMemories({
84
+ tableName: 'messages',
85
+ roomId,
86
+ count: 20, // Reduced from 100 since we only need context
87
+ });
88
+
89
+ for (const entity of candidateEntities) {
90
+ const interactions: Memory[] = [];
91
+ let interactionScore = 0;
92
+
93
+ // First get direct replies using inReplyTo
94
+ const directReplies = recentMessages.filter(
95
+ (msg) =>
96
+ (msg.entityId === sourceEntityId && msg.content.inReplyTo === entity.id) ||
97
+ (msg.entityId === entity.id && msg.content.inReplyTo === sourceEntityId)
98
+ );
99
+
100
+ interactions.push(...directReplies);
101
+
102
+ // Get relationship strength from metadata
103
+ const relationship = relationships.find(
104
+ (rel) =>
105
+ (rel.sourceEntityId === sourceEntityId && rel.targetEntityId === entity.id) ||
106
+ (rel.targetEntityId === sourceEntityId && rel.sourceEntityId === entity.id)
107
+ );
108
+
109
+ if (relationship?.metadata?.interactions) {
110
+ interactionScore = relationship.metadata.interactions as number;
111
+ }
112
+
113
+ // Add bonus points for recent direct replies
114
+ interactionScore += directReplies.length;
115
+
116
+ // Keep last few messages for context
117
+ const uniqueInteractions = [...new Set(interactions)];
118
+ results.push({
119
+ entity,
120
+ interactions: uniqueInteractions.slice(-5), // Only keep last 5 messages for context
121
+ count: Math.round(interactionScore),
122
+ });
123
+ }
124
+
125
+ // Sort by interaction score descending
126
+ return results.sort((a, b) => b.count - a.count);
127
+ }
128
+
129
+ /**
130
+ * Finds an entity by name in the given runtime environment.
131
+ *
132
+ * @param {IAgentRuntime} runtime - The agent runtime environment.
133
+ * @param {Memory} message - The memory message containing relevant information.
134
+ * @param {State} state - The current state of the system.
135
+ * @returns {Promise<Entity | null>} A promise that resolves to the found entity or null if not found.
136
+ */
137
+ export async function findEntityByName(
138
+ runtime: IAgentRuntime,
139
+ message: Memory,
140
+ state: State
141
+ ): Promise<Entity | null> {
142
+ const room = state.data.room ?? (await runtime.getRoom(message.roomId));
143
+ if (!room) {
144
+ logger.warn('Room not found for entity search');
145
+ return null;
146
+ }
147
+
148
+ const world = room.worldId ? await runtime.getWorld(room.worldId) : null;
149
+
150
+ // Get all entities in the room with their components
151
+ const entitiesInRoom = await runtime.getEntitiesForRoom(room.id, true);
152
+
153
+ // Filter components for each entity based on permissions
154
+ const filteredEntities = await Promise.all(
155
+ entitiesInRoom.map(async (entity) => {
156
+ if (!entity.components) return entity;
157
+
158
+ // Get world roles if we have a world
159
+ const worldRoles = world?.metadata?.roles || {};
160
+
161
+ // Filter components based on permissions
162
+ entity.components = entity.components.filter((component) => {
163
+ // 1. Pass if sourceEntityId matches the requesting entity
164
+ if (component.sourceEntityId === message.entityId) return true;
165
+
166
+ // 2. Pass if sourceEntityId is an owner/admin of the current world
167
+ if (world && component.sourceEntityId) {
168
+ const sourceRole = worldRoles[component.sourceEntityId];
169
+ if (sourceRole === 'OWNER' || sourceRole === 'ADMIN') return true;
170
+ }
171
+
172
+ // 3. Pass if sourceEntityId is the agentId
173
+ if (component.sourceEntityId === runtime.agentId) return true;
174
+
175
+ // Filter out components that don't meet any criteria
176
+ return false;
177
+ });
178
+
179
+ return entity;
180
+ })
181
+ );
182
+
183
+ // Get relationships for the message sender
184
+ const relationships = await runtime.getRelationships({
185
+ entityId: message.entityId,
186
+ });
187
+
188
+ // Get entities from relationships
189
+ const relationshipEntities = await Promise.all(
190
+ relationships.map(async (rel) => {
191
+ const entityId =
192
+ rel.sourceEntityId === message.entityId ? rel.targetEntityId : rel.sourceEntityId;
193
+ return runtime.getEntityById(entityId);
194
+ })
195
+ );
196
+
197
+ // Filter out nulls and combine with room entities
198
+ const allEntities = [
199
+ ...filteredEntities,
200
+ ...relationshipEntities.filter((e): e is Entity => e !== null),
201
+ ];
202
+
203
+ // Get interaction strength data for relationship entities
204
+ const interactionData = await getRecentInteractions(
205
+ runtime,
206
+ message.entityId,
207
+ allEntities,
208
+ room.id,
209
+ relationships
210
+ );
211
+
212
+ // Compose context for LLM
213
+ const prompt = composePrompt({
214
+ state: {
215
+ roomName: room.name || room.id,
216
+ worldName: world?.name || 'Unknown',
217
+ entitiesInRoom: JSON.stringify(filteredEntities, null, 2),
218
+ entityId: message.entityId,
219
+ senderId: message.entityId,
220
+ },
221
+ template: entityResolutionTemplate,
222
+ });
223
+
224
+ // Use LLM to analyze and resolve the entity
225
+ const result = await runtime.useModel(ModelType.TEXT_SMALL, {
226
+ prompt,
227
+ stopSequences: [],
228
+ });
229
+
230
+ // Parse LLM response
231
+ const resolution = parseKeyValueXml(result);
232
+ if (!resolution) {
233
+ logger.warn('Failed to parse entity resolution result');
234
+ return null;
235
+ }
236
+
237
+ // If we got an exact entity ID match
238
+ if (resolution.type === 'EXACT_MATCH' && resolution.entityId) {
239
+ const entity = await runtime.getEntityById(resolution.entityId as UUID);
240
+ if (entity) {
241
+ // Filter components again for the returned entity
242
+ if (entity.components) {
243
+ const worldRoles = world?.metadata?.roles || {};
244
+ entity.components = entity.components.filter((component) => {
245
+ if (component.sourceEntityId === message.entityId) return true;
246
+ if (world && component.sourceEntityId) {
247
+ const sourceRole = worldRoles[component.sourceEntityId];
248
+ if (sourceRole === 'OWNER' || sourceRole === 'ADMIN') return true;
249
+ }
250
+ if (component.sourceEntityId === runtime.agentId) return true;
251
+ return false;
252
+ });
253
+ }
254
+ return entity;
255
+ }
256
+ }
257
+
258
+ // For username/name/relationship matches, search through all entities
259
+ // Handle matches - parseKeyValueXml returns nested structures differently
260
+ let matchesArray: any[] = [];
261
+ if (resolution.matches?.match) {
262
+ // Normalize to array
263
+ matchesArray = Array.isArray(resolution.matches.match)
264
+ ? resolution.matches.match
265
+ : [resolution.matches.match];
266
+ }
267
+
268
+ if (matchesArray.length > 0 && matchesArray[0]?.name) {
269
+ const matchName = matchesArray[0].name.toLowerCase();
270
+
271
+ // Find matching entity by username/handle in components or by name
272
+ const matchingEntity = allEntities.find((entity) => {
273
+ // Check names
274
+ if (entity.names.some((n) => n.toLowerCase() === matchName)) return true;
275
+
276
+ // Check components for username/handle match
277
+ return entity.components?.some(
278
+ (c) =>
279
+ (c.data.username as string)?.toLowerCase() === matchName ||
280
+ (c.data.handle as string)?.toLowerCase() === matchName
281
+ );
282
+ });
283
+
284
+ if (matchingEntity) {
285
+ // If this is a relationship match, sort by interaction strength
286
+ if (resolution.type === 'RELATIONSHIP_MATCH') {
287
+ const interactionInfo = interactionData.find((d) => d.entity.id === matchingEntity.id);
288
+ if (interactionInfo && interactionInfo.count > 0) {
289
+ return matchingEntity;
290
+ }
291
+ } else {
292
+ return matchingEntity;
293
+ }
294
+ }
295
+ }
296
+
297
+ return null;
298
+ }
299
+
300
+ /**
301
+ * Function to create a unique UUID based on the runtime and base user ID.
302
+ *
303
+ * @param {RuntimeContext} runtime - The runtime context object.
304
+ * @param {UUID|string} baseUserId - The base user ID to use in generating the UUID.
305
+ * @returns {UUID} - The unique UUID generated based on the runtime and base user ID.
306
+ */
307
+ export const createUniqueUuid = (runtime: IAgentRuntime, baseUserId: UUID | string): UUID => {
308
+ // If the base user ID is the agent ID, return it directly
309
+ if (baseUserId === runtime.agentId) {
310
+ return runtime.agentId;
311
+ }
312
+
313
+ // Use a deterministic approach to generate a new UUID based on both IDs
314
+ // This creates a unique ID for each user+agent combination while still being deterministic
315
+ const combinedString = `${baseUserId}:${runtime.agentId}`;
316
+
317
+ // Create a namespace UUID (version 5) from the combined string
318
+ return stringToUuid(combinedString);
319
+ };
320
+
321
+ /**
322
+ * Get details for a list of entities.
323
+ */
324
+ /**
325
+ * Retrieves entity details for a specific room from the database.
326
+ *
327
+ * @param {Object} params - The input parameters
328
+ * @param {IAgentRuntime} params.runtime - The Agent Runtime instance
329
+ * @param {UUID} params.roomId - The ID of the room to retrieve entity details for
330
+ * @returns {Promise<Array>} - A promise that resolves to an array of unique entity details
331
+ */
332
+ export async function getEntityDetails({
333
+ runtime,
334
+ roomId,
335
+ }: {
336
+ runtime: IAgentRuntime;
337
+ roomId: UUID;
338
+ }) {
339
+ // Parallelize the two async operations
340
+ const [room, roomEntities] = await Promise.all([
341
+ runtime.getRoom(roomId),
342
+ runtime.getEntitiesForRoom(roomId, true),
343
+ ]);
344
+
345
+ // Use a Map for uniqueness checking while processing entities
346
+ const uniqueEntities = new Map();
347
+
348
+ // Process entities in a single pass
349
+ for (const entity of roomEntities) {
350
+ if (uniqueEntities.has(entity.id)) continue;
351
+
352
+ // Merge component data more efficiently
353
+ const allData = {};
354
+ for (const component of entity.components || []) {
355
+ Object.assign(allData, component.data);
356
+ }
357
+
358
+ // Process merged data
359
+ const mergedData: Record<string, any> = {};
360
+ for (const [key, value] of Object.entries(allData)) {
361
+ if (!mergedData[key]) {
362
+ mergedData[key] = value;
363
+ continue;
364
+ }
365
+
366
+ if (Array.isArray(mergedData[key]) && Array.isArray(value)) {
367
+ // Use Set for deduplication in arrays
368
+ mergedData[key] = [...new Set([...mergedData[key], ...value])];
369
+ } else if (typeof mergedData[key] === 'object' && typeof value === 'object') {
370
+ mergedData[key] = { ...mergedData[key], ...value };
371
+ }
372
+ }
373
+
374
+ // Create the entity details
375
+ uniqueEntities.set(entity.id, {
376
+ id: entity.id,
377
+ name: room?.source
378
+ ? (entity.metadata[room.source] as { name?: string })?.name || entity.names[0]
379
+ : entity.names[0],
380
+ names: entity.names,
381
+ data: JSON.stringify({ ...mergedData, ...entity.metadata }),
382
+ });
383
+ }
384
+
385
+ return Array.from(uniqueEntities.values());
386
+ }
387
+
388
+ /**
389
+ * Format entities into a string
390
+ * @param entities - list of entities
391
+ * @returns string
392
+ */
393
+ /**
394
+ * Format the given entities into a string representation.
395
+ *
396
+ * @param {Object} options - The options object.
397
+ * @param {Entity[]} options.entities - The list of entities to format.
398
+ * @returns {string} A formatted string representing the entities.
399
+ */
400
+ export function formatEntities({ entities }: { entities: Entity[] }) {
401
+ const entityStrings = entities.map((entity: Entity) => {
402
+ const header = `"${entity.names.join('" aka "')}"\nID: ${entity.id}${entity.metadata && Object.keys(entity.metadata).length > 0 ? `\nData: ${JSON.stringify(entity.metadata)}\n` : '\n'}`;
403
+ return header;
404
+ });
405
+ return entityStrings.join('\n');
406
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Browser-specific entry point for @elizaos/core
3
+ *
4
+ * This file exports only browser-compatible modules and provides
5
+ * stubs or alternatives for Node.js-specific functionality.
6
+ */
7
+
8
+ // Export everything from types (type-only, safe for browser)
9
+ export * from './types';
10
+
11
+ // Export utils first to avoid circular dependency issues
12
+ export * from './utils';
13
+
14
+ // Export schemas
15
+ export * from './schemas/character';
16
+
17
+ // Export browser-compatible utilities
18
+ export * from './utils/environment';
19
+ export * from './utils/buffer';
20
+ // Note: Excluding server-health as it's Node-specific
21
+
22
+ // Export core modules (all browser-compatible after refactoring)
23
+ export * from './actions';
24
+ export * from './database';
25
+ export * from './entities';
26
+ export * from './logger';
27
+ export * from './prompts';
28
+ export * from './roles';
29
+ export * from './runtime';
30
+ export * from './settings';
31
+ export * from './services';
32
+ export * from './search';
33
+
34
+ // Export Sentry instrumentation (browser version)
35
+ export * from './sentry/instrument.browser';
36
+
37
+ // Browser-specific exports or stubs for Node-only features
38
+ export const isBrowser = true;
39
+ export const isNode = false;
40
+
41
+ /**
42
+ * Browser stub for server health checks
43
+ * In browser environment, this is a no-op
44
+ */
45
+ export const serverHealth = {
46
+ check: async () => ({ status: 'not-applicable', environment: 'browser' }),
47
+ isHealthy: () => true,
48
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Node.js-specific entry point for @elizaos/core
3
+ *
4
+ * This file exports all modules including Node.js-specific functionality.
5
+ * This is the full API surface of the core package.
6
+ */
7
+
8
+ // Export everything from types
9
+ export * from './types';
10
+
11
+ // Export utils first to avoid circular dependency issues
12
+ export * from './utils';
13
+
14
+ // Export schemas
15
+ export * from './schemas/character';
16
+
17
+ // Export all utilities (including Node-specific ones)
18
+ export * from './utils/environment';
19
+ export * from './utils/buffer';
20
+ export * from './utils/server-health';
21
+
22
+ // Export all core modules
23
+ export * from './actions';
24
+ export * from './database';
25
+ export * from './entities';
26
+ export * from './logger';
27
+ export * from './prompts';
28
+ export * from './roles';
29
+ export * from './runtime';
30
+ export * from './settings';
31
+ export * from './services';
32
+ export * from './search';
33
+
34
+ // Export Sentry instrumentation (node version)
35
+ export * from './sentry/instrument.node';
36
+
37
+ // Node-specific exports
38
+ export const isBrowser = false;
39
+ export const isNode = true;
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Main entry point for @elizaos/core
3
+ *
4
+ * This is the default export that includes all modules.
5
+ * The build system creates separate bundles for Node.js and browser environments.
6
+ * Package.json conditional exports handle the routing to the correct build.
7
+ */
8
+
9
+ // Export everything from types
10
+ export * from './types';
11
+
12
+ // Export utils first to avoid circular dependency issues
13
+ export * from './utils';
14
+
15
+ // Export schemas
16
+ export * from './schemas/character';
17
+
18
+ // Export environment utilities
19
+ export * from './utils/environment';
20
+
21
+ // Export buffer utilities
22
+ export * from './utils/buffer';
23
+
24
+ // Then all other exports
25
+ export * from './actions';
26
+ export * from './database';
27
+ export * from './entities';
28
+ export * from './logger';
29
+ export * from './prompts';
30
+ export * from './roles';
31
+ export * from './runtime';
32
+ export * from './settings';
33
+ export * from './services';
34
+ export * from './search';
35
+
36
+ // Export Sentry instrumentation
37
+ export * from './sentry/instrument';
38
+
39
+ // Environment detection utilities
40
+ export const isBrowser =
41
+ typeof globalThis !== 'undefined' &&
42
+ typeof (globalThis as any).window !== 'undefined' &&
43
+ typeof (globalThis as any).document !== 'undefined';
44
+ export const isNode =
45
+ typeof process !== 'undefined' &&
46
+ typeof process.versions !== 'undefined' &&
47
+ typeof process.versions.node !== 'undefined';
48
+
49
+ // Re-export server health with a conditional stub for browser environments
50
+ export * from './utils/server-health';