From c591af14b0c802099e7c41badb5dac58266c8c43 Mon Sep 17 00:00:00 2001 From: Acbox Liu Date: Sun, 22 Feb 2026 01:27:24 +0800 Subject: [PATCH] feat: bot inbox (#77) * feat: bot inbox * feat: unified header * fix: missing tool_call usage * feat: add group name in header --- agent/src/agent.ts | 287 +++++++------- agent/src/models.ts | 9 +- agent/src/modules/chat.ts | 6 +- agent/src/prompts/system.ts | 21 +- agent/src/types/action.ts | 1 + agent/src/types/agent.ts | 8 + cmd/agent/main.go | 14 +- db/migrations/0001_init.up.sql | 15 + db/migrations/0011_add_inbox.down.sql | 8 + db/migrations/0011_add_inbox.up.sql | 17 + db/queries/bots.sql | 12 +- db/queries/inbox.sql | 61 +++ db/queries/messages.sql | 24 ++ db/queries/settings.sql | 6 +- internal/bots/service.go | 47 ++- internal/channel/inbound/channel.go | 67 +++- internal/channel/inbound/channel_test.go | 7 +- internal/conversation/flow/resolver.go | 249 +++++++++--- .../flow/resolver_stream_order_test.go | 4 + .../conversation/flow/resolver_trim_test.go | 23 +- internal/conversation/types.go | 1 + internal/db/sqlc/bots.sql.go | 178 ++++++++- internal/db/sqlc/conversations.sql.go | 2 +- internal/db/sqlc/inbox.sql.go | 283 ++++++++++++++ internal/db/sqlc/messages.sql.go | 84 +++++ internal/db/sqlc/models.go | 11 + internal/db/sqlc/settings.sql.go | 26 +- internal/handlers/inbox.go | 268 +++++++++++++ internal/inbox/service.go | 284 ++++++++++++++ internal/mcp/providers/inbox/provider.go | 152 ++++++++ internal/mcp/providers/message/provider.go | 2 +- internal/message/service.go | 46 +++ internal/message/types.go | 1 + internal/settings/service.go | 17 +- internal/settings/types.go | 3 + packages/sdk/src/@pinia/colada.gen.ts | 109 +++++- packages/sdk/src/index.ts | 4 +- packages/sdk/src/sdk.gen.ts | 58 ++- packages/sdk/src/types.gen.ts | 266 +++++++++++++ spec/docs.go | 355 ++++++++++++++++++ spec/swagger.json | 355 ++++++++++++++++++ spec/swagger.yaml | 236 ++++++++++++ 42 files changed, 3367 insertions(+), 260 deletions(-) create mode 100644 db/migrations/0011_add_inbox.down.sql create mode 100644 db/migrations/0011_add_inbox.up.sql create mode 100644 db/queries/inbox.sql create mode 100644 internal/db/sqlc/inbox.sql.go create mode 100644 internal/handlers/inbox.go create mode 100644 internal/inbox/service.go create mode 100644 internal/mcp/providers/inbox/provider.go diff --git a/agent/src/agent.ts b/agent/src/agent.ts index 6977e54c..16339fca 100644 --- a/agent/src/agent.ts +++ b/agent/src/agent.ts @@ -32,6 +32,18 @@ import { getMCPTools } from './tools/mcp' import { getTools } from './tools' import { buildIdentityHeaders } from './utils/headers' +const buildStepUsages = ( + steps: { usage: LanguageModelUsage; response: { messages: unknown[] } }[], +): (LanguageModelUsage | null)[] => { + const usages: (LanguageModelUsage | null)[] = [] + for (const step of steps) { + for (let i = 0; i < step.response.messages.length; i++) { + usages.push(i === 0 ? step.usage : null) + } + } + return usages +} + export const buildNativeImageParts = (attachments: GatewayInputAttachment[]): ImagePart[] => { return attachments .filter((attachment) => @@ -59,6 +71,7 @@ export const createAgent = ( displayName: '', }, auth, + inbox = [], }: AgentParams, fetch: AuthFetcher, ) => { @@ -144,6 +157,7 @@ export const createAgent = ( identityContent, soulContent, toolsContent, + inbox, }) } @@ -203,7 +217,7 @@ export const createAgent = ( input.skills.forEach((skill) => enableSkill(skill)) const systemPrompt = await generateSystemPrompt() const { tools, close } = await getAgentTools() - const { response, reasoning, text, usage } = await generateText({ + const { response, reasoning, text, usage, steps } = await generateText({ model, messages, system: systemPrompt, @@ -218,6 +232,7 @@ export const createAgent = ( }, tools, }) + const stepUsages = buildStepUsages(steps) const { cleanedText, attachments: textAttachments } = extractAttachmentsFromText(text) const { messages: strippedMessages, attachments: messageAttachments } = @@ -231,6 +246,7 @@ export const createAgent = ( userPrompt, ...strippedMessages, ], + usages: [null, ...stepUsages] as (LanguageModelUsage | null)[], reasoning: reasoning.map((part) => part.text), usage, text: cleanedText, @@ -258,7 +274,7 @@ export const createAgent = ( } const messages = [...params.messages, userPrompt] const { tools, close } = await getAgentTools() - const { response, reasoning, text, usage } = await generateText({ + const { response, reasoning, text, usage, steps } = await generateText({ model, messages, system: generateSubagentSystemPrompt(), @@ -273,8 +289,10 @@ export const createAgent = ( }, tools, }) + const stepUsages = buildStepUsages(steps) return { messages: [userPrompt, ...response.messages], + usages: [null, ...stepUsages] as (LanguageModelUsage | null)[], reasoning: reasoning.map((part) => part.text), usage, text, @@ -299,7 +317,7 @@ export const createAgent = ( const messages = [...params.messages, scheduleMessage] params.skills.forEach((skill) => enableSkill(skill)) const { tools, close } = await getAgentTools() - const { response, reasoning, text, usage } = await generateText({ + const { response, reasoning, text, usage, steps } = await generateText({ model, messages, system: await generateSystemPrompt(), @@ -309,8 +327,10 @@ export const createAgent = ( }, tools, }) + const stepUsages = buildStepUsages(steps) return { messages: [scheduleMessage, ...response.messages], + usages: [null, ...stepUsages] as (LanguageModelUsage | null)[], reasoning: reasoning.map((part) => part.text), usage, text, @@ -350,148 +370,157 @@ export const createAgent = ( messages: ModelMessage[]; reasoning: string[]; usage: LanguageModelUsage | null; + usages: (LanguageModelUsage | null)[]; } = { messages: [], reasoning: [], usage: null, + usages: [], } const { tools, close } = await getAgentTools() - const { fullStream } = streamText({ - model, - messages, - system: systemPrompt, - stopWhen: stepCountIs(Infinity), - prepareStep: () => { - return { - system: systemPrompt, - } - }, - tools, - onFinish: async ({ usage, reasoning, response }) => { - await close() - result.usage = usage as never - result.reasoning = reasoning.map((part) => part.text) - result.messages = response.messages - }, - }) - yield { - type: 'agent_start', - input, - } - for await (const chunk of fullStream) { - if (chunk.type === 'error') { - throw new Error( - resolveStreamErrorMessage((chunk as { error?: unknown }).error), - ) + try { + const { fullStream } = streamText({ + model, + messages, + system: systemPrompt, + stopWhen: stepCountIs(Infinity), + prepareStep: () => { + return { + system: systemPrompt, + } + }, + tools, + onFinish: async ({ usage, reasoning, response, steps }) => { + await close() + result.usage = usage as never + result.reasoning = reasoning.map((part) => part.text) + result.messages = response.messages + result.usages = buildStepUsages(steps) + }, + }) + yield { + type: 'agent_start', + input, } - switch (chunk.type) { - case 'reasoning-start': - yield { - type: 'reasoning_start', - metadata: chunk, - } - break - case 'reasoning-delta': - yield { - type: 'reasoning_delta', - delta: chunk.text, - } - break - case 'reasoning-end': - yield { - type: 'reasoning_end', - metadata: chunk, - } - break - case 'text-start': - yield { - type: 'text_start', - } - break - case 'text-delta': { - const { visibleText, attachments } = attachmentsExtractor.push( - chunk.text, + for await (const chunk of fullStream) { + if (chunk.type === 'error') { + throw new Error( + resolveStreamErrorMessage((chunk as { error?: unknown }).error), ) - if (visibleText) { + } + switch (chunk.type) { + case 'reasoning-start': yield { - type: 'text_delta', - delta: visibleText, + type: 'reasoning_start', + metadata: chunk, } + break + case 'reasoning-delta': + yield { + type: 'reasoning_delta', + delta: chunk.text, + } + break + case 'reasoning-end': + yield { + type: 'reasoning_end', + metadata: chunk, + } + break + case 'text-start': + yield { + type: 'text_start', + } + break + case 'text-delta': { + const { visibleText, attachments } = attachmentsExtractor.push( + chunk.text, + ) + if (visibleText) { + yield { + type: 'text_delta', + delta: visibleText, + } + } + if (attachments.length) { + yield { + type: 'attachment_delta', + attachments, + } + } + break } - if (attachments.length) { + case 'text-end': { + // Flush any remaining buffered content before ending the text stream. + const remainder = attachmentsExtractor.flushRemainder() + if (remainder.visibleText) { + yield { + type: 'text_delta', + delta: remainder.visibleText, + } + } + if (remainder.attachments.length) { + yield { + type: 'attachment_delta', + attachments: remainder.attachments, + } + } + yield { + type: 'text_end', + metadata: chunk, + } + break + } + case 'tool-call': + yield { + type: 'tool_call_start', + toolName: chunk.toolName, + toolCallId: chunk.toolCallId, + input: chunk.input, + metadata: chunk, + } + break + case 'tool-result': + yield { + type: 'tool_call_end', + toolName: chunk.toolName, + toolCallId: chunk.toolCallId, + input: chunk.input, + result: chunk.output, + metadata: chunk, + } + break + case 'file': yield { type: 'attachment_delta', - attachments, + attachments: [ + { + type: 'image', + url: `data:${chunk.file.mediaType ?? 'image/png'};base64,${chunk.file.base64}`, + mime: chunk.file.mediaType ?? 'image/png', + }, + ], } - } - break } - case 'text-end': { - // Flush any remaining buffered content before ending the text stream. - const remainder = attachmentsExtractor.flushRemainder() - if (remainder.visibleText) { - yield { - type: 'text_delta', - delta: remainder.visibleText, - } - } - if (remainder.attachments.length) { - yield { - type: 'attachment_delta', - attachments: remainder.attachments, - } - } - yield { - type: 'text_end', - metadata: chunk, - } - break - } - case 'tool-call': - yield { - type: 'tool_call_start', - toolName: chunk.toolName, - toolCallId: chunk.toolCallId, - input: chunk.input, - metadata: chunk, - } - break - case 'tool-result': - yield { - type: 'tool_call_end', - toolName: chunk.toolName, - toolCallId: chunk.toolCallId, - input: chunk.input, - result: chunk.output, - metadata: chunk, - } - break - case 'file': - yield { - type: 'attachment_delta', - attachments: [ - { - type: 'image', - url: `data:${chunk.file.mediaType ?? 'image/png'};base64,${chunk.file.base64}`, - mime: chunk.file.mediaType ?? 'image/png', - }, - ], - } } - } - - const { messages: strippedMessages } = stripAttachmentsFromMessages( - result.messages, - ) - yield { - type: 'agent_end', - messages: [ - userPrompt, - ...strippedMessages, - ], - reasoning: result.reasoning, - usage: result.usage!, - skills: getEnabledSkills(), + + const { messages: strippedMessages } = stripAttachmentsFromMessages( + result.messages, + ) + yield { + type: 'agent_end', + messages: [ + userPrompt, + ...strippedMessages, + ], + usages: [null, ...result.usages], + reasoning: result.reasoning, + usage: result.usage!, + skills: getEnabledSkills(), + } + } catch (error) { + console.error(error) + throw error } } diff --git a/agent/src/models.ts b/agent/src/models.ts index 1407c0e8..fdee0da6 100644 --- a/agent/src/models.ts +++ b/agent/src/models.ts @@ -75,4 +75,11 @@ export const StdioMCPConnectionModel = z.object({ cwd: z.string().optional(), }) -export const MCPConnectionModel = z.union([HTTPMCPConnectionModel, SSEMCPConnectionModel, StdioMCPConnectionModel]) \ No newline at end of file +export const MCPConnectionModel = z.union([HTTPMCPConnectionModel, SSEMCPConnectionModel, StdioMCPConnectionModel]) + +export const InboxItemModel = z.object({ + id: z.string(), + source: z.string(), + content: z.record(z.string(), z.unknown()).default({}), + createdAt: z.string(), +}) \ No newline at end of file diff --git a/agent/src/modules/chat.ts b/agent/src/modules/chat.ts index 06a3a5e3..164a1672 100644 --- a/agent/src/modules/chat.ts +++ b/agent/src/modules/chat.ts @@ -4,7 +4,7 @@ import { createAgent } from '../agent' import { createAuthFetcher, getBaseUrl } from '../index' import { ModelConfig } from '../types' import { bearerMiddleware } from '../middlewares/bearer' -import { AgentSkillModel, AllowedActionModel, AttachmentModel, IdentityContextModel, MCPConnectionModel, ModelConfigModel, ScheduleModel } from '../models' +import { AgentSkillModel, AllowedActionModel, AttachmentModel, IdentityContextModel, InboxItemModel, MCPConnectionModel, ModelConfigModel, ScheduleModel } from '../models' import { allActions } from '../types' import { sseChunked } from '../utils/sse' @@ -20,6 +20,7 @@ const AgentModel = z.object({ identity: IdentityContextModel, attachments: z.array(AttachmentModel).optional().default([]), mcpConnections: z.array(MCPConnectionModel).optional().default([]), + inbox: z.array(InboxItemModel).optional().default([]), }) export const chatModule = new Elysia({ prefix: '/chat' }) @@ -40,6 +41,7 @@ export const chatModule = new Elysia({ prefix: '/chat' }) }, skills: body.usableSkills, mcpConnections: body.mcpConnections, + inbox: body.inbox, }, authFetcher) return ask({ query: body.query, @@ -69,6 +71,7 @@ export const chatModule = new Elysia({ prefix: '/chat' }) }, skills: body.usableSkills, mcpConnections: body.mcpConnections, + inbox: body.inbox, }, authFetcher) for await (const action of stream({ query: body.query, @@ -108,6 +111,7 @@ export const chatModule = new Elysia({ prefix: '/chat' }) }, skills: body.usableSkills, mcpConnections: body.mcpConnections, + inbox: body.inbox, }, authFetcher) return triggerSchedule({ schedule: body.schedule, diff --git a/agent/src/prompts/system.ts b/agent/src/prompts/system.ts index 052eee80..a6dea358 100644 --- a/agent/src/prompts/system.ts +++ b/agent/src/prompts/system.ts @@ -1,5 +1,5 @@ import { block, quote } from './utils' -import { AgentSkill } from '../types' +import { AgentSkill, InboxItem } from '../types' export interface SystemParams { date: Date @@ -14,6 +14,7 @@ export interface SystemParams { soulContent?: string toolsContent?: string attachments?: string[] + inbox?: InboxItem[] } export const skillPrompt = (skill: AgentSkill) => { @@ -25,6 +26,21 @@ ${skill.content} `.trim() } +const formatInbox = (items: InboxItem[]): string => { + if (!items || items.length === 0) return '' + return ` +## Inbox + +You have ${items.length} unread message(s) in your inbox. These are messages from group conversations where you were not directly mentioned, or notifications from external sources. Review them to stay informed about ongoing discussions. + + +${JSON.stringify(items)} + + +Use ${quote('search_inbox')} to find older messages by keyword. +`.trim() +} + export const system = ({ date, language, @@ -36,6 +52,7 @@ export const system = ({ identityContent, soulContent, toolsContent, + inbox = [], }: SystemParams) => { // ── Static section (stable prefix for LLM prompt caching) ────────── const staticHeaders = { @@ -120,6 +137,8 @@ ${toolsContent} ${enabledSkills.map(skill => skillPrompt(skill)).join('\n\n---\n\n')} +${formatInbox(inbox)} + ## Session Context --- diff --git a/agent/src/types/action.ts b/agent/src/types/action.ts index 2c123307..94dcbdf3 100644 --- a/agent/src/types/action.ts +++ b/agent/src/types/action.ts @@ -64,6 +64,7 @@ export interface AgentEndAction extends BaseAction { skills: string[] reasoning: string[] usage: LanguageModelUsage + usages: (LanguageModelUsage | null)[] } export type AgentAction = diff --git a/agent/src/types/agent.ts b/agent/src/types/agent.ts index 40c657f1..0ad28c16 100644 --- a/agent/src/types/agent.ts +++ b/agent/src/types/agent.ts @@ -30,6 +30,13 @@ export enum AgentAction { export const allActions = Object.values(AgentAction) +export interface InboxItem { + id: string + source: string + content: Record + createdAt: string +} + export interface AgentParams { model: ModelConfig language?: string @@ -41,6 +48,7 @@ export interface AgentParams { identity?: IdentityContext auth: AgentAuthContext skills?: AgentSkill[] + inbox?: InboxItem[] } export interface AgentInput { diff --git a/cmd/agent/main.go b/cmd/agent/main.go index f564ca26..06c7143d 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -40,12 +40,14 @@ import ( "github.com/memohai/memoh/internal/embeddings" "github.com/memohai/memoh/internal/handlers" "github.com/memohai/memoh/internal/healthcheck" + "github.com/memohai/memoh/internal/inbox" channelchecker "github.com/memohai/memoh/internal/healthcheck/checkers/channel" mcpchecker "github.com/memohai/memoh/internal/healthcheck/checkers/mcp" "github.com/memohai/memoh/internal/logger" "github.com/memohai/memoh/internal/mcp" mcpcontainer "github.com/memohai/memoh/internal/mcp/providers/container" mcpcontacts "github.com/memohai/memoh/internal/mcp/providers/contacts" + mcpinbox "github.com/memohai/memoh/internal/mcp/providers/inbox" mcpmemory "github.com/memohai/memoh/internal/mcp/providers/memory" mcpmessage "github.com/memohai/memoh/internal/mcp/providers/message" mcpschedule "github.com/memohai/memoh/internal/mcp/providers/schedule" @@ -160,6 +162,7 @@ func runServe() { identities.NewService, bind.NewService, event.NewHub, + inbox.NewService, // services requiring provide functions provideRouteService, @@ -201,6 +204,7 @@ func runServe() { provideServerHandler(handlers.NewChannelHandler), provideServerHandler(provideUsersHandler), provideServerHandler(handlers.NewMCPHandler), + provideServerHandler(handlers.NewInboxHandler), provideServerHandler(provideCLIHandler), provideServerHandler(provideWebHandler), @@ -373,10 +377,11 @@ func provideScheduleTriggerer(resolver *flow.Resolver) schedule.Triggerer { // conversation flow // --------------------------------------------------------------------------- -func provideChatResolver(log *slog.Logger, cfg config.Config, modelsService *models.Service, queries *dbsqlc.Queries, memoryService *memory.Service, chatService *conversation.Service, msgService *message.DBService, settingsService *settings.Service, mediaService *media.Service, containerdHandler *handlers.ContainerdHandler) *flow.Resolver { +func provideChatResolver(log *slog.Logger, cfg config.Config, modelsService *models.Service, queries *dbsqlc.Queries, memoryService *memory.Service, chatService *conversation.Service, msgService *message.DBService, settingsService *settings.Service, mediaService *media.Service, containerdHandler *handlers.ContainerdHandler, inboxService *inbox.Service) *flow.Resolver { resolver := flow.NewResolver(log, modelsService, queries, memoryService, chatService, msgService, settingsService, cfg.AgentGateway.BaseURL(), 120*time.Second) resolver.SetSkillLoader(&skillLoaderAdapter{handler: containerdHandler}) resolver.SetGatewayAssetLoader(&gatewayAssetLoaderAdapter{media: mediaService}) + resolver.SetInboxService(inboxService) return resolver } @@ -408,11 +413,13 @@ func provideChannelRouter( preauthService *preauth.Service, bindService *bind.Service, mediaService *media.Service, + inboxService *inbox.Service, rc *boot.RuntimeConfig, ) *inbound.ChannelInboundProcessor { processor := inbound.NewChannelInboundProcessor(log, registry, routeService, msgService, resolver, identityService, botService, policyService, preauthService, bindService, rc.JwtSecret, 5*time.Minute) processor.SetMediaService(mediaService) processor.SetStreamObserver(local.NewRouteHubBroadcaster(hub)) + processor.SetInboxService(inboxService) return processor } @@ -436,7 +443,7 @@ func provideContainerdHandler(log *slog.Logger, service ctr.Service, manager *mc return handlers.NewContainerdHandler(log, service, manager, cfg.MCP, cfg.Containerd.Namespace, botService, accountService, policyService, queries) } -func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service) *mcp.ToolGatewayService { +func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service, inboxService *inbox.Service) *mcp.ToolGatewayService { var assetResolver mcpmessage.AssetResolver if mediaService != nil { assetResolver = &mediaAssetResolverAdapter{media: mediaService} @@ -446,6 +453,7 @@ func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManag scheduleExec := mcpschedule.NewExecutor(log, scheduleService) memoryExec := mcpmemory.NewExecutor(log, memoryService, chatService, accountService) webExec := mcpweb.NewExecutor(log, settingsService, searchProviderService) + inboxExec := mcpinbox.NewExecutor(log, inboxService) execWorkDir := cfg.MCP.DataMount if strings.TrimSpace(execWorkDir) == "" { execWorkDir = config.DefaultDataMount @@ -457,7 +465,7 @@ func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManag svc := mcp.NewToolGatewayService( log, - []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec}, + []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec, inboxExec}, []mcp.ToolSource{fedSource}, ) containerdHandler.SetToolGatewayService(svc) diff --git a/db/migrations/0001_init.up.sql b/db/migrations/0001_init.up.sql index 421f812b..ff24ef37 100644 --- a/db/migrations/0001_init.up.sql +++ b/db/migrations/0001_init.up.sql @@ -121,6 +121,7 @@ CREATE TABLE IF NOT EXISTS bots ( max_context_tokens INTEGER NOT NULL DEFAULT 0, language TEXT NOT NULL DEFAULT 'auto', allow_guest BOOLEAN NOT NULL DEFAULT false, + max_inbox_items INTEGER NOT NULL DEFAULT 50, chat_model_id UUID REFERENCES models(id) ON DELETE SET NULL, memory_model_id UUID REFERENCES models(id) ON DELETE SET NULL, embedding_model_id UUID REFERENCES models(id) ON DELETE SET NULL, @@ -390,3 +391,17 @@ CREATE TABLE IF NOT EXISTS bot_history_message_assets ( CREATE INDEX IF NOT EXISTS idx_message_assets_message_id ON bot_history_message_assets(message_id); +-- bot_inbox: per-bot message inbox for non-mentioned group messages, emails, etc. +CREATE TABLE IF NOT EXISTS bot_inbox ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + source TEXT NOT NULL DEFAULT '', + content JSONB NOT NULL DEFAULT '{}'::jsonb, + is_read BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + read_at TIMESTAMPTZ +); + +CREATE INDEX IF NOT EXISTS idx_bot_inbox_bot_unread ON bot_inbox(bot_id, created_at DESC) WHERE is_read = FALSE; +CREATE INDEX IF NOT EXISTS idx_bot_inbox_bot_created ON bot_inbox(bot_id, created_at DESC); + diff --git a/db/migrations/0011_add_inbox.down.sql b/db/migrations/0011_add_inbox.down.sql new file mode 100644 index 00000000..5181f7f4 --- /dev/null +++ b/db/migrations/0011_add_inbox.down.sql @@ -0,0 +1,8 @@ +-- 0011_add_inbox (down) +-- Remove bot_inbox table and max_inbox_items column. + +DROP INDEX IF EXISTS idx_bot_inbox_bot_created; +DROP INDEX IF EXISTS idx_bot_inbox_bot_unread; +DROP TABLE IF EXISTS bot_inbox; + +ALTER TABLE bots DROP COLUMN IF EXISTS max_inbox_items; diff --git a/db/migrations/0011_add_inbox.up.sql b/db/migrations/0011_add_inbox.up.sql new file mode 100644 index 00000000..90720717 --- /dev/null +++ b/db/migrations/0011_add_inbox.up.sql @@ -0,0 +1,17 @@ +-- 0011_add_inbox +-- Add bot_inbox table and max_inbox_items setting to bots. + +ALTER TABLE bots ADD COLUMN IF NOT EXISTS max_inbox_items INTEGER NOT NULL DEFAULT 50; + +CREATE TABLE IF NOT EXISTS bot_inbox ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + source TEXT NOT NULL DEFAULT '', + content JSONB NOT NULL DEFAULT '{}'::jsonb, + is_read BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + read_at TIMESTAMPTZ +); + +CREATE INDEX IF NOT EXISTS idx_bot_inbox_bot_unread ON bot_inbox(bot_id, created_at DESC) WHERE is_read = FALSE; +CREATE INDEX IF NOT EXISTS idx_bot_inbox_bot_created ON bot_inbox(bot_id, created_at DESC); diff --git a/db/queries/bots.sql b/db/queries/bots.sql index fc1bbdb9..6f892703 100644 --- a/db/queries/bots.sql +++ b/db/queries/bots.sql @@ -1,21 +1,21 @@ -- name: CreateBot :one INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: GetBotByID :one -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE id = $1; -- name: ListBotsByOwner :many -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE owner_user_id = $1 ORDER BY created_at DESC; -- name: ListBotsByMember :many -SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at +SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.max_inbox_items, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at FROM bots b JOIN bot_members m ON m.bot_id = b.id WHERE m.user_id = $1 @@ -29,14 +29,14 @@ SET display_name = $2, metadata = $5, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: UpdateBotOwner :one UPDATE bots SET owner_user_id = $2, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: UpdateBotStatus :exec UPDATE bots diff --git a/db/queries/inbox.sql b/db/queries/inbox.sql new file mode 100644 index 00000000..2336f224 --- /dev/null +++ b/db/queries/inbox.sql @@ -0,0 +1,61 @@ +-- name: CreateInboxItem :one +INSERT INTO bot_inbox (bot_id, source, content) +VALUES (sqlc.arg(bot_id), sqlc.arg(source), sqlc.arg(content)) +RETURNING *; + +-- name: GetInboxItemByID :one +SELECT * FROM bot_inbox +WHERE id = sqlc.arg(id) + AND bot_id = sqlc.arg(bot_id); + +-- name: ListInboxItems :many +SELECT * FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id) + AND (sqlc.narg(is_read)::boolean IS NULL OR is_read = sqlc.narg(is_read)::boolean) + AND (sqlc.narg(source)::text IS NULL OR source = sqlc.narg(source)::text) +ORDER BY created_at DESC +LIMIT sqlc.arg(max_count) +OFFSET sqlc.arg(item_offset); + +-- name: ListUnreadInboxItems :many +SELECT * FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id) + AND is_read = FALSE +ORDER BY created_at ASC +LIMIT sqlc.arg(max_count); + +-- name: MarkInboxItemsRead :exec +UPDATE bot_inbox +SET is_read = TRUE, + read_at = now() +WHERE bot_id = sqlc.arg(bot_id) + AND id = ANY(sqlc.arg(ids)::uuid[]) + AND is_read = FALSE; + +-- name: SearchInboxItems :many +SELECT * FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id) + AND content::text ILIKE '%' || sqlc.arg(query) || '%' + AND (sqlc.narg(start_time)::timestamptz IS NULL OR created_at >= sqlc.narg(start_time)::timestamptz) + AND (sqlc.narg(end_time)::timestamptz IS NULL OR created_at <= sqlc.narg(end_time)::timestamptz) + AND (sqlc.narg(include_read)::boolean IS NULL OR sqlc.narg(include_read)::boolean = TRUE OR is_read = FALSE) +ORDER BY created_at DESC +LIMIT sqlc.arg(max_count); + +-- name: CountUnreadInboxItems :one +SELECT count(*) FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id) + AND is_read = FALSE; + +-- name: CountInboxItems :one +SELECT count(*) FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id); + +-- name: DeleteInboxItem :exec +DELETE FROM bot_inbox +WHERE id = sqlc.arg(id) + AND bot_id = sqlc.arg(bot_id); + +-- name: DeleteInboxItemsByBot :exec +DELETE FROM bot_inbox +WHERE bot_id = sqlc.arg(bot_id); diff --git a/db/queries/messages.sql b/db/queries/messages.sql index 84c70fec..8caada51 100644 --- a/db/queries/messages.sql +++ b/db/queries/messages.sql @@ -86,6 +86,30 @@ WHERE m.bot_id = sqlc.arg(bot_id) AND m.created_at >= sqlc.arg(created_at) ORDER BY m.created_at ASC; +-- name: ListActiveMessagesSince :many +SELECT + m.id, + m.bot_id, + m.route_id, + m.sender_channel_identity_id, + m.sender_account_user_id AS sender_user_id, + m.channel_type AS platform, + m.source_message_id AS external_message_id, + m.source_reply_to_message_id, + m.role, + m.content, + m.metadata, + m.usage, + m.created_at, + ci.display_name AS sender_display_name, + ci.avatar_url AS sender_avatar_url +FROM bot_history_messages m +LEFT JOIN channel_identities ci ON ci.id = m.sender_channel_identity_id +WHERE m.bot_id = sqlc.arg(bot_id) + AND m.created_at >= sqlc.arg(created_at) + AND (m.metadata->>'trigger_mode' IS NULL OR m.metadata->>'trigger_mode' != 'passive_sync') +ORDER BY m.created_at ASC; + -- name: ListMessagesBefore :many SELECT m.id, diff --git a/db/queries/settings.sql b/db/queries/settings.sql index 821c48eb..d170a1be 100644 --- a/db/queries/settings.sql +++ b/db/queries/settings.sql @@ -3,6 +3,7 @@ SELECT bots.id AS bot_id, bots.max_context_load_time, bots.max_context_tokens, + bots.max_inbox_items, bots.language, bots.allow_guest, chat_models.model_id AS chat_model_id, @@ -21,6 +22,7 @@ WITH updated AS ( UPDATE bots SET max_context_load_time = sqlc.arg(max_context_load_time), max_context_tokens = sqlc.arg(max_context_tokens), + max_inbox_items = sqlc.arg(max_inbox_items), language = sqlc.arg(language), allow_guest = sqlc.arg(allow_guest), chat_model_id = COALESCE(sqlc.narg(chat_model_id)::uuid, bots.chat_model_id), @@ -29,12 +31,13 @@ WITH updated AS ( search_provider_id = COALESCE(sqlc.narg(search_provider_id)::uuid, bots.search_provider_id), updated_at = now() WHERE bots.id = sqlc.arg(id) - RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id + RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.max_inbox_items, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id ) SELECT updated.id AS bot_id, updated.max_context_load_time, updated.max_context_tokens, + updated.max_inbox_items, updated.language, updated.allow_guest, chat_models.model_id AS chat_model_id, @@ -51,6 +54,7 @@ LEFT JOIN search_providers ON search_providers.id = updated.search_provider_id; UPDATE bots SET max_context_load_time = 1440, max_context_tokens = 0, + max_inbox_items = 50, language = 'auto', allow_guest = false, chat_model_id = NULL, diff --git a/internal/bots/service.go b/internal/bots/service.go index 257fbd31..8235c28d 100644 --- a/internal/bots/service.go +++ b/internal/bots/service.go @@ -142,11 +142,11 @@ func (s *Service) Create(ctx context.Context, ownerUserID string, req CreateBotR if err != nil { return Bot{}, err } - bot, err := toBot(row) + bot, err := toBot(asSQLCBot(row)) if err != nil { return Bot{}, err } - if err := s.attachCheckSummary(ctx, &bot, row); err != nil { + if err := s.attachCheckSummary(ctx, &bot, asSQLCBot(row)); err != nil { return Bot{}, err } s.enqueueCreateLifecycle(bot.ID) @@ -166,11 +166,11 @@ func (s *Service) Get(ctx context.Context, botID string) (Bot, error) { if err != nil { return Bot{}, err } - bot, err := toBot(row) + bot, err := toBot(asSQLCBot(row)) if err != nil { return Bot{}, err } - if err := s.attachCheckSummary(ctx, &bot, row); err != nil { + if err := s.attachCheckSummary(ctx, &bot, asSQLCBot(row)); err != nil { return Bot{}, err } return bot, nil @@ -191,11 +191,11 @@ func (s *Service) ListByOwner(ctx context.Context, ownerUserID string) ([]Bot, e } items := make([]Bot, 0, len(rows)) for _, row := range rows { - item, err := toBot(row) + item, err := toBot(asSQLCBot(row)) if err != nil { return nil, err } - if err := s.attachCheckSummary(ctx, &item, row); err != nil { + if err := s.attachCheckSummary(ctx, &item, asSQLCBot(row)); err != nil { return nil, err } items = append(items, item) @@ -218,11 +218,11 @@ func (s *Service) ListByMember(ctx context.Context, channelIdentityID string) ([ } items := make([]Bot, 0, len(rows)) for _, row := range rows { - item, err := toBot(row) + item, err := toBot(asSQLCBot(row)) if err != nil { return nil, err } - if err := s.attachCheckSummary(ctx, &item, row); err != nil { + if err := s.attachCheckSummary(ctx, &item, asSQLCBot(row)); err != nil { return nil, err } items = append(items, item) @@ -305,11 +305,11 @@ func (s *Service) Update(ctx context.Context, botID string, req UpdateBotRequest if err != nil { return Bot{}, err } - bot, err := toBot(row) + bot, err := toBot(asSQLCBot(row)) if err != nil { return Bot{}, err } - if err := s.attachCheckSummary(ctx, &bot, row); err != nil { + if err := s.attachCheckSummary(ctx, &bot, asSQLCBot(row)); err != nil { return Bot{}, err } return bot, nil @@ -338,11 +338,11 @@ func (s *Service) TransferOwner(ctx context.Context, botID string, ownerUserID s if err != nil { return Bot{}, err } - bot, err := toBot(row) + bot, err := toBot(asSQLCBot(row)) if err != nil { return Bot{}, err } - if err := s.attachCheckSummary(ctx, &bot, row); err != nil { + if err := s.attachCheckSummary(ctx, &bot, asSQLCBot(row)); err != nil { return Bot{}, err } return bot, nil @@ -387,7 +387,7 @@ func (s *Service) ListChecks(ctx context.Context, botID string) ([]BotCheck, err if err != nil { return nil, err } - return s.buildRuntimeChecks(ctx, row, true) + return s.buildRuntimeChecks(ctx, asSQLCBot(row), true) } func (s *Service) enqueueCreateLifecycle(botID string) { @@ -617,6 +617,27 @@ func normalizeMemberRole(raw string) (string, error) { } } +func asSQLCBot(v any) sqlc.Bot { + switch r := v.(type) { + case sqlc.Bot: + return r + case sqlc.CreateBotRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + case sqlc.GetBotByIDRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + case sqlc.ListBotsByOwnerRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + case sqlc.ListBotsByMemberRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + case sqlc.UpdateBotProfileRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + case sqlc.UpdateBotOwnerRow: + return sqlc.Bot{ID: r.ID, OwnerUserID: r.OwnerUserID, Type: r.Type, DisplayName: r.DisplayName, AvatarUrl: r.AvatarUrl, IsActive: r.IsActive, Status: r.Status, MaxContextLoadTime: r.MaxContextLoadTime, MaxContextTokens: r.MaxContextTokens, MaxInboxItems: r.MaxInboxItems, Language: r.Language, AllowGuest: r.AllowGuest, ChatModelID: r.ChatModelID, MemoryModelID: r.MemoryModelID, EmbeddingModelID: r.EmbeddingModelID, SearchProviderID: r.SearchProviderID, Metadata: r.Metadata, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt} + default: + return sqlc.Bot{} + } +} + func toBot(row sqlc.Bot) (Bot, error) { displayName := "" if row.DisplayName.Valid { diff --git a/internal/channel/inbound/channel.go b/internal/channel/inbound/channel.go index 433a7009..1e93ac00 100644 --- a/internal/channel/inbound/channel.go +++ b/internal/channel/inbound/channel.go @@ -19,6 +19,7 @@ import ( "github.com/memohai/memoh/internal/channel/route" "github.com/memohai/memoh/internal/conversation" "github.com/memohai/memoh/internal/conversation/flow" + "github.com/memohai/memoh/internal/inbox" "github.com/memohai/memoh/internal/media" messagepkg "github.com/memohai/memoh/internal/message" ) @@ -54,6 +55,7 @@ type ChannelInboundProcessor struct { routeResolver RouteResolver message messagepkg.Writer mediaService mediaIngestor + inboxService *inbox.Service registry *channel.Registry logger *slog.Logger jwtSecret string @@ -122,6 +124,15 @@ func (p *ChannelInboundProcessor) SetStreamObserver(observer channel.StreamObser p.observer = observer } +// SetInboxService configures the inbox service for storing non-mentioned +// group messages as inbox items. +func (p *ChannelInboundProcessor) SetInboxService(service *inbox.Service) { + if p == nil { + return + } + p.inboxService = service +} + // HandleInbound processes an inbound channel message through identity resolution and chat gateway. func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel.ChannelConfig, msg channel.InboundMessage, sender channel.StreamReplySender) error { if p.runner == nil { @@ -213,7 +224,7 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel slog.Int("attachments", len(attachments)), ) } - p.persistInboundUser(ctx, resolved.RouteID, identity, msg, text, attachments, "passive_sync") + p.createInboxItem(ctx, identity, msg, text, attachments, resolved.RouteID) return nil } userMessagePersisted := p.persistInboundUser(ctx, resolved.RouteID, identity, msg, text, attachments, "active_chat") @@ -356,6 +367,7 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel ChatToken: chatToken, ExternalMessageID: sourceMessageID, ConversationType: msg.Conversation.Type, + ConversationName: msg.Conversation.Name, Query: text, CurrentChannel: msg.Channel.String(), Channels: []string{msg.Channel.String()}, @@ -656,6 +668,7 @@ func (p *ChannelInboundProcessor) persistInboundUser( strings.TrimSpace(identity.DisplayName), msg.Channel.String(), strings.TrimSpace(msg.Conversation.Type), + strings.TrimSpace(msg.Conversation.Name), attachmentPaths, query, ) @@ -692,6 +705,58 @@ func (p *ChannelInboundProcessor) persistInboundUser( return true } +func (p *ChannelInboundProcessor) createInboxItem( + ctx context.Context, + ident InboundIdentity, + msg channel.InboundMessage, + text string, + attachments []conversation.ChatAttachment, + routeID string, +) { + if p.inboxService == nil { + return + } + botID := strings.TrimSpace(ident.BotID) + if botID == "" { + return + } + trimmedText := strings.TrimSpace(text) + if trimmedText == "" && len(attachments) == 0 { + return + } + displayName := strings.TrimSpace(ident.DisplayName) + if displayName == "" { + displayName = "Unknown" + } + + var attachmentPaths []string + for _, att := range attachments { + if p := strings.TrimSpace(att.Path); p != "" { + attachmentPaths = append(attachmentPaths, p) + } + } + + meta := flow.BuildUserMessageMeta( + strings.TrimSpace(ident.ChannelIdentityID), + displayName, + msg.Channel.String(), + strings.TrimSpace(msg.Conversation.Type), + strings.TrimSpace(msg.Conversation.Name), + attachmentPaths, + ) + content := meta.ToMap() + content["text"] = trimmedText + content["route_id"] = strings.TrimSpace(routeID) + + if _, err := p.inboxService.Create(ctx, inbox.CreateRequest{ + BotID: botID, + Source: msg.Channel.String(), + Content: content, + }); err != nil && p.logger != nil { + p.logger.Warn("create inbox item failed", slog.Any("error", err), slog.String("bot_id", botID)) + } +} + func buildChannelMessage(output conversation.AssistantOutput, capabilities channel.ChannelCapabilities) channel.Message { msg := channel.Message{} if strings.TrimSpace(output.Content) != "" { diff --git a/internal/channel/inbound/channel_test.go b/internal/channel/inbound/channel_test.go index 3e75386f..9ec09a65 100644 --- a/internal/channel/inbound/channel_test.go +++ b/internal/channel/inbound/channel_test.go @@ -850,11 +850,8 @@ func TestChannelInboundProcessorPersonalGroupOwnerWithoutMentionUsesPassivePersi if len(sender.sent) != 0 { t.Fatalf("owner group message without mention should not send reply") } - if len(chatSvc.persisted) != 1 { - t.Fatalf("expected one passive persisted message, got: %d", len(chatSvc.persisted)) - } - if got := chatSvc.persisted[0].Metadata["trigger_mode"]; got != "passive_sync" { - t.Fatalf("expected trigger_mode passive_sync, got: %v", got) + if len(chatSvc.persisted) != 0 { + t.Fatalf("non-mentioned message should not persist to messages (only inbox), got: %d", len(chatSvc.persisted)) } } diff --git a/internal/conversation/flow/resolver.go b/internal/conversation/flow/resolver.go index 77de0eb2..8ec07486 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -21,6 +21,7 @@ import ( "github.com/memohai/memoh/internal/conversation" "github.com/memohai/memoh/internal/db" "github.com/memohai/memoh/internal/db/sqlc" + "github.com/memohai/memoh/internal/inbox" "github.com/memohai/memoh/internal/memory" messagepkg "github.com/memohai/memoh/internal/message" "github.com/memohai/memoh/internal/models" @@ -75,6 +76,7 @@ type Resolver struct { conversationSvc ConversationSettingsReader messageService messagepkg.Service settingsService *settings.Service + inboxService *inbox.Service skillLoader SkillLoader assetLoader gatewayAssetLoader gatewayBaseURL string @@ -129,6 +131,12 @@ func (r *Resolver) SetGatewayAssetLoader(loader gatewayAssetLoader) { r.assetLoader = loader } +// SetInboxService configures inbox support for injecting unread items into the +// system prompt and marking them as read after a response. +func (r *Resolver) SetInboxService(service *inbox.Service) { + r.inboxService = service +} + // --- gateway payload --- type gatewayModelConfig struct { @@ -156,6 +164,13 @@ type gatewaySkill struct { Metadata map[string]any `json:"metadata,omitempty"` } +type gatewayInboxItem struct { + ID string `json:"id"` + Source string `json:"source"` + Content map[string]any `json:"content"` + CreatedAt string `json:"createdAt"` +} + type gatewayRequest struct { Model gatewayModelConfig `json:"model"` ActiveContextTime int `json:"activeContextTime"` @@ -168,12 +183,14 @@ type gatewayRequest struct { Query string `json:"query"` Identity gatewayIdentity `json:"identity"` Attachments []any `json:"attachments"` + Inbox []gatewayInboxItem `json:"inbox,omitempty"` } type gatewayResponse struct { Messages []conversation.ModelMessage `json:"messages"` Skills []string `json:"skills"` Usage json.RawMessage `json:"usage,omitempty"` + Usages []json.RawMessage `json:"usages,omitempty"` } type gatewayUsage struct { @@ -219,9 +236,10 @@ func (t triggerScheduleRequest) MarshalJSON() ([]byte, error) { // --- resolved context (shared by Chat / StreamChat / TriggerSchedule) --- type resolvedContext struct { - payload gatewayRequest - model models.GetResponse - provider sqlc.LlmProvider + payload gatewayRequest + model models.GetResponse + provider sqlc.LlmProvider + inboxItemIDs []string } func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (resolvedContext, error) { @@ -285,6 +303,13 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r historyBudget = 0 } + r.logger.Debug("context token budget", + slog.Int("max_tokens", maxTokens), + slog.Int("overhead", overhead), + slog.Int("system_prompt_reserve", systemPromptReserve), + slog.Int("history_budget", historyBudget), + ) + var messages []conversation.ModelMessage if !skipHistory && r.conversationSvc != nil { loaded, loadErr := r.loadMessages(ctx, req.ChatID, maxCtx) @@ -293,6 +318,12 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r } loaded = pruneHistoryForGateway(loaded) messages = trimMessagesByTokens(loaded, historyBudget) + r.logger.Debug("context trim result", + slog.Int("loaded_messages", len(loaded)), + slog.Int("kept_messages", len(messages)), + slog.Int("trimmed_messages", len(loaded)-len(messages)), + slog.Int("history_budget", historyBudget), + ) } if memoryMsg != nil { messages = append(messages, *memoryMsg) @@ -322,6 +353,31 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r usableSkills = []gatewaySkill{} } + var inboxGatewayItems []gatewayInboxItem + var inboxItemIDs []string + if r.inboxService != nil { + maxInbox := botSettings.MaxInboxItems + if maxInbox <= 0 { + maxInbox = settings.DefaultMaxInboxItems + } + items, err := r.inboxService.ListUnread(ctx, req.BotID, maxInbox) + if err != nil { + r.logger.Warn("failed to load inbox items", slog.String("bot_id", req.BotID), slog.Any("error", err)) + } else if len(items) > 0 { + inboxGatewayItems = make([]gatewayInboxItem, 0, len(items)) + inboxItemIDs = make([]string, 0, len(items)) + for _, item := range items { + inboxGatewayItems = append(inboxGatewayItems, gatewayInboxItem{ + ID: item.ID, + Source: item.Source, + Content: item.Content, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + }) + inboxItemIDs = append(inboxItemIDs, item.ID) + } + } + } + attachments := r.routeAndMergeAttachments(ctx, chatModel, req) displayName := r.resolveDisplayName(ctx, req) @@ -330,6 +386,7 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r displayName, req.CurrentChannel, strings.TrimSpace(req.ConversationType), + strings.TrimSpace(req.ConversationName), extractFileRefPaths(attachments), req.Query, ) @@ -360,9 +417,10 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r SessionToken: req.ChatToken, }, Attachments: attachments, + Inbox: inboxGatewayItems, } - return resolvedContext{payload: payload, model: chatModel, provider: provider}, nil + return resolvedContext{payload: payload, model: chatModel, provider: provider, inboxItemIDs: inboxItemIDs}, nil } // --- Chat --- @@ -378,9 +436,10 @@ func (r *Resolver) Chat(ctx context.Context, req conversation.ChatRequest) (conv if err != nil { return conversation.ChatResponse{}, err } - if err := r.storeRound(ctx, req, resp.Messages, resp.Usage); err != nil { + if err := r.storeRound(ctx, req, resp.Messages, resp.Usage, resp.Usages); err != nil { return conversation.ChatResponse{}, err } + r.markInboxRead(ctx, req.BotID, rc.inboxItemIDs) return conversation.ChatResponse{ Messages: resp.Messages, Skills: resp.Skills, @@ -432,7 +491,7 @@ func (r *Resolver) TriggerSchedule(ctx context.Context, botID string, payload sc if err != nil { return err } - return r.storeRound(ctx, req, resp.Messages, resp.Usage) + return r.storeRound(ctx, req, resp.Messages, resp.Usage, resp.Usages) } // --- StreamChat --- @@ -481,7 +540,9 @@ func (r *Resolver) StreamChat(ctx context.Context, req conversation.ChatRequest) slog.Any("error", err), ) errCh <- err + return } + r.markInboxRead(ctx, streamReq.BotID, rc.inboxItemIDs) }() return chunkCh, errCh } @@ -679,15 +740,16 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ Data json.RawMessage `json:"data"` Messages []conversation.ModelMessage `json:"messages"` Usage json.RawMessage `json:"usage,omitempty"` + Usages []json.RawMessage `json:"usages,omitempty"` } if err := json.Unmarshal(data, &envelope); err == nil { if (envelope.Type == "agent_end" || envelope.Type == "done") && len(envelope.Messages) > 0 { - return true, r.storeRound(ctx, req, envelope.Messages, envelope.Usage) + return true, r.storeRound(ctx, req, envelope.Messages, envelope.Usage, envelope.Usages) } if envelope.Type == "done" && len(envelope.Data) > 0 { var resp gatewayResponse if err := json.Unmarshal(envelope.Data, &resp); err == nil && len(resp.Messages) > 0 { - return true, r.storeRound(ctx, req, resp.Messages, resp.Usage) + return true, r.storeRound(ctx, req, resp.Messages, resp.Usage, resp.Usages) } } } @@ -695,7 +757,7 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ // fallback: data: {messages: [...]} var resp gatewayResponse if err := json.Unmarshal(data, &resp); err == nil && len(resp.Messages) > 0 { - return true, r.storeRound(ctx, req, resp.Messages, resp.Usage) + return true, r.storeRound(ctx, req, resp.Messages, resp.Usage, resp.Usages) } return false, nil } @@ -959,8 +1021,9 @@ func (r *Resolver) resolveContainerID(ctx context.Context, botID, explicit strin // --- message loading --- type messageWithUsage struct { - Message conversation.ModelMessage - UsageInputTokens *int + Message conversation.ModelMessage + UsageInputTokens *int + UsageOutputTokens *int } func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMinutes int) ([]messageWithUsage, error) { @@ -968,7 +1031,7 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi return nil, nil } since := time.Now().UTC().Add(-time.Duration(maxContextMinutes) * time.Minute) - msgs, err := r.messageService.ListSince(ctx, chatID, since) + msgs, err := r.messageService.ListActiveSince(ctx, chatID, since) if err != nil { return nil, err } @@ -983,13 +1046,15 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi mm.Role = m.Role } var inputTokens *int + var outputTokens *int if len(m.Usage) > 0 { var u gatewayUsage if json.Unmarshal(m.Usage, &u) == nil { inputTokens = u.InputTokens + outputTokens = u.OutputTokens } } - result = append(result, messageWithUsage{Message: mm, UsageInputTokens: inputTokens}) + result = append(result, messageWithUsage{Message: mm, UsageInputTokens: inputTokens, UsageOutputTokens: outputTokens}) } return result, nil } @@ -1012,44 +1077,21 @@ func trimMessagesByTokens(messages []messageWithUsage, maxTokens int) []conversa return result } - // Scan backwards. When a message with UsageInputTokens is found, that value - // represents the cumulative input tokens for all messages up to and including - // that message. Messages after it are estimated with chars/4. + // Scan from newest to oldest, accumulating per-message outputTokens from + // stored usage data. Messages without usage (user / tool) are included for + // free — the outputTokens of surrounding assistant turns already account + // for the context they consumed. totalTokens := 0 - anchorFound := false cutoff := 0 - - tailEstimate := 0 + messagesWithUsage := 0 for i := len(messages) - 1; i >= 0; i-- { - if !anchorFound && messages[i].UsageInputTokens != nil { - anchorFound = true - totalTokens = *messages[i].UsageInputTokens + tailEstimate - if totalTokens > maxTokens { - cutoff = i + 1 - break - } - continue + if messages[i].UsageOutputTokens != nil { + totalTokens += *messages[i].UsageOutputTokens + messagesWithUsage++ } - est := estimateMessageTokens(messages[i].Message) - if anchorFound { - totalTokens += est - if totalTokens > maxTokens { - cutoff = i + 1 - break - } - } else { - tailEstimate += est - } - } - - if !anchorFound { - totalTokens = 0 - for i := len(messages) - 1; i >= 0; i-- { - totalTokens += estimateMessageTokens(messages[i].Message) - if totalTokens > maxTokens { - cutoff = i + 1 - break - } + if totalTokens > maxTokens { + cutoff = i + 1 + break } } @@ -1060,6 +1102,15 @@ func trimMessagesByTokens(messages []messageWithUsage, maxTokens int) []conversa cutoff++ } + slog.Debug("trimMessagesByTokens", + slog.Int("total_messages", len(messages)), + slog.Int("messages_with_usage", messagesWithUsage), + slog.Int("accumulated_output_tokens", totalTokens), + slog.Int("max_tokens", maxTokens), + slog.Int("cutoff_index", cutoff), + slog.Int("kept_messages", len(messages)-cutoff), + ) + result := make([]conversation.ModelMessage, 0, len(messages)-cutoff) for _, m := range messages[cutoff:] { result = append(result, m.Message) @@ -1187,24 +1238,28 @@ func (r *Resolver) persistUserMessage(ctx context.Context, req conversation.Chat return err } -func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage) error { +func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage, usages []json.RawMessage) error { fullRound := make([]conversation.ModelMessage, 0, len(messages)) - for _, m := range messages { + roundUsages := make([]json.RawMessage, 0, len(usages)) + for i, m := range messages { if req.UserMessagePersisted && m.Role == "user" && strings.TrimSpace(m.TextContent()) == strings.TrimSpace(req.Query) { continue } fullRound = append(fullRound, m) + if i < len(usages) { + roundUsages = append(roundUsages, usages[i]) + } } if len(fullRound) == 0 { return nil } - r.storeMessages(ctx, req, fullRound, usage) + r.storeMessages(ctx, req, fullRound, usage, roundUsages) go r.storeMemory(context.WithoutCancel(ctx), req.BotID, fullRound) return nil } -func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage) { +func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage, usages []json.RawMessage) { if r.messageService == nil { return } @@ -1254,7 +1309,9 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque assets = append(assets, outboundAssets...) } var msgUsage json.RawMessage - if i == len(messages)-1 && len(usage) > 0 { + if i < len(usages) && len(usages[i]) > 0 && !isJSONNull(usages[i]) { + msgUsage = usages[i] + } else if i == len(messages)-1 && len(usage) > 0 { msgUsage = usage } if _, err := r.messageService.Persist(ctx, messagepkg.PersistInput{ @@ -1276,6 +1333,10 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque } } +func isJSONNull(data json.RawMessage) bool { + return len(data) == 0 || bytes.Equal(bytes.TrimSpace(data), []byte("null")) +} + // outboundAssetRefsToMessageRefs converts outbound asset refs from the streaming // collector into message-level asset refs for persistence. func outboundAssetRefsToMessageRefs(refs []conversation.OutboundAssetRef) []messagepkg.AssetRef { @@ -1569,6 +1630,17 @@ func (r *Resolver) listCandidates(ctx context.Context, providerFilter string) ([ return filtered, nil } +// --- inbox --- + +func (r *Resolver) markInboxRead(ctx context.Context, botID string, ids []string) { + if r.inboxService == nil || len(ids) == 0 { + return + } + if err := r.inboxService.MarkRead(ctx, botID, ids); err != nil { + r.logger.Warn("failed to mark inbox items as read", slog.String("bot_id", botID), slog.Any("error", err)) + } +} + // --- settings --- func (r *Resolver) loadBotSettings(ctx context.Context, botID string) (settings.Settings, error) { @@ -1696,21 +1768,78 @@ func parseResolverUUID(id string) (pgtype.UUID, error) { return db.ParseUUID(id) } +// UserMessageMeta holds the structured metadata attached to every user +// message. It is the single source of truth shared by the YAML header +// (sent to the LLM) and the inbox content JSONB. +type UserMessageMeta struct { + ChannelIdentityID string `json:"channel-identity-id"` + DisplayName string `json:"display-name"` + Channel string `json:"channel"` + ConversationType string `json:"conversation-type"` + ConversationName string `json:"conversation-name,omitempty"` + Time string `json:"time"` + AttachmentPaths []string `json:"attachments"` +} + +// BuildUserMessageMeta constructs a UserMessageMeta from the inbound +// parameters. Both FormatUserHeader and inbox content use this. +func BuildUserMessageMeta(channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string) UserMessageMeta { + if attachmentPaths == nil { + attachmentPaths = []string{} + } + return UserMessageMeta{ + ChannelIdentityID: channelIdentityID, + DisplayName: displayName, + Channel: channel, + ConversationType: conversationType, + ConversationName: conversationName, + Time: time.Now().UTC().Format(time.RFC3339), + AttachmentPaths: attachmentPaths, + } +} + +// ToMap returns the metadata as a map with the same keys used in the YAML +// header, suitable for storing as inbox content JSONB. +func (m UserMessageMeta) ToMap() map[string]any { + result := map[string]any{ + "channel-identity-id": m.ChannelIdentityID, + "display-name": m.DisplayName, + "channel": m.Channel, + "conversation-type": m.ConversationType, + "time": m.Time, + "attachments": m.AttachmentPaths, + } + if m.ConversationName != "" { + result["conversation-name"] = m.ConversationName + } + return result +} + // FormatUserHeader wraps a user query with YAML front-matter metadata so // the LLM sees structured context (sender, channel, time, attachments) // alongside the raw message. This must be the single source of truth for // user-message formatting — the agent gateway must NOT add its own header. -func FormatUserHeader(channelIdentityID, displayName, channel, conversationType string, attachmentPaths []string, query string) string { +func FormatUserHeader(channelIdentityID, displayName, channel, conversationType, conversationName string, attachmentPaths []string, query string) string { + meta := BuildUserMessageMeta(channelIdentityID, displayName, channel, conversationType, conversationName, attachmentPaths) + return FormatUserHeaderFromMeta(meta, query) +} + +// FormatUserHeaderFromMeta formats a pre-built UserMessageMeta into the +// YAML front-matter string sent to the LLM. +func FormatUserHeaderFromMeta(meta UserMessageMeta, query string) string { var sb strings.Builder sb.WriteString("---\n") - writeYAMLString(&sb, "channel-identity-id", channelIdentityID) - writeYAMLString(&sb, "display-name", displayName) - writeYAMLString(&sb, "channel", channel) - writeYAMLString(&sb, "conversation-type", conversationType) - writeYAMLString(&sb, "time", time.Now().UTC().Format(time.RFC3339)) - if len(attachmentPaths) > 0 { + writeYAMLString(&sb, "channel-identity-id", meta.ChannelIdentityID) + writeYAMLString(&sb, "display-name", meta.DisplayName) + writeYAMLString(&sb, "channel", meta.Channel) + writeYAMLString(&sb, "conversation-type", meta.ConversationType) + if meta.ConversationName != "" { + writeYAMLString(&sb, "conversation-name", meta.ConversationName) + } + writeYAMLString(&sb, "time", meta.Time) + if len(meta.AttachmentPaths) > 0 { sb.WriteString("attachments:\n") - for _, p := range attachmentPaths { + for _, p := range meta.AttachmentPaths { sb.WriteString(" - ") sb.WriteString(p) sb.WriteByte('\n') diff --git a/internal/conversation/flow/resolver_stream_order_test.go b/internal/conversation/flow/resolver_stream_order_test.go index 6e7646e5..d7d82181 100644 --- a/internal/conversation/flow/resolver_stream_order_test.go +++ b/internal/conversation/flow/resolver_stream_order_test.go @@ -37,6 +37,10 @@ func (s *blockingMessageService) ListSince(ctx context.Context, botID string, si return nil, nil } +func (s *blockingMessageService) ListActiveSince(ctx context.Context, botID string, since time.Time) ([]messagepkg.Message, error) { + return nil, nil +} + func (s *blockingMessageService) ListLatest(ctx context.Context, botID string, limit int32) ([]messagepkg.Message, error) { return nil, nil } diff --git a/internal/conversation/flow/resolver_trim_test.go b/internal/conversation/flow/resolver_trim_test.go index e9a7aaa9..266298cc 100644 --- a/internal/conversation/flow/resolver_trim_test.go +++ b/internal/conversation/flow/resolver_trim_test.go @@ -6,6 +6,8 @@ import ( "github.com/memohai/memoh/internal/conversation" ) +func intPtr(v int) *int { return &v } + func TestTrimMessagesByTokens_DropsLeadingOrphanTool(t *testing.T) { t.Parallel() @@ -30,6 +32,7 @@ func TestTrimMessagesByTokens_DropsLeadingOrphanTool(t *testing.T) { }, }, }, + UsageOutputTokens: intPtr(50), }, { Message: conversation.ModelMessage{ @@ -43,10 +46,13 @@ func TestTrimMessagesByTokens_DropsLeadingOrphanTool(t *testing.T) { Role: "assistant", Content: conversation.NewTextContent("done"), }, + UsageOutputTokens: intPtr(60), }, } - trimmed := trimMessagesByTokens(messages, 2) + // Budget 70: assistant(60) fits, adding assistant-tool-call(50) exceeds → + // cutoff lands on the tool message which must be skipped. + trimmed := trimMessagesByTokens(messages, 70) if len(trimmed) == 0 { t.Fatal("expected non-empty trimmed messages") } @@ -73,6 +79,7 @@ func TestTrimMessagesByTokens_KeepsToolWhenPaired(t *testing.T) { }, }, }, + UsageOutputTokens: intPtr(10), }, { Message: conversation.ModelMessage{ @@ -91,3 +98,17 @@ func TestTrimMessagesByTokens_KeepsToolWhenPaired(t *testing.T) { t.Fatalf("unexpected role order: %q -> %q", trimmed[0].Role, trimmed[1].Role) } } + +func TestTrimMessagesByTokens_NoUsage_KeepsAll(t *testing.T) { + t.Parallel() + + messages := []messageWithUsage{ + {Message: conversation.ModelMessage{Role: "user", Content: conversation.NewTextContent("hello")}}, + {Message: conversation.ModelMessage{Role: "assistant", Content: conversation.NewTextContent("hi")}}, + } + + trimmed := trimMessagesByTokens(messages, 10) + if len(trimmed) != 2 { + t.Fatalf("messages without outputTokens should all be kept, got %d", len(trimmed)) + } +} diff --git a/internal/conversation/types.go b/internal/conversation/types.go index 65d6cd07..1772a934 100644 --- a/internal/conversation/types.go +++ b/internal/conversation/types.go @@ -228,6 +228,7 @@ type ChatRequest struct { ChatToken string `json:"-"` ExternalMessageID string `json:"-"` ConversationType string `json:"-"` + ConversationName string `json:"-"` UserMessagePersisted bool `json:"-"` // OutboundAssetCollector returns asset refs accumulated during outbound streaming. diff --git a/internal/db/sqlc/bots.sql.go b/internal/db/sqlc/bots.sql.go index 2e958ac8..9133e155 100644 --- a/internal/db/sqlc/bots.sql.go +++ b/internal/db/sqlc/bots.sql.go @@ -14,7 +14,7 @@ import ( const createBot = `-- name: CreateBot :one INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type CreateBotParams struct { @@ -27,7 +27,29 @@ type CreateBotParams struct { Status string `json:"status"` } -func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (Bot, error) { +type CreateBotRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (CreateBotRow, error) { row := q.db.QueryRow(ctx, createBot, arg.OwnerUserID, arg.Type, @@ -37,7 +59,7 @@ func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (Bot, erro arg.Metadata, arg.Status, ) - var i Bot + var i CreateBotRow err := row.Scan( &i.ID, &i.OwnerUserID, @@ -48,6 +70,7 @@ func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (Bot, erro &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -85,14 +108,36 @@ func (q *Queries) DeleteBotMember(ctx context.Context, arg DeleteBotMemberParams } const getBotByID = `-- name: GetBotByID :one -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE id = $1 ` -func (q *Queries) GetBotByID(ctx context.Context, id pgtype.UUID) (Bot, error) { +type GetBotByIDRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) GetBotByID(ctx context.Context, id pgtype.UUID) (GetBotByIDRow, error) { row := q.db.QueryRow(ctx, getBotByID, id) - var i Bot + var i GetBotByIDRow err := row.Scan( &i.ID, &i.OwnerUserID, @@ -103,6 +148,7 @@ func (q *Queries) GetBotByID(ctx context.Context, id pgtype.UUID) (Bot, error) { &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -173,22 +219,44 @@ func (q *Queries) ListBotMembers(ctx context.Context, botID pgtype.UUID) ([]BotM } const listBotsByMember = `-- name: ListBotsByMember :many -SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at +SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.max_inbox_items, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at FROM bots b JOIN bot_members m ON m.bot_id = b.id WHERE m.user_id = $1 ORDER BY b.created_at DESC ` -func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]Bot, error) { +type ListBotsByMemberRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]ListBotsByMemberRow, error) { rows, err := q.db.Query(ctx, listBotsByMember, userID) if err != nil { return nil, err } defer rows.Close() - var items []Bot + var items []ListBotsByMemberRow for rows.Next() { - var i Bot + var i ListBotsByMemberRow if err := rows.Scan( &i.ID, &i.OwnerUserID, @@ -199,6 +267,7 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -220,21 +289,43 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B } const listBotsByOwner = `-- name: ListBotsByOwner :many -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE owner_user_id = $1 ORDER BY created_at DESC ` -func (q *Queries) ListBotsByOwner(ctx context.Context, ownerUserID pgtype.UUID) ([]Bot, error) { +type ListBotsByOwnerRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) ListBotsByOwner(ctx context.Context, ownerUserID pgtype.UUID) ([]ListBotsByOwnerRow, error) { rows, err := q.db.Query(ctx, listBotsByOwner, ownerUserID) if err != nil { return nil, err } defer rows.Close() - var items []Bot + var items []ListBotsByOwnerRow for rows.Next() { - var i Bot + var i ListBotsByOwnerRow if err := rows.Scan( &i.ID, &i.OwnerUserID, @@ -245,6 +336,7 @@ func (q *Queries) ListBotsByOwner(ctx context.Context, ownerUserID pgtype.UUID) &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -270,7 +362,7 @@ UPDATE bots SET owner_user_id = $2, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type UpdateBotOwnerParams struct { @@ -278,9 +370,31 @@ type UpdateBotOwnerParams struct { OwnerUserID pgtype.UUID `json:"owner_user_id"` } -func (q *Queries) UpdateBotOwner(ctx context.Context, arg UpdateBotOwnerParams) (Bot, error) { +type UpdateBotOwnerRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) UpdateBotOwner(ctx context.Context, arg UpdateBotOwnerParams) (UpdateBotOwnerRow, error) { row := q.db.QueryRow(ctx, updateBotOwner, arg.ID, arg.OwnerUserID) - var i Bot + var i UpdateBotOwnerRow err := row.Scan( &i.ID, &i.OwnerUserID, @@ -291,6 +405,7 @@ func (q *Queries) UpdateBotOwner(ctx context.Context, arg UpdateBotOwnerParams) &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -312,7 +427,7 @@ SET display_name = $2, metadata = $5, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, max_inbox_items, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type UpdateBotProfileParams struct { @@ -323,7 +438,29 @@ type UpdateBotProfileParams struct { Metadata []byte `json:"metadata"` } -func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfileParams) (Bot, error) { +type UpdateBotProfileRow struct { + ID pgtype.UUID `json:"id"` + OwnerUserID pgtype.UUID `json:"owner_user_id"` + Type string `json:"type"` + DisplayName pgtype.Text `json:"display_name"` + AvatarUrl pgtype.Text `json:"avatar_url"` + IsActive bool `json:"is_active"` + Status string `json:"status"` + MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` + Language string `json:"language"` + AllowGuest bool `json:"allow_guest"` + ChatModelID pgtype.UUID `json:"chat_model_id"` + MemoryModelID pgtype.UUID `json:"memory_model_id"` + EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` + SearchProviderID pgtype.UUID `json:"search_provider_id"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfileParams) (UpdateBotProfileRow, error) { row := q.db.QueryRow(ctx, updateBotProfile, arg.ID, arg.DisplayName, @@ -331,7 +468,7 @@ func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfilePara arg.IsActive, arg.Metadata, ) - var i Bot + var i UpdateBotProfileRow err := row.Scan( &i.ID, &i.OwnerUserID, @@ -342,6 +479,7 @@ func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfilePara &i.Status, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, diff --git a/internal/db/sqlc/conversations.sql.go b/internal/db/sqlc/conversations.sql.go index cbc6eaf1..434ca46f 100644 --- a/internal/db/sqlc/conversations.sql.go +++ b/internal/db/sqlc/conversations.sql.go @@ -590,7 +590,7 @@ WITH updated AS ( SET display_name = $1, updated_at = now() WHERE bots.id = $2 - RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at + RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, max_inbox_items, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ) SELECT updated.id AS id, diff --git a/internal/db/sqlc/inbox.sql.go b/internal/db/sqlc/inbox.sql.go new file mode 100644 index 00000000..fac83287 --- /dev/null +++ b/internal/db/sqlc/inbox.sql.go @@ -0,0 +1,283 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: inbox.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const countInboxItems = `-- name: CountInboxItems :one +SELECT count(*) FROM bot_inbox +WHERE bot_id = $1 +` + +func (q *Queries) CountInboxItems(ctx context.Context, botID pgtype.UUID) (int64, error) { + row := q.db.QueryRow(ctx, countInboxItems, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countUnreadInboxItems = `-- name: CountUnreadInboxItems :one +SELECT count(*) FROM bot_inbox +WHERE bot_id = $1 + AND is_read = FALSE +` + +func (q *Queries) CountUnreadInboxItems(ctx context.Context, botID pgtype.UUID) (int64, error) { + row := q.db.QueryRow(ctx, countUnreadInboxItems, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const createInboxItem = `-- name: CreateInboxItem :one +INSERT INTO bot_inbox (bot_id, source, content) +VALUES ($1, $2, $3) +RETURNING id, bot_id, source, content, is_read, created_at, read_at +` + +type CreateInboxItemParams struct { + BotID pgtype.UUID `json:"bot_id"` + Source string `json:"source"` + Content []byte `json:"content"` +} + +func (q *Queries) CreateInboxItem(ctx context.Context, arg CreateInboxItemParams) (BotInbox, error) { + row := q.db.QueryRow(ctx, createInboxItem, arg.BotID, arg.Source, arg.Content) + var i BotInbox + err := row.Scan( + &i.ID, + &i.BotID, + &i.Source, + &i.Content, + &i.IsRead, + &i.CreatedAt, + &i.ReadAt, + ) + return i, err +} + +const deleteInboxItem = `-- name: DeleteInboxItem :exec +DELETE FROM bot_inbox +WHERE id = $1 + AND bot_id = $2 +` + +type DeleteInboxItemParams struct { + ID pgtype.UUID `json:"id"` + BotID pgtype.UUID `json:"bot_id"` +} + +func (q *Queries) DeleteInboxItem(ctx context.Context, arg DeleteInboxItemParams) error { + _, err := q.db.Exec(ctx, deleteInboxItem, arg.ID, arg.BotID) + return err +} + +const deleteInboxItemsByBot = `-- name: DeleteInboxItemsByBot :exec +DELETE FROM bot_inbox +WHERE bot_id = $1 +` + +func (q *Queries) DeleteInboxItemsByBot(ctx context.Context, botID pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteInboxItemsByBot, botID) + return err +} + +const getInboxItemByID = `-- name: GetInboxItemByID :one +SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox +WHERE id = $1 + AND bot_id = $2 +` + +type GetInboxItemByIDParams struct { + ID pgtype.UUID `json:"id"` + BotID pgtype.UUID `json:"bot_id"` +} + +func (q *Queries) GetInboxItemByID(ctx context.Context, arg GetInboxItemByIDParams) (BotInbox, error) { + row := q.db.QueryRow(ctx, getInboxItemByID, arg.ID, arg.BotID) + var i BotInbox + err := row.Scan( + &i.ID, + &i.BotID, + &i.Source, + &i.Content, + &i.IsRead, + &i.CreatedAt, + &i.ReadAt, + ) + return i, err +} + +const listInboxItems = `-- name: ListInboxItems :many +SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox +WHERE bot_id = $1 + AND ($2::boolean IS NULL OR is_read = $2::boolean) + AND ($3::text IS NULL OR source = $3::text) +ORDER BY created_at DESC +LIMIT $5 +OFFSET $4 +` + +type ListInboxItemsParams struct { + BotID pgtype.UUID `json:"bot_id"` + IsRead pgtype.Bool `json:"is_read"` + Source pgtype.Text `json:"source"` + ItemOffset int32 `json:"item_offset"` + MaxCount int32 `json:"max_count"` +} + +func (q *Queries) ListInboxItems(ctx context.Context, arg ListInboxItemsParams) ([]BotInbox, error) { + rows, err := q.db.Query(ctx, listInboxItems, + arg.BotID, + arg.IsRead, + arg.Source, + arg.ItemOffset, + arg.MaxCount, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotInbox + for rows.Next() { + var i BotInbox + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Source, + &i.Content, + &i.IsRead, + &i.CreatedAt, + &i.ReadAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUnreadInboxItems = `-- name: ListUnreadInboxItems :many +SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox +WHERE bot_id = $1 + AND is_read = FALSE +ORDER BY created_at ASC +LIMIT $2 +` + +type ListUnreadInboxItemsParams struct { + BotID pgtype.UUID `json:"bot_id"` + MaxCount int32 `json:"max_count"` +} + +func (q *Queries) ListUnreadInboxItems(ctx context.Context, arg ListUnreadInboxItemsParams) ([]BotInbox, error) { + rows, err := q.db.Query(ctx, listUnreadInboxItems, arg.BotID, arg.MaxCount) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotInbox + for rows.Next() { + var i BotInbox + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Source, + &i.Content, + &i.IsRead, + &i.CreatedAt, + &i.ReadAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const markInboxItemsRead = `-- name: MarkInboxItemsRead :exec +UPDATE bot_inbox +SET is_read = TRUE, + read_at = now() +WHERE bot_id = $1 + AND id = ANY($2::uuid[]) + AND is_read = FALSE +` + +type MarkInboxItemsReadParams struct { + BotID pgtype.UUID `json:"bot_id"` + Ids []pgtype.UUID `json:"ids"` +} + +func (q *Queries) MarkInboxItemsRead(ctx context.Context, arg MarkInboxItemsReadParams) error { + _, err := q.db.Exec(ctx, markInboxItemsRead, arg.BotID, arg.Ids) + return err +} + +const searchInboxItems = `-- name: SearchInboxItems :many +SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox +WHERE bot_id = $1 + AND content::text ILIKE '%' || $2 || '%' + AND ($3::timestamptz IS NULL OR created_at >= $3::timestamptz) + AND ($4::timestamptz IS NULL OR created_at <= $4::timestamptz) + AND ($5::boolean IS NULL OR $5::boolean = TRUE OR is_read = FALSE) +ORDER BY created_at DESC +LIMIT $6 +` + +type SearchInboxItemsParams struct { + BotID pgtype.UUID `json:"bot_id"` + Query pgtype.Text `json:"query"` + StartTime pgtype.Timestamptz `json:"start_time"` + EndTime pgtype.Timestamptz `json:"end_time"` + IncludeRead pgtype.Bool `json:"include_read"` + MaxCount int32 `json:"max_count"` +} + +func (q *Queries) SearchInboxItems(ctx context.Context, arg SearchInboxItemsParams) ([]BotInbox, error) { + rows, err := q.db.Query(ctx, searchInboxItems, + arg.BotID, + arg.Query, + arg.StartTime, + arg.EndTime, + arg.IncludeRead, + arg.MaxCount, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotInbox + for rows.Next() { + var i BotInbox + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Source, + &i.Content, + &i.IsRead, + &i.CreatedAt, + &i.ReadAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/db/sqlc/messages.sql.go b/internal/db/sqlc/messages.sql.go index b1299353..dd44e086 100644 --- a/internal/db/sqlc/messages.sql.go +++ b/internal/db/sqlc/messages.sql.go @@ -127,6 +127,90 @@ func (q *Queries) DeleteMessagesByBot(ctx context.Context, botID pgtype.UUID) er return err } +const listActiveMessagesSince = `-- name: ListActiveMessagesSince :many +SELECT + m.id, + m.bot_id, + m.route_id, + m.sender_channel_identity_id, + m.sender_account_user_id AS sender_user_id, + m.channel_type AS platform, + m.source_message_id AS external_message_id, + m.source_reply_to_message_id, + m.role, + m.content, + m.metadata, + m.usage, + m.created_at, + ci.display_name AS sender_display_name, + ci.avatar_url AS sender_avatar_url +FROM bot_history_messages m +LEFT JOIN channel_identities ci ON ci.id = m.sender_channel_identity_id +WHERE m.bot_id = $1 + AND m.created_at >= $2 + AND (m.metadata->>'trigger_mode' IS NULL OR m.metadata->>'trigger_mode' != 'passive_sync') +ORDER BY m.created_at ASC +` + +type ListActiveMessagesSinceParams struct { + BotID pgtype.UUID `json:"bot_id"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type ListActiveMessagesSinceRow struct { + ID pgtype.UUID `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + RouteID pgtype.UUID `json:"route_id"` + SenderChannelIdentityID pgtype.UUID `json:"sender_channel_identity_id"` + SenderUserID pgtype.UUID `json:"sender_user_id"` + Platform pgtype.Text `json:"platform"` + ExternalMessageID pgtype.Text `json:"external_message_id"` + SourceReplyToMessageID pgtype.Text `json:"source_reply_to_message_id"` + Role string `json:"role"` + Content []byte `json:"content"` + Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + SenderDisplayName pgtype.Text `json:"sender_display_name"` + SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"` +} + +func (q *Queries) ListActiveMessagesSince(ctx context.Context, arg ListActiveMessagesSinceParams) ([]ListActiveMessagesSinceRow, error) { + rows, err := q.db.Query(ctx, listActiveMessagesSince, arg.BotID, arg.CreatedAt) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListActiveMessagesSinceRow + for rows.Next() { + var i ListActiveMessagesSinceRow + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.RouteID, + &i.SenderChannelIdentityID, + &i.SenderUserID, + &i.Platform, + &i.ExternalMessageID, + &i.SourceReplyToMessageID, + &i.Role, + &i.Content, + &i.Metadata, + &i.Usage, + &i.CreatedAt, + &i.SenderDisplayName, + &i.SenderAvatarUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listMessages = `-- name: ListMessages :many SELECT m.id, diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 55be558c..02ecf3a1 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -20,6 +20,7 @@ type Bot struct { MaxContextTokens int32 `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` + MaxInboxItems int32 `json:"max_inbox_items"` ChatModelID pgtype.UUID `json:"chat_model_id"` MemoryModelID pgtype.UUID `json:"memory_model_id"` EmbeddingModelID pgtype.UUID `json:"embedding_model_id"` @@ -83,6 +84,16 @@ type BotHistoryMessageAsset struct { CreatedAt pgtype.Timestamptz `json:"created_at"` } +type BotInbox struct { + ID pgtype.UUID `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + Source string `json:"source"` + Content []byte `json:"content"` + IsRead bool `json:"is_read"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + ReadAt pgtype.Timestamptz `json:"read_at"` +} + type BotMember struct { BotID pgtype.UUID `json:"bot_id"` UserID pgtype.UUID `json:"user_id"` diff --git a/internal/db/sqlc/settings.sql.go b/internal/db/sqlc/settings.sql.go index 7f3a612e..c3b36ee5 100644 --- a/internal/db/sqlc/settings.sql.go +++ b/internal/db/sqlc/settings.sql.go @@ -15,6 +15,7 @@ const deleteSettingsByBotID = `-- name: DeleteSettingsByBotID :exec UPDATE bots SET max_context_load_time = 1440, max_context_tokens = 0, + max_inbox_items = 50, language = 'auto', allow_guest = false, chat_model_id = NULL, @@ -35,6 +36,7 @@ SELECT bots.id AS bot_id, bots.max_context_load_time, bots.max_context_tokens, + bots.max_inbox_items, bots.language, bots.allow_guest, chat_models.model_id AS chat_model_id, @@ -53,6 +55,7 @@ type GetSettingsByBotIDRow struct { BotID pgtype.UUID `json:"bot_id"` MaxContextLoadTime int32 `json:"max_context_load_time"` MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.Text `json:"chat_model_id"` @@ -68,6 +71,7 @@ func (q *Queries) GetSettingsByBotID(ctx context.Context, id pgtype.UUID) (GetSe &i.BotID, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -83,20 +87,22 @@ WITH updated AS ( UPDATE bots SET max_context_load_time = $1, max_context_tokens = $2, - language = $3, - allow_guest = $4, - chat_model_id = COALESCE($5::uuid, bots.chat_model_id), - memory_model_id = COALESCE($6::uuid, bots.memory_model_id), - embedding_model_id = COALESCE($7::uuid, bots.embedding_model_id), - search_provider_id = COALESCE($8::uuid, bots.search_provider_id), + max_inbox_items = $3, + language = $4, + allow_guest = $5, + chat_model_id = COALESCE($6::uuid, bots.chat_model_id), + memory_model_id = COALESCE($7::uuid, bots.memory_model_id), + embedding_model_id = COALESCE($8::uuid, bots.embedding_model_id), + search_provider_id = COALESCE($9::uuid, bots.search_provider_id), updated_at = now() - WHERE bots.id = $9 - RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id + WHERE bots.id = $10 + RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.max_inbox_items, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id ) SELECT updated.id AS bot_id, updated.max_context_load_time, updated.max_context_tokens, + updated.max_inbox_items, updated.language, updated.allow_guest, chat_models.model_id AS chat_model_id, @@ -113,6 +119,7 @@ LEFT JOIN search_providers ON search_providers.id = updated.search_provider_id type UpsertBotSettingsParams struct { MaxContextLoadTime int32 `json:"max_context_load_time"` MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.UUID `json:"chat_model_id"` @@ -126,6 +133,7 @@ type UpsertBotSettingsRow struct { BotID pgtype.UUID `json:"bot_id"` MaxContextLoadTime int32 `json:"max_context_load_time"` MaxContextTokens int32 `json:"max_context_tokens"` + MaxInboxItems int32 `json:"max_inbox_items"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.Text `json:"chat_model_id"` @@ -138,6 +146,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa row := q.db.QueryRow(ctx, upsertBotSettings, arg.MaxContextLoadTime, arg.MaxContextTokens, + arg.MaxInboxItems, arg.Language, arg.AllowGuest, arg.ChatModelID, @@ -151,6 +160,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa &i.BotID, &i.MaxContextLoadTime, &i.MaxContextTokens, + &i.MaxInboxItems, &i.Language, &i.AllowGuest, &i.ChatModelID, diff --git a/internal/handlers/inbox.go b/internal/handlers/inbox.go new file mode 100644 index 00000000..f70cf5ea --- /dev/null +++ b/internal/handlers/inbox.go @@ -0,0 +1,268 @@ +package handlers + +import ( + "context" + "log/slog" + "net/http" + "strconv" + "strings" + + "github.com/labstack/echo/v4" + + "github.com/memohai/memoh/internal/accounts" + "github.com/memohai/memoh/internal/bots" + "github.com/memohai/memoh/internal/inbox" +) + +type InboxHandler struct { + service *inbox.Service + botService *bots.Service + accountService *accounts.Service + logger *slog.Logger +} + +func NewInboxHandler(log *slog.Logger, service *inbox.Service, botService *bots.Service, accountService *accounts.Service) *InboxHandler { + return &InboxHandler{ + service: service, + botService: botService, + accountService: accountService, + logger: log.With(slog.String("handler", "inbox")), + } +} + +func (h *InboxHandler) Register(e *echo.Echo) { + group := e.Group("/bots/:bot_id/inbox") + group.GET("", h.List) + group.GET("/count", h.Count) + group.GET("/:id", h.GetByID) + group.POST("", h.Create) + group.DELETE("/:id", h.Delete) + group.POST("/mark-read", h.MarkRead) +} + +// List godoc +// @Summary List inbox items +// @Description List inbox items for a bot with optional filters +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Param is_read query string false "Filter by read status (true/false)" +// @Param source query string false "Filter by source" +// @Param limit query int false "Max items to return" default(50) +// @Param offset query int false "Offset for pagination" default(0) +// @Success 200 {array} inbox.Item +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox [get] +func (h *InboxHandler) List(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + filter := inbox.ListFilter{ + Source: strings.TrimSpace(c.QueryParam("source")), + Limit: parseIntOr(c.QueryParam("limit"), 50), + Offset: parseIntOr(c.QueryParam("offset"), 0), + } + if isReadStr := strings.TrimSpace(c.QueryParam("is_read")); isReadStr != "" { + val := strings.EqualFold(isReadStr, "true") + filter.IsRead = &val + } + items, err := h.service.List(c.Request().Context(), botID, filter) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, items) +} + +// GetByID godoc +// @Summary Get inbox item +// @Description Get a single inbox item by ID +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Param id path string true "Inbox item ID" +// @Success 200 {object} inbox.Item +// @Failure 400 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox/{id} [get] +func (h *InboxHandler) GetByID(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + itemID := strings.TrimSpace(c.Param("id")) + if itemID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "item id is required") + } + item, err := h.service.GetByID(c.Request().Context(), botID, itemID) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "inbox item not found") + } + return c.JSON(http.StatusOK, item) +} + +// Create godoc +// @Summary Create inbox item +// @Description Create a new inbox item (for external integrations) +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Param payload body inbox.CreateRequest true "Inbox item payload" +// @Success 201 {object} inbox.Item +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox [post] +func (h *InboxHandler) Create(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + var req inbox.CreateRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + req.BotID = botID + if len(req.Content) == 0 { + return echo.NewHTTPError(http.StatusBadRequest, "content is required") + } + item, err := h.service.Create(c.Request().Context(), req) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusCreated, item) +} + +// Delete godoc +// @Summary Delete inbox item +// @Description Delete a single inbox item +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Param id path string true "Inbox item ID" +// @Success 204 "No Content" +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox/{id} [delete] +func (h *InboxHandler) Delete(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + itemID := strings.TrimSpace(c.Param("id")) + if itemID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "item id is required") + } + if err := h.service.Delete(c.Request().Context(), botID, itemID); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.NoContent(http.StatusNoContent) +} + +type markReadRequest struct { + IDs []string `json:"ids"` +} + +// MarkRead godoc +// @Summary Mark inbox items as read +// @Description Batch mark inbox items as read +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Param payload body markReadRequest true "Item IDs to mark as read" +// @Success 204 "No Content" +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox/mark-read [post] +func (h *InboxHandler) MarkRead(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + var req markReadRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + if len(req.IDs) == 0 { + return echo.NewHTTPError(http.StatusBadRequest, "ids is required") + } + if err := h.service.MarkRead(c.Request().Context(), botID, req.IDs); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.NoContent(http.StatusNoContent) +} + +// Count godoc +// @Summary Count inbox items +// @Description Count unread and total inbox items +// @Tags inbox +// @Param bot_id path string true "Bot ID" +// @Success 200 {object} inbox.CountResult +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/inbox/count [get] +func (h *InboxHandler) Count(c echo.Context) error { + channelIdentityID, err := RequireChannelIdentityID(c) + if err != nil { + return err + } + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot id is required") + } + if _, err := h.authorizeBotAccess(c.Request().Context(), channelIdentityID, botID); err != nil { + return err + } + result, err := h.service.Count(c.Request().Context(), botID) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, result) +} + +func (h *InboxHandler) authorizeBotAccess(ctx context.Context, channelIdentityID, botID string) (bots.Bot, error) { + return AuthorizeBotAccess(ctx, h.botService, h.accountService, channelIdentityID, botID, bots.AccessPolicy{AllowPublicMember: false}) +} + +func parseIntOr(s string, fallback int) int { + s = strings.TrimSpace(s) + if s == "" { + return fallback + } + v, err := strconv.Atoi(s) + if err != nil { + return fallback + } + return v +} diff --git a/internal/inbox/service.go b/internal/inbox/service.go new file mode 100644 index 00000000..6ba5fb46 --- /dev/null +++ b/internal/inbox/service.go @@ -0,0 +1,284 @@ +package inbox + +import ( + "context" + "encoding/json" + "log/slog" + "time" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + + "github.com/memohai/memoh/internal/db" + "github.com/memohai/memoh/internal/db/sqlc" +) + +type Service struct { + queries *sqlc.Queries + logger *slog.Logger +} + +func NewService(log *slog.Logger, queries *sqlc.Queries) *Service { + if log == nil { + log = slog.Default() + } + return &Service{ + queries: queries, + logger: log.With(slog.String("service", "inbox")), + } +} + +type Item struct { + ID string `json:"id"` + BotID string `json:"bot_id"` + Source string `json:"source"` + Content map[string]any `json:"content"` + IsRead bool `json:"is_read"` + CreatedAt time.Time `json:"created_at"` + ReadAt time.Time `json:"read_at,omitempty"` +} + +type CreateRequest struct { + BotID string `json:"bot_id"` + Source string `json:"source"` + Content map[string]any `json:"content"` +} + +type ListFilter struct { + IsRead *bool `json:"is_read,omitempty"` + Source string `json:"source,omitempty"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +type SearchRequest struct { + Query string `json:"query"` + StartTime *time.Time `json:"start_time,omitempty"` + EndTime *time.Time `json:"end_time,omitempty"` + IncludeRead *bool `json:"include_read,omitempty"` + Limit int `json:"limit"` +} + +type CountResult struct { + Unread int64 `json:"unread"` + Total int64 `json:"total"` +} + +func (s *Service) Create(ctx context.Context, req CreateRequest) (Item, error) { + botUUID, err := db.ParseUUID(req.BotID) + if err != nil { + return Item{}, err + } + content, err := json.Marshal(req.Content) + if err != nil { + return Item{}, err + } + + row, err := s.queries.CreateInboxItem(ctx, sqlc.CreateInboxItemParams{ + BotID: botUUID, + Source: req.Source, + Content: content, + }) + if err != nil { + return Item{}, err + } + return rowToItem(row), nil +} + +func (s *Service) GetByID(ctx context.Context, botID, itemID string) (Item, error) { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return Item{}, err + } + itemUUID, err := db.ParseUUID(itemID) + if err != nil { + return Item{}, err + } + row, err := s.queries.GetInboxItemByID(ctx, sqlc.GetInboxItemByIDParams{ + ID: itemUUID, + BotID: botUUID, + }) + if err != nil { + return Item{}, err + } + return rowToItem(row), nil +} + +func (s *Service) List(ctx context.Context, botID string, filter ListFilter) ([]Item, error) { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return nil, err + } + limit := filter.Limit + if limit <= 0 { + limit = 50 + } + if limit > 500 { + limit = 500 + } + rows, err := s.queries.ListInboxItems(ctx, sqlc.ListInboxItemsParams{ + BotID: botUUID, + IsRead: boolOrNull(filter.IsRead), + Source: textOrNull(filter.Source), + MaxCount: int32(limit), + ItemOffset: int32(filter.Offset), + }) + if err != nil { + return nil, err + } + return rowsToItems(rows), nil +} + +func (s *Service) ListUnread(ctx context.Context, botID string, limit int) ([]Item, error) { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return nil, err + } + if limit <= 0 { + limit = 50 + } + rows, err := s.queries.ListUnreadInboxItems(ctx, sqlc.ListUnreadInboxItemsParams{ + BotID: botUUID, + MaxCount: int32(limit), + }) + if err != nil { + return nil, err + } + return rowsToItems(rows), nil +} + +func (s *Service) MarkRead(ctx context.Context, botID string, ids []string) error { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return err + } + pgIDs := make([]pgtype.UUID, 0, len(ids)) + for _, id := range ids { + pgID, err := db.ParseUUID(id) + if err != nil { + continue + } + pgIDs = append(pgIDs, pgID) + } + if len(pgIDs) == 0 { + return nil + } + return s.queries.MarkInboxItemsRead(ctx, sqlc.MarkInboxItemsReadParams{ + BotID: botUUID, + Ids: pgIDs, + }) +} + +func (s *Service) Search(ctx context.Context, botID string, req SearchRequest) ([]Item, error) { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return nil, err + } + limit := req.Limit + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + params := sqlc.SearchInboxItemsParams{ + BotID: botUUID, + Query: pgtype.Text{String: req.Query, Valid: true}, + MaxCount: int32(limit), + } + if req.StartTime != nil { + params.StartTime = pgtype.Timestamptz{Time: *req.StartTime, Valid: true} + } + if req.EndTime != nil { + params.EndTime = pgtype.Timestamptz{Time: *req.EndTime, Valid: true} + } + if req.IncludeRead != nil { + params.IncludeRead = pgtype.Bool{Bool: *req.IncludeRead, Valid: true} + } + rows, err := s.queries.SearchInboxItems(ctx, params) + if err != nil { + return nil, err + } + return rowsToItems(rows), nil +} + +func (s *Service) Count(ctx context.Context, botID string) (CountResult, error) { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return CountResult{}, err + } + unread, err := s.queries.CountUnreadInboxItems(ctx, botUUID) + if err != nil { + return CountResult{}, err + } + total, err := s.queries.CountInboxItems(ctx, botUUID) + if err != nil { + return CountResult{}, err + } + return CountResult{Unread: unread, Total: total}, nil +} + +func (s *Service) Delete(ctx context.Context, botID, itemID string) error { + botUUID, err := db.ParseUUID(botID) + if err != nil { + return err + } + itemUUID, err := db.ParseUUID(itemID) + if err != nil { + return err + } + return s.queries.DeleteInboxItem(ctx, sqlc.DeleteInboxItemParams{ + ID: itemUUID, + BotID: botUUID, + }) +} + +// --- conversion helpers --- + +func rowToItem(row sqlc.BotInbox) Item { + var content map[string]any + if len(row.Content) > 0 { + _ = json.Unmarshal(row.Content, &content) + } + if content == nil { + content = map[string]any{} + } + return Item{ + ID: pgUUIDToString(row.ID), + BotID: pgUUIDToString(row.BotID), + Source: row.Source, + Content: content, + IsRead: row.IsRead, + CreatedAt: db.TimeFromPg(row.CreatedAt), + ReadAt: db.TimeFromPg(row.ReadAt), + } +} + +func rowsToItems(rows []sqlc.BotInbox) []Item { + items := make([]Item, 0, len(rows)) + for _, row := range rows { + items = append(items, rowToItem(row)) + } + return items +} + +func pgUUIDToString(id pgtype.UUID) string { + if !id.Valid { + return "" + } + return uuid.UUID(id.Bytes).String() +} + +func textOrNull(s string) pgtype.Text { + if s == "" { + return pgtype.Text{} + } + return pgtype.Text{String: s, Valid: true} +} + +func boolOrNull(b *bool) pgtype.Bool { + if b == nil { + return pgtype.Bool{} + } + return pgtype.Bool{Bool: *b, Valid: true} +} diff --git a/internal/mcp/providers/inbox/provider.go b/internal/mcp/providers/inbox/provider.go new file mode 100644 index 00000000..0b4c80a1 --- /dev/null +++ b/internal/mcp/providers/inbox/provider.go @@ -0,0 +1,152 @@ +package inbox + +import ( + "context" + "fmt" + "log/slog" + "strings" + "time" + + mcpgw "github.com/memohai/memoh/internal/mcp" + + inboxsvc "github.com/memohai/memoh/internal/inbox" +) + +const ( + toolSearchInbox = "search_inbox" + defaultSearchLimit = 20 + maxSearchLimit = 100 +) + +type Executor struct { + service *inboxsvc.Service + logger *slog.Logger +} + +func NewExecutor(log *slog.Logger, service *inboxsvc.Service) *Executor { + if log == nil { + log = slog.Default() + } + return &Executor{ + service: service, + logger: log.With(slog.String("provider", "inbox_tool")), + } +} + +func (e *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionContext) ([]mcpgw.ToolDescriptor, error) { + if e.service == nil { + return []mcpgw.ToolDescriptor{}, nil + } + return []mcpgw.ToolDescriptor{ + { + Name: toolSearchInbox, + Description: "Search historical inbox messages by keyword. Inbox contains messages from group conversations where the bot was not directly mentioned, as well as notifications from external sources.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "query": map[string]any{ + "type": "string", + "description": "Search keyword to match against inbox message content", + }, + "start_time": map[string]any{ + "type": "string", + "description": "ISO 8601 start time filter (e.g. 2025-01-01T00:00:00Z)", + }, + "end_time": map[string]any{ + "type": "string", + "description": "ISO 8601 end time filter", + }, + "limit": map[string]any{ + "type": "integer", + "description": "Maximum number of results (default 20, max 100)", + }, + "include_read": map[string]any{ + "type": "boolean", + "description": "Whether to include already-read items (default true)", + }, + }, + "required": []string{"query"}, + }, + }, + }, nil +} + +func (e *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContext, toolName string, arguments map[string]any) (map[string]any, error) { + if toolName != toolSearchInbox { + return nil, mcpgw.ErrToolNotFound + } + if e.service == nil { + return mcpgw.BuildToolErrorResult("inbox service not available"), nil + } + + query := mcpgw.StringArg(arguments, "query") + if query == "" { + return mcpgw.BuildToolErrorResult("query is required"), nil + } + botID := strings.TrimSpace(session.BotID) + if botID == "" { + return mcpgw.BuildToolErrorResult("bot_id is required"), nil + } + + limit := defaultSearchLimit + if value, ok, err := mcpgw.IntArg(arguments, "limit"); err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } else if ok { + limit = value + } + if limit <= 0 { + limit = defaultSearchLimit + } + if limit > maxSearchLimit { + limit = maxSearchLimit + } + + req := inboxsvc.SearchRequest{ + Query: query, + Limit: limit, + } + + if startStr := mcpgw.StringArg(arguments, "start_time"); startStr != "" { + t, err := time.Parse(time.RFC3339, startStr) + if err != nil { + return mcpgw.BuildToolErrorResult(fmt.Sprintf("invalid start_time: %v", err)), nil + } + req.StartTime = &t + } + if endStr := mcpgw.StringArg(arguments, "end_time"); endStr != "" { + t, err := time.Parse(time.RFC3339, endStr) + if err != nil { + return mcpgw.BuildToolErrorResult(fmt.Sprintf("invalid end_time: %v", err)), nil + } + req.EndTime = &t + } + if includeRead, ok, err := mcpgw.BoolArg(arguments, "include_read"); err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } else if ok { + req.IncludeRead = &includeRead + } + + items, err := e.service.Search(ctx, botID, req) + if err != nil { + e.logger.Warn("inbox search failed", slog.String("bot_id", botID), slog.Any("error", err)) + return mcpgw.BuildToolErrorResult("inbox search failed"), nil + } + + results := make([]map[string]any, 0, len(items)) + for _, item := range items { + entry := map[string]any{ + "id": item.ID, + "source": item.Source, + "content": item.Content, + "is_read": item.IsRead, + "created_at": item.CreatedAt.Format(time.RFC3339), + } + results = append(results, entry) + } + + return mcpgw.BuildToolSuccessResult(map[string]any{ + "query": query, + "total": len(results), + "results": results, + }), nil +} diff --git a/internal/mcp/providers/message/provider.go b/internal/mcp/providers/message/provider.go index e323c131..4f31993f 100644 --- a/internal/mcp/providers/message/provider.go +++ b/internal/mcp/providers/message/provider.go @@ -104,7 +104,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte "attachments": map[string]any{ "type": "array", "description": "File paths or URLs to attach. Each item is a container path (e.g. /data/media/ab/file.jpg), an HTTP URL, or an object with {path, url, type, name}.", - "items": map[string]any{}, + "items": map[string]any{}, }, "message": map[string]any{ "type": "object", diff --git a/internal/message/service.go b/internal/message/service.go index 9090b0e5..ba5c0378 100644 --- a/internal/message/service.go +++ b/internal/message/service.go @@ -167,6 +167,24 @@ func (s *DBService) ListSince(ctx context.Context, botID string, since time.Time return msgs, nil } +// ListActiveSince returns bot messages since a given time, excluding passive_sync messages. +func (s *DBService) ListActiveSince(ctx context.Context, botID string, since time.Time) ([]Message, error) { + pgBotID, err := dbpkg.ParseUUID(botID) + if err != nil { + return nil, err + } + rows, err := s.queries.ListActiveMessagesSince(ctx, sqlc.ListActiveMessagesSinceParams{ + BotID: pgBotID, + CreatedAt: pgtype.Timestamptz{Time: since, Valid: true}, + }) + if err != nil { + return nil, err + } + msgs := toMessagesFromActiveSince(rows) + s.enrichAssets(ctx, msgs) + return msgs, nil +} + // ListLatest returns the latest N bot messages (newest first in DB; caller may reverse for ASC). func (s *DBService) ListLatest(ctx context.Context, botID string, limit int32) ([]Message, error) { pgBotID, err := dbpkg.ParseUUID(botID) @@ -273,6 +291,26 @@ func toMessageFromSinceRow(row sqlc.ListMessagesSinceRow) Message { ) } +func toMessageFromActiveSinceRow(row sqlc.ListActiveMessagesSinceRow) Message { + return toMessageFields( + row.ID, + row.BotID, + row.RouteID, + row.SenderChannelIdentityID, + row.SenderUserID, + row.SenderDisplayName, + row.SenderAvatarUrl, + row.Platform, + row.ExternalMessageID, + row.SourceReplyToMessageID, + row.Role, + row.Content, + row.Metadata, + row.Usage, + row.CreatedAt, + ) +} + func toMessageFromLatestRow(row sqlc.ListMessagesLatestRow) Message { return toMessageFields( row.ID, @@ -345,6 +383,14 @@ func toMessagesFromSince(rows []sqlc.ListMessagesSinceRow) []Message { return messages } +func toMessagesFromActiveSince(rows []sqlc.ListActiveMessagesSinceRow) []Message { + messages := make([]Message, 0, len(rows)) + for _, row := range rows { + messages = append(messages, toMessageFromActiveSinceRow(row)) + } + return messages +} + func toMessagesFromLatest(rows []sqlc.ListMessagesLatestRow) []Message { messages := make([]Message, 0, len(rows)) for _, row := range rows { diff --git a/internal/message/types.go b/internal/message/types.go index 7cc87b9f..ba8d26d7 100644 --- a/internal/message/types.go +++ b/internal/message/types.go @@ -74,6 +74,7 @@ type Service interface { Writer List(ctx context.Context, botID string) ([]Message, error) ListSince(ctx context.Context, botID string, since time.Time) ([]Message, error) + ListActiveSince(ctx context.Context, botID string, since time.Time) ([]Message, error) ListLatest(ctx context.Context, botID string, limit int32) ([]Message, error) ListBefore(ctx context.Context, botID string, before time.Time, limit int32) ([]Message, error) DeleteByBot(ctx context.Context, botID string) error diff --git a/internal/settings/service.go b/internal/settings/service.go index a4464356..f7c2105b 100644 --- a/internal/settings/service.go +++ b/internal/settings/service.go @@ -54,13 +54,16 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest } isPersonalBot := strings.EqualFold(strings.TrimSpace(botRow.Type), "personal") - current := normalizeBotSetting(botRow.MaxContextLoadTime, botRow.MaxContextTokens, botRow.Language, botRow.AllowGuest) + current := normalizeBotSetting(botRow.MaxContextLoadTime, botRow.MaxContextTokens, botRow.MaxInboxItems, botRow.Language, botRow.AllowGuest) if req.MaxContextLoadTime != nil && *req.MaxContextLoadTime > 0 { current.MaxContextLoadTime = *req.MaxContextLoadTime } if req.MaxContextTokens != nil && *req.MaxContextTokens >= 0 { current.MaxContextTokens = *req.MaxContextTokens } + if req.MaxInboxItems != nil && *req.MaxInboxItems >= 0 { + current.MaxInboxItems = *req.MaxInboxItems + } if strings.TrimSpace(req.Language) != "" { current.Language = strings.TrimSpace(req.Language) } @@ -110,6 +113,7 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest ID: pgID, MaxContextLoadTime: int32(current.MaxContextLoadTime), MaxContextTokens: int32(current.MaxContextTokens), + MaxInboxItems: int32(current.MaxInboxItems), Language: current.Language, AllowGuest: current.AllowGuest, ChatModelID: chatModelUUID, @@ -134,10 +138,11 @@ func (s *Service) Delete(ctx context.Context, botID string) error { return s.queries.DeleteSettingsByBotID(ctx, pgID) } -func normalizeBotSetting(maxContextLoadTime int32, maxContextTokens int32, language string, allowGuest bool) Settings { +func normalizeBotSetting(maxContextLoadTime int32, maxContextTokens int32, maxInboxItems int32, language string, allowGuest bool) Settings { settings := Settings{ MaxContextLoadTime: int(maxContextLoadTime), MaxContextTokens: int(maxContextTokens), + MaxInboxItems: int(maxInboxItems), Language: strings.TrimSpace(language), AllowGuest: allowGuest, } @@ -147,6 +152,9 @@ func normalizeBotSetting(maxContextLoadTime int32, maxContextTokens int32, langu if settings.MaxContextTokens < 0 { settings.MaxContextTokens = 0 } + if settings.MaxInboxItems <= 0 { + settings.MaxInboxItems = DefaultMaxInboxItems + } if settings.Language == "" { settings.Language = DefaultLanguage } @@ -157,6 +165,7 @@ func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, row.MaxContextTokens, + row.MaxInboxItems, row.Language, row.AllowGuest, row.ChatModelID, @@ -170,6 +179,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, row.MaxContextTokens, + row.MaxInboxItems, row.Language, row.AllowGuest, row.ChatModelID, @@ -182,6 +192,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { func normalizeBotSettingsFields( maxContextLoadTime int32, maxContextTokens int32, + maxInboxItems int32, language string, allowGuest bool, chatModelID pgtype.Text, @@ -189,7 +200,7 @@ func normalizeBotSettingsFields( embeddingModelID pgtype.Text, searchProviderID pgtype.UUID, ) Settings { - settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, language, allowGuest) + settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, maxInboxItems, language, allowGuest) settings.ChatModelID = strings.TrimSpace(chatModelID.String) settings.MemoryModelID = strings.TrimSpace(memoryModelID.String) settings.EmbeddingModelID = strings.TrimSpace(embeddingModelID.String) diff --git a/internal/settings/types.go b/internal/settings/types.go index 93b3a790..7536208e 100644 --- a/internal/settings/types.go +++ b/internal/settings/types.go @@ -2,6 +2,7 @@ package settings const ( DefaultMaxContextLoadTime = 24 * 60 + DefaultMaxInboxItems = 50 DefaultLanguage = "auto" ) @@ -12,6 +13,7 @@ type Settings struct { SearchProviderID string `json:"search_provider_id"` MaxContextLoadTime int `json:"max_context_load_time"` MaxContextTokens int `json:"max_context_tokens"` + MaxInboxItems int `json:"max_inbox_items"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` } @@ -23,6 +25,7 @@ type UpsertRequest struct { SearchProviderID string `json:"search_provider_id,omitempty"` MaxContextLoadTime *int `json:"max_context_load_time,omitempty"` MaxContextTokens *int `json:"max_context_tokens,omitempty"` + MaxInboxItems *int `json:"max_inbox_items,omitempty"` Language string `json:"language,omitempty"` AllowGuest *bool `json:"allow_guest,omitempty"` } diff --git a/packages/sdk/src/@pinia/colada.gen.ts b/packages/sdk/src/@pinia/colada.gen.ts index 53f82d21..5e275111 100644 --- a/packages/sdk/src/@pinia/colada.gen.ts +++ b/packages/sdk/src/@pinia/colada.gen.ts @@ -4,8 +4,8 @@ import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@p import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; -import { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdContainer, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from '../sdk.gen'; -import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdResponse, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, GetBotsByBotIdContainerData, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpData, GetBotsByBotIdMcpExportData, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMessagesData, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleData, GetBotsByBotIdSettingsData, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsData, GetBotsByIdChannelByPlatformData, GetBotsByIdChecksData, GetBotsByIdData, GetBotsByIdMembersData, GetBotsData, GetChannelsByPlatformData, GetChannelsData, GetModelsByIdData, GetModelsCountData, GetModelsData, GetModelsModelByModelIdData, GetProvidersByIdData, GetProvidersByIdModelsData, GetProvidersCountData, GetProvidersData, GetProvidersNameByNameData, GetSearchProvidersByIdData, GetSearchProvidersData, GetSearchProvidersMetaData, GetUsersByIdData, GetUsersData, GetUsersMeChannelsByPlatformData, GetUsersMeData, GetUsersMeIdentitiesData, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusResponse, PostAuthLoginData, PostAuthLoginError, PostAuthLoginResponse, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleResponse, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsResponse, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesResponse, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendResponse, PostBotsData, PostBotsError, PostBotsResponse, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsResponse, PostModelsData, PostModelsError, PostModelsResponse, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestResponse, PostProvidersData, PostProvidersError, PostProvidersResponse, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersResponse, PostUsersData, PostUsersError, PostUsersResponse, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformResponse, PutBotsByIdData, PutBotsByIdError, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersResponse, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerResponse, PutBotsByIdResponse, PutModelsByIdData, PutModelsByIdError, PutModelsByIdResponse, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdResponse, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdResponse, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdResponse, PutUsersByIdData, PutUsersByIdError, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdResponse, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformResponse, PutUsersMeData, PutUsersMeError, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMeResponse } from '../types.gen'; +import { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdContainer, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from '../sdk.gen'; +import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdResponse, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, GetBotsByBotIdContainerData, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxData, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpData, GetBotsByBotIdMcpExportData, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMessagesData, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleData, GetBotsByBotIdSettingsData, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsData, GetBotsByIdChannelByPlatformData, GetBotsByIdChecksData, GetBotsByIdData, GetBotsByIdMembersData, GetBotsData, GetChannelsByPlatformData, GetChannelsData, GetModelsByIdData, GetModelsCountData, GetModelsData, GetModelsModelByModelIdData, GetProvidersByIdData, GetProvidersByIdModelsData, GetProvidersCountData, GetProvidersData, GetProvidersNameByNameData, GetSearchProvidersByIdData, GetSearchProvidersData, GetSearchProvidersMetaData, GetUsersByIdData, GetUsersData, GetUsersMeChannelsByPlatformData, GetUsersMeData, GetUsersMeIdentitiesData, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusResponse, PostAuthLoginData, PostAuthLoginError, PostAuthLoginResponse, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxResponse, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleResponse, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsResponse, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesResponse, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendResponse, PostBotsData, PostBotsError, PostBotsResponse, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsResponse, PostModelsData, PostModelsError, PostModelsResponse, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestResponse, PostProvidersData, PostProvidersError, PostProvidersResponse, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersResponse, PostUsersData, PostUsersError, PostUsersResponse, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformResponse, PutBotsByIdData, PutBotsByIdError, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersResponse, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerResponse, PutBotsByIdResponse, PutModelsByIdData, PutModelsByIdError, PutModelsByIdResponse, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdResponse, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdResponse, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdResponse, PutUsersByIdData, PutUsersByIdError, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdResponse, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformResponse, PutUsersMeData, PutUsersMeError, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMeResponse } from '../types.gen'; /** * Login @@ -258,6 +258,111 @@ export const postBotsByBotIdContainerStopMutation = (options?: Partial) => createQueryKey('getBotsByBotIdInbox', options); + +/** + * List inbox items + * + * List inbox items for a bot with optional filters + */ +export const getBotsByBotIdInboxQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdInboxQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdInbox({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Create inbox item + * + * Create a new inbox item (for external integrations) + */ +export const postBotsByBotIdInboxMutation = (options?: Partial>): UseMutationOptions, PostBotsByBotIdInboxError> => ({ + mutation: async (vars) => { + const { data } = await postBotsByBotIdInbox({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +export const getBotsByBotIdInboxCountQueryKey = (options: Options) => createQueryKey('getBotsByBotIdInboxCount', options); + +/** + * Count inbox items + * + * Count unread and total inbox items + */ +export const getBotsByBotIdInboxCountQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdInboxCountQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdInboxCount({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Mark inbox items as read + * + * Batch mark inbox items as read + */ +export const postBotsByBotIdInboxMarkReadMutation = (options?: Partial>): UseMutationOptions, PostBotsByBotIdInboxMarkReadError> => ({ + mutation: async (vars) => { + const { data } = await postBotsByBotIdInboxMarkRead({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +/** + * Delete inbox item + * + * Delete a single inbox item + */ +export const deleteBotsByBotIdInboxByIdMutation = (options?: Partial>): UseMutationOptions, DeleteBotsByBotIdInboxByIdError> => ({ + mutation: async (vars) => { + const { data } = await deleteBotsByBotIdInboxById({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +export const getBotsByBotIdInboxByIdQueryKey = (options: Options) => createQueryKey('getBotsByBotIdInboxById', options); + +/** + * Get inbox item + * + * Get a single inbox item by ID + */ +export const getBotsByBotIdInboxByIdQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdInboxByIdQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdInboxById({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + export const getBotsByBotIdMcpQueryKey = (options?: Options) => createQueryKey('getBotsByBotIdMcp', options); /** diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 4e29c7ae..23c2afe0 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdCliStream, getBotsByBotIdContainer, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsByBotIdWebStream, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen'; -export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileRequest, BotsBot, BotsBotCheck, BotsBotMember, BotsCreateBotRequest, BotsListBotsResponse, BotsListChecksResponse, BotsListMembersResponse, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpsertMemberRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamError, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponse, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextError, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponse, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdError, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponse, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsError, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponse, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsError, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponse, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamError, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponse, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersError, GetBotsByIdMembersErrors, GetBotsByIdMembersResponse, GetBotsByIdMembersResponses, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesError, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponse, GetUsersMeIdentitiesResponses, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersBatchDeleteRequest, HandlersChannelMeta, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersEmbeddingsInput, HandlersEmbeddingsRequest, HandlersEmbeddingsResponse, HandlersEmbeddingsUsage, HandlersErrorResponse, HandlersGetContainerResponse, HandlersListMyIdentitiesResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersSkillItem, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, IdentitiesChannelIdentity, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpUpsertRequest, MemoryCdfPoint, MemoryCompactResult, MemoryDeleteResponse, MemoryMemoryItem, MemoryMessage, MemoryRebuildResult, MemorySearchResponse, MemoryTopKBucket, MemoryUsageResponse, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsClientType, ModelsCountResponse, ModelsGetResponse, ModelsModelType, ModelsUpdateRequest, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponse, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsErrors, PostEmbeddingsResponse, PostEmbeddingsResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersResponse, PostUsersResponses, ProvidersCheckResult, ProvidersCheckStatus, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersTestResponse, ProvidersUpdateRequest, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersErrors, PutBotsByIdMembersResponse, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListResponse, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SettingsSettings, SettingsUpsertRequest, SubagentAddSkillsRequest, SubagentContextResponse, SubagentCreateRequest, SubagentListResponse, SubagentSkillsResponse, SubagentSubagent, SubagentUpdateContextRequest, SubagentUpdateRequest, SubagentUpdateSkillsRequest } from './types.gen'; +export { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdCliStream, getBotsByBotIdContainer, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsByBotIdWebStream, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen'; +export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileRequest, BotsBot, BotsBotCheck, BotsBotMember, BotsCreateBotRequest, BotsListBotsResponse, BotsListChecksResponse, BotsListMembersResponse, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpsertMemberRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamError, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponse, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdError, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponse, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountError, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponse, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxError, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponse, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextError, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponse, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdError, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponse, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsError, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponse, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsError, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponse, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamError, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponse, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersError, GetBotsByIdMembersErrors, GetBotsByIdMembersResponse, GetBotsByIdMembersResponses, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesError, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponse, GetUsersMeIdentitiesResponses, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersBatchDeleteRequest, HandlersChannelMeta, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersEmbeddingsInput, HandlersEmbeddingsRequest, HandlersEmbeddingsResponse, HandlersEmbeddingsUsage, HandlersErrorResponse, HandlersGetContainerResponse, HandlersListMyIdentitiesResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMarkReadRequest, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersSkillItem, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, IdentitiesChannelIdentity, InboxCountResult, InboxCreateRequest, InboxItem, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpUpsertRequest, MemoryCdfPoint, MemoryCompactResult, MemoryDeleteResponse, MemoryMemoryItem, MemoryMessage, MemoryRebuildResult, MemorySearchResponse, MemoryTopKBucket, MemoryUsageResponse, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsClientType, ModelsCountResponse, ModelsGetResponse, ModelsModelType, ModelsUpdateRequest, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponse, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponse, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsErrors, PostEmbeddingsResponse, PostEmbeddingsResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersResponse, PostUsersResponses, ProvidersCheckResult, ProvidersCheckStatus, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersTestResponse, ProvidersUpdateRequest, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersErrors, PutBotsByIdMembersResponse, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListResponse, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SettingsSettings, SettingsUpsertRequest, SubagentAddSkillsRequest, SubagentContextResponse, SubagentCreateRequest, SubagentListResponse, SubagentSkillsResponse, SubagentSubagent, SubagentUpdateContextRequest, SubagentUpdateRequest, SubagentUpdateSkillsRequest } from './types.gen'; diff --git a/packages/sdk/src/sdk.gen.ts b/packages/sdk/src/sdk.gen.ts index 7e041767..df268b98 100644 --- a/packages/sdk/src/sdk.gen.ts +++ b/packages/sdk/src/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksErrors, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersErrors, GetBotsByIdMembersResponses, GetBotsByIdResponses, GetBotsData, GetBotsErrors, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformErrors, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsErrors, GetChannelsResponses, GetModelsByIdData, GetModelsByIdErrors, GetModelsByIdResponses, GetModelsCountData, GetModelsCountErrors, GetModelsCountResponses, GetModelsData, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponses, GetModelsResponses, GetProvidersByIdData, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponses, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountErrors, GetProvidersCountResponses, GetProvidersData, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameErrors, GetProvidersNameByNameResponses, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponses, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdErrors, GetUsersByIdResponses, GetUsersData, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponses, GetUsersMeResponses, GetUsersResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginErrors, PostAuthLoginResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsErrors, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsErrors, PostEmbeddingsResponses, PostModelsData, PostModelsErrors, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestErrors, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersErrors, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersErrors, PostSearchProvidersResponses, PostUsersData, PostUsersErrors, PostUsersResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersErrors, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponses, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdErrors, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdErrors, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponses } from './types.gen'; +import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksErrors, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersErrors, GetBotsByIdMembersResponses, GetBotsByIdResponses, GetBotsData, GetBotsErrors, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformErrors, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsErrors, GetChannelsResponses, GetModelsByIdData, GetModelsByIdErrors, GetModelsByIdResponses, GetModelsCountData, GetModelsCountErrors, GetModelsCountResponses, GetModelsData, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponses, GetModelsResponses, GetProvidersByIdData, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponses, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountErrors, GetProvidersCountResponses, GetProvidersData, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameErrors, GetProvidersNameByNameResponses, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponses, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdErrors, GetUsersByIdResponses, GetUsersData, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponses, GetUsersMeResponses, GetUsersResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginErrors, PostAuthLoginResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsErrors, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsErrors, PostEmbeddingsResponses, PostModelsData, PostModelsErrors, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestErrors, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersErrors, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersErrors, PostSearchProvidersResponses, PostUsersData, PostUsersErrors, PostUsersResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersErrors, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponses, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdErrors, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdErrors, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponses } from './types.gen'; export type Options = Options2 & { /** @@ -152,6 +152,62 @@ export const postBotsByBotIdContainerStart = (options: Options) => (options.client ?? client).post({ url: '/bots/{bot_id}/container/stop', ...options }); +/** + * List inbox items + * + * List inbox items for a bot with optional filters + */ +export const getBotsByBotIdInbox = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/inbox', ...options }); + +/** + * Create inbox item + * + * Create a new inbox item (for external integrations) + */ +export const postBotsByBotIdInbox = (options: Options) => (options.client ?? client).post({ + url: '/bots/{bot_id}/inbox', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Count inbox items + * + * Count unread and total inbox items + */ +export const getBotsByBotIdInboxCount = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/inbox/count', ...options }); + +/** + * Mark inbox items as read + * + * Batch mark inbox items as read + */ +export const postBotsByBotIdInboxMarkRead = (options: Options) => (options.client ?? client).post({ + url: '/bots/{bot_id}/inbox/mark-read', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete inbox item + * + * Delete a single inbox item + */ +export const deleteBotsByBotIdInboxById = (options: Options) => (options.client ?? client).delete({ url: '/bots/{bot_id}/inbox/{id}', ...options }); + +/** + * Get inbox item + * + * Get a single inbox item by ID + */ +export const getBotsByBotIdInboxById = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/inbox/{id}', ...options }); + /** * List MCP connections * diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts index 495ee9ed..5e456290 100644 --- a/packages/sdk/src/types.gen.ts +++ b/packages/sdk/src/types.gen.ts @@ -490,6 +490,10 @@ export type HandlersListMyIdentitiesResponse = { user_id?: string; }; +export type HandlersMarkReadRequest = { + ids?: Array; +}; + export type HandlersMemoryAddPayload = { embedding_enabled?: boolean; filters?: { @@ -544,6 +548,31 @@ export type IdentitiesChannelIdentity = { user_id?: string; }; +export type InboxCountResult = { + total?: number; + unread?: number; +}; + +export type InboxCreateRequest = { + bot_id?: string; + content?: { + [key: string]: unknown; + }; + source?: string; +}; + +export type InboxItem = { + bot_id?: string; + content?: { + [key: string]: unknown; + }; + created_at?: string; + id?: string; + is_read?: boolean; + read_at?: string; + source?: string; +}; + export type McpExportResponse = { mcpServers?: { [key: string]: McpMcpServerEntry; @@ -890,6 +919,7 @@ export type SettingsSettings = { language?: string; max_context_load_time?: number; max_context_tokens?: number; + max_inbox_items?: number; memory_model_id?: string; search_provider_id?: string; }; @@ -901,6 +931,7 @@ export type SettingsUpsertRequest = { language?: string; max_context_load_time?: number; max_context_tokens?: number; + max_inbox_items?: number; memory_model_id?: string; search_provider_id?: string; }; @@ -1520,6 +1551,241 @@ export type PostBotsByBotIdContainerStopResponses = { export type PostBotsByBotIdContainerStopResponse = PostBotsByBotIdContainerStopResponses[keyof PostBotsByBotIdContainerStopResponses]; +export type GetBotsByBotIdInboxData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: { + /** + * Filter by read status (true/false) + */ + is_read?: string; + /** + * Filter by source + */ + source?: string; + /** + * Max items to return + */ + limit?: number; + /** + * Offset for pagination + */ + offset?: number; + }; + url: '/bots/{bot_id}/inbox'; +}; + +export type GetBotsByBotIdInboxErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetBotsByBotIdInboxError = GetBotsByBotIdInboxErrors[keyof GetBotsByBotIdInboxErrors]; + +export type GetBotsByBotIdInboxResponses = { + /** + * OK + */ + 200: Array; +}; + +export type GetBotsByBotIdInboxResponse = GetBotsByBotIdInboxResponses[keyof GetBotsByBotIdInboxResponses]; + +export type PostBotsByBotIdInboxData = { + /** + * Inbox item payload + */ + body: InboxCreateRequest; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: never; + url: '/bots/{bot_id}/inbox'; +}; + +export type PostBotsByBotIdInboxErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PostBotsByBotIdInboxError = PostBotsByBotIdInboxErrors[keyof PostBotsByBotIdInboxErrors]; + +export type PostBotsByBotIdInboxResponses = { + /** + * Created + */ + 201: InboxItem; +}; + +export type PostBotsByBotIdInboxResponse = PostBotsByBotIdInboxResponses[keyof PostBotsByBotIdInboxResponses]; + +export type GetBotsByBotIdInboxCountData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: never; + url: '/bots/{bot_id}/inbox/count'; +}; + +export type GetBotsByBotIdInboxCountErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetBotsByBotIdInboxCountError = GetBotsByBotIdInboxCountErrors[keyof GetBotsByBotIdInboxCountErrors]; + +export type GetBotsByBotIdInboxCountResponses = { + /** + * OK + */ + 200: InboxCountResult; +}; + +export type GetBotsByBotIdInboxCountResponse = GetBotsByBotIdInboxCountResponses[keyof GetBotsByBotIdInboxCountResponses]; + +export type PostBotsByBotIdInboxMarkReadData = { + /** + * Item IDs to mark as read + */ + body: HandlersMarkReadRequest; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: never; + url: '/bots/{bot_id}/inbox/mark-read'; +}; + +export type PostBotsByBotIdInboxMarkReadErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PostBotsByBotIdInboxMarkReadError = PostBotsByBotIdInboxMarkReadErrors[keyof PostBotsByBotIdInboxMarkReadErrors]; + +export type PostBotsByBotIdInboxMarkReadResponses = { + /** + * No Content + */ + 204: unknown; +}; + +export type DeleteBotsByBotIdInboxByIdData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + /** + * Inbox item ID + */ + id: string; + }; + query?: never; + url: '/bots/{bot_id}/inbox/{id}'; +}; + +export type DeleteBotsByBotIdInboxByIdErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type DeleteBotsByBotIdInboxByIdError = DeleteBotsByBotIdInboxByIdErrors[keyof DeleteBotsByBotIdInboxByIdErrors]; + +export type DeleteBotsByBotIdInboxByIdResponses = { + /** + * No Content + */ + 204: unknown; +}; + +export type GetBotsByBotIdInboxByIdData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + /** + * Inbox item ID + */ + id: string; + }; + query?: never; + url: '/bots/{bot_id}/inbox/{id}'; +}; + +export type GetBotsByBotIdInboxByIdErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Not Found + */ + 404: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetBotsByBotIdInboxByIdError = GetBotsByBotIdInboxByIdErrors[keyof GetBotsByBotIdInboxByIdErrors]; + +export type GetBotsByBotIdInboxByIdResponses = { + /** + * OK + */ + 200: InboxItem; +}; + +export type GetBotsByBotIdInboxByIdResponse = GetBotsByBotIdInboxByIdResponses[keyof GetBotsByBotIdInboxByIdResponses]; + export type GetBotsByBotIdMcpData = { body?: never; path?: never; diff --git a/spec/docs.go b/spec/docs.go index 6d6f7673..066bcae1 100644 --- a/spec/docs.go +++ b/spec/docs.go @@ -662,6 +662,291 @@ const docTemplate = `{ } } }, + "/bots/{bot_id}/inbox": { + "get": { + "description": "List inbox items for a bot with optional filters", + "tags": [ + "inbox" + ], + "summary": "List inbox items", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filter by read status (true/false)", + "name": "is_read", + "in": "query" + }, + { + "type": "string", + "description": "Filter by source", + "name": "source", + "in": "query" + }, + { + "type": "integer", + "default": 50, + "description": "Max items to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/inbox.Item" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new inbox item (for external integrations)", + "tags": [ + "inbox" + ], + "summary": "Create inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "description": "Inbox item payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/inbox.CreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/inbox.Item" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/count": { + "get": { + "description": "Count unread and total inbox items", + "tags": [ + "inbox" + ], + "summary": "Count inbox items", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/inbox.CountResult" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/mark-read": { + "post": { + "description": "Batch mark inbox items as read", + "tags": [ + "inbox" + ], + "summary": "Mark inbox items as read", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "description": "Item IDs to mark as read", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.markReadRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/{id}": { + "get": { + "description": "Get a single inbox item by ID", + "tags": [ + "inbox" + ], + "summary": "Get inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Inbox item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/inbox.Item" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a single inbox item", + "tags": [ + "inbox" + ], + "summary": "Delete inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Inbox item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, "/bots/{bot_id}/mcp": { "get": { "description": "List MCP connections for a bot", @@ -6262,6 +6547,17 @@ const docTemplate = `{ } } }, + "handlers.markReadRequest": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "handlers.memoryAddPayload": { "type": "object", "properties": { @@ -6389,6 +6685,59 @@ const docTemplate = `{ } } }, + "inbox.CountResult": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "unread": { + "type": "integer" + } + } + }, + "inbox.CreateRequest": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "content": { + "type": "object", + "additionalProperties": {} + }, + "source": { + "type": "string" + } + } + }, + "inbox.Item": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "content": { + "type": "object", + "additionalProperties": {} + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_read": { + "type": "boolean" + }, + "read_at": { + "type": "string" + }, + "source": { + "type": "string" + } + } + }, "mcp.ExportResponse": { "type": "object", "properties": { @@ -7245,6 +7594,9 @@ const docTemplate = `{ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -7274,6 +7626,9 @@ const docTemplate = `{ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, diff --git a/spec/swagger.json b/spec/swagger.json index 6c7bb580..8dd014e8 100644 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -653,6 +653,291 @@ } } }, + "/bots/{bot_id}/inbox": { + "get": { + "description": "List inbox items for a bot with optional filters", + "tags": [ + "inbox" + ], + "summary": "List inbox items", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filter by read status (true/false)", + "name": "is_read", + "in": "query" + }, + { + "type": "string", + "description": "Filter by source", + "name": "source", + "in": "query" + }, + { + "type": "integer", + "default": 50, + "description": "Max items to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Offset for pagination", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/inbox.Item" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new inbox item (for external integrations)", + "tags": [ + "inbox" + ], + "summary": "Create inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "description": "Inbox item payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/inbox.CreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/inbox.Item" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/count": { + "get": { + "description": "Count unread and total inbox items", + "tags": [ + "inbox" + ], + "summary": "Count inbox items", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/inbox.CountResult" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/mark-read": { + "post": { + "description": "Batch mark inbox items as read", + "tags": [ + "inbox" + ], + "summary": "Mark inbox items as read", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "description": "Item IDs to mark as read", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.markReadRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, + "/bots/{bot_id}/inbox/{id}": { + "get": { + "description": "Get a single inbox item by ID", + "tags": [ + "inbox" + ], + "summary": "Get inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Inbox item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/inbox.Item" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a single inbox item", + "tags": [ + "inbox" + ], + "summary": "Delete inbox item", + "parameters": [ + { + "type": "string", + "description": "Bot ID", + "name": "bot_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Inbox item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.ErrorResponse" + } + } + } + } + }, "/bots/{bot_id}/mcp": { "get": { "description": "List MCP connections for a bot", @@ -6253,6 +6538,17 @@ } } }, + "handlers.markReadRequest": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "handlers.memoryAddPayload": { "type": "object", "properties": { @@ -6380,6 +6676,59 @@ } } }, + "inbox.CountResult": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "unread": { + "type": "integer" + } + } + }, + "inbox.CreateRequest": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "content": { + "type": "object", + "additionalProperties": {} + }, + "source": { + "type": "string" + } + } + }, + "inbox.Item": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "content": { + "type": "object", + "additionalProperties": {} + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_read": { + "type": "boolean" + }, + "read_at": { + "type": "string" + }, + "source": { + "type": "string" + } + } + }, "mcp.ExportResponse": { "type": "object", "properties": { @@ -7236,6 +7585,9 @@ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -7265,6 +7617,9 @@ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, diff --git a/spec/swagger.yaml b/spec/swagger.yaml index fbf58cff..34f6d532 100644 --- a/spec/swagger.yaml +++ b/spec/swagger.yaml @@ -817,6 +817,13 @@ definitions: user_id: type: string type: object + handlers.markReadRequest: + properties: + ids: + items: + type: string + type: array + type: object handlers.memoryAddPayload: properties: embedding_enabled: @@ -901,6 +908,41 @@ definitions: user_id: type: string type: object + inbox.CountResult: + properties: + total: + type: integer + unread: + type: integer + type: object + inbox.CreateRequest: + properties: + bot_id: + type: string + content: + additionalProperties: {} + type: object + source: + type: string + type: object + inbox.Item: + properties: + bot_id: + type: string + content: + additionalProperties: {} + type: object + created_at: + type: string + id: + type: string + is_read: + type: boolean + read_at: + type: string + source: + type: string + type: object mcp.ExportResponse: properties: mcpServers: @@ -1469,6 +1511,8 @@ definitions: type: integer max_context_tokens: type: integer + max_inbox_items: + type: integer memory_model_id: type: string search_provider_id: @@ -1488,6 +1532,8 @@ definitions: type: integer max_context_tokens: type: integer + max_inbox_items: + type: integer memory_model_id: type: string search_provider_id: @@ -2028,6 +2074,196 @@ paths: summary: Stop container task for bot tags: - containerd + /bots/{bot_id}/inbox: + get: + description: List inbox items for a bot with optional filters + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + - description: Filter by read status (true/false) + in: query + name: is_read + type: string + - description: Filter by source + in: query + name: source + type: string + - default: 50 + description: Max items to return + in: query + name: limit + type: integer + - default: 0 + description: Offset for pagination + in: query + name: offset + type: integer + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/inbox.Item' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: List inbox items + tags: + - inbox + post: + description: Create a new inbox item (for external integrations) + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + - description: Inbox item payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/inbox.CreateRequest' + responses: + "201": + description: Created + schema: + $ref: '#/definitions/inbox.Item' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: Create inbox item + tags: + - inbox + /bots/{bot_id}/inbox/{id}: + delete: + description: Delete a single inbox item + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + - description: Inbox item ID + in: path + name: id + required: true + type: string + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: Delete inbox item + tags: + - inbox + get: + description: Get a single inbox item by ID + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + - description: Inbox item ID + in: path + name: id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/inbox.Item' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: Get inbox item + tags: + - inbox + /bots/{bot_id}/inbox/count: + get: + description: Count unread and total inbox items + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/inbox.CountResult' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: Count inbox items + tags: + - inbox + /bots/{bot_id}/inbox/mark-read: + post: + description: Batch mark inbox items as read + parameters: + - description: Bot ID + in: path + name: bot_id + required: true + type: string + - description: Item IDs to mark as read + in: body + name: payload + required: true + schema: + $ref: '#/definitions/handlers.markReadRequest' + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.ErrorResponse' + summary: Mark inbox items as read + tags: + - inbox /bots/{bot_id}/mcp: get: description: List MCP connections for a bot