@wabot-dev/framework 0.9.25 → 0.9.27

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.
@@ -14,7 +14,6 @@ import 'uuid';
14
14
  import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
15
15
  import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
16
16
  import { safeJsonParse } from '../../../feature/chat-bot/safeJsonParse.js';
17
- import { unconsumedMediaStartIndex } from '../../../feature/chat-bot/unconsumedMediaStartIndex.js';
18
17
  import { Anthropic } from '@anthropic-ai/sdk';
19
18
 
20
19
  const ANTHROPIC_SUPPORTED_IMAGE_MIME_TYPES = [
@@ -61,11 +60,10 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
61
60
  }
62
61
  mapChatItems(chatItems) {
63
62
  const messages = [];
64
- const mediaStart = unconsumedMediaStartIndex(chatItems);
65
- chatItems.forEach((chatItem, index) => {
63
+ chatItems.forEach((chatItem) => {
66
64
  switch (chatItem.type) {
67
65
  case 'humanMessage':
68
- messages.push(this.mapHumanMessage(chatItem.humanMessage, index >= mediaStart));
66
+ messages.push(this.mapHumanMessage(chatItem.humanMessage));
69
67
  break;
70
68
  case 'botMessage':
71
69
  messages.push(this.mapBotMessage(chatItem.botMessage));
@@ -77,7 +75,7 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
77
75
  });
78
76
  return messages;
79
77
  }
80
- mapHumanMessage(item, includeMedia) {
78
+ mapHumanMessage(item) {
81
79
  if (isChatMessageEmpty(item)) {
82
80
  throw new Error('User message content is empty');
83
81
  }
@@ -89,14 +87,14 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
89
87
  supportedDocumentMimeTypes: ANTHROPIC_SUPPORTED_DOCUMENT_MIME_TYPES,
90
88
  }),
91
89
  });
92
- if (includeMedia && item.images) {
90
+ if (item.images) {
93
91
  for (const image of item.images) {
94
92
  if (!ANTHROPIC_SUPPORTED_IMAGE_MIME_TYPES.includes(image.mimeType))
95
93
  continue;
96
94
  blocks.push({ type: 'image', source: this.toAnthropicImageSource(image) });
97
95
  }
98
96
  }
99
- if (includeMedia && item.documents) {
97
+ if (item.documents) {
100
98
  for (const doc of item.documents) {
101
99
  if (!ANTHROPIC_SUPPORTED_DOCUMENT_MIME_TYPES.includes(doc.mimeType))
102
100
  continue;
@@ -15,7 +15,6 @@ import 'uuid';
15
15
  import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
16
16
  import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
17
17
  import { safeJsonParse } from '../../../feature/chat-bot/safeJsonParse.js';
18
- import { unconsumedMediaStartIndex } from '../../../feature/chat-bot/unconsumedMediaStartIndex.js';
19
18
  import { GoogleGenAI } from '@google/genai';
20
19
 
21
20
  const GOOGLE_SUPPORTED_IMAGE_MIME_TYPES = [
@@ -83,11 +82,10 @@ let GoogleChatAdapter = class GoogleChatAdapter {
83
82
  }
84
83
  async mapChatItems(chatItems) {
85
84
  const contents = [];
86
- const mediaStart = unconsumedMediaStartIndex(chatItems);
87
- for (const [index, chatItem] of chatItems.entries()) {
85
+ for (const chatItem of chatItems) {
88
86
  switch (chatItem.type) {
89
87
  case 'humanMessage':
90
- contents.push(await this.mapHumanMessage(chatItem.humanMessage, index >= mediaStart));
88
+ contents.push(await this.mapHumanMessage(chatItem.humanMessage));
91
89
  break;
92
90
  case 'botMessage':
93
91
  contents.push(this.mapBotMessage(chatItem.botMessage));
@@ -99,7 +97,7 @@ let GoogleChatAdapter = class GoogleChatAdapter {
99
97
  }
100
98
  return contents;
101
99
  }
102
- async mapHumanMessage(item, includeMedia) {
100
+ async mapHumanMessage(item) {
103
101
  if (isChatMessageEmpty(item)) {
104
102
  throw new Error('User message content is empty');
105
103
  }
@@ -111,14 +109,14 @@ let GoogleChatAdapter = class GoogleChatAdapter {
111
109
  }),
112
110
  });
113
111
  const filesToSend = [];
114
- if (includeMedia && item.images) {
112
+ if (item.images) {
115
113
  for (const image of item.images) {
116
114
  if (!GOOGLE_SUPPORTED_IMAGE_MIME_TYPES.includes(image.mimeType))
117
115
  continue;
118
116
  filesToSend.push(image);
119
117
  }
120
118
  }
121
- if (includeMedia && item.documents) {
119
+ if (item.documents) {
122
120
  for (const doc of item.documents) {
123
121
  if (!GOOGLE_SUPPORTED_DOCUMENT_MIME_TYPES.includes(doc.mimeType))
124
122
  continue;
@@ -12,7 +12,6 @@ import 'uuid';
12
12
  import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
13
13
  import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
14
14
  import '../../../core/error/setupErrorHandlers.js';
15
- import { unconsumedMediaStartIndex } from '../../../feature/chat-bot/unconsumedMediaStartIndex.js';
16
15
  import { Logger } from '../../../core/logger/Logger.js';
17
16
  import { OpenAI } from 'openai';
18
17
 
@@ -52,11 +51,10 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
52
51
  }
53
52
  mapChatItems(chatItems) {
54
53
  const openIaInput = [];
55
- const mediaStart = unconsumedMediaStartIndex(chatItems);
56
- chatItems.forEach((chatItem, index) => {
54
+ chatItems.forEach((chatItem) => {
57
55
  switch (chatItem.type) {
58
56
  case 'humanMessage':
59
- openIaInput.push(this.mapConectionMessage(chatItem.humanMessage, index >= mediaStart));
57
+ openIaInput.push(this.mapConectionMessage(chatItem.humanMessage));
60
58
  break;
61
59
  case 'botMessage':
62
60
  openIaInput.push(this.mapBotMessage(chatItem.botMessage));
@@ -68,7 +66,7 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
68
66
  });
69
67
  return openIaInput;
70
68
  }
71
- mapConectionMessage(item, includeMedia) {
69
+ mapConectionMessage(item) {
72
70
  if (isChatMessageEmpty(item)) {
73
71
  throw new Error('User message content is empty');
74
72
  }
@@ -80,7 +78,7 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
80
78
  supportedDocumentMimeTypes: OPENAI_SUPPORTED_DOCUMENT_MIME_TYPES,
81
79
  }),
82
80
  });
83
- if (includeMedia && item.images) {
81
+ if (item.images) {
84
82
  for (const image of item.images) {
85
83
  if (!OPENAI_SUPPORTED_IMAGE_MIME_TYPES.includes(image.mimeType))
86
84
  continue;
@@ -91,7 +89,7 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
91
89
  });
92
90
  }
93
91
  }
94
- if (includeMedia && item.documents) {
92
+ if (item.documents) {
95
93
  for (const doc of item.documents) {
96
94
  if (!OPENAI_SUPPORTED_DOCUMENT_MIME_TYPES.includes(doc.mimeType))
97
95
  continue;
@@ -121,7 +119,7 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
121
119
  type: 'function_call',
122
120
  call_id: item.id,
123
121
  name: item.name,
124
- arguments: JSON.stringify(item.arguments),
122
+ arguments: item.arguments || '{}',
125
123
  },
126
124
  {
127
125
  type: 'function_call_output',
@@ -14,7 +14,6 @@ import 'uuid';
14
14
  import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
15
15
  import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
16
16
  import '../../../core/error/setupErrorHandlers.js';
17
- import { unconsumedMediaStartIndex } from '../../../feature/chat-bot/unconsumedMediaStartIndex.js';
18
17
  import { OpenRouter } from '@openrouter/sdk';
19
18
 
20
19
  const OPENROUTER_SUPPORTED_IMAGE_MIME_TYPES = [
@@ -67,11 +66,10 @@ let OpenRouterChatAdapter = class OpenRouterChatAdapter {
67
66
  }
68
67
  mapChatItems(chatItems) {
69
68
  const messages = [];
70
- const mediaStart = unconsumedMediaStartIndex(chatItems);
71
- chatItems.forEach((chatItem, index) => {
69
+ chatItems.forEach((chatItem) => {
72
70
  switch (chatItem.type) {
73
71
  case 'humanMessage':
74
- messages.push(this.mapHumanMessage(chatItem.humanMessage, index >= mediaStart));
72
+ messages.push(this.mapHumanMessage(chatItem.humanMessage));
75
73
  break;
76
74
  case 'botMessage':
77
75
  messages.push(this.mapBotMessage(chatItem.botMessage));
@@ -83,34 +81,38 @@ let OpenRouterChatAdapter = class OpenRouterChatAdapter {
83
81
  });
84
82
  return messages;
85
83
  }
86
- mapHumanMessage(item, includeMedia) {
84
+ mapHumanMessage(item) {
87
85
  if (isChatMessageEmpty(item)) {
88
86
  throw new Error('User message content is empty');
89
87
  }
90
88
  const contentParts = [];
91
- contentParts.push(extractChatMessageText(item, {
92
- supportedImageMimeTypes: OPENROUTER_SUPPORTED_IMAGE_MIME_TYPES,
93
- supportedDocumentMimeTypes: OPENROUTER_SUPPORTED_DOCUMENT_MIME_TYPES,
94
- }));
95
- if (includeMedia && item.images) {
89
+ contentParts.push({
90
+ type: 'text',
91
+ text: extractChatMessageText(item, {
92
+ supportedImageMimeTypes: OPENROUTER_SUPPORTED_IMAGE_MIME_TYPES,
93
+ supportedDocumentMimeTypes: OPENROUTER_SUPPORTED_DOCUMENT_MIME_TYPES,
94
+ }),
95
+ });
96
+ if (item.images) {
96
97
  for (const image of item.images) {
97
98
  if (!OPENROUTER_SUPPORTED_IMAGE_MIME_TYPES.includes(image.mimeType))
98
99
  continue;
99
100
  const imageUrl = image.publicUrl ?? image.base64Url;
100
101
  if (imageUrl)
101
- contentParts.push(imageUrl);
102
+ contentParts.push({ type: 'image_url', imageUrl: { url: imageUrl } });
102
103
  }
103
104
  }
104
- if (includeMedia && item.documents) {
105
+ if (item.documents) {
105
106
  for (const doc of item.documents) {
106
107
  if (!OPENROUTER_SUPPORTED_DOCUMENT_MIME_TYPES.includes(doc.mimeType))
107
108
  continue;
108
109
  const docUrl = doc.publicUrl ?? doc.base64Url;
109
- if (docUrl)
110
- contentParts.push(docUrl);
110
+ if (docUrl) {
111
+ contentParts.push({ type: 'file', file: { fileData: docUrl, filename: doc.name } });
112
+ }
111
113
  }
112
114
  }
113
- return { role: 'user', content: contentParts.join('\n') };
115
+ return { role: 'user', content: contentParts };
114
116
  }
115
117
  mapBotMessage(item) {
116
118
  if (!item.text) {
@@ -39,7 +39,8 @@ let CmdChannelServer = class CmdChannelServer {
39
39
  this.server = net.createServer((socket) => this.handleConnection(socket));
40
40
  this.server.on('error', (err) => logger.error('cmd channel server error', err));
41
41
  this.server.listen(socketPath, () => {
42
- logger.info(`cmd channel server listening at ${socketPath}`);
42
+ const displayPath = path.relative(process.cwd(), socketPath) || socketPath;
43
+ logger.info(`cmd channel server listening at ${displayPath}`);
43
44
  });
44
45
  process.once('exit', () => this.shutdown());
45
46
  process.once('SIGINT', () => {
@@ -5,7 +5,7 @@ import { MindsetOperator } from '../mindset/MindsetOperator.js';
5
5
  import { ChatAdapter } from './ChatAdapter.js';
6
6
  import { ChatItem } from './ChatItem.js';
7
7
  import { ChatMemory } from './ChatMemory.js';
8
- import { pendingMediaStartIndex } from './pendingMediaStartIndex.js';
8
+ import { stripAnsweredMedia } from './stripAnsweredMedia.js';
9
9
  import { Logger } from '../../core/logger/Logger.js';
10
10
 
11
11
  const MAX_CONSECUTIVE_INVALID_ARGS = 2;
@@ -46,12 +46,12 @@ let ChatBot = class ChatBot {
46
46
  const tools = this.mindset.tools();
47
47
  const identity = await this.mindset.identity();
48
48
  const prevItemsData = prevItems.map((x) => x.getData());
49
- // Only media from the pending exchange is actually sent to the model; images
50
- // in already-answered messages are not, so they must not force a vision model.
51
- const mediaStart = pendingMediaStartIndex(prevItemsData);
52
- const needsVision = prevItemsData.some((data, index) => index >= mediaStart &&
53
- data.type === 'humanMessage' &&
54
- (data.humanMessage.images?.length ?? 0) > 0);
49
+ // The bot not the provider adapters decides which media reaches the
50
+ // model: keep binaries only for the pending exchange and drop already-
51
+ // answered ones. The leftover media also determines whether this call needs
52
+ // a vision model.
53
+ const sentItems = stripAnsweredMedia(prevItemsData);
54
+ const needsVision = sentItems.some((data) => data.type === 'humanMessage' && (data.humanMessage.images?.length ?? 0) > 0);
55
55
  const kind = needsVision ? 'visionLlm' : 'llm';
56
56
  const candidates = await this.mindset.resolveModels(kind);
57
57
  if (candidates.length === 0) {
@@ -61,7 +61,7 @@ let ChatBot = class ChatBot {
61
61
  models: candidates,
62
62
  systemPrompt,
63
63
  tools,
64
- prevItems: prevItemsData,
64
+ prevItems: sentItems,
65
65
  });
66
66
  for (const newItemData of newItemsData) {
67
67
  if (newItemData.type === 'functionCall') {
@@ -8,6 +8,14 @@
8
8
  * re-sending the binary would make the model analyze the same files again on
9
9
  * every turn (and re-upload them, wasting tokens).
10
10
  *
11
+ * The boundary is the last bot message, not the last model output, so media
12
+ * stays attached for every call of a tool loop within the pending exchange. The
13
+ * provider APIs are stateless — each call resends the whole history — so the
14
+ * bytes must travel on every call up to the bot's reply for the model to keep
15
+ * seeing the file while it works through tool calls (prompt caching keeps the
16
+ * re-send cheap). Do not stop at function calls, or the model goes blind to the
17
+ * image right when it composes its post-tool answer.
18
+ *
11
19
  * Returns 0 when the bot has not replied yet, so the whole pending exchange
12
20
  * keeps its media.
13
21
  */
@@ -0,0 +1,36 @@
1
+ import { pendingMediaStartIndex } from './pendingMediaStartIndex.js';
2
+
3
+ function stripAnsweredMedia(items) {
4
+ const start = pendingMediaStartIndex(items);
5
+ return items.map((item, index) => {
6
+ if (index >= start || item.type !== 'humanMessage')
7
+ return item;
8
+ if (!item.humanMessage.images && !item.humanMessage.documents)
9
+ return item;
10
+ const humanMessage = { ...item.humanMessage };
11
+ delete humanMessage.images;
12
+ delete humanMessage.documents;
13
+ // A media-only message becomes empty once its binaries are stripped, which
14
+ // makes the provider adapters reject it as empty content. Leave a short
15
+ // placeholder so the answered turn stays in the history coherently.
16
+ if (isStrippedMessageEmpty(humanMessage)) {
17
+ humanMessage.text = describeStrippedMedia(item.humanMessage);
18
+ }
19
+ return { type: 'humanMessage', humanMessage };
20
+ });
21
+ }
22
+ function isStrippedMessageEmpty(message) {
23
+ return !message.text && !message.object;
24
+ }
25
+ function describeStrippedMedia(message) {
26
+ const parts = [];
27
+ const images = message.images?.length ?? 0;
28
+ const documents = message.documents?.length ?? 0;
29
+ if (images > 0)
30
+ parts.push(`${images} image${images > 1 ? 's' : ''}`);
31
+ if (documents > 0)
32
+ parts.push(`${documents} document${documents > 1 ? 's' : ''}`);
33
+ return `[sent ${parts.join(' and ')}]`;
34
+ }
35
+
36
+ export { stripAnsweredMedia };
@@ -1173,6 +1173,14 @@ declare class ChatBotMetadataStore {
1173
1173
  * re-sending the binary would make the model analyze the same files again on
1174
1174
  * every turn (and re-upload them, wasting tokens).
1175
1175
  *
1176
+ * The boundary is the last bot message, not the last model output, so media
1177
+ * stays attached for every call of a tool loop within the pending exchange. The
1178
+ * provider APIs are stateless — each call resends the whole history — so the
1179
+ * bytes must travel on every call up to the bot's reply for the model to keep
1180
+ * seeing the file while it works through tool calls (prompt caching keeps the
1181
+ * re-send cheap). Do not stop at function calls, or the model goes blind to the
1182
+ * image right when it composes its post-tool answer.
1183
+ *
1176
1184
  * Returns 0 when the bot has not replied yet, so the whole pending exchange
1177
1185
  * keeps its media.
1178
1186
  */
@@ -1182,24 +1190,7 @@ declare function runChatAdapters(adapters: IConstructor<IChatAdapter>[]): void;
1182
1190
 
1183
1191
  declare function safeJsonParse<T = unknown>(json: string | undefined | null, context?: string): T;
1184
1192
 
1185
- /**
1186
- * Index of the first item whose media (images/documents) the model has not been
1187
- * shown yet — i.e. right after the last model output, be it a bot message or a
1188
- * function call.
1189
- *
1190
- * A file's binary should only be sent on the model's first exposure to it. Once
1191
- * the model has produced anything in response to a human message (a function
1192
- * call during a tool loop, or a reply), it has already analyzed that message's
1193
- * files; re-sending the bytes on the next call would analyze the same file
1194
- * again. This is the within-turn counterpart of {@link pendingMediaStartIndex}:
1195
- * that one keeps a turn's media "active" for vision-model selection across the
1196
- * whole tool loop, while this one stops re-uploading the bytes after the first
1197
- * call.
1198
- *
1199
- * Returns 0 when the model has not produced any output yet, so a brand-new
1200
- * message keeps its media.
1201
- */
1202
- declare function unconsumedMediaStartIndex(items: IChatItem[]): number;
1193
+ declare function stripAnsweredMedia(items: IChatItem[]): IChatItem[];
1203
1194
 
1204
1195
  interface IProjectRunnerConfig {
1205
1196
  directories?: string[];
@@ -2731,4 +2722,4 @@ declare function HtmlModule(options: IHtmlModuleOptions): {
2731
2722
  new (): {};
2732
2723
  };
2733
2724
 
2734
- export { AnthropicChatAdapter, ApiKey, ApiKeyGuardMiddleware, ApiKeyHandshakeGuardMiddleware, ApiKeyRepository, Async, AsyncMetadataStore, Auth, Chat, ChatAdapter, ChatAdapterMetadataStore, ChatAdapterRegistry, ChatBot, ChatBotMetadataStore, ChatItem, ChatMemory, ChatOperator, ChatRepository, ChatResolver, type ClientMap, CmdChannel, CmdChannelConfig, CmdChannelServer, type CmdClientMessage, type CmdServerMessage, type ConfigReference, type ConfigReferenceType, ConfigResolver, Container, ControllerMetadataStore, CronJob, CronJobRepository, CrudRepository, CustomError, DeepSeekChatAdapter, DescriptionMetadataStore, EXPRESS_REQ, EXPRESS_RES, Entity, Env, type ErrorSeverity, ExpressProvider, GoogleChatAdapter, type GoogleChatAdapterV2Options, HtmlModule, HttpServerProvider, type IApiKeyData, type IApiKeyRepository, type IArrayValidationError, type IArrayValidationResult, type IBotMessageItem, type IBuiltQuery, type IChannelMessage, type IChannelMetadata, type IChatAdapter, type IChatAdapterDecoratorConfig, type IChatAdapterMetadata, type IChatAdapterNextItemsReq, type IChatAdapterNextItemsRes, type IChatAssociation, type IChatBot, type IChatBotMetadata, type IChatChannel, type IChatConnection, type IChatControllerMetadata, type IChatData, type IChatItem, type IChatItemData, type IChatItemType, type IChatMemory, type IChatMessage, type IChatMessageDocument, type IChatMessageFile, type IChatMessageImage, type IChatMessagesPrivateFile, type IChatMessagesPublicFile, type IChatRepository, type IChatType, type ICmdChannelEntry, type ICmdChannelHandlers, type ICmdChannelMessage, type ICmdImage, type ICmdIncomingMessage, type ICmdReceivedMessage, type ICommandConfig, type ICommandHandler, type ICommandHandlerConfig, type IConstructor, type ICronConfig, type ICronHandler, type ICronJobData, type ICronJobRepository, type ICrudRepository, type ICustomErrorData, type IDedupConfig, type IDescriptionMetadata, type IEndPointConfig, type IEndPointMetadata, type IEntityData, type IEnvType, type IErrorHandlersConfig, type IErrorMonitor, type IErrorMonitorContext, type IExtractChatMessageTextOptions, type IFunctionCall, type IFunctionCallItem, type IGenerateApiKeyReq, type IGenerateApiKeyRes, type IHandshakeMiddleware, type IHandshakeMiddlewareMetadata, type IHtmlModuleOptions, type IHumanMessageItem, type IJobData, type IJobOptions, type IJobRepository, type IJwtRefreshTokenData, type IJwtRefreshTokenRepository, type IKapsoChannelConfig, type IKapsoChannelMessage, type IKapsoChannelMessageListener, type IKapsoChatMessage, type IKapsoConversation, type IKapsoEvent, type IKapsoIncomingMessage, type IKapsoMessageReceivedEvent, type IKapsoReceivedMessage, type IKapsoUnknownEvent, type ILanguageModelUsage, type ILockKey, type ILocker, type ILockerKey, type IMemoryRepositoryAdapterOptions, type IMessageContext, type IMiddleware, type IMiddlewareMetadata, type IMindset, type IMindsetConfig, type IMindsetIdentity, type IMindsetLlm, type IMindsetMetadata, type IMindsetModelKind, type IMindsetModelRef, type IMindsetModels, type IMindsetModuleConfig, type IMindsetModuleMetadata, type IMindsetParameterSchema, type IMindsetTool, type IMindsetToolParameter, type IModelValidationError, type IModelValidationResult, type IModelValidatorsInfo, type IMoneyData, type IPersistentData, type IPgRepositoryConfig, type IProjectRunnerConfig, type IPropertyValidatorInfo, type IQueryAst, type IQueryCondition, type IQueryMethodMetadata, type IQueryOrderBy, type IReceivedMessage, type IRemoteApiKeyFetcher, type IRepositoryAdapter, type IRepositoryConfig, type IRepositoryRuntime, type IRestControllerConfig, type IRestControllerMetadata, type IScanProjectFilesOptions, type IScheduleAt, type IScheduleDelay, type ISendWhatsAppMessageReq, type ISendWhatsAppTemplateReq, type ISocketChannelConfig, type ISocketChannelMessage, type ISocketChannelReceivedMessage, type ISocketControllerConfig, type ISocketControllerMetadata, type ISocketEventConfig, type ISocketEventMetadata, type ISocketReceivedMessage, type IStorableData, type ITelegramChannelConfig, type ITelegramChannelMessage, type ITelegramReceivedMessage, type ITransactionAdapter, type IValidateArrayOptions, type IValidateArrayOptionsWithItemsValidators, type IValidateInputShape, type IValidateIsInOptions, type IValidateIsRecordOptions, type IValidateMaxOptions, type IValidateMinOptions, type IValidationError, type IValidationResult, type IValidator, type IValidatorMetadata, type IWasenderChannelConfig, type IWasenderChannelMessageListener, type IWasenderDeviceListMetadata, type IWasenderEvent, type IWasenderMessageContent, type IWasenderMessageContextInfo, type IWasenderMessageKey, type IWasenderMessageReceivedData, type IWasenderMessageReceivedEvent, type IWasenderQrUpdatedEvent, type IWasenderReceivedMessage, type IWhatsAppCloudContact, type IWhatsAppCloudMessage, type IWhatsAppCloudMessageMetadata, type IWhatsAppCloudTemplate, type IWhatsAppCloudTemplateComponent, type IWhatsAppCloudTemplateResponse, type IWhatsAppCloudWebhookPayload, type IWhatsAppSender, type IWhatsAppTemplateData, type IWhatsAppTemplateParameter, type IchatControllerConfig, InMemoryChatMemory, InMemoryChatRepository, InMemoryCronJobRepository, InMemoryJobRepository, InMemoryLockKey, InMemoryLocker, Job, JobRepository, JobRunner, Jwt, JwtAccessAndRefreshTokenDto, JwtConfig, JwtGuardMiddleware, JwtHandshakeGuardMiddleware, JwtRefreshToken, JwtRefreshTokenRepository, JwtSigner, JwtTokenDto, KapsoChannel, KapsoChannelConfig, KapsoReceiver, KapsoSender, KapsoWebhookController, Lifecycle, Locker, Logger, MEMORY_ADAPTER_ID, Mapper, MemoryRepositoryAdapter, MemoryRepositoryExtension, Mindset, MindsetMetadataStore, MindsetOperator, Money, MoneyDto, OpenRouterChatAdapter, OpenaiChatAdapter, PG_ADAPTER_ID, Password, type PasswordHashOptions, Persistent, PgApiKeyRepository, PgChatMemory, PgChatRepository, PgCronJobRepository, PgCrudRepository, PgJobRepository, PgJsonRepositoryAdapter, PgJwtRefreshTokenRepository, PgLockKey, PgLocker, PgRepositoryBase, PgRepositoryBase as PgRepositoryExtension, PgTransactionAdapter, ProjectRunner, type QueryConnector, type QueryOperator, type QueryPrefix, Random, RemoteApiKeyRepository, RepositoryAdapterRegistry, RepositoryMetadataStore, type ResolvedConfig, RestControllerMetadataStore, RestRequest, SocketChannel, SocketChannelConfig, SocketChannelMessageFile, SocketChannelReceivedMessage, SocketControllerMetadataStore, SocketServerConfig, SocketServerProvider, Storable, TelegramChannel, TelegramChannelConfig, TransactionMetadataStore, UnionChatAdapter, ValidationMetadataStore, WabotChatAdapter, WasenderChannel, WasenderChannelConfig, WasenderReceiver, WasenderSender, WasenderWebhookController, WhatsAppApiSender, WhatsAppReceiverByCloudApi, WhatsAppSender, apiKeyGuard, apiKeyHandshakeGuard, bool, boolArr, buildQuerySql, chatAdapter, chatBot, chatController, chatItemTypeOptions, cmd, cmdChannelName, cmdChannelSocketPath, command, commandHandler, computeDedupKey, container, cronHandler, description, errorToPlainObject, evaluateQueryAst, extractChatMessageText, extractNumberFromWasenderMessageKey, getClientMap, getPgClient, handshakeMiddlewares, inject, injectable, isArray, isBoolean, isChatMessageEmpty, isDate, isIn, isModel, isNotEmpty, isNumber, isOptional, isPresent, isRecord, isRetryableError, isString, jwtGuard, jwtHandshakeGuard, kapso, kapsoChannelName, markdownToTelegramHtml, max, memExtension, middleware, min, mindset, mindsetModule, modelInfo, num, numArr, obj, onDelete, onGet, onPost, onPut, onSocketEvent, parseQueryMethodName, pendingMediaStartIndex, pgExtension, pgStorage, query, queryExtension, readJsonFromFile, repository, resolveConfigReferences, restController, run, runChatAdapters, runChatControllers, runCmdClient, runCommandHandlers, runCronHandlers, runRestControllers, runSocketControllers, safeJsonParse, scanProjectFiles, scoped, setupErrorHandlers, singleton, socket, socketChannelName, socketController, stopCommandHandlers, stopCronHandlers, str, strArr, telegram, telegramChannelName, transaction, unconsumedMediaStartIndex, validateAndTransform, validateArray, validateIsBoolean, validateIsDate, validateIsIn, validateIsNotEmpty, validateIsNumber, validateIsPresent, validateIsRecord, validateIsString, validateMax, validateMin, validateModel, wasender, wasenderChannelName, withPgClient, withPgTransaction, writeJsonToFile };
2725
+ export { AnthropicChatAdapter, ApiKey, ApiKeyGuardMiddleware, ApiKeyHandshakeGuardMiddleware, ApiKeyRepository, Async, AsyncMetadataStore, Auth, Chat, ChatAdapter, ChatAdapterMetadataStore, ChatAdapterRegistry, ChatBot, ChatBotMetadataStore, ChatItem, ChatMemory, ChatOperator, ChatRepository, ChatResolver, type ClientMap, CmdChannel, CmdChannelConfig, CmdChannelServer, type CmdClientMessage, type CmdServerMessage, type ConfigReference, type ConfigReferenceType, ConfigResolver, Container, ControllerMetadataStore, CronJob, CronJobRepository, CrudRepository, CustomError, DeepSeekChatAdapter, DescriptionMetadataStore, EXPRESS_REQ, EXPRESS_RES, Entity, Env, type ErrorSeverity, ExpressProvider, GoogleChatAdapter, type GoogleChatAdapterV2Options, HtmlModule, HttpServerProvider, type IApiKeyData, type IApiKeyRepository, type IArrayValidationError, type IArrayValidationResult, type IBotMessageItem, type IBuiltQuery, type IChannelMessage, type IChannelMetadata, type IChatAdapter, type IChatAdapterDecoratorConfig, type IChatAdapterMetadata, type IChatAdapterNextItemsReq, type IChatAdapterNextItemsRes, type IChatAssociation, type IChatBot, type IChatBotMetadata, type IChatChannel, type IChatConnection, type IChatControllerMetadata, type IChatData, type IChatItem, type IChatItemData, type IChatItemType, type IChatMemory, type IChatMessage, type IChatMessageDocument, type IChatMessageFile, type IChatMessageImage, type IChatMessagesPrivateFile, type IChatMessagesPublicFile, type IChatRepository, type IChatType, type ICmdChannelEntry, type ICmdChannelHandlers, type ICmdChannelMessage, type ICmdImage, type ICmdIncomingMessage, type ICmdReceivedMessage, type ICommandConfig, type ICommandHandler, type ICommandHandlerConfig, type IConstructor, type ICronConfig, type ICronHandler, type ICronJobData, type ICronJobRepository, type ICrudRepository, type ICustomErrorData, type IDedupConfig, type IDescriptionMetadata, type IEndPointConfig, type IEndPointMetadata, type IEntityData, type IEnvType, type IErrorHandlersConfig, type IErrorMonitor, type IErrorMonitorContext, type IExtractChatMessageTextOptions, type IFunctionCall, type IFunctionCallItem, type IGenerateApiKeyReq, type IGenerateApiKeyRes, type IHandshakeMiddleware, type IHandshakeMiddlewareMetadata, type IHtmlModuleOptions, type IHumanMessageItem, type IJobData, type IJobOptions, type IJobRepository, type IJwtRefreshTokenData, type IJwtRefreshTokenRepository, type IKapsoChannelConfig, type IKapsoChannelMessage, type IKapsoChannelMessageListener, type IKapsoChatMessage, type IKapsoConversation, type IKapsoEvent, type IKapsoIncomingMessage, type IKapsoMessageReceivedEvent, type IKapsoReceivedMessage, type IKapsoUnknownEvent, type ILanguageModelUsage, type ILockKey, type ILocker, type ILockerKey, type IMemoryRepositoryAdapterOptions, type IMessageContext, type IMiddleware, type IMiddlewareMetadata, type IMindset, type IMindsetConfig, type IMindsetIdentity, type IMindsetLlm, type IMindsetMetadata, type IMindsetModelKind, type IMindsetModelRef, type IMindsetModels, type IMindsetModuleConfig, type IMindsetModuleMetadata, type IMindsetParameterSchema, type IMindsetTool, type IMindsetToolParameter, type IModelValidationError, type IModelValidationResult, type IModelValidatorsInfo, type IMoneyData, type IPersistentData, type IPgRepositoryConfig, type IProjectRunnerConfig, type IPropertyValidatorInfo, type IQueryAst, type IQueryCondition, type IQueryMethodMetadata, type IQueryOrderBy, type IReceivedMessage, type IRemoteApiKeyFetcher, type IRepositoryAdapter, type IRepositoryConfig, type IRepositoryRuntime, type IRestControllerConfig, type IRestControllerMetadata, type IScanProjectFilesOptions, type IScheduleAt, type IScheduleDelay, type ISendWhatsAppMessageReq, type ISendWhatsAppTemplateReq, type ISocketChannelConfig, type ISocketChannelMessage, type ISocketChannelReceivedMessage, type ISocketControllerConfig, type ISocketControllerMetadata, type ISocketEventConfig, type ISocketEventMetadata, type ISocketReceivedMessage, type IStorableData, type ITelegramChannelConfig, type ITelegramChannelMessage, type ITelegramReceivedMessage, type ITransactionAdapter, type IValidateArrayOptions, type IValidateArrayOptionsWithItemsValidators, type IValidateInputShape, type IValidateIsInOptions, type IValidateIsRecordOptions, type IValidateMaxOptions, type IValidateMinOptions, type IValidationError, type IValidationResult, type IValidator, type IValidatorMetadata, type IWasenderChannelConfig, type IWasenderChannelMessageListener, type IWasenderDeviceListMetadata, type IWasenderEvent, type IWasenderMessageContent, type IWasenderMessageContextInfo, type IWasenderMessageKey, type IWasenderMessageReceivedData, type IWasenderMessageReceivedEvent, type IWasenderQrUpdatedEvent, type IWasenderReceivedMessage, type IWhatsAppCloudContact, type IWhatsAppCloudMessage, type IWhatsAppCloudMessageMetadata, type IWhatsAppCloudTemplate, type IWhatsAppCloudTemplateComponent, type IWhatsAppCloudTemplateResponse, type IWhatsAppCloudWebhookPayload, type IWhatsAppSender, type IWhatsAppTemplateData, type IWhatsAppTemplateParameter, type IchatControllerConfig, InMemoryChatMemory, InMemoryChatRepository, InMemoryCronJobRepository, InMemoryJobRepository, InMemoryLockKey, InMemoryLocker, Job, JobRepository, JobRunner, Jwt, JwtAccessAndRefreshTokenDto, JwtConfig, JwtGuardMiddleware, JwtHandshakeGuardMiddleware, JwtRefreshToken, JwtRefreshTokenRepository, JwtSigner, JwtTokenDto, KapsoChannel, KapsoChannelConfig, KapsoReceiver, KapsoSender, KapsoWebhookController, Lifecycle, Locker, Logger, MEMORY_ADAPTER_ID, Mapper, MemoryRepositoryAdapter, MemoryRepositoryExtension, Mindset, MindsetMetadataStore, MindsetOperator, Money, MoneyDto, OpenRouterChatAdapter, OpenaiChatAdapter, PG_ADAPTER_ID, Password, type PasswordHashOptions, Persistent, PgApiKeyRepository, PgChatMemory, PgChatRepository, PgCronJobRepository, PgCrudRepository, PgJobRepository, PgJsonRepositoryAdapter, PgJwtRefreshTokenRepository, PgLockKey, PgLocker, PgRepositoryBase, PgRepositoryBase as PgRepositoryExtension, PgTransactionAdapter, ProjectRunner, type QueryConnector, type QueryOperator, type QueryPrefix, Random, RemoteApiKeyRepository, RepositoryAdapterRegistry, RepositoryMetadataStore, type ResolvedConfig, RestControllerMetadataStore, RestRequest, SocketChannel, SocketChannelConfig, SocketChannelMessageFile, SocketChannelReceivedMessage, SocketControllerMetadataStore, SocketServerConfig, SocketServerProvider, Storable, TelegramChannel, TelegramChannelConfig, TransactionMetadataStore, UnionChatAdapter, ValidationMetadataStore, WabotChatAdapter, WasenderChannel, WasenderChannelConfig, WasenderReceiver, WasenderSender, WasenderWebhookController, WhatsAppApiSender, WhatsAppReceiverByCloudApi, WhatsAppSender, apiKeyGuard, apiKeyHandshakeGuard, bool, boolArr, buildQuerySql, chatAdapter, chatBot, chatController, chatItemTypeOptions, cmd, cmdChannelName, cmdChannelSocketPath, command, commandHandler, computeDedupKey, container, cronHandler, description, errorToPlainObject, evaluateQueryAst, extractChatMessageText, extractNumberFromWasenderMessageKey, getClientMap, getPgClient, handshakeMiddlewares, inject, injectable, isArray, isBoolean, isChatMessageEmpty, isDate, isIn, isModel, isNotEmpty, isNumber, isOptional, isPresent, isRecord, isRetryableError, isString, jwtGuard, jwtHandshakeGuard, kapso, kapsoChannelName, markdownToTelegramHtml, max, memExtension, middleware, min, mindset, mindsetModule, modelInfo, num, numArr, obj, onDelete, onGet, onPost, onPut, onSocketEvent, parseQueryMethodName, pendingMediaStartIndex, pgExtension, pgStorage, query, queryExtension, readJsonFromFile, repository, resolveConfigReferences, restController, run, runChatAdapters, runChatControllers, runCmdClient, runCommandHandlers, runCronHandlers, runRestControllers, runSocketControllers, safeJsonParse, scanProjectFiles, scoped, setupErrorHandlers, singleton, socket, socketChannelName, socketController, stopCommandHandlers, stopCronHandlers, str, strArr, stripAnsweredMedia, telegram, telegramChannelName, transaction, validateAndTransform, validateArray, validateIsBoolean, validateIsDate, validateIsIn, validateIsNotEmpty, validateIsNumber, validateIsPresent, validateIsRecord, validateIsString, validateMax, validateMin, validateModel, wasender, wasenderChannelName, withPgClient, withPgTransaction, writeJsonToFile };
package/dist/src/index.js CHANGED
@@ -80,7 +80,7 @@ export { ChatBotMetadataStore } from './feature/chat-bot/metadata/ChatBotMetadat
80
80
  export { pendingMediaStartIndex } from './feature/chat-bot/pendingMediaStartIndex.js';
81
81
  export { runChatAdapters } from './feature/chat-bot/runChatAdapters.js';
82
82
  export { safeJsonParse } from './feature/chat-bot/safeJsonParse.js';
83
- export { unconsumedMediaStartIndex } from './feature/chat-bot/unconsumedMediaStartIndex.js';
83
+ export { stripAnsweredMedia } from './feature/chat-bot/stripAnsweredMedia.js';
84
84
  export { chatController } from './feature/chat-controller/metadata/controller/@chatController.js';
85
85
  export { ControllerMetadataStore } from './feature/chat-controller/metadata/ControllerMetadataStore.js';
86
86
  export { ChatResolver } from './feature/chat-controller/ChatResolver.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wabot-dev/framework",
3
- "version": "0.9.25",
3
+ "version": "0.9.27",
4
4
  "description": "Framework for IA Chat Bots",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -1,27 +0,0 @@
1
- /**
2
- * Index of the first item whose media (images/documents) the model has not been
3
- * shown yet — i.e. right after the last model output, be it a bot message or a
4
- * function call.
5
- *
6
- * A file's binary should only be sent on the model's first exposure to it. Once
7
- * the model has produced anything in response to a human message (a function
8
- * call during a tool loop, or a reply), it has already analyzed that message's
9
- * files; re-sending the bytes on the next call would analyze the same file
10
- * again. This is the within-turn counterpart of {@link pendingMediaStartIndex}:
11
- * that one keeps a turn's media "active" for vision-model selection across the
12
- * whole tool loop, while this one stops re-uploading the bytes after the first
13
- * call.
14
- *
15
- * Returns 0 when the model has not produced any output yet, so a brand-new
16
- * message keeps its media.
17
- */
18
- function unconsumedMediaStartIndex(items) {
19
- for (let i = items.length - 1; i >= 0; i--) {
20
- const type = items[i].type;
21
- if (type === 'botMessage' || type === 'functionCall')
22
- return i + 1;
23
- }
24
- return 0;
25
- }
26
-
27
- export { unconsumedMediaStartIndex };