Add GET /bots/:bot_id/sessions/:session_id/info API endpoint that returns
per-session message count, latest input token usage with model context window,
aggregated KV cache hit rate, and skills invoked via use_skill tool calls.
Frontend Info tab in the right sidebar now displays this data in a compact
key-value layout with a context usage progress bar and clickable skill links.
* feat: add Supermarket integration (MCP & Skill marketplace) (#309)
* feat: add Supermarket integration (MCP & Skill marketplace)
Backend:
- Add [supermarket] config section with base_url (default: supermarket.memoh.ai)
- Add SupermarketHandler with proxy endpoints for MCPs, Skills, and Tags
- Add install endpoints: POST /bots/:id/supermarket/install-mcp (creates MCP
connection with env vars) and install-skill (downloads tar.gz, extracts to
container via gRPC)
- Register handler in FX wiring, generate Swagger docs and TypeScript SDK
Frontend:
- Add /settings/supermarket route with Store icon in sidebar
- Create supermarket page with search, tag filtering, MCP and Skill sections
- Add MCP/Skill card components with tag badges and install buttons
- Add install dialogs: MCP (bot selector + env var form), Skill (bot selector)
- Add i18n entries for en.json and zh.json
* fix: improve supermarket install UX
- Create BotSelect component with avatar + name using UI Select
- Replace NativeSelect in install dialogs and usage page with BotSelect
- Change MCP install flow: navigate to bot detail MCP tab with pre-filled
draft instead of direct install, letting users review before saving
- Move Supermarket sidebar entry between Browser and Usage
* web: remove supermarket page top tag selector bar
Drop the horizontal tag chips and getSupermarketTags fetch; keep
search and tag filter via card tag clicks with clearable badge.
* web: add homepage link to supermarket MCP and Skill cards
Show an external-link icon next to the card title when homepage is
available, opening in a new tab on click.
* refactor: move skills directory from .skills to skills and enrich prompt
- Change skills storage path from `/data/.skills` to `/data/skills`
- Add usage instructions and directory location to the Skills section
in the system prompt
* feat(web): add Activity Bar and right sidebar panel to chat page
Replace the old file manager panel with a multi-tab right sidebar system:
- Activity Bar with Terminal, Files, and Info tabs
- Resizable right panel with tab switching
- Extract shared Terminal component from bot-terminal.vue
- Add bottom preview layout mode to FileManager
- Delete session button with confirmation dialog
- Fix FileManager scroll in flex column layout (min-h-0)
* feat(web): add session-type-aware UI for chat interface
- Make IM/heartbeat/schedule/subagent sessions read-only (hide input box)
- Render heartbeat user messages as info blocks with trigger metadata and
link to heartbeat logs
- Render schedule user messages as info blocks with task metadata and
link to schedule settings
- Render subagent user messages as full-width markdown boxes
- Add clickable spawn task results to navigate to subagent sessions
* feat(web): add sidebar collapse functionality
- Add SidebarRail for edge drag-to-collapse interaction in both sidebars
- Center align icons when collapsed in bot list and settings sidebar
- Hide text labels and dropdown menus in collapsed state
- Keep create bot button in header
* fix(web): ensure secondary sidebar remains visible after page refresh
When the primary sidebar was collapsed, refreshing the page would cause
the secondary sidebar (e.g., providers list) to disappear.
The MasterDetailSidebarLayout had its own SidebarProvider but didn't
set default-open, causing it to read the same sidebar_state cookie
and follow the primary sidebar's collapsed state.
Fix by explicitly setting :default-open="true" on the secondary
SidebarProvider to ensure it always stays expanded.
---------
Co-authored-by: Acbox Liu <acbox0328@gmail.com>
Reorganize Supermarket into a tabbed UI with Skills as the first tab
and MCP as the second tab. Add a GitHub submit button in the header
linking to https://github.com/memohai/supermarket.
- Make IM/heartbeat/schedule/subagent sessions read-only (hide input box)
- Render heartbeat user messages as info blocks with trigger metadata and
link to heartbeat logs
- Render schedule user messages as info blocks with task metadata and
link to schedule settings
- Render subagent user messages as full-width markdown boxes
- Add clickable spawn task results to navigate to subagent sessions
Replace the old file manager panel with a multi-tab right sidebar system:
- Activity Bar with Terminal, Files, and Info tabs
- Resizable right panel with tab switching
- Extract shared Terminal component from bot-terminal.vue
- Add bottom preview layout mode to FileManager
- Delete session button with confirmation dialog
- Fix FileManager scroll in flex column layout (min-h-0)
* feat: add Supermarket integration (MCP & Skill marketplace)
Backend:
- Add [supermarket] config section with base_url (default: supermarket.memoh.ai)
- Add SupermarketHandler with proxy endpoints for MCPs, Skills, and Tags
- Add install endpoints: POST /bots/:id/supermarket/install-mcp (creates MCP
connection with env vars) and install-skill (downloads tar.gz, extracts to
container via gRPC)
- Register handler in FX wiring, generate Swagger docs and TypeScript SDK
Frontend:
- Add /settings/supermarket route with Store icon in sidebar
- Create supermarket page with search, tag filtering, MCP and Skill sections
- Add MCP/Skill card components with tag badges and install buttons
- Add install dialogs: MCP (bot selector + env var form), Skill (bot selector)
- Add i18n entries for en.json and zh.json
* fix: improve supermarket install UX
- Create BotSelect component with avatar + name using UI Select
- Replace NativeSelect in install dialogs and usage page with BotSelect
- Change MCP install flow: navigate to bot detail MCP tab with pre-filled
draft instead of direct install, letting users review before saving
- Move Supermarket sidebar entry between Browser and Usage
* web: remove supermarket page top tag selector bar
Drop the horizontal tag chips and getSupermarketTags fetch; keep
search and tag filter via card tag clicks with clearable badge.
* web: add homepage link to supermarket MCP and Skill cards
Show an external-link icon next to the card title when homepage is
available, opening in a new tab on click.
- Add localfs storage provider as fallback when containerfs is unreachable
- Wrap media service with fallback provider in both entry points
- Fix gallery lightbox src matching by comparing pathnames only
When messages like [assistant(tool_calls), tool, assistant(text)] are
merged into a single ChatMessage by convertMessagesToChats, the merged
message uses the first assistant's ID. The SSE /messages/events backlog
(which uses >= for the since filter) could re-deliver the final assistant
message, and hasMessageWithId failed to recognize it because the merged
ID was different. This caused the last assistant message to appear twice
after a page refresh.
Track all original server message IDs in a Set (knownServerMessageIds)
so that hasMessageWithId can catch messages whose IDs were absorbed
during merging.
Use stream-monaco's useMonaco composable instead of raw monaco-editor
to share the same theme registration system with markstream-vue.
Pass isDark prop to MarkdownRender to follow app dark mode.
Three independent bugs fixed:
1. IM channels were sending raw <attachments>/<reactions>/<speech> tag blocks
alongside file attachments. Now ExtractAssistantOutputs strips these tags
before building the outbound channel message.
2. WebUI rendered these tags as markdown after page refresh. Now
extractMessageText strips agent tags for non-user messages.
3. WebUI lost attachment blocks after refresh because convertMessagesToChats
did not call buildAssetBlocks when merging assistant messages into a
pending tool-call group. Also made LinkOutboundAssets session-aware so
assets are linked to the correct assistant message.
The SDK persists tool results as ToolResultPart with a "result" JSON field,
but extractAllToolResults was only reading "output", causing exec tool
stdout/stderr/exit_code to be lost when loading chat history.
Replace the static Globe icon with dynamic type-specific icons
(MessageSquare, HeartPulse, Clock, GitBranch) that match the
session item icons, updating both icon and color on filter change.
Allow users to select a different model and reasoning effort level
directly from the chat input toolbar, overriding the bot defaults
on a per-message basis. The backend accepts optional model_id and
reasoning_effort parameters via both WebSocket and HTTP APIs, with
request-level values taking priority over bot/session settings.
- Backend: extend wsClientMessage and LocalChannelMessageRequest with
model_id/reasoning_effort fields; add ReasoningEffort to ChatRequest;
update resolver to prioritize request-level reasoning effort
- Frontend: add ModelOptions and ReasoningEffortSelect shared components;
refactor model-select to reuse ModelOptions; add model/reasoning
selectors to chat input toolbar; initialize from bot settings
- Regenerate swagger spec and TypeScript SDK
Replace fixed-width session sidebar with a draggable resize handle on
its right edge (180–480px, persisted to localStorage). Convert the file
manager from a Sheet overlay to an embedded right-side panel with a
left-edge resize handle (320–800px, also persisted).
Allow users to configure what percentage of older messages to compact,
keeping the most recent portion intact. Default ratio is 80%, meaning
the oldest 80% of uncompacted messages are summarized while the newest
20% remain as-is for full-fidelity context.
Extract ModelCapabilities and ContextWindowBadge into shared components.
Model type badge moved to title row with icon, capabilities shown as
icon-only colored tags, context window formatted as colored badge (k/M).
Also add capability and context info to model select dropdown options.
- Prevent Enter from sending message during IME composing (keyCode 229)
- Remove separator line between textarea and toolbar
- Change send/stop buttons to compact circular icon-only style
- Fix send icon color to white for dark mode visibility
- Add missing hidden file input element so the attach button works
Replace all FontAwesome icon usage across 80+ Vue files with lucide-vue-next
components. Remove FontAwesome dependencies (@fortawesome/*) and global
registration from main.ts. Delete unused components (data-table, warning-banner,
session-metadata, bot-sidebar/bot-item in home, message-list, tts-provider-select),
dead utilities (channel-icons.ts, custom-icons.ts), and stale assets (vue.svg).
Update AGENTS.md to reflect the new icon strategy.
Add version and commit_hash fields to the /ping endpoint response,
sourced from the existing internal/version package (ldflags or
Go build info). The frontend capabilities store reads these values
and displays them as badges at the bottom of the Profile page.
Remove per-session-type (chat/heartbeat/schedule) bar series from the
Daily Tokens chart, keeping only aggregated Total Input and Total Output
as stacked bars for a cleaner visualization.
- Fix cookie check logic: use `!includes('sidebar_state=false')` instead
of `includes('sidebar_state=true')` so sidebar defaults to open when
no cookie is set
- Add --sidebar-width CSS variable binding to desktop sidebar element
- Adjust SIDEBAR_WIDTH_MOBILE value
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the global streaming ref with a streamingSessionId that
records which session is actively streaming. The streaming computed
now returns true only when the current session matches, so switching
sessions no longer leaks the generating indicator to unrelated sessions.
Constrain the main layout to viewport height (h-dvh) and override
SidebarProvider's min-h-svh so the height chain propagates correctly.
Change main-container overflow from auto to hidden so the outer
container never scrolls. Use absolute-positioning pattern for
session sidebar ScrollArea (matching chat messages pattern) to
ensure sessions scroll independently while the chat input stays fixed.
These two fields controlled history context window (time-based) and token-based
trimming. They are no longer needed — the resolver now always uses the hardcoded
24-hour default and skips token-based history trimming.
- Add `enable` column (default false) to search_providers and tts_providers tables
- Auto-create default entries for all provider types on startup (disabled by default)
- Add enable/disable Switch toggle in frontend for both search and TTS providers
- Show green status dot in sidebar for enabled providers, sort enabled first
- Filter bot settings dropdowns to only show enabled providers
Add a dropdown menu to each bot item in the chat sidebar with:
- "Details" option to navigate to the bot's settings page
- "Pin/Unpin" option to pin bots to the top of the list, persisted via localStorage
Backend
- New subject kinds: all / channel_identity / channel_type
- Source scope fields on bot_acl_rules: source_channel,
source_conversation_type, source_conversation_id, source_thread_id
- Fix source_scope_check constraint: resolve source_channel server-side
(channel_type → subject_channel_type; channel_identity → DB lookup)
- Add GET /bots/:id/acl/channel-types/:type/conversations to list
observed conversations by platform type
- ListObservedConversations: include private/DM chats, normalise
conversation_type; COALESCE(name, handle) for display name
- enrichConversationAvatar: persist entry.Name → conversation_name
(keeps Telegram group titles current on every message)
- Unify Priority type to int32 across Go types to match DB INTEGER;
remove all int/int32 casts in service layer
- Fix duplicate nil guard in Evaluate; drop dead SourceScope.Channel field
- Migration 0048_acl_redesign
Frontend
- Drag-and-drop rule priority reordering (SortableJS/useSortable);
fix reorder: compute new order from oldIndex/newIndex directly,
not from the array (which useSortable syncs after onEnd)
- Conversation scope selector: searchable popover backed by observed
conversations (by identity or platform type); collapsible manual-ID fallback
- Display: name as primary label, stable channel·type·id always shown
as subtitle for verification
- bot-terminal: accessibility fix on close-tab button (keyboard events)
- i18n: drag-to-reorder, conversation source, manual IDs (en/zh)
Tests: update fakeChatACL to Evaluate interface; fix SourceScope literals.
SDK/spec regenerated.
* feat(web): add provider oauth management ui
* feat: add OAuth callback support on port 1455
* feat: enhance reasoning effort options and support for OpenAI Codex OAuth
* feat: update twilight-ai dependency to v0.3.4
* refactor: promote openai-codex to first-class client_type, remove auth_type
Replace the previous openai-responses + metadata auth_type=openai-codex-oauth
combo with a dedicated openai-codex client_type. OAuth requirement is now
determined solely by client_type, eliminating the auth_type concept from the
LLM provider domain entirely.
- Add openai-codex to DB CHECK constraint (migration 0047) with data migration
- Add ClientTypeOpenAICodex constant and dedicated SDK/probe branches
- Remove AuthType from SDKModelConfig, ModelCredentials, TriggerConfig, etc.
- Simplify supportsOAuth to check client_type == openai-codex
- Add conf/providers/codex.yaml preset with Codex catalog models
- Frontend: replace auth_type selector with client_type-driven OAuth UI
---------
Co-authored-by: Acbox <acbox0328@gmail.com>