@multiplayer-app/ai-agent-node 0.1.0-beta.8 → 0.1.0-beta.81
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.
- package/README.md +2 -2
- package/dist/cjs/config.cjs +88 -37
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.ts +62 -23
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/helpers/AIHelper.cjs +134 -68
- package/dist/cjs/helpers/AIHelper.cjs.map +1 -1
- package/dist/cjs/helpers/AIHelper.d.ts +24 -16
- package/dist/cjs/helpers/AIHelper.d.ts.map +1 -1
- package/dist/cjs/helpers/AIHelper.test.cjs +22 -15
- package/dist/cjs/helpers/AIHelper.test.cjs.map +1 -1
- package/dist/cjs/helpers/ConfigHelper.cjs +15 -6
- package/dist/cjs/helpers/ConfigHelper.cjs.map +1 -1
- package/dist/cjs/helpers/ConfigHelper.d.ts.map +1 -1
- package/dist/cjs/helpers/FileHelper.cjs +131 -151
- package/dist/cjs/helpers/FileHelper.cjs.map +1 -1
- package/dist/cjs/helpers/FileHelper.d.ts +19 -25
- package/dist/cjs/helpers/FileHelper.d.ts.map +1 -1
- package/dist/cjs/helpers/index.cjs +0 -1
- package/dist/cjs/helpers/index.cjs.map +1 -1
- package/dist/cjs/helpers/index.d.ts +0 -1
- package/dist/cjs/helpers/index.d.ts.map +1 -1
- package/dist/cjs/index.cjs +125 -28
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +47 -11
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/libs/index.cjs +0 -1
- package/dist/cjs/libs/index.cjs.map +1 -1
- package/dist/cjs/libs/index.d.ts +0 -1
- package/dist/cjs/libs/index.d.ts.map +1 -1
- package/dist/cjs/libs/s3/index.cjs +3 -39
- package/dist/cjs/libs/s3/index.cjs.map +1 -1
- package/dist/cjs/libs/s3/index.d.ts +1 -2
- package/dist/cjs/libs/s3/index.d.ts.map +1 -1
- package/dist/cjs/libs/s3/s3.lib.cjs +173 -186
- package/dist/cjs/libs/s3/s3.lib.cjs.map +1 -1
- package/dist/cjs/libs/s3/s3.lib.d.ts +29 -22
- package/dist/cjs/libs/s3/s3.lib.d.ts.map +1 -1
- package/dist/cjs/processors/ActivityProcessor.cjs +53 -0
- package/dist/cjs/processors/ActivityProcessor.cjs.map +1 -0
- package/dist/cjs/processors/ActivityProcessor.d.ts +34 -0
- package/dist/cjs/processors/ActivityProcessor.d.ts.map +1 -0
- package/dist/cjs/processors/ActivityProcessor.test.cjs +139 -0
- package/dist/cjs/processors/ActivityProcessor.test.cjs.map +1 -0
- package/dist/cjs/processors/ActivityProcessor.test.d.ts +2 -0
- package/dist/cjs/processors/ActivityProcessor.test.d.ts.map +1 -0
- package/dist/cjs/processors/AgentProcessor.cjs +47 -0
- package/dist/cjs/processors/AgentProcessor.cjs.map +1 -0
- package/dist/cjs/processors/AgentProcessor.d.ts +25 -0
- package/dist/cjs/processors/AgentProcessor.d.ts.map +1 -0
- package/dist/cjs/processors/AgentProcessor.test.cjs +103 -0
- package/dist/cjs/processors/AgentProcessor.test.cjs.map +1 -0
- package/dist/cjs/processors/AgentProcessor.test.d.ts +2 -0
- package/dist/cjs/processors/AgentProcessor.test.d.ts.map +1 -0
- package/dist/cjs/processors/ChatProcessor.cjs +1029 -148
- package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
- package/dist/cjs/processors/ChatProcessor.d.ts +108 -12
- package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/cjs/processors/ChatProcessor.test.cjs +803 -0
- package/dist/cjs/processors/ChatProcessor.test.cjs.map +1 -0
- package/dist/cjs/processors/ChatProcessor.test.d.ts +2 -0
- package/dist/cjs/processors/ChatProcessor.test.d.ts.map +1 -0
- package/dist/cjs/processors/index.cjs +2 -0
- package/dist/cjs/processors/index.cjs.map +1 -1
- package/dist/cjs/processors/index.d.ts +2 -0
- package/dist/cjs/processors/index.d.ts.map +1 -1
- package/dist/cjs/services/AIService.cjs +114 -68
- package/dist/cjs/services/AIService.cjs.map +1 -1
- package/dist/cjs/services/AIService.d.ts +34 -13
- package/dist/cjs/services/AIService.d.ts.map +1 -1
- package/dist/cjs/services/CredentialProvider.cjs +62 -0
- package/dist/cjs/services/CredentialProvider.cjs.map +1 -0
- package/dist/cjs/services/CredentialProvider.d.ts +20 -0
- package/dist/cjs/services/CredentialProvider.d.ts.map +1 -0
- package/dist/cjs/services/CredentialProvider.test.cjs +71 -0
- package/dist/cjs/services/CredentialProvider.test.cjs.map +1 -0
- package/dist/cjs/services/CredentialProvider.test.d.ts +2 -0
- package/dist/cjs/services/CredentialProvider.test.d.ts.map +1 -0
- package/dist/cjs/services/ExecutionContext.cjs +3 -0
- package/dist/cjs/services/ExecutionContext.cjs.map +1 -0
- package/dist/cjs/services/ExecutionContext.d.ts +4 -0
- package/dist/cjs/services/ExecutionContext.d.ts.map +1 -0
- package/dist/cjs/services/InternalEventsHandler.cjs +3 -3
- package/dist/cjs/services/InternalEventsHandler.cjs.map +1 -1
- package/dist/cjs/services/InternalEventsHandler.d.ts +3 -1
- package/dist/cjs/services/InternalEventsHandler.d.ts.map +1 -1
- package/dist/cjs/services/ModelAccessPolicy.cjs +24 -0
- package/dist/cjs/services/ModelAccessPolicy.cjs.map +1 -0
- package/dist/cjs/services/ModelAccessPolicy.d.ts +7 -0
- package/dist/cjs/services/ModelAccessPolicy.d.ts.map +1 -0
- package/dist/cjs/services/ModelFetcher.cjs +2 -8
- package/dist/cjs/services/ModelFetcher.cjs.map +1 -1
- package/dist/cjs/services/ModelFetcher.d.ts +2 -7
- package/dist/cjs/services/ModelFetcher.d.ts.map +1 -1
- package/dist/cjs/services/ProviderClientFactory.cjs +29 -0
- package/dist/cjs/services/ProviderClientFactory.cjs.map +1 -0
- package/dist/cjs/services/ProviderClientFactory.d.ts +9 -0
- package/dist/cjs/services/ProviderClientFactory.d.ts.map +1 -0
- package/dist/cjs/services/RedisService.cjs +20 -16
- package/dist/cjs/services/RedisService.cjs.map +1 -1
- package/dist/cjs/services/RedisService.d.ts +5 -2
- package/dist/cjs/services/RedisService.d.ts.map +1 -1
- package/dist/cjs/services/SocketService.cjs +8 -8
- package/dist/cjs/services/SocketService.cjs.map +1 -1
- package/dist/cjs/services/SocketService.d.ts +9 -6
- package/dist/cjs/services/SocketService.d.ts.map +1 -1
- package/dist/cjs/services/TenantCredentialResolver.cjs +136 -0
- package/dist/cjs/services/TenantCredentialResolver.cjs.map +1 -0
- package/dist/cjs/services/TenantCredentialResolver.d.ts +32 -0
- package/dist/cjs/services/TenantCredentialResolver.d.ts.map +1 -0
- package/dist/cjs/services/TenantCredentialResolver.test.cjs +113 -0
- package/dist/cjs/services/TenantCredentialResolver.test.cjs.map +1 -0
- package/dist/cjs/services/TenantCredentialResolver.test.d.ts +2 -0
- package/dist/cjs/services/TenantCredentialResolver.test.d.ts.map +1 -0
- package/dist/cjs/services/index.cjs +5 -1
- package/dist/cjs/services/index.cjs.map +1 -1
- package/dist/cjs/services/index.d.ts +5 -1
- package/dist/cjs/services/index.d.ts.map +1 -1
- package/dist/cjs/store/AgentStore.cjs +14 -4
- package/dist/cjs/store/AgentStore.cjs.map +1 -1
- package/dist/cjs/store/AgentStore.d.ts +3 -1
- package/dist/cjs/store/AgentStore.d.ts.map +1 -1
- package/dist/cjs/store/ConfigStore.cjs +14 -3
- package/dist/cjs/store/ConfigStore.cjs.map +1 -1
- package/dist/cjs/store/ConfigStore.d.ts +2 -0
- package/dist/cjs/store/ConfigStore.d.ts.map +1 -1
- package/dist/cjs/tools/generateChartTool.d.ts +2 -2
- package/dist/cjs/tools/proposeFormValuesTool.d.ts +2 -2
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/utils/utils.cjs +31 -0
- package/dist/cjs/utils/utils.cjs.map +1 -0
- package/dist/cjs/utils/utils.d.ts +5 -0
- package/dist/cjs/utils/utils.d.ts.map +1 -0
- package/dist/esm/config.d.ts +62 -23
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +88 -35
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/helpers/AIHelper.d.ts +24 -16
- package/dist/esm/helpers/AIHelper.d.ts.map +1 -1
- package/dist/esm/helpers/AIHelper.js +141 -73
- package/dist/esm/helpers/AIHelper.js.map +1 -1
- package/dist/esm/helpers/AIHelper.test.js +22 -15
- package/dist/esm/helpers/AIHelper.test.js.map +1 -1
- package/dist/esm/helpers/ConfigHelper.d.ts.map +1 -1
- package/dist/esm/helpers/ConfigHelper.js +15 -6
- package/dist/esm/helpers/ConfigHelper.js.map +1 -1
- package/dist/esm/helpers/FileHelper.d.ts +19 -25
- package/dist/esm/helpers/FileHelper.d.ts.map +1 -1
- package/dist/esm/helpers/FileHelper.js +131 -146
- package/dist/esm/helpers/FileHelper.js.map +1 -1
- package/dist/esm/helpers/index.d.ts +0 -1
- package/dist/esm/helpers/index.d.ts.map +1 -1
- package/dist/esm/helpers/index.js +0 -1
- package/dist/esm/helpers/index.js.map +1 -1
- package/dist/esm/index.d.ts +47 -11
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +98 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/libs/index.d.ts +0 -1
- package/dist/esm/libs/index.d.ts.map +1 -1
- package/dist/esm/libs/index.js +0 -1
- package/dist/esm/libs/index.js.map +1 -1
- package/dist/esm/libs/s3/index.d.ts +1 -2
- package/dist/esm/libs/s3/index.d.ts.map +1 -1
- package/dist/esm/libs/s3/index.js +1 -2
- package/dist/esm/libs/s3/index.js.map +1 -1
- package/dist/esm/libs/s3/s3.lib.d.ts +29 -22
- package/dist/esm/libs/s3/s3.lib.d.ts.map +1 -1
- package/dist/esm/libs/s3/s3.lib.js +177 -172
- package/dist/esm/libs/s3/s3.lib.js.map +1 -1
- package/dist/esm/processors/ActivityProcessor.d.ts +34 -0
- package/dist/esm/processors/ActivityProcessor.d.ts.map +1 -0
- package/dist/esm/processors/ActivityProcessor.js +50 -0
- package/dist/esm/processors/ActivityProcessor.js.map +1 -0
- package/dist/esm/processors/ActivityProcessor.test.d.ts +2 -0
- package/dist/esm/processors/ActivityProcessor.test.d.ts.map +1 -0
- package/dist/esm/processors/ActivityProcessor.test.js +137 -0
- package/dist/esm/processors/ActivityProcessor.test.js.map +1 -0
- package/dist/esm/processors/AgentProcessor.d.ts +25 -0
- package/dist/esm/processors/AgentProcessor.d.ts.map +1 -0
- package/dist/esm/processors/AgentProcessor.js +44 -0
- package/dist/esm/processors/AgentProcessor.js.map +1 -0
- package/dist/esm/processors/AgentProcessor.test.d.ts +2 -0
- package/dist/esm/processors/AgentProcessor.test.d.ts.map +1 -0
- package/dist/esm/processors/AgentProcessor.test.js +101 -0
- package/dist/esm/processors/AgentProcessor.test.js.map +1 -0
- package/dist/esm/processors/ChatProcessor.d.ts +108 -12
- package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/esm/processors/ChatProcessor.js +1038 -150
- package/dist/esm/processors/ChatProcessor.js.map +1 -1
- package/dist/esm/processors/ChatProcessor.test.d.ts +2 -0
- package/dist/esm/processors/ChatProcessor.test.d.ts.map +1 -0
- package/dist/esm/processors/ChatProcessor.test.js +801 -0
- package/dist/esm/processors/ChatProcessor.test.js.map +1 -0
- package/dist/esm/processors/index.d.ts +2 -0
- package/dist/esm/processors/index.d.ts.map +1 -1
- package/dist/esm/processors/index.js +2 -0
- package/dist/esm/processors/index.js.map +1 -1
- package/dist/esm/services/AIService.d.ts +34 -13
- package/dist/esm/services/AIService.d.ts.map +1 -1
- package/dist/esm/services/AIService.js +118 -68
- package/dist/esm/services/AIService.js.map +1 -1
- package/dist/esm/services/CredentialProvider.d.ts +20 -0
- package/dist/esm/services/CredentialProvider.d.ts.map +1 -0
- package/dist/esm/services/CredentialProvider.js +60 -0
- package/dist/esm/services/CredentialProvider.js.map +1 -0
- package/dist/esm/services/CredentialProvider.test.d.ts +2 -0
- package/dist/esm/services/CredentialProvider.test.d.ts.map +1 -0
- package/dist/esm/services/CredentialProvider.test.js +69 -0
- package/dist/esm/services/CredentialProvider.test.js.map +1 -0
- package/dist/esm/services/ExecutionContext.d.ts +4 -0
- package/dist/esm/services/ExecutionContext.d.ts.map +1 -0
- package/dist/esm/services/ExecutionContext.js +2 -0
- package/dist/esm/services/ExecutionContext.js.map +1 -0
- package/dist/esm/services/InternalEventsHandler.d.ts +3 -1
- package/dist/esm/services/InternalEventsHandler.d.ts.map +1 -1
- package/dist/esm/services/InternalEventsHandler.js +4 -3
- package/dist/esm/services/InternalEventsHandler.js.map +1 -1
- package/dist/esm/services/ModelAccessPolicy.d.ts +7 -0
- package/dist/esm/services/ModelAccessPolicy.d.ts.map +1 -0
- package/dist/esm/services/ModelAccessPolicy.js +20 -0
- package/dist/esm/services/ModelAccessPolicy.js.map +1 -0
- package/dist/esm/services/ModelFetcher.d.ts +2 -7
- package/dist/esm/services/ModelFetcher.d.ts.map +1 -1
- package/dist/esm/services/ModelFetcher.js +2 -8
- package/dist/esm/services/ModelFetcher.js.map +1 -1
- package/dist/esm/services/ProviderClientFactory.d.ts +9 -0
- package/dist/esm/services/ProviderClientFactory.d.ts.map +1 -0
- package/dist/esm/services/ProviderClientFactory.js +25 -0
- package/dist/esm/services/ProviderClientFactory.js.map +1 -0
- package/dist/esm/services/RedisService.d.ts +5 -2
- package/dist/esm/services/RedisService.d.ts.map +1 -1
- package/dist/esm/services/RedisService.js +21 -14
- package/dist/esm/services/RedisService.js.map +1 -1
- package/dist/esm/services/SocketService.d.ts +9 -6
- package/dist/esm/services/SocketService.d.ts.map +1 -1
- package/dist/esm/services/SocketService.js +10 -6
- package/dist/esm/services/SocketService.js.map +1 -1
- package/dist/esm/services/TenantCredentialResolver.d.ts +32 -0
- package/dist/esm/services/TenantCredentialResolver.d.ts.map +1 -0
- package/dist/esm/services/TenantCredentialResolver.js +133 -0
- package/dist/esm/services/TenantCredentialResolver.js.map +1 -0
- package/dist/esm/services/TenantCredentialResolver.test.d.ts +2 -0
- package/dist/esm/services/TenantCredentialResolver.test.d.ts.map +1 -0
- package/dist/esm/services/TenantCredentialResolver.test.js +111 -0
- package/dist/esm/services/TenantCredentialResolver.test.js.map +1 -0
- package/dist/esm/services/index.d.ts +5 -1
- package/dist/esm/services/index.d.ts.map +1 -1
- package/dist/esm/services/index.js +5 -1
- package/dist/esm/services/index.js.map +1 -1
- package/dist/esm/store/AgentStore.d.ts +3 -1
- package/dist/esm/store/AgentStore.d.ts.map +1 -1
- package/dist/esm/store/AgentStore.js +15 -2
- package/dist/esm/store/AgentStore.js.map +1 -1
- package/dist/esm/store/ConfigStore.d.ts +2 -0
- package/dist/esm/store/ConfigStore.d.ts.map +1 -1
- package/dist/esm/store/ConfigStore.js +14 -3
- package/dist/esm/store/ConfigStore.js.map +1 -1
- package/dist/esm/tools/generateChartTool.d.ts +2 -2
- package/dist/esm/tools/proposeFormValuesTool.d.ts +2 -2
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/utils.d.ts +5 -0
- package/dist/esm/utils/utils.d.ts.map +1 -0
- package/dist/esm/utils/utils.js +26 -0
- package/dist/esm/utils/utils.js.map +1 -0
- package/package.json +7 -8
- package/dist/cjs/helpers/SetupHelper.cjs +0 -37
- package/dist/cjs/helpers/SetupHelper.cjs.map +0 -1
- package/dist/cjs/helpers/SetupHelper.d.ts +0 -5
- package/dist/cjs/helpers/SetupHelper.d.ts.map +0 -1
- package/dist/cjs/libs/kafka/config.cjs +0 -8
- package/dist/cjs/libs/kafka/config.cjs.map +0 -1
- package/dist/cjs/libs/kafka/config.d.ts +0 -5
- package/dist/cjs/libs/kafka/config.d.ts.map +0 -1
- package/dist/cjs/libs/kafka/consumer.cjs +0 -131
- package/dist/cjs/libs/kafka/consumer.cjs.map +0 -1
- package/dist/cjs/libs/kafka/consumer.d.ts +0 -16
- package/dist/cjs/libs/kafka/consumer.d.ts.map +0 -1
- package/dist/cjs/libs/kafka/index.cjs +0 -19
- package/dist/cjs/libs/kafka/index.cjs.map +0 -1
- package/dist/cjs/libs/kafka/index.d.ts +0 -3
- package/dist/cjs/libs/kafka/index.d.ts.map +0 -1
- package/dist/cjs/libs/kafka/kafka.cjs +0 -27
- package/dist/cjs/libs/kafka/kafka.cjs.map +0 -1
- package/dist/cjs/libs/kafka/kafka.d.ts +0 -3
- package/dist/cjs/libs/kafka/kafka.d.ts.map +0 -1
- package/dist/cjs/libs/kafka/producer.cjs +0 -48
- package/dist/cjs/libs/kafka/producer.cjs.map +0 -1
- package/dist/cjs/libs/kafka/producer.d.ts +0 -11
- package/dist/cjs/libs/kafka/producer.d.ts.map +0 -1
- package/dist/cjs/libs/logger/config.cjs +0 -8
- package/dist/cjs/libs/logger/config.cjs.map +0 -1
- package/dist/cjs/libs/logger/config.d.ts +0 -5
- package/dist/cjs/libs/logger/config.d.ts.map +0 -1
- package/dist/cjs/libs/s3/config.cjs +0 -10
- package/dist/cjs/libs/s3/config.cjs.map +0 -1
- package/dist/cjs/libs/s3/config.d.ts +0 -7
- package/dist/cjs/libs/s3/config.d.ts.map +0 -1
- package/dist/cjs/services/KafkaService.cjs +0 -122
- package/dist/cjs/services/KafkaService.cjs.map +0 -1
- package/dist/cjs/services/KafkaService.d.ts +0 -35
- package/dist/cjs/services/KafkaService.d.ts.map +0 -1
- package/dist/esm/helpers/SetupHelper.d.ts +0 -5
- package/dist/esm/helpers/SetupHelper.d.ts.map +0 -1
- package/dist/esm/helpers/SetupHelper.js +0 -32
- package/dist/esm/helpers/SetupHelper.js.map +0 -1
- package/dist/esm/libs/kafka/config.d.ts +0 -5
- package/dist/esm/libs/kafka/config.d.ts.map +0 -1
- package/dist/esm/libs/kafka/config.js +0 -5
- package/dist/esm/libs/kafka/config.js.map +0 -1
- package/dist/esm/libs/kafka/consumer.d.ts +0 -16
- package/dist/esm/libs/kafka/consumer.d.ts.map +0 -1
- package/dist/esm/libs/kafka/consumer.js +0 -125
- package/dist/esm/libs/kafka/consumer.js.map +0 -1
- package/dist/esm/libs/kafka/index.d.ts +0 -3
- package/dist/esm/libs/kafka/index.d.ts.map +0 -1
- package/dist/esm/libs/kafka/index.js +0 -3
- package/dist/esm/libs/kafka/index.js.map +0 -1
- package/dist/esm/libs/kafka/kafka.d.ts +0 -3
- package/dist/esm/libs/kafka/kafka.d.ts.map +0 -1
- package/dist/esm/libs/kafka/kafka.js +0 -24
- package/dist/esm/libs/kafka/kafka.js.map +0 -1
- package/dist/esm/libs/kafka/producer.d.ts +0 -11
- package/dist/esm/libs/kafka/producer.d.ts.map +0 -1
- package/dist/esm/libs/kafka/producer.js +0 -45
- package/dist/esm/libs/kafka/producer.js.map +0 -1
- package/dist/esm/libs/logger/config.d.ts +0 -5
- package/dist/esm/libs/logger/config.d.ts.map +0 -1
- package/dist/esm/libs/logger/config.js +0 -5
- package/dist/esm/libs/logger/config.js.map +0 -1
- package/dist/esm/libs/s3/config.d.ts +0 -7
- package/dist/esm/libs/s3/config.d.ts.map +0 -1
- package/dist/esm/libs/s3/config.js +0 -7
- package/dist/esm/libs/s3/config.js.map +0 -1
- package/dist/esm/services/KafkaService.d.ts +0 -35
- package/dist/esm/services/KafkaService.d.ts.map +0 -1
- package/dist/esm/services/KafkaService.js +0 -123
- package/dist/esm/services/KafkaService.js.map +0 -1
|
@@ -1,76 +1,369 @@
|
|
|
1
|
-
import { AgentStatus, AgentToolCallStatus, ChatType, MessageRole, SortOrder, StreamChunkType } from '@multiplayer-app/ai-agent-types';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { AIHelper } from '../helpers';
|
|
5
|
-
import {
|
|
6
|
-
import { ContextLimiter } from '../helpers';
|
|
7
|
-
import { config } from '../config';
|
|
8
|
-
import { PassThrough, pipeline } from 'stream';
|
|
9
|
-
import { promisify } from 'util';
|
|
1
|
+
import { ActivityOperationName, AgentStatus, AgentToolCallStatus, AgentToolType, ChatType, MessageRole, SortOrder, StreamChunkType } from '@multiplayer-app/ai-agent-types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AgentProcessEventType, ConfigStore } from '../store';
|
|
4
|
+
import { AIHelper, ContextLimiter, FileHelper } from '../helpers';
|
|
5
|
+
import { PassThrough } from 'stream';
|
|
10
6
|
import { logger } from '../libs/logger';
|
|
11
|
-
|
|
7
|
+
import { AgentSessionKind } from "@multiplayer-app/ai-agent-types";
|
|
8
|
+
import { getAgentToolName } from "../utils/utils";
|
|
12
9
|
export class ChatProcessor {
|
|
10
|
+
aiHelper;
|
|
13
11
|
chatRepository;
|
|
14
12
|
messageRepository;
|
|
15
13
|
artifactStore;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
activityRepository;
|
|
15
|
+
agentConfigRepository;
|
|
16
|
+
config;
|
|
17
|
+
socketService;
|
|
18
|
+
agentStore;
|
|
19
|
+
debug;
|
|
20
|
+
getChatUserId(chat) {
|
|
21
|
+
return chat.tenants?.userId;
|
|
22
|
+
}
|
|
23
|
+
constructor(params) {
|
|
24
|
+
this.chatRepository = params.chatRepository;
|
|
25
|
+
this.messageRepository = params.messageRepository;
|
|
26
|
+
this.artifactStore = params.artifactStore;
|
|
27
|
+
this.config = params.config;
|
|
28
|
+
this.socketService = params.socketService;
|
|
29
|
+
this.agentStore = params.agentStore;
|
|
30
|
+
this.activityRepository = params.activityRepository;
|
|
31
|
+
this.agentConfigRepository = params.agentConfigRepository;
|
|
32
|
+
const fileHelper = new FileHelper(params.s3Lib, this.config);
|
|
33
|
+
this.aiHelper = new AIHelper(fileHelper, this.config, params.tenantCredentialResolver);
|
|
34
|
+
this.debug = this.config.debug;
|
|
20
35
|
}
|
|
21
36
|
getTemporaryTitle(contextKey) {
|
|
22
37
|
return `${contextKey} session ${new Date().toISOString()}`;
|
|
23
38
|
}
|
|
39
|
+
isTemporaryTitle(title) {
|
|
40
|
+
// Matches pattern: "{contextKey} session {ISO_DATE_STRING}"
|
|
41
|
+
// Example: "default session 2026-01-23T10:30:45.123Z"
|
|
42
|
+
// toISOString() always includes milliseconds, so we require them
|
|
43
|
+
const pattern = /^.+ session \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
44
|
+
return pattern.test(title);
|
|
45
|
+
}
|
|
46
|
+
isPlainObject(value) {
|
|
47
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
getExecutionTenants(payload) {
|
|
50
|
+
const contextTenants = payload.executionContext?.tenants;
|
|
51
|
+
if (contextTenants && Object.keys(contextTenants).length > 0) {
|
|
52
|
+
return contextTenants;
|
|
53
|
+
}
|
|
54
|
+
return payload.tenants ?? {};
|
|
55
|
+
}
|
|
56
|
+
getParticipantIds(chat) {
|
|
57
|
+
const primary = chat.userId ?? this.getChatUserId(chat);
|
|
58
|
+
return Array.from(new Set([primary, ...(chat.participantIds ?? [])].filter(Boolean)));
|
|
59
|
+
}
|
|
60
|
+
hasChatAccess(chat, userId) {
|
|
61
|
+
if (!userId)
|
|
62
|
+
return true;
|
|
63
|
+
return this.getParticipantIds(chat).includes(userId);
|
|
64
|
+
}
|
|
65
|
+
emitChatToParticipants(chat, chatToEmit, excludeSocketId) {
|
|
66
|
+
for (const userId of this.getParticipantIds(chatToEmit.participantIds ? chatToEmit : chat)) {
|
|
67
|
+
this.socketService.emitChatUpdate(userId, chatToEmit, excludeSocketId);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
emitMessageToParticipants(chat, message, excludeSocketId) {
|
|
71
|
+
for (const userId of this.getParticipantIds(chat)) {
|
|
72
|
+
this.socketService.emitMessageUpdate(userId, message, excludeSocketId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async updateChatAndEmit(chat, update, excludeSocketId) {
|
|
76
|
+
await this.chatRepository.update(chat.id, update);
|
|
77
|
+
const updatedChat = await this.chatRepository.findById(chat.id);
|
|
78
|
+
const chatToEmit = updatedChat ?? { ...chat, ...update };
|
|
79
|
+
this.emitChatToParticipants(chat, chatToEmit, excludeSocketId);
|
|
80
|
+
return chatToEmit;
|
|
81
|
+
}
|
|
82
|
+
async startSubagentProcess(params) {
|
|
83
|
+
const parentUserId = params.parentChat.userId ?? this.getChatUserId(params.parentChat) ?? 'guest';
|
|
84
|
+
const childChat = await this.chatRepository.create({
|
|
85
|
+
title: `${params.subAgentConfig.name} subagent`,
|
|
86
|
+
type: ChatType.Agent,
|
|
87
|
+
status: AgentStatus.Streaming,
|
|
88
|
+
sessionKind: AgentSessionKind.SUBAGENT,
|
|
89
|
+
parentChatId: params.parentChat.id,
|
|
90
|
+
parentMessageId: params.parentMessage.id,
|
|
91
|
+
parentToolCallId: params.parentToolCallId,
|
|
92
|
+
contextKey: params.parentChat.contextKey,
|
|
93
|
+
userId: parentUserId,
|
|
94
|
+
tenants: { ...params.parentChat.tenants },
|
|
95
|
+
model: params.subAgentConfig.defaultModel || params.parentChat.model,
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
const parentToolCall = params.parentMessage.toolCalls?.find((toolCall) => toolCall.id === params.parentToolCallId);
|
|
99
|
+
if (parentToolCall) {
|
|
100
|
+
parentToolCall.input = {
|
|
101
|
+
...(parentToolCall.input ?? {}),
|
|
102
|
+
subagentChatId: childChat.id,
|
|
103
|
+
};
|
|
104
|
+
parentToolCall.subagentChatId = childChat.id;
|
|
105
|
+
const updatedParentMessage = await this.messageRepository.update(params.parentMessage.id, {
|
|
106
|
+
toolCalls: params.parentMessage.toolCalls,
|
|
107
|
+
});
|
|
108
|
+
if (updatedParentMessage) {
|
|
109
|
+
this.emitMessageToParticipants(params.parentChat, updatedParentMessage);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.emitChatToParticipants(params.parentChat, childChat);
|
|
113
|
+
const executionTenants = this.getExecutionTenants(params);
|
|
114
|
+
const abortController = this.agentStore.registerSubAgentProcess(childChat.id, params.parentChat.id);
|
|
115
|
+
const userMessage = await this.createMessage(childChat, MessageRole.User, {
|
|
116
|
+
content: JSON.stringify(params.input),
|
|
117
|
+
agentName: undefined
|
|
118
|
+
});
|
|
119
|
+
await this.agentStore.shareAgentProcessEvent(childChat.id, {
|
|
120
|
+
type: AgentProcessEventType.Update,
|
|
121
|
+
data: userMessage
|
|
122
|
+
});
|
|
123
|
+
let assistantMessage = await this.createMessage(childChat, MessageRole.Assistant, {
|
|
124
|
+
content: '',
|
|
125
|
+
agentName: params.subAgentConfig.name
|
|
126
|
+
});
|
|
127
|
+
const parentActivity = await this.activityRepository.create({
|
|
128
|
+
ownerId: parentUserId,
|
|
129
|
+
groupId: params.parentChat.id,
|
|
130
|
+
name: ActivityOperationName.SUBAGENT,
|
|
131
|
+
tenants: executionTenants,
|
|
132
|
+
sourceId: assistantMessage.id,
|
|
133
|
+
sourceType: 'AgentMessage',
|
|
134
|
+
metadata: {
|
|
135
|
+
modelId: this.aiHelper.getLanguageModelId(childChat.model),
|
|
136
|
+
agentOptions: {
|
|
137
|
+
name: params.subAgentConfig.name,
|
|
138
|
+
modelId: this.aiHelper.getLanguageModelId(params.subAgentConfig.defaultModel),
|
|
139
|
+
temperature: params.subAgentConfig.temperature,
|
|
140
|
+
maxOutputTokens: params.subAgentConfig.maxOutputTokens,
|
|
141
|
+
topP: params.subAgentConfig.topP,
|
|
142
|
+
topK: params.subAgentConfig.topK,
|
|
143
|
+
presencePenalty: params.subAgentConfig.presencePenalty,
|
|
144
|
+
frequencyPenalty: params.subAgentConfig.frequencyPenalty,
|
|
145
|
+
stopSequences: params.subAgentConfig.stopSequences,
|
|
146
|
+
seed: params.subAgentConfig.seed,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
|
|
151
|
+
activity: parentActivity.id
|
|
152
|
+
});
|
|
153
|
+
const agentOptions = this.aiHelper.getAgentOptionsFromConfig(params.subAgentConfig, params.context);
|
|
154
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
155
|
+
return this.storeStepActivity({
|
|
156
|
+
chat: params.parentChat,
|
|
157
|
+
parentId: assistantMessage.activity,
|
|
158
|
+
stepResult,
|
|
159
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
160
|
+
sourceId: assistantMessage.id,
|
|
161
|
+
sourceType: 'AgentMessage',
|
|
162
|
+
tenants: executionTenants,
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(parentUserId, agentOptions.name);
|
|
166
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
167
|
+
await this.streamMessageStep({
|
|
168
|
+
chat: childChat,
|
|
169
|
+
existingMessages: [userMessage, updatedAssistantMessage],
|
|
170
|
+
assistantMessage: updatedAssistantMessage,
|
|
171
|
+
agentOptions,
|
|
172
|
+
signal: abortController.signal,
|
|
173
|
+
tenants: executionTenants,
|
|
174
|
+
executionContext: params.executionContext,
|
|
175
|
+
context: params.context,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
await this.storeSubagentResponse({
|
|
180
|
+
subagentChat: childChat,
|
|
181
|
+
content: undefined,
|
|
182
|
+
error: error,
|
|
183
|
+
context: params.context,
|
|
184
|
+
executionContext: params.executionContext,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
mergeMissingUsageFields(target, source) {
|
|
189
|
+
for (const [key, sourceValue] of Object.entries(source)) {
|
|
190
|
+
const targetValue = target[key];
|
|
191
|
+
if (targetValue === undefined) {
|
|
192
|
+
target[key] = sourceValue;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (this.isPlainObject(targetValue) && this.isPlainObject(sourceValue)) {
|
|
196
|
+
this.mergeMissingUsageFields(targetValue, sourceValue);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
extractCostUsageFields(source) {
|
|
201
|
+
const extracted = {};
|
|
202
|
+
for (const [key, value] of Object.entries(source)) {
|
|
203
|
+
const normalizedKey = key.toLowerCase();
|
|
204
|
+
if (normalizedKey.includes('cost')) {
|
|
205
|
+
extracted[key] = value;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (this.isPlainObject(value)) {
|
|
209
|
+
const nestedCostFields = this.extractCostUsageFields(value);
|
|
210
|
+
if (Object.keys(nestedCostFields).length > 0) {
|
|
211
|
+
extracted[key] = nestedCostFields;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return extracted;
|
|
216
|
+
}
|
|
217
|
+
mergeUsageWithProviderMetadata(stepUsage, providerMetadata) {
|
|
218
|
+
const mergedUsage = this.isPlainObject(stepUsage) ? { ...stepUsage } : {};
|
|
219
|
+
let hasProviderUsage = false;
|
|
220
|
+
if (this.isPlainObject(providerMetadata)) {
|
|
221
|
+
for (const providerEntry of Object.values(providerMetadata)) {
|
|
222
|
+
if (!this.isPlainObject(providerEntry) || !this.isPlainObject(providerEntry.usage)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const providerCostUsage = this.extractCostUsageFields(providerEntry.usage);
|
|
226
|
+
if (Object.keys(providerCostUsage).length === 0) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
hasProviderUsage = true;
|
|
230
|
+
this.mergeMissingUsageFields(mergedUsage, providerCostUsage);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (this.isPlainObject(stepUsage)) {
|
|
234
|
+
return mergedUsage;
|
|
235
|
+
}
|
|
236
|
+
if (hasProviderUsage) {
|
|
237
|
+
return mergedUsage;
|
|
238
|
+
}
|
|
239
|
+
return stepUsage;
|
|
240
|
+
}
|
|
241
|
+
async validateToolCallInput(params) {
|
|
242
|
+
const agentOptions = await this.aiHelper.getAgentOptions({
|
|
243
|
+
agentName: params.agentName,
|
|
244
|
+
});
|
|
245
|
+
const tool = agentOptions.tools?.[params.toolName] ?? undefined;
|
|
246
|
+
const schema = tool?.inputSchema;
|
|
247
|
+
if (!schema) {
|
|
248
|
+
throw new Error(`Tool "${params.toolName}" schema not found`);
|
|
249
|
+
}
|
|
250
|
+
if (!(schema instanceof z.ZodType)) {
|
|
251
|
+
throw new Error(`Tool "${params.toolName}" inputSchema must be a Zod schema`);
|
|
252
|
+
}
|
|
253
|
+
const result = schema.safeParse(params.input);
|
|
254
|
+
if (!result.success) {
|
|
255
|
+
throw new Error(`Invalid tool input for "${params.toolName}": ${result.error.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
24
258
|
async listChats(params) {
|
|
25
|
-
// Build filter object from params
|
|
26
|
-
|
|
259
|
+
// Build filter object from params. Always exclude subagent chats:
|
|
260
|
+
// the repository translates ROOT into `sessionKind !== SUBAGENT`, which
|
|
261
|
+
// also includes legacy chats where the field is unset.
|
|
262
|
+
const filter = {
|
|
263
|
+
sessionKind: AgentSessionKind.ROOT
|
|
264
|
+
};
|
|
27
265
|
if (params?.userId) {
|
|
266
|
+
// Chats visible to this user: owner, tenant owner, or participant (multi-user).
|
|
28
267
|
filter.userId = params.userId;
|
|
29
268
|
}
|
|
30
269
|
if (params?.contextKey) {
|
|
31
270
|
filter.contextKey = params.contextKey;
|
|
32
271
|
}
|
|
33
|
-
|
|
272
|
+
if (params?.multiUser !== undefined) {
|
|
273
|
+
filter.multiUser = params.multiUser;
|
|
274
|
+
}
|
|
275
|
+
// Build query options for sort and limit with defaults.
|
|
34
276
|
const options = {
|
|
35
277
|
sort: {
|
|
36
278
|
field: params?.sortField ?? 'updatedAt',
|
|
37
279
|
order: params?.sortOrder ?? SortOrder.Desc
|
|
38
280
|
},
|
|
281
|
+
skip: params?.skip,
|
|
39
282
|
limit: params?.limit
|
|
40
283
|
};
|
|
41
|
-
|
|
42
|
-
|
|
284
|
+
const [total, data] = await Promise.all([
|
|
285
|
+
this.chatRepository.count(filter),
|
|
286
|
+
this.chatRepository.find(filter, options)
|
|
287
|
+
]);
|
|
288
|
+
return {
|
|
289
|
+
cursor: {
|
|
290
|
+
...(params?.skip != null ? { skip: params.skip } : {}),
|
|
291
|
+
...(params?.limit != null ? { limit: params.limit } : {}),
|
|
292
|
+
total
|
|
293
|
+
},
|
|
294
|
+
data
|
|
295
|
+
};
|
|
43
296
|
}
|
|
44
297
|
async getChat(chatId) {
|
|
45
298
|
const chat = await this.chatRepository.findById(chatId);
|
|
46
299
|
if (!chat) {
|
|
47
300
|
throw new Error('Chat not found');
|
|
48
301
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
302
|
+
return chat;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Get messages for a chat with optional cursor pagination.
|
|
306
|
+
* When limit is omitted, returns all messages (backward compatible).
|
|
307
|
+
* Messages are returned in chronological order (oldest → newest).
|
|
308
|
+
*/
|
|
309
|
+
async getMessages(chatId, options) {
|
|
310
|
+
const chat = await this.chatRepository.findById(chatId);
|
|
311
|
+
if (!chat) {
|
|
312
|
+
throw new Error('Chat not found');
|
|
313
|
+
}
|
|
314
|
+
return this.messageRepository.findByChatIdPaginated(chatId, options);
|
|
54
315
|
}
|
|
55
316
|
async deleteChat(chatId) {
|
|
317
|
+
// Collect all subagent descendants BEFORE removing the parent, otherwise the
|
|
318
|
+
// `parentChatId` link is severed and we'd orphan the subtree. Subagents can
|
|
319
|
+
// themselves spawn subagents, so traversal must be recursive.
|
|
320
|
+
const descendantIds = await this.collectDescendantChatIds(chatId);
|
|
56
321
|
const deleted = await this.chatRepository.delete(chatId);
|
|
57
322
|
if (!deleted) {
|
|
58
323
|
throw new Error('Chat not found');
|
|
59
324
|
}
|
|
60
|
-
|
|
325
|
+
// Delete descendant chat rows in parallel. Use `Promise.allSettled` so a
|
|
326
|
+
// missing/already-deleted descendant doesn't abort cleanup of the rest.
|
|
327
|
+
if (descendantIds.length > 0) {
|
|
328
|
+
await Promise.allSettled(descendantIds.map(id => this.chatRepository.delete(id)));
|
|
329
|
+
}
|
|
330
|
+
const allIds = [chatId, ...descendantIds];
|
|
331
|
+
await Promise.all([
|
|
332
|
+
...allIds.map(id => this.messageRepository.deleteByChatId(id)),
|
|
333
|
+
...allIds.map(id => this.activityRepository.deleteByGroupId(id)),
|
|
334
|
+
]);
|
|
335
|
+
for (const id of allIds) {
|
|
336
|
+
this.artifactStore.deleteArtifacts(id);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Walk the subagent tree rooted at `rootChatId` and return every descendant
|
|
341
|
+
* chat id (excluding the root itself). BFS, with parallel fan-out per level
|
|
342
|
+
* so deeply nested trees don't serialize one DB roundtrip per depth.
|
|
343
|
+
*/
|
|
344
|
+
async collectDescendantChatIds(rootChatId) {
|
|
345
|
+
const descendants = [];
|
|
346
|
+
let frontier = [rootChatId];
|
|
347
|
+
while (frontier.length > 0) {
|
|
348
|
+
const childLists = await Promise.all(frontier.map(id => this.chatRepository.find({ parentChatId: id })));
|
|
349
|
+
const nextFrontier = [];
|
|
350
|
+
for (const list of childLists) {
|
|
351
|
+
for (const child of list) {
|
|
352
|
+
descendants.push(child.id);
|
|
353
|
+
nextFrontier.push(child.id);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
frontier = nextFrontier;
|
|
357
|
+
}
|
|
358
|
+
return descendants;
|
|
61
359
|
}
|
|
62
360
|
async upsertAndGetChat(payload, excludeSocketId) {
|
|
63
|
-
const targetUserId = payload.userId ?? 'guest';
|
|
64
361
|
let chat = null;
|
|
65
362
|
if (payload.chatId) {
|
|
66
363
|
chat = await this.chatRepository.findById(payload.chatId);
|
|
67
364
|
if (!chat) {
|
|
68
365
|
throw new Error('Chat not found');
|
|
69
366
|
}
|
|
70
|
-
// Ensure chat is associated with the caller's userId
|
|
71
|
-
if (chat.userId && chat.userId !== targetUserId) {
|
|
72
|
-
throw new Error('Chat does not belong to this user');
|
|
73
|
-
}
|
|
74
367
|
}
|
|
75
368
|
if (!chat) {
|
|
76
369
|
chat = await this.createChat(payload, excludeSocketId);
|
|
@@ -92,7 +385,7 @@ export class ChatProcessor {
|
|
|
92
385
|
if (!content) {
|
|
93
386
|
return this.getTemporaryTitle(payload.contextKey);
|
|
94
387
|
}
|
|
95
|
-
return
|
|
388
|
+
return this.aiHelper.generateTitleForMessage(payload.contextKey, [{
|
|
96
389
|
role: MessageRole.User,
|
|
97
390
|
content: content
|
|
98
391
|
}]);
|
|
@@ -103,34 +396,38 @@ export class ChatProcessor {
|
|
|
103
396
|
role,
|
|
104
397
|
content: messageData.content,
|
|
105
398
|
agentName: messageData.agentName,
|
|
399
|
+
sender: messageData.sender,
|
|
400
|
+
mentions: messageData.mentions ?? [],
|
|
401
|
+
annotations: messageData.annotations,
|
|
106
402
|
attachments: attachments ?? [],
|
|
107
403
|
reasoning: "",
|
|
108
404
|
toolCalls: []
|
|
109
405
|
});
|
|
110
|
-
|
|
111
|
-
socketService.emitMessageUpdate(chat.userId, message, excludeSocketId);
|
|
112
|
-
}
|
|
406
|
+
this.emitMessageToParticipants(chat, message, excludeSocketId);
|
|
113
407
|
return message;
|
|
114
408
|
}
|
|
115
409
|
async createChat(payload, excludeSocketId) {
|
|
116
|
-
const targetUserId = payload.userId ?? 'guest';
|
|
410
|
+
const targetUserId = payload.userId ?? payload.tenants?.userId ?? 'guest';
|
|
117
411
|
const contextKey = 'contextKey' in payload ? payload.contextKey : 'default';
|
|
118
412
|
const metadata = 'metadata' in payload ? payload.metadata : {};
|
|
413
|
+
const requestedParticipantIds = 'participantIds' in payload && Array.isArray(payload.participantIds) ? payload.participantIds : [];
|
|
414
|
+
const participantIds = Array.from(new Set([targetUserId, ...requestedParticipantIds].filter(Boolean)));
|
|
119
415
|
const chat = await this.chatRepository.create({
|
|
120
416
|
title: this.getTemporaryTitle(contextKey),
|
|
121
417
|
type: ChatType.Chat,
|
|
122
418
|
status: AgentStatus.Streaming,
|
|
419
|
+
sessionKind: AgentSessionKind.ROOT,
|
|
123
420
|
contextKey,
|
|
124
421
|
userId: targetUserId,
|
|
422
|
+
tenants: {
|
|
423
|
+
...(payload.tenants ?? {}),
|
|
424
|
+
userId: targetUserId,
|
|
425
|
+
},
|
|
426
|
+
participantIds,
|
|
125
427
|
metadata,
|
|
126
428
|
...(payload.model ? { model: payload.model } : {})
|
|
127
429
|
});
|
|
128
|
-
|
|
129
|
-
const initialChatResponse = {
|
|
130
|
-
...chat,
|
|
131
|
-
messages: []
|
|
132
|
-
};
|
|
133
|
-
socketService.emitChatUpdate(targetUserId, initialChatResponse, excludeSocketId);
|
|
430
|
+
this.emitChatToParticipants(chat, chat, excludeSocketId);
|
|
134
431
|
return chat;
|
|
135
432
|
}
|
|
136
433
|
/**
|
|
@@ -185,33 +482,163 @@ export class ChatProcessor {
|
|
|
185
482
|
await this.messageRepository.update(message.id, { toolCalls });
|
|
186
483
|
// Bump chat updatedAt so listChats ordering reflects the action.
|
|
187
484
|
await this.chatRepository.update(chat.id, { updatedAt: now });
|
|
188
|
-
|
|
485
|
+
this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
|
|
189
486
|
if (params.systemMessage) {
|
|
190
487
|
//todo discuss support for system messages
|
|
191
488
|
//await this.createMessage(chat, MessageRole.System, params.systemMessage, undefined, params.excludeSocketId);
|
|
192
489
|
}
|
|
193
490
|
return { ok: true };
|
|
194
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Update selected fields (input/output/status) on a specific tool call.
|
|
494
|
+
*/
|
|
495
|
+
async updateToolCall(params) {
|
|
496
|
+
if (params.input === undefined &&
|
|
497
|
+
params.output === undefined &&
|
|
498
|
+
params.status === undefined &&
|
|
499
|
+
params.subagentChatId === undefined) {
|
|
500
|
+
throw new Error('At least one tool call update field must be provided');
|
|
501
|
+
}
|
|
502
|
+
const chat = await this.chatRepository.findById(params.chatId);
|
|
503
|
+
if (!chat) {
|
|
504
|
+
throw new Error('Chat not found');
|
|
505
|
+
}
|
|
506
|
+
if (params.userId && !this.hasChatAccess(chat, params.userId)) {
|
|
507
|
+
throw new Error('Chat does not belong to this user');
|
|
508
|
+
}
|
|
509
|
+
const message = await this.messageRepository.findById(params.messageId);
|
|
510
|
+
if (!message || message.chat !== params.chatId) {
|
|
511
|
+
throw new Error('Message not found or does not belong to this chat');
|
|
512
|
+
}
|
|
513
|
+
const targetToolCall = (message.toolCalls ?? []).find((tc) => tc.id === params.toolCallId);
|
|
514
|
+
if (!targetToolCall) {
|
|
515
|
+
throw new Error('Tool call not found in message');
|
|
516
|
+
}
|
|
517
|
+
if (params.input !== undefined) {
|
|
518
|
+
await this.validateToolCallInput({
|
|
519
|
+
agentName: message.agentName,
|
|
520
|
+
toolName: targetToolCall.name,
|
|
521
|
+
input: params.input,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
const updatedMessage = await this.messageRepository.updateToolCall(params.messageId, params.toolCallId, {
|
|
525
|
+
...(params.input !== undefined ? { input: params.input } : {}),
|
|
526
|
+
...(params.output !== undefined ? { output: params.output } : {}),
|
|
527
|
+
...(params.status !== undefined ? { status: params.status } : {}),
|
|
528
|
+
...(params.subagentChatId !== undefined ? { subagentChatId: params.subagentChatId } : {}),
|
|
529
|
+
});
|
|
530
|
+
if (!updatedMessage) {
|
|
531
|
+
throw new Error('Tool call not found in message');
|
|
532
|
+
}
|
|
533
|
+
const now = new Date().toISOString();
|
|
534
|
+
await this.chatRepository.update(chat.id, { updatedAt: now });
|
|
535
|
+
this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
|
|
536
|
+
return updatedMessage;
|
|
537
|
+
}
|
|
538
|
+
async storeSubagentResponse(params) {
|
|
539
|
+
const { subagentChat, content, error, context, executionContext } = params;
|
|
540
|
+
if (!subagentChat.parentChatId || !subagentChat.parentMessageId) {
|
|
541
|
+
throw new Error(`Parent chat or message not found for subagent ${subagentChat.id}`);
|
|
542
|
+
}
|
|
543
|
+
const chat = await this.chatRepository.findById(subagentChat.parentChatId);
|
|
544
|
+
if (!chat) {
|
|
545
|
+
throw new Error(`Parent chat not found for subagent ${subagentChat.id}`);
|
|
546
|
+
}
|
|
547
|
+
const assistantMessage = await this.messageRepository.findById(subagentChat.parentMessageId);
|
|
548
|
+
if (!assistantMessage || assistantMessage.chat !== subagentChat.parentChatId) {
|
|
549
|
+
throw new Error(`Assistant message with id ${subagentChat.parentMessageId} not found`);
|
|
550
|
+
}
|
|
551
|
+
const toolCall = assistantMessage.toolCalls?.find(tc => tc.id === subagentChat.parentToolCallId);
|
|
552
|
+
if (!toolCall) {
|
|
553
|
+
throw new Error(`Tool call with id ${subagentChat.parentToolCallId} not found`);
|
|
554
|
+
}
|
|
555
|
+
if (error) {
|
|
556
|
+
toolCall.output = {
|
|
557
|
+
type: 'error',
|
|
558
|
+
message: error,
|
|
559
|
+
};
|
|
560
|
+
toolCall.status = AgentToolCallStatus.Failed;
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
toolCall.output = content;
|
|
564
|
+
toolCall.status = AgentToolCallStatus.Succeeded;
|
|
565
|
+
}
|
|
566
|
+
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
567
|
+
const agentOptions = await this.aiHelper.getAgentOptions({
|
|
568
|
+
agentName: assistantMessage.agentName,
|
|
569
|
+
context: context,
|
|
570
|
+
executionContext: executionContext,
|
|
571
|
+
});
|
|
572
|
+
const executionTenants = this.getExecutionTenants(params);
|
|
573
|
+
const abortController = this.agentStore.registerAgentProcess(chat.id);
|
|
574
|
+
const existingMessages = await this.messageRepository.findByChatId(chat.id);
|
|
575
|
+
// Limit context to prevent exceeding token limits
|
|
576
|
+
const limitedMessages = ContextLimiter.limitContext(existingMessages, {
|
|
577
|
+
maxMessages: this.config.ai.maxContextMessages,
|
|
578
|
+
keepFirstUserMessage: true,
|
|
579
|
+
keepSystemMessages: true,
|
|
580
|
+
});
|
|
581
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
582
|
+
return this.storeStepActivity({
|
|
583
|
+
chat,
|
|
584
|
+
parentId: assistantMessage.activity,
|
|
585
|
+
stepResult,
|
|
586
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
587
|
+
sourceId: assistantMessage.id,
|
|
588
|
+
sourceType: 'AgentMessage',
|
|
589
|
+
tenants: executionTenants,
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? chat.userId ?? 'guest', agentOptions.name);
|
|
593
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
594
|
+
await this.streamMessageStep({
|
|
595
|
+
chat,
|
|
596
|
+
existingMessages: limitedMessages,
|
|
597
|
+
assistantMessage,
|
|
598
|
+
agentOptions,
|
|
599
|
+
signal: abortController.signal,
|
|
600
|
+
tenants: executionTenants,
|
|
601
|
+
executionContext,
|
|
602
|
+
context,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
195
605
|
async streamMessageStep(params) {
|
|
196
606
|
const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
|
|
607
|
+
const persistAndBroadcastToolCalls = async () => {
|
|
608
|
+
await this.messageRepository.update(assistantMessage.id, { toolCalls: assistantMessage.toolCalls ?? [] });
|
|
609
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
610
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
|
|
611
|
+
};
|
|
197
612
|
if (signal.aborted) {
|
|
198
613
|
return;
|
|
199
614
|
}
|
|
200
|
-
const result = await
|
|
615
|
+
const result = await this.aiHelper.streamAssistantResponse(existingMessages, signal, {
|
|
616
|
+
...agentOptions,
|
|
617
|
+
executionContext: params.executionContext,
|
|
618
|
+
});
|
|
201
619
|
try {
|
|
202
620
|
for await (const chunk of result.fullStream) {
|
|
203
621
|
if (chunk.type === 'error') {
|
|
204
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
|
|
622
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
|
|
205
623
|
assistantMessage.content = chunk.error.message || 'Sorry, I cannot process you request due to the error';
|
|
206
624
|
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
207
|
-
await this.
|
|
625
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.Error }, excludeSocketId);
|
|
626
|
+
if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
|
|
627
|
+
await this.storeSubagentResponse({
|
|
628
|
+
subagentChat: chat,
|
|
629
|
+
content: undefined,
|
|
630
|
+
error: chunk.error,
|
|
631
|
+
context: params.context,
|
|
632
|
+
executionContext: params.executionContext,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
208
635
|
continue;
|
|
209
636
|
}
|
|
210
637
|
if (chunk.type === 'abort') {
|
|
211
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
|
|
212
|
-
await this.
|
|
638
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
|
|
639
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.Aborted }, excludeSocketId);
|
|
213
640
|
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
214
|
-
|
|
641
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
215
642
|
continue;
|
|
216
643
|
}
|
|
217
644
|
if (chunk.type === 'finish') {
|
|
@@ -228,38 +655,76 @@ export class ChatProcessor {
|
|
|
228
655
|
((totalUsage.promptTokens ?? 0) + (totalUsage.completionTokens ?? 0));
|
|
229
656
|
}
|
|
230
657
|
await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
|
|
658
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
231
659
|
if (chunk.finishReason === 'stop') {
|
|
232
|
-
|
|
233
|
-
|
|
660
|
+
let nextChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Finished }, excludeSocketId);
|
|
661
|
+
if (chat.title && this.isTemporaryTitle(chat.title)) {
|
|
662
|
+
const title = await this.aiHelper.generateTitleForMessage(chat.contextKey, existingMessages.filter(m => m.role === MessageRole.User), (stepResult) => {
|
|
663
|
+
return this.storeStepActivity({
|
|
664
|
+
chat,
|
|
665
|
+
stepResult,
|
|
666
|
+
name: ActivityOperationName.TITLE_GENERATION,
|
|
667
|
+
sourceId: chat.id,
|
|
668
|
+
sourceType: 'Chat',
|
|
669
|
+
tenants: params.tenants || {},
|
|
670
|
+
});
|
|
671
|
+
}, params.executionContext);
|
|
672
|
+
nextChat = await this.updateChatAndEmit(nextChat, { title }, excludeSocketId);
|
|
234
673
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
674
|
+
if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
|
|
675
|
+
await this.storeSubagentResponse({
|
|
676
|
+
subagentChat: chat,
|
|
677
|
+
content: assistantMessage.content,
|
|
678
|
+
error: undefined,
|
|
679
|
+
context: params.context,
|
|
680
|
+
executionContext: params.executionContext,
|
|
681
|
+
});
|
|
243
682
|
}
|
|
244
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
245
|
-
continue;
|
|
246
683
|
}
|
|
247
684
|
if (chunk.finishReason === 'tool-calls') {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
685
|
+
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.output === undefined && toolCall.status === AgentToolCallStatus.Running);
|
|
686
|
+
if (!toolCall) {
|
|
687
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
688
|
+
await this.storeSubagentResponse({
|
|
689
|
+
subagentChat: chat,
|
|
690
|
+
content: JSON.stringify(assistantMessage.toolCalls),
|
|
691
|
+
error: undefined,
|
|
692
|
+
context: params.context,
|
|
693
|
+
executionContext: params.executionContext,
|
|
694
|
+
});
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
|
|
698
|
+
if (toolCall.requiresConfirmation && toolCall.approvalId) {
|
|
699
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.WaitingForUserAction }, excludeSocketId);
|
|
700
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
251
701
|
}
|
|
252
|
-
else {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
702
|
+
else if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
|
|
703
|
+
void this.startSubagentProcess({
|
|
704
|
+
parentChat: chat,
|
|
705
|
+
parentMessage: assistantMessage,
|
|
706
|
+
parentToolCallId: toolCall.id,
|
|
707
|
+
toolName: toolCall.name,
|
|
708
|
+
input: toolCall.input,
|
|
709
|
+
subAgentConfig: toolConfig.data.subAgent,
|
|
710
|
+
context: params.context,
|
|
711
|
+
executionContext: params.executionContext,
|
|
712
|
+
}).catch(error => {
|
|
713
|
+
logger.error(error);
|
|
714
|
+
//todo: store error in response
|
|
256
715
|
});
|
|
257
|
-
await this.streamMessageStep(params);
|
|
258
716
|
}
|
|
259
|
-
continue;
|
|
260
717
|
}
|
|
261
718
|
//todo: Handle other finish reasons // length, content, error, other, undefined
|
|
262
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
719
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
720
|
+
if (assistantMessage.activity) {
|
|
721
|
+
const groupedMeta = await this.activityRepository.getGroupedMetadataByParentId(assistantMessage.activity);
|
|
722
|
+
await this.activityRepository.updateMetadata(assistantMessage.activity, {
|
|
723
|
+
usage: groupedMeta,
|
|
724
|
+
finishReason: chunk.finishReason,
|
|
725
|
+
responseTimestamp: new Date().toISOString(),
|
|
726
|
+
});
|
|
727
|
+
}
|
|
263
728
|
continue;
|
|
264
729
|
}
|
|
265
730
|
if (chunk.type === 'text-delta') {
|
|
@@ -268,55 +733,85 @@ export class ChatProcessor {
|
|
|
268
733
|
...assistantMessage,
|
|
269
734
|
content: assistantMessage.content
|
|
270
735
|
};
|
|
271
|
-
|
|
272
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
|
|
736
|
+
this.emitMessageToParticipants(chat, updatedMessage, excludeSocketId);
|
|
737
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: updatedMessage });
|
|
273
738
|
await this.messageRepository.update(assistantMessage.id, { content: assistantMessage.content });
|
|
274
739
|
continue;
|
|
275
740
|
}
|
|
276
741
|
if (chunk.type === 'tool-input-start') {
|
|
277
742
|
if (!assistantMessage.toolCalls)
|
|
278
743
|
assistantMessage.toolCalls = [];
|
|
279
|
-
assistantMessage.toolCalls.
|
|
280
|
-
|
|
281
|
-
name
|
|
282
|
-
status
|
|
283
|
-
|
|
284
|
-
|
|
744
|
+
const existingToolCall = assistantMessage.toolCalls.find((toolCall) => toolCall.id === chunk.id);
|
|
745
|
+
if (existingToolCall) {
|
|
746
|
+
existingToolCall.name = chunk.toolName;
|
|
747
|
+
if (!existingToolCall.status) {
|
|
748
|
+
existingToolCall.status = AgentToolCallStatus.Pending;
|
|
749
|
+
}
|
|
750
|
+
existingToolCall.input = existingToolCall.input ?? {};
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
assistantMessage.toolCalls.push({
|
|
754
|
+
id: chunk.id,
|
|
755
|
+
name: chunk.toolName,
|
|
756
|
+
status: AgentToolCallStatus.Pending,
|
|
757
|
+
input: {},
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
await persistAndBroadcastToolCalls();
|
|
285
761
|
continue;
|
|
286
762
|
}
|
|
287
|
-
if (chunk.type === 'tool-call') {
|
|
288
|
-
|
|
289
|
-
|
|
763
|
+
if (chunk.type === 'tool-call') {
|
|
764
|
+
if (!assistantMessage.toolCalls)
|
|
765
|
+
assistantMessage.toolCalls = [];
|
|
766
|
+
let toolCall = assistantMessage.toolCalls.find(toolCall => toolCall.id === chunk.toolCallId);
|
|
767
|
+
if (!toolCall) {
|
|
768
|
+
toolCall = {
|
|
769
|
+
id: chunk.toolCallId,
|
|
770
|
+
name: chunk.toolName,
|
|
771
|
+
status: AgentToolCallStatus.Running,
|
|
772
|
+
input: chunk.input,
|
|
773
|
+
};
|
|
774
|
+
assistantMessage.toolCalls.push(toolCall);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
290
777
|
toolCall.status = AgentToolCallStatus.Running;
|
|
291
778
|
toolCall.input = chunk.input;
|
|
292
779
|
}
|
|
780
|
+
await persistAndBroadcastToolCalls();
|
|
293
781
|
continue;
|
|
294
782
|
}
|
|
295
783
|
if (chunk.type === 'tool-error') {
|
|
296
784
|
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
|
|
297
785
|
if (toolCall) {
|
|
298
786
|
toolCall.status = AgentToolCallStatus.Failed;
|
|
299
|
-
toolCall.error = JSON.stringify(chunk.error);
|
|
787
|
+
toolCall.error = chunk.error.message || JSON.stringify(chunk.error);
|
|
788
|
+
await persistAndBroadcastToolCalls();
|
|
300
789
|
}
|
|
301
790
|
continue;
|
|
302
791
|
}
|
|
303
792
|
if (chunk.type === 'tool-approval-request') {
|
|
304
793
|
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCall.toolCallId);
|
|
305
794
|
if (toolCall) {
|
|
795
|
+
const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
|
|
796
|
+
if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
|
|
797
|
+
// TODO: allow needsApproval flow for the subagents too
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
306
800
|
toolCall.requiresConfirmation = true;
|
|
307
801
|
toolCall.approvalId = chunk.approvalId;
|
|
802
|
+
if (toolCall.name === AgentToolType.REQUEST_CLARIFICATION) {
|
|
803
|
+
toolCall.requiresUserAction = true;
|
|
804
|
+
}
|
|
805
|
+
await persistAndBroadcastToolCalls();
|
|
308
806
|
}
|
|
309
807
|
continue;
|
|
310
808
|
}
|
|
311
809
|
if (chunk.type === 'tool-result') {
|
|
312
810
|
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
|
|
313
811
|
if (toolCall) {
|
|
314
|
-
if (chunk.output.requiresApproval) {
|
|
315
|
-
toolCall.requiresConfirmation = true;
|
|
316
|
-
continue;
|
|
317
|
-
}
|
|
318
812
|
toolCall.status = AgentToolCallStatus.Succeeded;
|
|
319
813
|
toolCall.output = chunk.output;
|
|
814
|
+
await persistAndBroadcastToolCalls();
|
|
320
815
|
}
|
|
321
816
|
continue;
|
|
322
817
|
}
|
|
@@ -324,9 +819,9 @@ export class ChatProcessor {
|
|
|
324
819
|
if (!assistantMessage.reasoning)
|
|
325
820
|
assistantMessage.reasoning = '';
|
|
326
821
|
assistantMessage.reasoning += chunk.text;
|
|
327
|
-
|
|
822
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
328
823
|
await this.messageRepository.update(assistantMessage.id, { reasoning: assistantMessage.reasoning });
|
|
329
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
|
|
824
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
|
|
330
825
|
continue;
|
|
331
826
|
}
|
|
332
827
|
}
|
|
@@ -335,7 +830,7 @@ export class ChatProcessor {
|
|
|
335
830
|
if (streamError instanceof Error && streamError.name === 'AbortError') {
|
|
336
831
|
return;
|
|
337
832
|
}
|
|
338
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
|
|
833
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: streamError });
|
|
339
834
|
throw streamError;
|
|
340
835
|
}
|
|
341
836
|
}
|
|
@@ -347,9 +842,10 @@ export class ChatProcessor {
|
|
|
347
842
|
if (!assistantMessage || assistantMessage.chat !== chat.id) {
|
|
348
843
|
throw new Error(`Assistant message with id ${payload.messageId} not found`);
|
|
349
844
|
}
|
|
350
|
-
const agentOptions = await
|
|
845
|
+
const agentOptions = await this.aiHelper.getAgentOptions({
|
|
351
846
|
agentName: assistantMessage.agentName,
|
|
352
|
-
context: payload.context
|
|
847
|
+
context: payload.context,
|
|
848
|
+
executionContext: payload.executionContext,
|
|
353
849
|
});
|
|
354
850
|
const toolCall = assistantMessage.toolCalls?.find(tc => tc.approvalId === payload.approvalId);
|
|
355
851
|
if (!toolCall) {
|
|
@@ -360,14 +856,20 @@ export class ChatProcessor {
|
|
|
360
856
|
};
|
|
361
857
|
}
|
|
362
858
|
toolCall.approved = payload.approved;
|
|
363
|
-
toolCall.
|
|
859
|
+
toolCall.userResponse = payload.userResponse;
|
|
364
860
|
if (!toolCall.approved) {
|
|
365
861
|
toolCall.output = {
|
|
366
862
|
type: 'execution-denied',
|
|
367
|
-
reason: toolCall.
|
|
863
|
+
reason: toolCall.userResponse
|
|
368
864
|
};
|
|
369
865
|
toolCall.status = AgentToolCallStatus.Failed;
|
|
370
866
|
}
|
|
867
|
+
else if (toolCall.requiresUserAction) {
|
|
868
|
+
toolCall.output = {
|
|
869
|
+
userResponse: toolCall.userResponse
|
|
870
|
+
};
|
|
871
|
+
toolCall.status = AgentToolCallStatus.Succeeded;
|
|
872
|
+
}
|
|
371
873
|
else {
|
|
372
874
|
const executeFunction = agentOptions.tools?.[toolCall.name]?.execute;
|
|
373
875
|
if (executeFunction) {
|
|
@@ -399,33 +901,333 @@ export class ChatProcessor {
|
|
|
399
901
|
};
|
|
400
902
|
}
|
|
401
903
|
async processNewUserMessage(chat, payload, prevMessages, excludeSocketId) {
|
|
904
|
+
const executionTenants = this.getExecutionTenants(payload);
|
|
905
|
+
const chatUserId = this.getChatUserId(chat) ?? 'guest';
|
|
402
906
|
const userMessage = await this.createMessage(chat, MessageRole.User, { content: payload.content, agentName: undefined }, payload.attachments);
|
|
403
|
-
await agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: userMessage });
|
|
404
|
-
|
|
907
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: userMessage });
|
|
908
|
+
let assistantMessage = await this.createMessage(chat, MessageRole.Assistant, { content: '', agentName: undefined }, undefined, excludeSocketId);
|
|
909
|
+
const parentActivity = await this.activityRepository.create({
|
|
910
|
+
ownerId: chatUserId,
|
|
911
|
+
groupId: chat.id,
|
|
912
|
+
name: ActivityOperationName.MESSAGE_PROCESSING,
|
|
913
|
+
tenants: executionTenants,
|
|
914
|
+
sourceId: userMessage.id,
|
|
915
|
+
sourceType: 'AgentMessage',
|
|
916
|
+
metadata: {
|
|
917
|
+
contextKey: chat.contextKey,
|
|
918
|
+
modelId: this.aiHelper.getLanguageModelId(chat.model),
|
|
919
|
+
},
|
|
920
|
+
});
|
|
921
|
+
const agentOptions = await this.aiHelper.getAgentOptions({
|
|
405
922
|
contextKey: chat.contextKey,
|
|
406
|
-
messages:
|
|
923
|
+
messages: [...prevMessages, userMessage],
|
|
407
924
|
context: payload.context,
|
|
408
925
|
agentName: undefined,
|
|
409
926
|
modelId: chat.model,
|
|
927
|
+
executionContext: payload.executionContext,
|
|
928
|
+
}, {
|
|
929
|
+
onStepFinish: async (stepResult) => {
|
|
930
|
+
await this.storeStepActivity({
|
|
931
|
+
chat,
|
|
932
|
+
parentId: parentActivity.id,
|
|
933
|
+
stepResult,
|
|
934
|
+
name: ActivityOperationName.AGENT_SELECTION,
|
|
935
|
+
sourceId: userMessage.id,
|
|
936
|
+
sourceType: 'AgentMessage',
|
|
937
|
+
tenants: executionTenants,
|
|
938
|
+
});
|
|
939
|
+
}
|
|
410
940
|
});
|
|
411
|
-
const
|
|
412
|
-
|
|
941
|
+
const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
|
|
942
|
+
agentName: agentOptions.name,
|
|
943
|
+
activity: parentActivity.id
|
|
944
|
+
});
|
|
945
|
+
await this.activityRepository.update(parentActivity.id, {
|
|
946
|
+
metadata: {
|
|
947
|
+
...parentActivity.metadata,
|
|
948
|
+
agentOptions: {
|
|
949
|
+
name: agentOptions.name,
|
|
950
|
+
modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
|
|
951
|
+
temperature: agentOptions.temperature,
|
|
952
|
+
maxOutputTokens: agentOptions.maxOutputTokens,
|
|
953
|
+
topP: agentOptions.topP,
|
|
954
|
+
topK: agentOptions.topK,
|
|
955
|
+
presencePenalty: agentOptions.presencePenalty,
|
|
956
|
+
frequencyPenalty: agentOptions.frequencyPenalty,
|
|
957
|
+
stopSequences: agentOptions.stopSequences,
|
|
958
|
+
seed: agentOptions.seed,
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
});
|
|
962
|
+
if (!updatedAssistantMessage) {
|
|
963
|
+
throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
|
|
964
|
+
}
|
|
965
|
+
assistantMessage = updatedAssistantMessage;
|
|
413
966
|
return {
|
|
414
967
|
userMessage,
|
|
415
968
|
assistantMessage,
|
|
416
969
|
agentOptions,
|
|
417
970
|
};
|
|
418
971
|
}
|
|
972
|
+
getAgentNamesForContext(contextKey) {
|
|
973
|
+
try {
|
|
974
|
+
return ConfigStore.getInstance().getAgentsForContext(contextKey).map((config) => config.name);
|
|
975
|
+
}
|
|
976
|
+
catch {
|
|
977
|
+
return [];
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
normalizeMentionName(value) {
|
|
981
|
+
return value.trim().replace(/^@/, '').toLowerCase();
|
|
982
|
+
}
|
|
983
|
+
extractMentionNames(content) {
|
|
984
|
+
const mentions = content.matchAll(/(^|[\s([{])@([a-zA-Z0-9_.-]+)/g);
|
|
985
|
+
return Array.from(mentions, (match) => match[2]).filter(Boolean);
|
|
986
|
+
}
|
|
987
|
+
getExplicitAgentMention(params) {
|
|
988
|
+
const agentNames = this.getAgentNamesForContext(params.contextKey);
|
|
989
|
+
const normalizedAgentNames = new Map(agentNames.map((name) => [this.normalizeMentionName(name), name]));
|
|
990
|
+
for (const mention of params.mentions ?? []) {
|
|
991
|
+
if (mention.type !== 'agent')
|
|
992
|
+
continue;
|
|
993
|
+
const normalized = this.normalizeMentionName(mention.name);
|
|
994
|
+
return { mentioned: true, agentName: normalizedAgentNames.get(normalized) };
|
|
995
|
+
}
|
|
996
|
+
for (const name of this.extractMentionNames(params.content)) {
|
|
997
|
+
const normalized = this.normalizeMentionName(name);
|
|
998
|
+
if (normalized === 'agent') {
|
|
999
|
+
return { mentioned: true };
|
|
1000
|
+
}
|
|
1001
|
+
const agentName = normalizedAgentNames.get(normalized);
|
|
1002
|
+
if (agentName) {
|
|
1003
|
+
return { mentioned: true, agentName };
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return { mentioned: false };
|
|
1007
|
+
}
|
|
1008
|
+
hasNonAgentMention(params) {
|
|
1009
|
+
const agentNames = this.getAgentNamesForContext(params.contextKey);
|
|
1010
|
+
const normalizedAgentNames = new Set(['agent', ...agentNames.map((name) => this.normalizeMentionName(name))]);
|
|
1011
|
+
if ((params.mentions ?? []).some((mention) => mention.type === 'user')) {
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
return this.extractMentionNames(params.content).some((name) => {
|
|
1015
|
+
const normalized = this.normalizeMentionName(name);
|
|
1016
|
+
return !normalizedAgentNames.has(normalized);
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
renderMessageForDecision(message) {
|
|
1020
|
+
const sender = message.sender?.displayName || message.sender?.id || message.agentName || message.role;
|
|
1021
|
+
return `${sender}: ${message.content}`;
|
|
1022
|
+
}
|
|
1023
|
+
async analyzeAgentShouldRespond(params) {
|
|
1024
|
+
try {
|
|
1025
|
+
const recentMessages = [...params.previousMessages.slice(-8), params.userMessage]
|
|
1026
|
+
.map((message) => this.renderMessageForDecision(message))
|
|
1027
|
+
.join('\n');
|
|
1028
|
+
const response = await this.aiHelper.getAssistantResponse([
|
|
1029
|
+
{
|
|
1030
|
+
role: MessageRole.User,
|
|
1031
|
+
content: `Conversation:\n${recentMessages}\n\nShould the AI agent respond to the latest message?`
|
|
1032
|
+
}
|
|
1033
|
+
], {
|
|
1034
|
+
system: [
|
|
1035
|
+
'You decide whether an AI agent should reply in a multi-user chat.',
|
|
1036
|
+
'Reply with exactly YES or NO.',
|
|
1037
|
+
'YES only when the latest message asks for help, requests agent action, or is clearly addressed to the AI.',
|
|
1038
|
+
'NO when the latest message is human-to-human chatter, status, acknowledgement, or otherwise does not need the AI.'
|
|
1039
|
+
].join('\n'),
|
|
1040
|
+
temperature: 0,
|
|
1041
|
+
maxOutputTokens: 5,
|
|
1042
|
+
model: params.chat.model,
|
|
1043
|
+
executionContext: params.executionContext
|
|
1044
|
+
});
|
|
1045
|
+
return /^yes\b/i.test(response.trim());
|
|
1046
|
+
}
|
|
1047
|
+
catch (error) {
|
|
1048
|
+
logger.warn({ error, chatId: params.chat.id }, 'Agent response decision failed');
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
async shouldRunAgentForMultiUserMessage(params) {
|
|
1053
|
+
const firstUserMessage = !params.previousMessages.some((message) => message.role === MessageRole.User);
|
|
1054
|
+
if (firstUserMessage) {
|
|
1055
|
+
return { shouldRun: true };
|
|
1056
|
+
}
|
|
1057
|
+
const explicitAgent = this.getExplicitAgentMention({
|
|
1058
|
+
content: params.payload.content,
|
|
1059
|
+
mentions: params.payload.mentions,
|
|
1060
|
+
contextKey: params.chat.contextKey,
|
|
1061
|
+
});
|
|
1062
|
+
if (explicitAgent.mentioned) {
|
|
1063
|
+
return { shouldRun: true, agentName: explicitAgent.agentName };
|
|
1064
|
+
}
|
|
1065
|
+
if (this.hasNonAgentMention({
|
|
1066
|
+
content: params.payload.content,
|
|
1067
|
+
mentions: params.payload.mentions,
|
|
1068
|
+
contextKey: params.chat.contextKey,
|
|
1069
|
+
})) {
|
|
1070
|
+
return { shouldRun: false };
|
|
1071
|
+
}
|
|
1072
|
+
const shouldRun = await this.analyzeAgentShouldRespond({
|
|
1073
|
+
chat: params.chat,
|
|
1074
|
+
previousMessages: params.previousMessages,
|
|
1075
|
+
userMessage: params.userMessage,
|
|
1076
|
+
executionContext: params.payload.executionContext,
|
|
1077
|
+
});
|
|
1078
|
+
return { shouldRun };
|
|
1079
|
+
}
|
|
1080
|
+
async prepareAgentResponseForUserMessage(chat, payload, prevMessages, userMessage, excludeSocketId, agentName) {
|
|
1081
|
+
const executionTenants = this.getExecutionTenants(payload);
|
|
1082
|
+
let assistantMessage = await this.createMessage(chat, MessageRole.Assistant, {
|
|
1083
|
+
content: '',
|
|
1084
|
+
agentName: agentName,
|
|
1085
|
+
sender: {
|
|
1086
|
+
id: agentName ?? 'agent',
|
|
1087
|
+
type: 'agent',
|
|
1088
|
+
displayName: agentName ?? 'Agent',
|
|
1089
|
+
}
|
|
1090
|
+
}, undefined, excludeSocketId);
|
|
1091
|
+
const ownerId = payload.userId ?? chat.userId ?? this.getChatUserId(chat) ?? 'guest';
|
|
1092
|
+
const parentActivity = await this.activityRepository.create({
|
|
1093
|
+
ownerId,
|
|
1094
|
+
groupId: chat.id,
|
|
1095
|
+
name: ActivityOperationName.MESSAGE_PROCESSING,
|
|
1096
|
+
tenants: executionTenants,
|
|
1097
|
+
sourceId: userMessage.id,
|
|
1098
|
+
sourceType: 'AgentMessage',
|
|
1099
|
+
metadata: {
|
|
1100
|
+
contextKey: chat.contextKey,
|
|
1101
|
+
modelId: this.aiHelper.getLanguageModelId(chat.model),
|
|
1102
|
+
},
|
|
1103
|
+
});
|
|
1104
|
+
const agentOptions = await this.aiHelper.getAgentOptions(agentName
|
|
1105
|
+
? {
|
|
1106
|
+
agentName,
|
|
1107
|
+
modelId: chat.model,
|
|
1108
|
+
context: payload.context,
|
|
1109
|
+
executionContext: payload.executionContext,
|
|
1110
|
+
}
|
|
1111
|
+
: {
|
|
1112
|
+
contextKey: chat.contextKey,
|
|
1113
|
+
messages: [...prevMessages, userMessage],
|
|
1114
|
+
context: payload.context,
|
|
1115
|
+
agentName: undefined,
|
|
1116
|
+
modelId: chat.model,
|
|
1117
|
+
executionContext: payload.executionContext,
|
|
1118
|
+
}, {
|
|
1119
|
+
onStepFinish: async (stepResult) => {
|
|
1120
|
+
await this.storeStepActivity({
|
|
1121
|
+
chat,
|
|
1122
|
+
parentId: parentActivity.id,
|
|
1123
|
+
stepResult,
|
|
1124
|
+
name: ActivityOperationName.AGENT_SELECTION,
|
|
1125
|
+
sourceId: userMessage.id,
|
|
1126
|
+
sourceType: 'AgentMessage',
|
|
1127
|
+
tenants: executionTenants,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
|
|
1132
|
+
agentName: agentOptions.name,
|
|
1133
|
+
activity: parentActivity.id,
|
|
1134
|
+
sender: {
|
|
1135
|
+
id: agentOptions.name,
|
|
1136
|
+
type: 'agent',
|
|
1137
|
+
displayName: agentOptions.name,
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
await this.activityRepository.update(parentActivity.id, {
|
|
1141
|
+
metadata: {
|
|
1142
|
+
...parentActivity.metadata,
|
|
1143
|
+
agentOptions: {
|
|
1144
|
+
name: agentOptions.name,
|
|
1145
|
+
modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
|
|
1146
|
+
temperature: agentOptions.temperature,
|
|
1147
|
+
maxOutputTokens: agentOptions.maxOutputTokens,
|
|
1148
|
+
topP: agentOptions.topP,
|
|
1149
|
+
topK: agentOptions.topK,
|
|
1150
|
+
presencePenalty: agentOptions.presencePenalty,
|
|
1151
|
+
frequencyPenalty: agentOptions.frequencyPenalty,
|
|
1152
|
+
stopSequences: agentOptions.stopSequences,
|
|
1153
|
+
seed: agentOptions.seed,
|
|
1154
|
+
},
|
|
1155
|
+
},
|
|
1156
|
+
});
|
|
1157
|
+
if (!updatedAssistantMessage) {
|
|
1158
|
+
throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
|
|
1159
|
+
}
|
|
1160
|
+
assistantMessage = updatedAssistantMessage;
|
|
1161
|
+
return {
|
|
1162
|
+
assistantMessage,
|
|
1163
|
+
agentOptions,
|
|
1164
|
+
executionTenants,
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
async streamAgentResponseForUserMessage(params) {
|
|
1168
|
+
const { assistantMessage, agentOptions, executionTenants } = await this.prepareAgentResponseForUserMessage(params.chat, params.payload, params.previousMessages, params.userMessage, params.excludeSocketId, params.agentName);
|
|
1169
|
+
const messagesForAgent = [...params.previousMessages, params.userMessage];
|
|
1170
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
1171
|
+
return this.storeStepActivity({
|
|
1172
|
+
chat: params.chat,
|
|
1173
|
+
parentId: assistantMessage.activity,
|
|
1174
|
+
stepResult,
|
|
1175
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
1176
|
+
sourceId: assistantMessage.id,
|
|
1177
|
+
sourceType: 'AgentMessage',
|
|
1178
|
+
tenants: executionTenants,
|
|
1179
|
+
});
|
|
1180
|
+
};
|
|
1181
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(params.payload.userId ?? params.chat.userId ?? this.getChatUserId(params.chat) ?? 'guest', agentOptions.name);
|
|
1182
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
1183
|
+
await this.streamMessageStep({
|
|
1184
|
+
chat: params.chat,
|
|
1185
|
+
existingMessages: messagesForAgent,
|
|
1186
|
+
assistantMessage,
|
|
1187
|
+
agentOptions,
|
|
1188
|
+
excludeSocketId: params.excludeSocketId,
|
|
1189
|
+
signal: params.signal,
|
|
1190
|
+
tenants: executionTenants,
|
|
1191
|
+
executionContext: params.payload.executionContext,
|
|
1192
|
+
context: params.payload.context,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
async storeStepActivity(params) {
|
|
1196
|
+
try {
|
|
1197
|
+
const stepResult = params.stepResult;
|
|
1198
|
+
const mergedUsage = this.mergeUsageWithProviderMetadata(stepResult.usage, stepResult.providerMetadata);
|
|
1199
|
+
const activity = await this.activityRepository.create({
|
|
1200
|
+
ownerId: this.getChatUserId(params.chat) ?? 'guest',
|
|
1201
|
+
groupId: params.chat.id,
|
|
1202
|
+
name: params.name,
|
|
1203
|
+
tenants: params.tenants || {},
|
|
1204
|
+
sourceId: params.sourceId,
|
|
1205
|
+
sourceType: params.sourceType,
|
|
1206
|
+
metadata: {
|
|
1207
|
+
finishReason: stepResult.finishReason,
|
|
1208
|
+
usage: mergedUsage,
|
|
1209
|
+
responseTimestamp: stepResult.response.timestamp,
|
|
1210
|
+
modelId: stepResult.response.modelId,
|
|
1211
|
+
messages: this.debug ? stepResult.request?.body?.messages : undefined,
|
|
1212
|
+
},
|
|
1213
|
+
parentId: params.parentId,
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
catch (error) {
|
|
1217
|
+
logger.error({ error, chatId: params.chat.id, parentId: params.parentId }, 'Failed to store step activity');
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
419
1220
|
async streamMessage(chat, payload, excludeSocketId, onAgentStart) {
|
|
420
1221
|
try {
|
|
421
|
-
const
|
|
1222
|
+
const executionTenants = this.getExecutionTenants(payload);
|
|
1223
|
+
const abortController = this.agentStore.registerAgentProcess(chat.id);
|
|
422
1224
|
onAgentStart?.();
|
|
423
1225
|
let assistantMessage;
|
|
424
1226
|
let agentOptions;
|
|
425
1227
|
const existingMessages = await this.messageRepository.findByChatId(chat.id);
|
|
426
1228
|
// Limit context to prevent exceeding token limits
|
|
427
1229
|
const limitedMessages = ContextLimiter.limitContext(existingMessages, {
|
|
428
|
-
maxMessages: config.ai.maxContextMessages,
|
|
1230
|
+
maxMessages: this.config.ai.maxContextMessages,
|
|
429
1231
|
keepFirstUserMessage: true,
|
|
430
1232
|
keepSystemMessages: true,
|
|
431
1233
|
});
|
|
@@ -447,6 +1249,19 @@ export class ChatProcessor {
|
|
|
447
1249
|
agentOptions = result.agentOptions;
|
|
448
1250
|
limitedMessages.push(result.userMessage);
|
|
449
1251
|
}
|
|
1252
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
1253
|
+
return this.storeStepActivity({
|
|
1254
|
+
chat,
|
|
1255
|
+
parentId: assistantMessage.activity,
|
|
1256
|
+
stepResult,
|
|
1257
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
1258
|
+
sourceId: assistantMessage.id,
|
|
1259
|
+
sourceType: 'AgentMessage',
|
|
1260
|
+
tenants: executionTenants,
|
|
1261
|
+
});
|
|
1262
|
+
};
|
|
1263
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? 'guest', agentOptions.name);
|
|
1264
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
450
1265
|
await this.streamMessageStep({
|
|
451
1266
|
chat,
|
|
452
1267
|
existingMessages: limitedMessages,
|
|
@@ -454,12 +1269,30 @@ export class ChatProcessor {
|
|
|
454
1269
|
agentOptions,
|
|
455
1270
|
excludeSocketId: excludeSocketId,
|
|
456
1271
|
signal: abortController.signal,
|
|
1272
|
+
tenants: executionTenants,
|
|
1273
|
+
executionContext: payload.executionContext,
|
|
1274
|
+
context: payload.context,
|
|
457
1275
|
});
|
|
458
1276
|
}
|
|
459
1277
|
catch (error) {
|
|
460
1278
|
throw error;
|
|
461
1279
|
}
|
|
462
1280
|
}
|
|
1281
|
+
getAvailableTools(agentOptions, userPreferences) {
|
|
1282
|
+
const toolNames = Object.keys(agentOptions.tools || {});
|
|
1283
|
+
const availableToolsMap = Object.fromEntries(toolNames.map(key => [key, true]));
|
|
1284
|
+
const agentsConfiguration = ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
|
|
1285
|
+
agentsConfiguration.tools.forEach((tool) => {
|
|
1286
|
+
const toolTitle = getAgentToolName(tool);
|
|
1287
|
+
if (tool.disabledByDefault) {
|
|
1288
|
+
availableToolsMap[toolTitle] = false;
|
|
1289
|
+
}
|
|
1290
|
+
if (tool.userConfigurable && userPreferences?.tools?.[toolTitle] !== undefined) {
|
|
1291
|
+
availableToolsMap[toolTitle] = userPreferences.tools[toolTitle];
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
return Object.keys(availableToolsMap).filter(key => availableToolsMap[key]);
|
|
1295
|
+
}
|
|
463
1296
|
listArtifacts(chatId) {
|
|
464
1297
|
return this.artifactStore.listArtifacts(chatId);
|
|
465
1298
|
}
|
|
@@ -490,7 +1323,7 @@ export class ChatProcessor {
|
|
|
490
1323
|
return null;
|
|
491
1324
|
}
|
|
492
1325
|
}
|
|
493
|
-
getMessageStream(chatId) {
|
|
1326
|
+
getMessageStream(chatId, signal) {
|
|
494
1327
|
const stream = new PassThrough();
|
|
495
1328
|
let ended = false;
|
|
496
1329
|
const endStream = (error) => {
|
|
@@ -511,13 +1344,15 @@ export class ChatProcessor {
|
|
|
511
1344
|
}
|
|
512
1345
|
};
|
|
513
1346
|
const pushChunk = (chunk) => {
|
|
514
|
-
if (ended || stream.destroyed || stream.writableEnded)
|
|
1347
|
+
if (ended || stream.destroyed || stream.writableEnded || signal?.aborted)
|
|
515
1348
|
return;
|
|
516
1349
|
stream.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
517
1350
|
};
|
|
518
1351
|
const listener = (event) => {
|
|
519
|
-
if (
|
|
1352
|
+
if (signal?.aborted) {
|
|
1353
|
+
endStream();
|
|
520
1354
|
return;
|
|
1355
|
+
}
|
|
521
1356
|
try {
|
|
522
1357
|
const chunk = this.eventToChunk(event);
|
|
523
1358
|
if (chunk) {
|
|
@@ -527,7 +1362,6 @@ export class ChatProcessor {
|
|
|
527
1362
|
event.type === AgentProcessEventType.Error ||
|
|
528
1363
|
event.type === AgentProcessEventType.Aborted) {
|
|
529
1364
|
endStream();
|
|
530
|
-
agentStore.removeListener(chatId, listener);
|
|
531
1365
|
}
|
|
532
1366
|
}
|
|
533
1367
|
catch (err) {
|
|
@@ -536,21 +1370,18 @@ export class ChatProcessor {
|
|
|
536
1370
|
error: err instanceof Error ? err.message : 'Unknown error occurred',
|
|
537
1371
|
});
|
|
538
1372
|
endStream(err);
|
|
539
|
-
agentStore.removeListener(chatId, listener);
|
|
540
1373
|
}
|
|
541
1374
|
};
|
|
542
|
-
// Cleanup on consumer side closing
|
|
543
1375
|
const closeHandler = () => {
|
|
544
|
-
agentStore.removeListener(chatId, listener);
|
|
1376
|
+
this.agentStore.removeListener(chatId, listener);
|
|
545
1377
|
};
|
|
546
1378
|
const errorHandler = (err) => {
|
|
547
|
-
|
|
548
|
-
agentStore.removeListener(chatId, listener);
|
|
1379
|
+
signal?.removeEventListener('abort', endStream);
|
|
549
1380
|
endStream(err);
|
|
550
1381
|
};
|
|
551
1382
|
stream.on('close', closeHandler);
|
|
552
1383
|
stream.on('error', errorHandler);
|
|
553
|
-
const listenerAttached = agentStore.addListener(chatId, listener);
|
|
1384
|
+
const listenerAttached = this.agentStore.addListener(chatId, listener);
|
|
554
1385
|
if (!listenerAttached) {
|
|
555
1386
|
// Remove stream listeners if listener attachment failed to prevent memory leak
|
|
556
1387
|
stream.removeListener('close', closeHandler);
|
|
@@ -559,50 +1390,107 @@ export class ChatProcessor {
|
|
|
559
1390
|
}
|
|
560
1391
|
return stream;
|
|
561
1392
|
}
|
|
562
|
-
async
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
type: StreamChunkType.Chat,
|
|
571
|
-
chatId: chat.id,
|
|
572
|
-
chat,
|
|
573
|
-
})}\n\n`);
|
|
574
|
-
}
|
|
575
|
-
await this.streamMessage(chat, payload, excludeSocketId, async () => {
|
|
576
|
-
if (stream.destroyed || stream.writableEnded) {
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
const messageStream = this.getMessageStream(chat.id);
|
|
580
|
-
// If client disconnects, destroy inner stream
|
|
581
|
-
stream.on('close', () => {
|
|
582
|
-
if (!messageStream.destroyed) {
|
|
583
|
-
messageStream.destroy();
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
await pipelineAsync(messageStream, stream);
|
|
587
|
-
});
|
|
1393
|
+
async upsertAndGetMultiUserChat(payload, excludeSocketId) {
|
|
1394
|
+
const targetUserId = payload.userId ?? 'guest';
|
|
1395
|
+
let chat = null;
|
|
1396
|
+
let isNewChat = false;
|
|
1397
|
+
if (payload.chatId) {
|
|
1398
|
+
chat = await this.chatRepository.findById(payload.chatId);
|
|
1399
|
+
if (!chat) {
|
|
1400
|
+
throw new Error('Chat not found');
|
|
588
1401
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
1402
|
+
if (!this.hasChatAccess(chat, targetUserId)) {
|
|
1403
|
+
throw new Error('Chat does not belong to this user');
|
|
1404
|
+
}
|
|
1405
|
+
const participantIds = Array.from(new Set([
|
|
1406
|
+
...this.getParticipantIds(chat),
|
|
1407
|
+
...(payload.participantIds ?? []),
|
|
1408
|
+
targetUserId,
|
|
1409
|
+
].filter(Boolean)));
|
|
1410
|
+
const updates = {};
|
|
1411
|
+
if (participantIds.length !== this.getParticipantIds(chat).length) {
|
|
1412
|
+
updates.participantIds = participantIds;
|
|
1413
|
+
}
|
|
1414
|
+
if ('model' in payload) {
|
|
1415
|
+
updates.model = payload.model || undefined;
|
|
599
1416
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1417
|
+
if (Object.keys(updates).length > 0) {
|
|
1418
|
+
chat = await this.updateChatAndEmit(chat, updates, excludeSocketId);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (!chat) {
|
|
1422
|
+
chat = await this.createChat(payload, excludeSocketId);
|
|
1423
|
+
isNewChat = true;
|
|
1424
|
+
}
|
|
1425
|
+
return { chat, isNewChat };
|
|
1426
|
+
}
|
|
1427
|
+
async postMultiUserMessage(payload, excludeSocketId) {
|
|
1428
|
+
const { chat } = await this.upsertAndGetMultiUserChat(payload, excludeSocketId);
|
|
1429
|
+
const existingMessages = await this.messageRepository.findByChatId(chat.id);
|
|
1430
|
+
const sender = {
|
|
1431
|
+
id: payload.userId ?? 'guest',
|
|
1432
|
+
type: 'user',
|
|
1433
|
+
...(payload.senderDisplayName ? { displayName: payload.senderDisplayName } : {}),
|
|
1434
|
+
...(payload.senderAvatarUrl ? { avatarUrl: payload.senderAvatarUrl } : {}),
|
|
1435
|
+
};
|
|
1436
|
+
const userMessage = await this.createMessage(chat, MessageRole.User, {
|
|
1437
|
+
content: payload.content,
|
|
1438
|
+
agentName: undefined,
|
|
1439
|
+
sender,
|
|
1440
|
+
mentions: payload.mentions ?? [],
|
|
1441
|
+
}, payload.attachments, excludeSocketId);
|
|
1442
|
+
const decision = await this.shouldRunAgentForMultiUserMessage({
|
|
1443
|
+
chat,
|
|
1444
|
+
previousMessages: existingMessages,
|
|
1445
|
+
userMessage,
|
|
1446
|
+
payload,
|
|
1447
|
+
});
|
|
1448
|
+
if (!decision.shouldRun) {
|
|
1449
|
+
const updatedChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Finished }, excludeSocketId);
|
|
1450
|
+
return { chat: updatedChat, message: userMessage, agentTriggered: false };
|
|
1451
|
+
}
|
|
1452
|
+
const updatedChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Streaming }, undefined);
|
|
1453
|
+
const abortController = this.agentStore.registerAgentProcess(chat.id);
|
|
1454
|
+
const limitedMessages = ContextLimiter.limitContext(existingMessages, {
|
|
1455
|
+
maxMessages: this.config.ai.maxContextMessages,
|
|
1456
|
+
keepFirstUserMessage: true,
|
|
1457
|
+
keepSystemMessages: true,
|
|
1458
|
+
});
|
|
1459
|
+
void this.streamAgentResponseForUserMessage({
|
|
1460
|
+
chat,
|
|
1461
|
+
payload,
|
|
1462
|
+
previousMessages: limitedMessages,
|
|
1463
|
+
userMessage,
|
|
1464
|
+
excludeSocketId: undefined,
|
|
1465
|
+
signal: abortController.signal,
|
|
1466
|
+
agentName: decision.agentName,
|
|
1467
|
+
}).catch(async (error) => {
|
|
1468
|
+
logger.error({ error, chatId: chat.id }, 'Multi-user background agent flow failed');
|
|
1469
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.Error }, undefined);
|
|
1470
|
+
});
|
|
1471
|
+
return { chat: updatedChat, message: userMessage, agentTriggered: true };
|
|
1472
|
+
}
|
|
1473
|
+
async createMessageStream(payload, excludeSocketId, signal) {
|
|
1474
|
+
const stream = new PassThrough();
|
|
1475
|
+
this.runMessageStream(stream, payload, excludeSocketId, signal)
|
|
1476
|
+
.catch(err => {
|
|
1477
|
+
logger.error({ err }, 'Stream task failed');
|
|
604
1478
|
});
|
|
605
1479
|
return stream;
|
|
606
1480
|
}
|
|
1481
|
+
async runMessageStream(stream, payload, excludeSocketId, signal) {
|
|
1482
|
+
const chat = await this.upsertAndGetChat(payload, excludeSocketId);
|
|
1483
|
+
if (!('chatId' in payload) && !('messageId' in payload) && chat) {
|
|
1484
|
+
stream.write(`data: ${JSON.stringify({
|
|
1485
|
+
type: StreamChunkType.Chat,
|
|
1486
|
+
chatId: chat.id,
|
|
1487
|
+
chat,
|
|
1488
|
+
})}\n\n`);
|
|
1489
|
+
}
|
|
1490
|
+
await this.streamMessage(chat, payload, excludeSocketId, () => {
|
|
1491
|
+
const messageStream = this.getMessageStream(chat.id, signal);
|
|
1492
|
+
messageStream.pipe(stream);
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
607
1495
|
}
|
|
608
1496
|
//# sourceMappingURL=ChatProcessor.js.map
|