Bots can now be configured with an image generation model (must have
image-output compatibility). When set, the agent exposes a generate_image
tool that calls the model via Twilight AI SDK, saves the result to the
bot container filesystem, and returns the file path.
- Add image_model_id column to bots table (migration 0053)
- Update settings SQL queries, service, and types
- New ImageGenProvider tool provider in internal/agent/tools/
- Wire provider in both cmd/agent and cmd/memoh entry points
- Add image model selector to frontend bot settings with compat filtering
- Regenerate swagger, SDK types, and sqlc code
Pulls model list from OpenRouter API and auto-generates
conf/providers/openrouter.yaml with inferred compatibilities
(vision, tool-call, image-output, reasoning) from API metadata.
Models that lack the "tool-call" compatibility flag now run without
tools, preventing provider errors when the model does not support
function calling.
Introduce three inbound message handling modes for channel adapters:
- inject (default, /btw): when a route has an active agent stream,
inject the new user message into the running stream via the SDK's
PrepareStep hook between tool rounds. The message is interleaved at
the correct position in the persisted round.
- parallel (/now): start a new agent stream immediately, running
concurrently with any existing stream (preserves current behavior).
- queue (/next): enqueue the message and process it after the current
stream completes.
Key components:
- RouteDispatcher: per-route state management with inject channel,
task queue, and active-stream tracking.
- PrepareStep integration: drains inject channel between tool rounds,
records insertion position via InjectedRecorder for correct
persistence ordering.
- interleaveInjectedMessages: inserts injected user messages at their
actual injection position within the persisted message round.
- Parallel mode isolation: /now streams do not interact with the
dispatcher, preventing them from clearing another stream's active
state.
Rename session info endpoint from /sessions/:id/info to /sessions/:id/status
and update frontend tab label accordingly. Add /status slash command that
displays current session metrics (message count, context usage, cache hit
rate, used skills) as formatted text in any channel.
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)
- 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: 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.
The fallback provider introduced in 5aeb2fd3 wrapped containerfs but did
not implement storage.ContainerFileOpener, causing IngestContainerFile to
fail with "provider does not support container file reading". This broke
outbound file attachments on all IM channels (Telegram, Discord, etc.)
because container paths like /data/xxx.xlsx were passed as-is to the
platform API instead of being ingested into the media store first.
- 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
- Add SupportsToolCall to RunConfig; only inject tools into SDK when set
- Update twilight-ai to 497ad09 which adds SSE scanner 10MB buffer
(fixes token-too-long on large image payloads) and parses the images
array from OpenAI-compatible chat completions into StreamFilePart
Large directories like node_modules/.venv could return thousands of entries,
wasting tokens and causing timeouts. Add offset/limit pagination to ListDir
RPC and collapse heavy subdirectories (>50 items) into summaries in recursive
mode. Collapsing runs at the bridge layer before pagination so the page window
reflects the collapsed view.
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.
* Enhance Matrix bot setup documentation
Added instructions for obtaining an access token using the Matrix Client Login API and clarified the importance of keeping it secret. Updated details on bot capabilities and linked to the roadmap for future features.
* Add warning about access token security
Added important note about keeping the access token secret.
WiX MSI only supports numeric-only versions (X.Y.Z). When the tag
contains a pre-release identifier (e.g. 0.6.0-beta.5), override the
MSI version via `bundle.windows.wix.version` with the stripped numeric
portion while keeping the full semver for other bundle formats.
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.
The child-selector border styles (*:border-b *:border-x etc.) on
CollapsibleContent caused unexpected white outlines around thinking
blocks and tool-call detail panels in chat messages.
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
Container terminals were echoing raw ANSI escape sequences (^[[A, ^[[B,
etc.) instead of handling arrow keys because /bin/sh (dash/ash) lacks
readline support. Two changes fix this:
1. Bridge execPTY now directly exec's bare paths (e.g. /bin/bash) instead
of always wrapping through "/bin/sh -c", preserving readline behavior.
2. Terminal handler detects bash/zsh in the container and prefers them
over /bin/sh for interactive PTY sessions.
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.
Tauri desktop artifacts were always named with hardcoded version 0.1.0
(from tauri.conf.json) instead of the actual release version. Extract
the version from the git tag and update tauri.conf.json and Cargo.toml
before building.
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.
Bumps twilight-ai to 3ebcc56 which strips data URL prefixes and
validates media_type before sending to Anthropic API, preventing
400 errors for unsupported image MIME types.
- 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.