diff --git a/agent/package.json b/agent/package.json index 84830a47..813b9f3a 100644 --- a/agent/package.json +++ b/agent/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/agent-gateway", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "scripts": { "dev": "bun run --watch src/index.ts", "build": "bun build src/index.ts --outfile dist/index.js --target bun --minify", @@ -8,10 +8,7 @@ }, "dependencies": { "@memoh/config": "workspace:*", - "@ai-sdk/anthropic": "^3.0.9", - "@ai-sdk/google": "^3.0.6", - "@ai-sdk/mcp": "^1.0.6", - "@ai-sdk/openai": "^3.0.7", + "@memoh/agent": "workspace:*", "@elysiajs/bearer": "^1.4.2", "@elysiajs/cors": "^1.4.1", "@modelcontextprotocol/sdk": "^1.25.2", diff --git a/agent/src/index.ts b/agent/src/index.ts index 94126852..4ae5c75d 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -3,6 +3,7 @@ import { chatModule } from './modules/chat' import { corsMiddleware } from './middlewares/cors' import { errorMiddleware } from './middlewares/error' import { loadConfig, getBaseUrl as getBaseUrlByConfig } from '@memoh/config' +import { AuthFetcher } from '@memoh/agent' const config = loadConfig('../config.toml') @@ -10,10 +11,6 @@ export const getBaseUrl = () => { return getBaseUrlByConfig(config) } -export type AuthFetcher = ( - url: string, - options?: RequestInit, -) => Promise; export const createAuthFetcher = (bearer: string | undefined): AuthFetcher => { return async (url: string, options?: RequestInit) => { const requestOptions = options ?? {} diff --git a/agent/src/models.ts b/agent/src/models.ts index 1407c0e8..2bf0f0c5 100644 --- a/agent/src/models.ts +++ b/agent/src/models.ts @@ -1,5 +1,5 @@ import z from 'zod' -import { allActions } from './types' +import { allActions } from '@memoh/agent' export const AgentSkillModel = z.object({ name: z.string().min(1, 'Skill name is required'), @@ -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..4758ba91 100644 --- a/agent/src/modules/chat.ts +++ b/agent/src/modules/chat.ts @@ -1,11 +1,9 @@ import { Elysia } from 'elysia' import z from 'zod' -import { createAgent } from '../agent' +import { createAgent, ModelConfig, allActions } from '@memoh/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 { allActions } from '../types' +import { AgentSkillModel, AllowedActionModel, AttachmentModel, IdentityContextModel, InboxItemModel, MCPConnectionModel, ModelConfigModel, ScheduleModel } from '../models' import { sseChunked } from '../utils/sse' const AgentModel = z.object({ @@ -20,6 +18,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 +39,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 +69,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 +109,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/test/attachments.test.ts b/agent/src/test/attachments.test.ts deleted file mode 100644 index 84998edc..00000000 --- a/agent/src/test/attachments.test.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { describe, test, expect } from 'bun:test' -import type { ModelMessage } from 'ai' -import { - parseAttachmentPaths, - extractAttachmentsFromText, - stripAttachmentsFromMessages, - dedupeAttachments, - AttachmentsStreamExtractor, -} from '../utils/attachments' -import { buildNativeImageParts } from '../agent' -import type { ContainerFileAttachment, GatewayInputAttachment } from '../types/attachment' - -// --------------------------------------------------------------------------- -// parseAttachmentPaths -// --------------------------------------------------------------------------- - -describe('parseAttachmentPaths', () => { - test('parses standard list', () => { - const input = ` -- /path/to/file.pdf -- /path/to/video.mp4 - ` - expect(parseAttachmentPaths(input)).toEqual([ - '/path/to/file.pdf', - '/path/to/video.mp4', - ]) - }) - - test('ignores lines without leading dash', () => { - const input = ` -some random text -- /valid/path.txt -not a path -- /another/path.png - ` - expect(parseAttachmentPaths(input)).toEqual([ - '/valid/path.txt', - '/another/path.png', - ]) - }) - - test('returns empty array for empty input', () => { - expect(parseAttachmentPaths('')).toEqual([]) - }) - - test('handles extra whitespace around paths', () => { - const input = ' - /spaced/path.txt ' - expect(parseAttachmentPaths(input)).toEqual(['/spaced/path.txt']) - }) -}) - -// --------------------------------------------------------------------------- -// extractAttachmentsFromText -// --------------------------------------------------------------------------- - -describe('extractAttachmentsFromText', () => { - test('extracts a single block', () => { - const text = 'Hello world\n\n- /file.pdf\n\nGoodbye' - const { cleanedText, attachments } = extractAttachmentsFromText(text) - expect(attachments).toEqual([{ type: 'file', path: '/file.pdf' }]) - expect(cleanedText).toBe('Hello world\n\nGoodbye') - }) - - test('extracts multiple blocks', () => { - const text = [ - 'Start', - '', - '- /a.txt', - '', - 'Middle', - '', - '- /b.txt', - '', - 'End', - ].join('\n') - const { cleanedText, attachments } = extractAttachmentsFromText(text) - expect(attachments).toHaveLength(2) - expect(attachments.map(a => a.path)).toEqual(['/a.txt', '/b.txt']) - expect(cleanedText).toContain('Start') - expect(cleanedText).toContain('Middle') - expect(cleanedText).toContain('End') - expect(cleanedText).not.toContain('') - }) - - test('deduplicates paths across blocks', () => { - const text = [ - '', - '- /dup.txt', - '', - '', - '- /dup.txt', - '', - ].join('\n') - const { attachments } = extractAttachmentsFromText(text) - expect(attachments).toHaveLength(1) - expect(attachments[0].path).toBe('/dup.txt') - }) - - test('returns original text when no blocks present', () => { - const text = 'No attachments here' - const { cleanedText, attachments } = extractAttachmentsFromText(text) - expect(cleanedText).toBe('No attachments here') - expect(attachments).toEqual([]) - }) - - test('collapses excessive newlines left by removal', () => { - const text = 'Line1\n\n\n\n- /f.txt\n\n\n\nLine2' - const { cleanedText } = extractAttachmentsFromText(text) - // Should not have more than two consecutive newlines - expect(cleanedText).not.toMatch(/\n{3,}/) - }) -}) - -// --------------------------------------------------------------------------- -// stripAttachmentsFromMessages -// --------------------------------------------------------------------------- - -describe('stripAttachmentsFromMessages', () => { - test('strips from assistant message with string content', () => { - const messages: ModelMessage[] = [ - { role: 'user', content: 'hi' }, - { - role: 'assistant', - content: 'Here you go\n\n- /result.pdf\n', - }, - ] - const { messages: stripped, attachments } = stripAttachmentsFromMessages(messages) - expect(attachments).toEqual([{ type: 'file', path: '/result.pdf' }]) - const assistantMsg = stripped.find(m => m.role === 'assistant')! - expect((assistantMsg as { content: string }).content).not.toContain('') - }) - - test('strips from assistant message with array content containing TextPart', () => { - const messages: ModelMessage[] = [ - { - role: 'assistant', - content: [ - { type: 'text', text: 'Check this\n\n- /img.png\n' }, - ], - }, - ] - const { messages: stripped, attachments } = stripAttachmentsFromMessages(messages) - expect(attachments).toEqual([{ type: 'file', path: '/img.png' }]) - const content = (stripped[0] as { content: Array<{ type: string; text?: string }> }).content - expect(content[0].text).not.toContain('') - }) - - test('does not modify user or tool messages', () => { - const messages: ModelMessage[] = [ - { role: 'user', content: '\n- /should-stay.txt\n' }, - ] - const { messages: stripped, attachments } = stripAttachmentsFromMessages(messages) - expect(attachments).toEqual([]) - expect((stripped[0] as { content: string }).content).toContain('') - }) - - test('deduplicates attachments across messages', () => { - const messages: ModelMessage[] = [ - { role: 'assistant', content: '\n- /same.txt\n' }, - { role: 'assistant', content: '\n- /same.txt\n' }, - ] - const { attachments } = stripAttachmentsFromMessages(messages) - expect(attachments).toHaveLength(1) - }) -}) - -// --------------------------------------------------------------------------- -// dedupeAttachments -// --------------------------------------------------------------------------- - -describe('dedupeAttachments', () => { - test('deduplicates file attachments by path', () => { - const items: ContainerFileAttachment[] = [ - { type: 'file', path: '/a.txt' }, - { type: 'file', path: '/b.txt' }, - { type: 'file', path: '/a.txt' }, - ] - const result = dedupeAttachments(items) - expect(result).toHaveLength(2) - }) - - test('deduplicates image attachments by base64 prefix', () => { - const base64 = 'a'.repeat(100) - const result = dedupeAttachments([ - { type: 'image', base64 }, - { type: 'image', base64 }, - ]) - expect(result).toHaveLength(1) - }) - - test('keeps different types separate', () => { - const result = dedupeAttachments([ - { type: 'file', path: '/a.txt' }, - { type: 'image', base64: 'abc' }, - ]) - expect(result).toHaveLength(2) - }) -}) - -describe('buildNativeImageParts', () => { - test('keeps inline data url and public url images', () => { - const attachments: GatewayInputAttachment[] = [ - { type: 'image', transport: 'inline_data_url', payload: 'data:image/png;base64,AAAA' }, - { type: 'image', transport: 'public_url', payload: 'https://example.com/demo.png' }, - ] - const parts = buildNativeImageParts(attachments) - expect(parts).toHaveLength(2) - expect(parts[0].image).toBe('data:image/png;base64,AAAA') - expect(parts[1].image).toBe('https://example.com/demo.png') - }) - - test('drops tool_file_ref images', () => { - const attachments: GatewayInputAttachment[] = [ - { type: 'image', transport: 'tool_file_ref', payload: '/data/media/image/demo.png' }, - ] - const parts = buildNativeImageParts(attachments) - expect(parts).toEqual([]) - }) -}) - -// --------------------------------------------------------------------------- -// AttachmentsStreamExtractor -// --------------------------------------------------------------------------- - -describe('AttachmentsStreamExtractor', () => { - /** Helper: simulates streaming by feeding one character at a time. */ - const feedCharByChar = (extractor: AttachmentsStreamExtractor, text: string) => { - let visibleText = '' - const attachments: ContainerFileAttachment[] = [] - for (const ch of text) { - const result = extractor.push(ch) - visibleText += result.visibleText - attachments.push(...result.attachments) - } - const remainder = extractor.flushRemainder() - visibleText += remainder.visibleText - attachments.push(...remainder.attachments) - return { visibleText, attachments } - } - - /** Helper: simulates streaming by feeding the entire string at once. */ - const feedAtOnce = (extractor: AttachmentsStreamExtractor, text: string) => { - const result = extractor.push(text) - const remainder = extractor.flushRemainder() - return { - visibleText: result.visibleText + remainder.visibleText, - attachments: [...result.attachments, ...remainder.attachments], - } - } - - test('passes through plain text (char-by-char)', () => { - const ext = new AttachmentsStreamExtractor() - const { visibleText, attachments } = feedCharByChar(ext, 'Hello world') - expect(visibleText).toBe('Hello world') - expect(attachments).toEqual([]) - }) - - test('passes through plain text (all-at-once)', () => { - const ext = new AttachmentsStreamExtractor() - const { visibleText, attachments } = feedAtOnce(ext, 'Hello world') - expect(visibleText).toBe('Hello world') - expect(attachments).toEqual([]) - }) - - test('extracts attachments block (char-by-char)', () => { - const ext = new AttachmentsStreamExtractor() - const input = 'Before\n- /file.pdf\nAfter' - const { visibleText, attachments } = feedCharByChar(ext, input) - expect(visibleText).toBe('BeforeAfter') - expect(attachments).toEqual([{ type: 'file', path: '/file.pdf' }]) - }) - - test('extracts attachments block (all-at-once)', () => { - const ext = new AttachmentsStreamExtractor() - const input = 'Before\n- /file.pdf\nAfter' - const { visibleText, attachments } = feedAtOnce(ext, input) - expect(visibleText).toBe('BeforeAfter') - expect(attachments).toEqual([{ type: 'file', path: '/file.pdf' }]) - }) - - test('extracts multiple paths from one block', () => { - const ext = new AttachmentsStreamExtractor() - const input = '\n- /a.txt\n- /b.txt\n' - const { attachments } = feedCharByChar(ext, input) - expect(attachments.map(a => a.path)).toEqual(['/a.txt', '/b.txt']) - }) - - test('handles multiple blocks in one stream', () => { - const ext = new AttachmentsStreamExtractor() - const input = 'A\n- /x.txt\nB\n- /y.txt\nC' - const { visibleText, attachments } = feedCharByChar(ext, input) - expect(visibleText).toBe('ABC') - expect(attachments.map(a => a.path)).toEqual(['/x.txt', '/y.txt']) - }) - - test('handles chunk boundaries splitting the opening tag', () => { - const ext = new AttachmentsStreamExtractor() - let visible = '' - const attachments: ContainerFileAttachment[] = [] - - // Feed the opening tag across two chunks - let r = ext.push('Hello \n- /split.txt\n Done') - visible += r.visibleText - attachments.push(...r.attachments) - - const remainder = ext.flushRemainder() - visible += remainder.visibleText - attachments.push(...remainder.attachments) - - expect(visible).toBe('Hello Done') - expect(attachments).toEqual([{ type: 'file', path: '/split.txt' }]) - }) - - test('handles chunk boundaries splitting the closing tag', () => { - const ext = new AttachmentsStreamExtractor() - let visible = '' - const attachments: ContainerFileAttachment[] = [] - - let r = ext.push('\n- /f.txt\nTail') - visible += r.visibleText - attachments.push(...r.attachments) - - const remainder = ext.flushRemainder() - visible += remainder.visibleText - attachments.push(...remainder.attachments) - - expect(visible).toBe('Tail') - expect(attachments).toEqual([{ type: 'file', path: '/f.txt' }]) - }) - - test('flushRemainder returns raw text for unclosed block', () => { - const ext = new AttachmentsStreamExtractor() - ext.push('\n- /orphan.txt\n') - const remainder = ext.flushRemainder() - // Unclosed block should be returned as visible text - expect(remainder.visibleText).toContain('') - expect(remainder.visibleText).toContain('/orphan.txt') - expect(remainder.attachments).toEqual([]) - }) - - test('text without any angle brackets passes through immediately', () => { - const ext = new AttachmentsStreamExtractor() - const r = ext.push('simple text without tags') - // Most of the text should be emitted (minus a small buffered tail) - const remainder = ext.flushRemainder() - const full = r.visibleText + remainder.visibleText - expect(full).toBe('simple text without tags') - }) -}) - diff --git a/agent/src/test/sse_chunked.test.ts b/agent/src/test/sse_chunked.test.ts deleted file mode 100644 index 85cfb580..00000000 --- a/agent/src/test/sse_chunked.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { sseChunked } from '../utils/sse' - -function parseChunkedSSE(payload: string): string { - const lines = payload.split('\n') - const dataLines = lines.filter(line => line.startsWith('data:')) - return dataLines.map(line => line.slice('data:'.length)).join('') -} - -describe('sseChunked', () => { - test('reconstructs original payload losslessly', () => { - const input = JSON.stringify({ - type: 'tool_call_end', - toolName: 'big_tool', - toolCallId: 'call-1', - // include whitespace and unicode so trimming/surrogate splitting bugs show up - result: ' leading spaces\tand tabs\nand unicode 😀😃😄 ', - blob: 'x'.repeat(200_000), - }) - - const chunked = sseChunked(input, 1024).toSSE() - const reconstructed = parseChunkedSSE(chunked) - - expect(reconstructed).toBe(input) - }) - - test('chunkSize=1 does not produce invalid UTF-8 (surrogate pairs)', () => { - const input = `😀${'x'.repeat(1000)}😃` - const payload = sseChunked(input, 1).toSSE() - - // Simulate the UTF-8 encode/decode step that happens over the network. - const encoded = new TextEncoder().encode(payload) - const decoded = new TextDecoder().decode(encoded) - expect(decoded).toBe(payload) - - const reconstructed = parseChunkedSSE(decoded) - expect(reconstructed).toBe(input) - }) - - test('does not inject an extra space after data:', () => { - const input = ' abc' - const chunked = sseChunked(input, 2).toSSE() - expect(chunked.split('\n')[0]).toBe('data: a') - }) -}) diff --git a/agent/src/test/unified_mcp_tools.test.ts b/agent/src/test/unified_mcp_tools.test.ts deleted file mode 100644 index c580a365..00000000 --- a/agent/src/test/unified_mcp_tools.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { getMCPTools } from '../tools/mcp' - -describe('getMCPTools (unified endpoint)', () => { - test('loads tools from unified MCP HTTP endpoint', async () => { - const seenMethods: string[] = [] - const seenAuthHeaders: string[] = [] - - const server = Bun.serve({ - port: 0, - async fetch(request) { - seenAuthHeaders.push(request.headers.get('authorization') ?? '') - const body = await request.json().catch(() => ({} as Record)) - const method = typeof body?.method === 'string' ? body.method : '' - seenMethods.push(method) - - if (method === 'initialize') { - return Response.json({ - jsonrpc: '2.0', - id: body.id ?? null, - result: { - protocolVersion: '2025-06-18', - capabilities: { - tools: { - listChanged: false, - }, - }, - serverInfo: { - name: 'test-mcp', - version: '1.0.0', - }, - }, - }) - } - - if (method === 'notifications/initialized') { - return new Response(null, { status: 202 }) - } - - if (method === 'tools/list') { - return Response.json({ - jsonrpc: '2.0', - id: body.id ?? null, - result: { - tools: [ - { - name: 'search_memory', - description: 'Search memory', - inputSchema: { - type: 'object', - properties: { - query: { type: 'string' }, - }, - required: ['query'], - }, - }, - ], - }, - }) - } - - return Response.json({ - jsonrpc: '2.0', - id: body.id ?? null, - error: { - code: -32601, - message: 'method not found', - }, - }) - }, - }) - - try { - const endpoint = `http://127.0.0.1:${server.port}/bots/bot-1/tools` - const { tools, close } = await getMCPTools([{ - type: 'http', - name: 'builtin', - url: endpoint, - headers: { - Authorization: 'Bearer test-token', - }, - }]) - - expect(Object.keys(tools)).toContain('search_memory') - expect(seenMethods).toContain('initialize') - expect(seenMethods).toContain('tools/list') - expect(seenAuthHeaders.some(value => value === 'Bearer test-token')).toBe(true) - - await close() - } finally { - server.stop(true) - } - }) -}) diff --git a/agent/src/utils/container.ts b/agent/src/utils/container.ts deleted file mode 100644 index 547ace96..00000000 --- a/agent/src/utils/container.ts +++ /dev/null @@ -1,339 +0,0 @@ - -interface JSONRPCRequest { - jsonrpc: string - id: string | number - method: string - params?: unknown -} - -interface JSONRPCResponse { - jsonrpc: string - id: string | number - result?: T - error?: { - code: number - message: string - } -} - -interface ToolCallContent { - type: string - text?: string -} - -interface ToolCallResult { - content: ToolCallContent[] - isError?: boolean -} - -export interface FSFileEntry { - path: string - is_dir: boolean - size: number - mode: number - mod_time: string -} - -export interface FSReadResult { - content: string -} - -export interface FSReadBase64Result { - data: string - mime_type: string -} - -export interface FSWriteResult { - ok: boolean -} - -export interface FSListResult { - path: string - entries: FSFileEntry[] -} - -export interface FSStatResult { - entry: FSFileEntry -} - -export interface FSDeleteResult { - ok: boolean -} - -export interface FSApplyPatchResult { - ok: boolean -} - -export interface FSMkdirResult { - ok: boolean -} - -export interface FSRenameResult { - ok: boolean -} - -export interface GrepResult { - stdout: string - stderr: string - exit_code: number -} - -export interface EchoResult { - text: string -} - -export interface ToolInfo { - name: string - description?: string - inputSchema?: Record -} - -export interface ToolsListResult { - tools: ToolInfo[] -} - -export interface UseContainerOptions { - url: string - fetch: (url: string, options?: RequestInit) => Promise -} - -class JSONRPCClient { - private requestId = 0 - - constructor( - private url: string, - private fetch: (url: string, options?: RequestInit) => Promise - ) {} - - async call(method: string, params?: unknown): Promise { - const request: JSONRPCRequest = { - jsonrpc: '2.0', - id: ++this.requestId, - method, - params, - } - - const response = await this.fetch(this.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(request), - }) - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - - const jsonResponse: JSONRPCResponse = await response.json() - - if (jsonResponse.error) { - throw new Error( - `JSON-RPC Error ${jsonResponse.error.code}: ${jsonResponse.error.message}` - ) - } - - return jsonResponse.result as T - } -} - -class MCPToolCaller { - constructor(private rpcClient: JSONRPCClient) {} - - async callTool(toolName: string, args: Record): Promise { - const result = await this.rpcClient.call('tools/call', { - name: toolName, - arguments: args, - }) - - if (result.isError) { - const errorMessage = result.content?.[0]?.text || 'Tool execution failed' - throw new Error(errorMessage) - } - - const textContent = result.content?.[0]?.text - if (textContent) { - try { - return JSON.parse(textContent) as T - } catch { - return textContent as T - } - } - - throw new Error('No result content returned') - } -} - -class FileSystemOperations { - constructor(private toolCaller: MCPToolCaller) {} - - async read(path: string): Promise { - const result = await this.toolCaller.callTool('fs.read', { path }) - return result.content - } - - async readBase64(path: string): Promise { - return this.toolCaller.callTool('fs.read_base64', { path }) - } - - async write(path: string, content: string): Promise { - const result = await this.toolCaller.callTool('fs.write', { - path, - content, - }) - return result.ok - } - - async list(path: string = '.', recursive: boolean = false): Promise { - return this.toolCaller.callTool('fs.list', { path, recursive }) - } - - async stat(path: string): Promise { - const result = await this.toolCaller.callTool('fs.stat', { path }) - return result.entry - } - - async delete(path: string): Promise { - const result = await this.toolCaller.callTool('fs.delete', { path }) - return result.ok - } - - async mkdir(path: string): Promise { - const result = await this.toolCaller.callTool('fs.mkdir', { path }) - return result.ok - } - - async rename(source: string, destination: string): Promise { - const result = await this.toolCaller.callTool('fs.rename', { - source, - destination, - }) - return result.ok - } - - async applyPatch(path: string, patch: string): Promise { - const result = await this.toolCaller.callTool('fs.apply_patch', { - path, - patch, - }) - return result.ok - } - - async exists(path: string): Promise { - try { - await this.stat(path) - return true - } catch { - return false - } - } - - async readJSON(path: string): Promise { - const content = await this.read(path) - return JSON.parse(content) - } - - async writeJSON(path: string, data: unknown, pretty: boolean = true): Promise { - const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data) - return this.write(path, content) - } - - async append(path: string, content: string): Promise { - const exists = await this.exists(path) - if (exists) { - const existing = await this.read(path) - return this.write(path, existing + content) - } - return this.write(path, content) - } - - async copy(source: string, destination: string): Promise { - const stat = await this.stat(source) - if (stat.is_dir) { - throw new Error('Directory copy not implemented. Use list + copy for each file.') - } - const content = await this.read(source) - return this.write(destination, content) - } -} - -class SearchOperations { - constructor(private toolCaller: MCPToolCaller) {} - - async grep(pattern: string, args: string[] = []): Promise { - return this.toolCaller.callTool('grep', { pattern, args }) - } - - async search( - pattern: string, - options: { - caseSensitive?: boolean - lineNumbers?: boolean - filesOnly?: boolean - } = {} - ): Promise { - const args: string[] = ['-r'] - if (!options.caseSensitive) args.push('-i') - if (options.lineNumbers) args.push('-n') - if (options.filesOnly) args.push('-l') - - const result = await this.grep(pattern, args) - return result.stdout - } - - async findFiles(pattern: string): Promise { - const result = await this.grep(pattern, ['-r', '-l']) - return result.stdout - .split('\n') - .map((line) => line.trim()) - .filter((line) => line.length > 0) - } -} - -class UtilityOperations { - constructor(private toolCaller: MCPToolCaller) {} - - async echo(text: string): Promise { - const result = await this.toolCaller.callTool('echo', { text }) - return result.text - } - - async ping(): Promise { - try { - const result = await this.echo('ping') - return result === 'ping' - } catch { - return false - } - } -} - -export class ContainerClient { - private rpcClient: JSONRPCClient - private toolCaller: MCPToolCaller - - public readonly fs: FileSystemOperations - public readonly search: SearchOperations - public readonly utils: UtilityOperations - - constructor(options: UseContainerOptions) { - this.rpcClient = new JSONRPCClient(options.url, options.fetch) - this.toolCaller = new MCPToolCaller(this.rpcClient) - - this.fs = new FileSystemOperations(this.toolCaller) - this.search = new SearchOperations(this.toolCaller) - this.utils = new UtilityOperations(this.toolCaller) - } - - async listTools(): Promise { - return this.rpcClient.call('tools/list') - } - - async callTool(toolName: string, args: Record = {}): Promise { - return this.toolCaller.callTool(toolName, args) - } -} - -export const useContainer = (options: UseContainerOptions): ContainerClient => { - return new ContainerClient(options) -} \ No newline at end of file diff --git a/agent/src/utils/skill.ts b/agent/src/utils/skill.ts deleted file mode 100644 index e69de29b..00000000 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 6e0ed770..0fd77373 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, @@ -389,3 +390,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 cbcfbca8..3dc0087e 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.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.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/docker-compose.yml b/docker-compose.yml index b94986a6..225b2afc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: POSTGRES_USER: memoh POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-memoh123} volumes: - - postgres_data:/var/lib/postgresql/data + - postgres_data:/var/lib/postgresql - /etc/localtime:/etc/localtime:ro expose: - "5432" diff --git a/docs/package.json b/docs/package.json index b3223783..fcbc105a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/docs", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "private": true, "type": "module", "scripts": { 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 159b6eae..91f5ba2d 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -22,6 +22,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" @@ -76,6 +77,7 @@ type Resolver struct { conversationSvc ConversationSettingsReader messageService messagepkg.Service settingsService *settings.Service + inboxService *inbox.Service skillLoader SkillLoader assetLoader gatewayAssetLoader gatewayBaseURL string @@ -130,6 +132,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 { @@ -157,6 +165,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"` @@ -169,12 +184,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 { @@ -220,9 +237,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) { @@ -286,6 +304,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) @@ -294,6 +319,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) @@ -323,6 +354,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) @@ -331,6 +387,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, ) @@ -361,9 +418,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 --- @@ -379,9 +437,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, @@ -433,7 +492,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 --- @@ -482,7 +541,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 } @@ -680,15 +741,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) } } } @@ -696,7 +758,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 } @@ -960,8 +1022,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) { @@ -969,7 +1032,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 } @@ -984,13 +1047,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 } @@ -1013,44 +1078,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 } } @@ -1061,6 +1103,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) @@ -1188,24 +1239,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 } @@ -1255,7 +1310,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{ @@ -1277,6 +1334,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 { @@ -1590,6 +1651,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) { @@ -1717,21 +1789,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 24118046..f40d9e3d 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 a5445117..48a8fa8d 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.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.UUID `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.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.UUID `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 ec434bad..9b491047 100644 --- a/internal/settings/service.go +++ b/internal/settings/service.go @@ -57,13 +57,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) } @@ -113,6 +116,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, @@ -137,10 +141,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, } @@ -150,6 +155,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 } @@ -160,6 +168,7 @@ func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, row.MaxContextTokens, + row.MaxInboxItems, row.Language, row.AllowGuest, row.ChatModelID, @@ -173,6 +182,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, row.MaxContextTokens, + row.MaxInboxItems, row.Language, row.AllowGuest, row.ChatModelID, @@ -185,6 +195,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { func normalizeBotSettingsFields( maxContextLoadTime int32, maxContextTokens int32, + maxInboxItems int32, language string, allowGuest bool, chatModelID pgtype.UUID, @@ -192,7 +203,7 @@ func normalizeBotSettingsFields( embeddingModelID pgtype.UUID, searchProviderID pgtype.UUID, ) Settings { - settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, language, allowGuest) + settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, maxInboxItems, language, allowGuest) if chatModelID.Valid { settings.ChatModelID = uuid.UUID(chatModelID.Bytes).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/package.json b/package.json index 53914c9f..b819fb88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@memoh/monorepo", "private": true, - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "scripts": { "web:dev": "pnpm --filter @memoh/web dev", "web:build": "pnpm --filter @memoh/web build", diff --git a/packages/agent/.gitignore b/packages/agent/.gitignore new file mode 100644 index 00000000..a14702c4 --- /dev/null +++ b/packages/agent/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/agent/README.md b/packages/agent/README.md new file mode 100644 index 00000000..af2484c7 --- /dev/null +++ b/packages/agent/README.md @@ -0,0 +1,2 @@ +# @memoh/agent + diff --git a/packages/agent/package.json b/packages/agent/package.json new file mode 100644 index 00000000..894d2a4b --- /dev/null +++ b/packages/agent/package.json @@ -0,0 +1,26 @@ +{ + "name": "@memoh/agent", + "version": "0.1.0-beta.5", + "exports": { + ".": "./src/index.ts" + }, + "packageManager": "pnpm@10.27.0", + "module": "src/index.ts", + "type": "module", + "private": true, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@ai-sdk/anthropic": "^3.0.9", + "@ai-sdk/google": "^3.0.6", + "@ai-sdk/mcp": "^1.0.6", + "@ai-sdk/openai": "^3.0.7", + "@mozilla/readability": "^0.6.0", + "ai": "^6.0.25", + "jsdom": "^27.4.0", + "toml": "^3.0.0", + "turndown": "^7.2.2", + "zod": "^4.3.6" + } +} diff --git a/agent/src/agent.ts b/packages/agent/src/agent.ts similarity index 66% rename from agent/src/agent.ts rename to packages/agent/src/agent.ts index 6977e54c..8ad2973b 100644 --- a/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -12,15 +12,15 @@ import { AgentInput, AgentParams, AgentSkill, + AgentStreamAction, allActions, MCPConnection, Schedule, } from './types' import { ModelInput, hasInputModality } from './types/model' import { system, schedule, subagentSystem } from './prompts' -import { AuthFetcher } from './index' +import { AuthFetcher } from './types' import { createModel } from './model' -import { AgentAction } from './types/action' import { extractAttachmentsFromText, stripAttachmentsFromMessages, @@ -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, ) => { @@ -102,7 +115,8 @@ export const createAgent = ( }) const response = await fetch(url, { method: 'POST', headers, body }) if (!response.ok) return '' - const data = await response.json().catch(() => ({})) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data = await response.json().catch(() => ({})) as any const structured = data?.result?.structuredContent ?? data?.result?.content?.[0]?.text if (typeof structured === 'string') { @@ -144,6 +158,7 @@ export const createAgent = ( identityContent, soulContent, toolsContent, + inbox, }) } @@ -203,7 +218,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 +233,7 @@ export const createAgent = ( }, tools, }) + const stepUsages = buildStepUsages(steps) const { cleanedText, attachments: textAttachments } = extractAttachmentsFromText(text) const { messages: strippedMessages, attachments: messageAttachments } = @@ -231,6 +247,7 @@ export const createAgent = ( userPrompt, ...strippedMessages, ], + usages: [null, ...stepUsages] as (LanguageModelUsage | null)[], reasoning: reasoning.map((part) => part.text), usage, text: cleanedText, @@ -258,7 +275,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 +290,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 +318,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 +328,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, @@ -340,7 +361,7 @@ export const createAgent = ( return 'Model stream failed' } - async function* stream(input: AgentInput): AsyncGenerator { + async function* stream(input: AgentInput): AsyncGenerator { const userPrompt = generateUserPrompt(input) const messages = [...input.messages, userPrompt] input.skills.forEach((skill) => enableSkill(skill)) @@ -350,148 +371,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/packages/agent/src/index.ts b/packages/agent/src/index.ts new file mode 100644 index 00000000..dc057cf1 --- /dev/null +++ b/packages/agent/src/index.ts @@ -0,0 +1,6 @@ +export * from './agent' +export * from './types' +export * from './model' +export * from './utils' +export * from './tools' +export * from './prompts' \ No newline at end of file diff --git a/agent/src/model.ts b/packages/agent/src/model.ts similarity index 100% rename from agent/src/model.ts rename to packages/agent/src/model.ts diff --git a/agent/src/prompts/index.ts b/packages/agent/src/prompts/index.ts similarity index 100% rename from agent/src/prompts/index.ts rename to packages/agent/src/prompts/index.ts diff --git a/agent/src/prompts/schedule.ts b/packages/agent/src/prompts/schedule.ts similarity index 100% rename from agent/src/prompts/schedule.ts rename to packages/agent/src/prompts/schedule.ts diff --git a/agent/src/prompts/subagent.ts b/packages/agent/src/prompts/subagent.ts similarity index 100% rename from agent/src/prompts/subagent.ts rename to packages/agent/src/prompts/subagent.ts diff --git a/agent/src/prompts/system.ts b/packages/agent/src/prompts/system.ts similarity index 86% rename from agent/src/prompts/system.ts rename to packages/agent/src/prompts/system.ts index 052eee80..a6dea358 100644 --- a/agent/src/prompts/system.ts +++ b/packages/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/prompts/utils.ts b/packages/agent/src/prompts/utils.ts similarity index 100% rename from agent/src/prompts/utils.ts rename to packages/agent/src/prompts/utils.ts diff --git a/agent/src/tools/index.ts b/packages/agent/src/tools/index.ts similarity index 88% rename from agent/src/tools/index.ts rename to packages/agent/src/tools/index.ts index 1e1b2230..ed1659a2 100644 --- a/agent/src/tools/index.ts +++ b/packages/agent/src/tools/index.ts @@ -1,4 +1,4 @@ -import { AuthFetcher } from '..' +import { AuthFetcher } from '../types' import { AgentAction, AgentAuthContext, IdentityContext, ModelConfig } from '../types' import { ToolSet } from 'ai' import { getWebTools } from './web' @@ -32,3 +32,8 @@ export const getTools = ( } return tools } + +export * from './web' +export * from './subagent' +export * from './skill' +export * from './mcp' diff --git a/agent/src/tools/mcp.ts b/packages/agent/src/tools/mcp.ts similarity index 96% rename from agent/src/tools/mcp.ts rename to packages/agent/src/tools/mcp.ts index 90260ea0..062e98a2 100644 --- a/agent/src/tools/mcp.ts +++ b/packages/agent/src/tools/mcp.ts @@ -1,6 +1,6 @@ import { HTTPMCPConnection, MCPConnection, SSEMCPConnection, StdioMCPConnection } from '../types' import { createMCPClient } from '@ai-sdk/mcp' -import { AuthFetcher } from '../index' +import { AuthFetcher } from '../types' import type { AgentAuthContext } from '../types/agent' type MCPToolOptions = { @@ -59,7 +59,7 @@ export const getMCPTools = async (connections: MCPConnection[], options: MCPTool const text = await response.text().catch(() => '') throw new Error(`mcp-stdio failed: ${response.status} ${text}`) } - const data = await response.json().catch(() => ({} as { url?: string })) + const data = await response.json().catch(() => ({})) as { url?: string } const rawUrl = typeof data?.url === 'string' ? data.url : '' if (!rawUrl) { throw new Error('mcp-stdio response missing url') diff --git a/agent/src/tools/skill.ts b/packages/agent/src/tools/skill.ts similarity index 100% rename from agent/src/tools/skill.ts rename to packages/agent/src/tools/skill.ts diff --git a/agent/src/tools/subagent.ts b/packages/agent/src/tools/subagent.ts similarity index 98% rename from agent/src/tools/subagent.ts rename to packages/agent/src/tools/subagent.ts index d73b394f..42c9ba02 100644 --- a/agent/src/tools/subagent.ts +++ b/packages/agent/src/tools/subagent.ts @@ -2,7 +2,7 @@ import { tool } from 'ai' import { z } from 'zod' import { createAgent } from '../agent' import { ModelConfig, AgentAuthContext } from '../types' -import { AuthFetcher } from '..' +import { AuthFetcher } from '../types' import { AgentAction, IdentityContext } from '../types/agent' export interface SubagentToolParams { diff --git a/agent/src/tools/web.ts b/packages/agent/src/tools/web.ts similarity index 100% rename from agent/src/tools/web.ts rename to packages/agent/src/tools/web.ts diff --git a/agent/src/types/action.ts b/packages/agent/src/types/action.ts similarity index 95% rename from agent/src/types/action.ts rename to packages/agent/src/types/action.ts index 2c123307..4487ddee 100644 --- a/agent/src/types/action.ts +++ b/packages/agent/src/types/action.ts @@ -64,9 +64,10 @@ export interface AgentEndAction extends BaseAction { skills: string[] reasoning: string[] usage: LanguageModelUsage + usages: (LanguageModelUsage | null)[] } -export type AgentAction = +export type AgentStreamAction = | AgentStartAction | ReasoningStartAction | ReasoningDeltaAction diff --git a/agent/src/types/agent.ts b/packages/agent/src/types/agent.ts similarity index 89% rename from agent/src/types/agent.ts rename to packages/agent/src/types/agent.ts index 40c657f1..0ad28c16 100644 --- a/agent/src/types/agent.ts +++ b/packages/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/agent/src/types/attachment.ts b/packages/agent/src/types/attachment.ts similarity index 100% rename from agent/src/types/attachment.ts rename to packages/agent/src/types/attachment.ts diff --git a/packages/agent/src/types/auth.ts b/packages/agent/src/types/auth.ts new file mode 100644 index 00000000..f137970d --- /dev/null +++ b/packages/agent/src/types/auth.ts @@ -0,0 +1,4 @@ +export type AuthFetcher = ( + url: string, + options?: RequestInit, +) => Promise \ No newline at end of file diff --git a/agent/src/types/index.ts b/packages/agent/src/types/index.ts similarity index 60% rename from agent/src/types/index.ts rename to packages/agent/src/types/index.ts index dd5abe62..451baee9 100644 --- a/agent/src/types/index.ts +++ b/packages/agent/src/types/index.ts @@ -2,4 +2,6 @@ export * from './agent' export * from './model' export * from './schedule' export * from './attachment' -export * from './mcp' \ No newline at end of file +export * from './mcp' +export * from './auth' +export * from './action' \ No newline at end of file diff --git a/agent/src/types/mcp.ts b/packages/agent/src/types/mcp.ts similarity index 100% rename from agent/src/types/mcp.ts rename to packages/agent/src/types/mcp.ts diff --git a/agent/src/types/model.ts b/packages/agent/src/types/model.ts similarity index 100% rename from agent/src/types/model.ts rename to packages/agent/src/types/model.ts diff --git a/agent/src/types/schedule.ts b/packages/agent/src/types/schedule.ts similarity index 100% rename from agent/src/types/schedule.ts rename to packages/agent/src/types/schedule.ts diff --git a/agent/src/utils/attachments.ts b/packages/agent/src/utils/attachments.ts similarity index 100% rename from agent/src/utils/attachments.ts rename to packages/agent/src/utils/attachments.ts diff --git a/agent/src/utils/headers.ts b/packages/agent/src/utils/headers.ts similarity index 100% rename from agent/src/utils/headers.ts rename to packages/agent/src/utils/headers.ts diff --git a/packages/agent/src/utils/index.ts b/packages/agent/src/utils/index.ts new file mode 100644 index 00000000..48ff40e2 --- /dev/null +++ b/packages/agent/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './attachments' +export * from './headers' \ No newline at end of file diff --git a/packages/agent/tsconfig.json b/packages/agent/tsconfig.json new file mode 100644 index 00000000..94111105 --- /dev/null +++ b/packages/agent/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "jsx": "react-jsx", + "outDir": "./dist", + "rootDir": "./src", + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} + diff --git a/packages/cli/package.json b/packages/cli/package.json index 4179551a..58a5e623 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/cli", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "description": "Command line interface and core API for Memoh", "exports": { ".": "./src/index.ts", diff --git a/packages/config/package.json b/packages/config/package.json index 62b2e083..9ebb5ea6 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/config", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "exports": { ".": "./src/index.ts" }, diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4a03fd63..d165af60 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/sdk", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "description": "", "exports": { ".": "./src/index.ts", 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 b62d339d..e89e62f0 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; @@ -891,6 +920,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; }; @@ -902,6 +932,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; }; @@ -1521,6 +1552,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/packages/ui/package.json b/packages/ui/package.json index 096b5a34..44f7fedf 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@memoh/ui", - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 65d026ab..88761721 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@memoh/web", "private": true, - "version": "0.1.0-beta.4", + "version": "0.1.0-beta.5", "type": "module", "scripts": { "dev": "vite", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e75b7d8..6d62ca38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,24 +48,15 @@ importers: agent: dependencies: - '@ai-sdk/anthropic': - specifier: ^3.0.9 - version: 3.0.9(zod@4.3.5) - '@ai-sdk/google': - specifier: ^3.0.6 - version: 3.0.6(zod@4.3.5) - '@ai-sdk/mcp': - specifier: ^1.0.6 - version: 1.0.6(zod@4.3.5) - '@ai-sdk/openai': - specifier: ^3.0.7 - version: 3.0.7(zod@4.3.5) '@elysiajs/bearer': specifier: ^1.4.2 version: 1.4.2(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) '@elysiajs/cors': specifier: ^1.4.1 version: 1.4.1(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) + '@memoh/agent': + specifier: workspace:* + version: link:../packages/agent '@memoh/config': specifier: workspace:* version: link:../packages/config @@ -113,6 +104,42 @@ importers: specifier: ^3.5.0 version: 3.5.26(typescript@5.9.3) + packages/agent: + dependencies: + '@ai-sdk/anthropic': + specifier: ^3.0.9 + version: 3.0.9(zod@4.3.6) + '@ai-sdk/google': + specifier: ^3.0.6 + version: 3.0.6(zod@4.3.6) + '@ai-sdk/mcp': + specifier: ^1.0.6 + version: 1.0.6(zod@4.3.6) + '@ai-sdk/openai': + specifier: ^3.0.7 + version: 3.0.7(zod@4.3.6) + '@mozilla/readability': + specifier: ^0.6.0 + version: 0.6.0 + ai: + specifier: ^6.0.25 + version: 6.0.25(zod@4.3.6) + jsdom: + specifier: ^27.4.0 + version: 27.4.0 + toml: + specifier: ^3.0.0 + version: 3.0.0 + turndown: + specifier: ^7.2.2 + version: 7.2.2 + typescript: + specifier: ^5 + version: 5.9.3 + zod: + specifier: ^4.3.6 + version: 4.3.6 + packages/cli: dependencies: '@memoh/sdk': @@ -5131,6 +5158,9 @@ packages: zod@4.3.5: resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -5138,11 +5168,11 @@ snapshots: '@acemir/cssom@0.9.31': {} - '@ai-sdk/anthropic@3.0.9(zod@4.3.5)': + '@ai-sdk/anthropic@3.0.9(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.4(zod@4.3.5) - zod: 4.3.5 + '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) + zod: 4.3.6 '@ai-sdk/gateway@3.0.10(zod@4.3.5)': dependencies: @@ -5151,24 +5181,31 @@ snapshots: '@vercel/oidc': 3.1.0 zod: 4.3.5 - '@ai-sdk/google@3.0.6(zod@4.3.5)': + '@ai-sdk/gateway@3.0.10(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.4(zod@4.3.5) - zod: 4.3.5 + '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) + '@vercel/oidc': 3.1.0 + zod: 4.3.6 - '@ai-sdk/mcp@1.0.6(zod@4.3.5)': + '@ai-sdk/google@3.0.6(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.5(zod@4.3.5) + '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) + zod: 4.3.6 + + '@ai-sdk/mcp@1.0.6(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 3.0.2 + '@ai-sdk/provider-utils': 4.0.5(zod@4.3.6) pkce-challenge: 5.0.1 - zod: 4.3.5 + zod: 4.3.6 - '@ai-sdk/openai@3.0.7(zod@4.3.5)': + '@ai-sdk/openai@3.0.7(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.4(zod@4.3.5) - zod: 4.3.5 + '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) + zod: 4.3.6 '@ai-sdk/provider-utils@4.0.4(zod@4.3.5)': dependencies: @@ -5177,12 +5214,19 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.3.5 - '@ai-sdk/provider-utils@4.0.5(zod@4.3.5)': + '@ai-sdk/provider-utils@4.0.4(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.6 - zod: 4.3.5 + zod: 4.3.6 + + '@ai-sdk/provider-utils@4.0.5(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 3.0.2 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 4.3.6 '@ai-sdk/provider@3.0.2': dependencies: @@ -7230,6 +7274,14 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.3.5 + ai@6.0.25(zod@4.3.6): + dependencies: + '@ai-sdk/gateway': 3.0.10(zod@4.3.6) + '@ai-sdk/provider': 3.0.2 + '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) + '@opentelemetry/api': 1.9.0 + zod: 4.3.6 + ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: ajv: 8.13.0 @@ -9966,4 +10018,6 @@ snapshots: zod@4.3.5: {} + zod@4.3.6: {} + zwitch@2.0.4: {} diff --git a/spec/docs.go b/spec/docs.go index e3099f26..0d9545f0 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": { @@ -7248,6 +7597,9 @@ const docTemplate = `{ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -7277,6 +7629,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 b61536d0..933fc957 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": { @@ -7239,6 +7588,9 @@ "max_context_tokens": { "type": "integer" }, + "max_inbox_items": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -7268,6 +7620,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 d1b93e4f..3407175c 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: @@ -1471,6 +1513,8 @@ definitions: type: integer max_context_tokens: type: integer + max_inbox_items: + type: integer memory_model_id: type: string search_provider_id: @@ -1490,6 +1534,8 @@ definitions: type: integer max_context_tokens: type: integer + max_inbox_items: + type: integer memory_model_id: type: string search_provider_id: @@ -2030,6 +2076,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