feat(plugins): plugin pages, per-user config, capabilities gate, mobile/telegram refactors

- Plugin HTTP routes + web pages (plugin-page-host, plugin-catalog, plugin-detail)
- Plugin access grants + per-user config (DB tables + API + frontend forms)
- Capabilities-based guard (caps.rs) replacing role-id checks
- Mobile connector: message routing, payload types, router refactor
- Telegram bot: auth flow, event handling improvements
- Honcho plugin: substantial rework
- Sidebar: plugin pages integration, role-driven visibility
- i18n: new strings for plugins, connectors, capabilities
- Remove unused mascot asset
This commit is contained in:
2026-07-19 20:47:09 +01:00
parent f85876350e
commit ba911ae8cb
50 changed files with 3186 additions and 305 deletions
@@ -408,9 +408,21 @@ impl ChatSessionHandler {
Box::pin(async move { handler(args).await.map(ToolResult::Text) }),
)));
}
// Memory + image tools (registered ad-hoc on the config).
// The ToolContext carries this session's id, owner user id and owner pool
// so owner-bound tools (cron management, the Honcho memory peer) act on the
// caller's own data. Built once and shared by memory tools and the registry.
let ctx = ToolContext {
session_id: self.session_id,
user_id: self.user_id.clone(),
pool: Arc::clone(&self.db),
// Snapshot the fs cell for the duration of this tool call — a concurrent
// shared-folder remount swaps the cell, the next call picks it up (§6).
fs: self.fs.load(),
};
// Memory + image tools (registered ad-hoc on the config). Memory tools route
// through `run_with` so the Honcho tools reach the caller's own peer.
if let Some(tool) = config.memory_tools.iter().find(|t| t.name() == name) {
return Some(tool.run(args));
return Some(tool.run_with(&ctx, args));
}
if let Some(tool) = config.image_tools.iter().find(|t| t.name() == name) {
return Some(tool.run(args));
@@ -426,15 +438,6 @@ impl ChatSessionHandler {
}
// Built-in registry tools (incl. execute_cmd, whose SimpleExecution kills
// the child via kill_on_drop when the work future is dropped on /stop).
// The ToolContext carries this session's id and owner pool so owner-bound
// registry tools (e.g. cron management) act on the caller's own database.
let ctx = ToolContext {
session_id: self.session_id,
pool: Arc::clone(&self.db),
// Snapshot the fs cell for the duration of this tool call — a concurrent
// shared-folder remount swaps the cell, the next call picks it up (§6).
fs: self.fs.load(),
};
self.tools.run(name, &ctx, args)
}
}
+3 -1
View File
@@ -547,7 +547,7 @@ impl ChatSessionHandler {
// providers with prefix caching (e.g. Alibaba/DeepSeek via OpenRouter)
// to cache the stable system prompt across turns even though Honcho
// memories change on every call.
let honcho_dynamic = match self.memory_manager.query_context(self.session_id, content).await {
let honcho_dynamic = match self.memory_manager.query_context(&self.user_id, self.session_id, content).await {
Some(mem_ctx) => {
trace!(
session_id = self.session_id,
@@ -659,6 +659,7 @@ impl ChatSessionHandler {
self.event_bus.user_message(ChatEvent {
session_id: self.session_id,
stack_id: stack.id,
user_id: self.user_id.clone(),
message_id: user_message_id,
role: ChatEventRole::User,
content: user_content,
@@ -671,6 +672,7 @@ impl ChatSessionHandler {
self.event_bus.assistant_response(ChatEvent {
session_id: self.session_id,
stack_id: stack.id,
user_id: self.user_id.clone(),
message_id,
role: ChatEventRole::Assistant,
content,