Commit Graph

62 Commits

Author SHA1 Message Date
Acbox 473d559042 feat(channel): structured tool-call IM display with edit-in-place
Introduce a new `show_tool_calls_in_im` bot setting plus a full overhaul of
how tool calls are surfaced in IM channels:

- Add per-bot setting + migration (0072) and expose through settings API /
  handlers / frontend SDK.
- Introduce a `toolCallDroppingStream` wrapper that filters tool_call_* events
  when the setting is off, keeping the rest of the stream intact.
- Add a shared `ToolCallPresentation` model (Header / Body blocks / Footer)
  with plain and Markdown renderers, and a per-tool formatter registry that
  produces rich output (e.g. `web_search` link lists, `list` directory
  previews, `exec` stdout/stderr tails) instead of raw JSON dumps.
- High-capability adapters (Telegram, Feishu, Matrix, Slack, Discord) now
  flush pre-text and then send ONE tool-call message per call, editing it
  in-place from `running` to `completed` / `failed`; mapping from callID to
  platform message ID is tracked per stream, with a fallback to a new
  message if the edit fails. Low-capability adapters (WeCom, QQ, DingTalk)
  keep posting a single final message, but now benefit from the same rich
  per-tool formatting.
- Suppress the early duplicate `EventToolCallStart` (from
  `sdk.ToolInputStartPart`) so that the SDK's final `StreamToolCallPart`
  remains the single source of truth for tool call start, preventing
  duplicated "running" bubbles in IM.
- Stop auto-populating `InputSummary` / `ResultSummary` after a per-tool
  formatter runs, which previously leaked the raw JSON result as a
  fallback footer underneath the formatted body.

Add regression tests for the formatters, the Markdown renderer, the
edit-in-place flow on Telegram/Matrix, and the JSON-leak guard on `list`.
2026-04-23 20:49:44 +08:00
Fodesu 8e013ad1ad feat(platform): add slack platform support (#385)
* feat(platform): add slack platform support

* docs: add slack channel setup guide

* feat: normalize slack unicode reactions

* chore(docs): remove unsupport feature

* fix(slack): harden adapter stream and identity handling

- ignore reaction and speech stream events in Slack outbound
  streams
  - normalize Slack conversation types to framework-standard values
  - route DiscoverSelf through the adapter API factory
  - add config-scoped Slack user display-name caching
  - expand adapter interface assertions and add regression coverage
  - add ChannelTypeSlack to well-known channel constants
2026-04-19 14:17:05 +08:00
aki 0e6c8ca451 fix(telegram): dedupe repeated inbound updates 2026-04-14 05:15:45 +08:00
KasuganoSora a40207ab6d feat: Misskey channel adapter, agent reliability hardening & stream error resilience (#359) 2026-04-13 17:10:50 +08:00
BBQ f376a2abe3 fix(channel): add wechatoa webhook delivery and proxy config (#356)
Unify webhook handling across channel adapters and add the WeChat Official Account channel so inbound routing and replies work without platform-specific handlers. Add adapter-scoped proxy support and stable config field ordering so restricted network environments can deliver WeChat and Telegram messages reliably.
2026-04-10 21:26:11 +08:00
BBQ d3bf6bc90a fix(channel,attachment): channel quality refactor & attachment pipeline fixes (#349)
* feat(channel): add DingTalk channel adapter

- Add DingTalk channel adapter (`internal/channel/adapters/dingtalk/`) using dingtalk-stream-sdk-go, supporting inbound message receiving and outbound text/markdown reply
- Register DingTalk adapter in cmd/agent and cmd/memoh
- Add go.mod dependency: github.com/memohai/dingtalk-stream-sdk-go
- Add Dingtalk and Wecom SVG icons and Vue components to @memohai/icon
- Refactor existing icon components to remove redundant inline wrappers
- Add `channelTypeDisplayName` util for consistent channel label resolution
- Add DingTalk/WeCom i18n entries (en/zh) for types and typesShort
- Extend channel-icon, bot-channels, channel-settings-panel to support dingtalk/wecom
- Use channelTypeDisplayName in profile page to replace ad-hoc i18n lookup

* fix(channel,attachment): channel quality refactor & attachment pipeline fixes

Channel module:
- Fix RemoveAdapter not cleaning connectionMeta (stale status leak)
- Fix preparedAttachmentTypeFromMime misclassifying image/gif
- Fix sleepWithContext time.After goroutine/timer leak
- Export IsDataURL/IsHTTPURL/IsDataPath, dedup across packages
- Cache OutboundPolicy in managerOutboundStream to avoid repeated lookups
- Split OutboundAttachmentStore: extract ContainerAttachmentIngester interface
- Add ManagerOption funcs (WithInboundQueueSize, WithInboundWorkers, WithRefreshInterval)
- Add thread-safety docs on OutboundStream / managerOutboundStream
- Add debug logs on successful send/edit paths
- Expand outbound_prepare_test.go with 21 new cases
- Convert no-receiver adapter helpers to package-level funcs; drop unused params

DingTalk adapter:
- Implement AttachmentResolver: download inbound media via /v1.0/robot/messageFiles/download
- Fix pure-image inbound messages failing due to missing resolver

Attachment pipeline:
- Fix images invisible to LLM in pipeline (DCP) path: inject InlineImages into
  last user message when cfg.Query is empty
- Fix public_url fallback: skip direct URL-to-LLM when ContentHash is set,
  always prefer inlined persisted asset
- Inject path: carry ImageParts through agent.InjectMessage; inline persisted
  attachments in resolver inject goroutine so mid-stream images reach the model
- Fix ResolveMime for images: prefer content-sniffed MIME over platform-declared
  MIME (fixes Feishu sending image/png header for actual JPEG content → API 400)
2026-04-09 14:36:11 +08:00
Acbox Liu 43c4153938 feat: introduce DCP pipeline layer for unified context assembly (#329)
* refactor: introduce DCP pipeline layer for unified context assembly

Introduce a Deterministic Context Pipeline (DCP) inspired by Cahciua,
providing event-driven context assembly for LLM conversations.

- Add `internal/pipeline/` package with Canonical Event types, Projection
  (reduce), Rendering (XML RC), Pipeline manager, and EventStore persistence
- Change user message format from YAML front-matter to XML `<message>` tags
  with self-contained attributes (sender, channel, conversation, type)
- Merge CLI/Web dual API into single `/local/` endpoint, remove CLI handler
- Add `bot_session_events` table for event persistence and cold-start replay
- Add `discuss` session type (reserved for future Cahciua-style mode)
- Wire pipeline into HandleInbound: adapt → persist → push on every message
- Lazy cold-start replay: load events from DB on first session access

* feat: implement discuss mode with reactive driver and probe gate

Add discuss session mode where the bot autonomously decides when to speak
in group chats via tool-gated output (send tool only, no direct text reply).

- Add discuss driver (per-session goroutine, RC watch, step loop via
  agent.Generate, TR persistence, late-binding prompt with mention hints)
- Add system_discuss.md prompt template ("text = inner monologue, send = speak")
- Add context composition (MergeContext, ComposeContext, TrimContext) for
  RC + assistant/tool message interleaving by timestamp
- Add probe gate: when discuss_probe_model_id is set, cheap model pre-filters
  group messages; no tool calls = silence, tool calls = activate primary
- Add /new [chat|discuss] command: explicit mode selection, defaults to
  discuss in groups, chat in DMs, chat-only for WebUI
- Add ResolveRunConfig on flow.Resolver for discuss driver to reuse
  model/tools/system-prompt resolution without reimplementing
- Fix send tool for discuss mode: same-conversation sends now go through
  SendDirect (channel adapter) instead of the local emitter shortcut
- Add target attribute to XML message format (reply_target for routing)
- Add discuss_probe_model_id to bots table settings
- Remove pipeline compaction (SetCompactCursor) — reuse existing compaction.Service
- Persist full SDK messages (including tool calls) in discuss mode

* refactor: unify DCP event layer, fix persistence and local channel

- Fix bot_session_events dedup index to include event_kind so that
  message + edit events for the same external_message_id coexist.
- Change CreateSessionEvent from :one to :exec so ON CONFLICT DO NOTHING
  does not produce spurious errors on duplicate delivery.
- Move ACL evaluation before event ingest; denied messages no longer
  enter bot_session_events or the in-memory pipeline.
- Let chat mode consume RenderedContext from the DCP pipeline when
  available, sharing the same event-driven context assembly as discuss.
- Collapse local WebSocket handler to route through HandleInbound
  instead of directly calling StreamChatWS, eliminating the dual
  business entry point.
- Extract buildBaseRunConfig shared builder so resolve() and
  ResolveRunConfig() no longer duplicate model/credentials/skills setup.
- Add StoreRound to RunConfigResolver interface so discuss driver
  persists assistant output with full metadata, usage, and memory
  extraction (same quality as chat mode).
- Fix discuss driver context: use context.Background() instead of the
  short-lived HTTP request context that was getting cancelled.
- Fix model ID passed to StoreRound: return database UUID from
  ResolveRunConfig instead of SDK model name.
- Remove dead CLIAdapter/CLIType and update legacy web/cli references
  in tests and comments.

* fix: stop idle discuss goroutines after 10min timeout

Discuss session goroutines were never cleaned up when a session became
inactive (e.g. after /new). Add a 10-minute idle timer that auto-exits
the goroutine and removes it from the sessions map when no new RC
arrives.

* refactor: pipeline details — event types, structured reply, display content

- Remove [User sent N attachments] placeholder text from buildInboundQuery;
  attachment info is now expressed via pipeline <attachment> tags.
- Unify in-reply-to as structured ReplyRef (Sender/Preview fields) across
  Telegram, Discord, Feishu, and Matrix adapters instead of prepending
  [Reply to ...] text into the message body. Remove now-unused
  buildTelegramQuotedText, buildDiscordQuotedText, buildMatrixQuotedText.
- Make AdaptInbound return CanonicalEvent interface and dispatch to
  adaptMessage/adaptEdit/adaptService based on metadata["event_type"].
- Add event_id column to bot_history_messages (migration 0059) so user
  messages can reference their canonical pipeline event.
- PersistEvent now returns the event UUID; HandleInbound passes it through
  to both persistPassiveMessage and ChatRequest.EventID for storeRound.
- Add FillDisplayContent to message service: extracts plain text from
  event_data for clean frontend display.
- Frontend extractMessageText prefers display_content when available,
  falling back to legacy strip logic for old messages.
- Fix: always generate headerifiedQuery for storage even when usePipeline
  is true, so user messages are persisted via storeRound in chat mode.

* fix: use json.Marshal for pipeline context content serialization

The manual string escaping in buildMessagesFromPipeline only handled
double quotes but not newlines, backslashes, and other JSON special
characters, producing invalid json.RawMessage values. The LLM then
received empty/malformed context and complained about having no history.

* fix: restore WebSocket handler to use StreamChatWS directly

The previous refactoring replaced the WS handler with HandleInbound +
RouteHub subscription, which broke streaming because RouteHub events
use a different format (channel.StreamEvent) than what the frontend
expects (flow.WSStreamEvent with text_delta, tool_call_start, etc.).

Restore the original direct StreamChatWS call path so WebUI streaming
works again. The WS handler now matches the pre-refactoring behavior
while all other changes (pipeline, ACL, event types, etc.) are kept.

* feat: store display_text directly in bot_history_messages

Instead of computing display content at API response time by querying
bot_session_events via event_id, store the raw user text in a dedicated
display_text column at write time. This works for all paths including
the WebSocket handler which does not go through the pipeline/event layer.

- Migration 0060: add display_text TEXT column
- PersistInput gains DisplayText; filled from trimmedText (passive) and
  req.Query (storeRound)
- toMessageFields reads display_text into DisplayContent
- Remove FillDisplayContent runtime query and ListSessionEventsByEventID
- Frontend already prefers display_content when available (no change)

* fix: display_text should contain raw user text, not XML-wrapped query

req.Query gets overwritten to headerifiedQuery (with XML <message> tags)
before storeRound runs. Add RawQuery field to ChatRequest to preserve
the original user text, and use it for display_text in storeMessages.

* fix(web): show discuss sessions

* refactor: introduce DCP pipeline layer for unified context assembly

Introduce a Deterministic Context Pipeline (DCP) inspired by Cahciua,
providing event-driven context assembly for LLM conversations.

- Add `internal/pipeline/` package with Canonical Event types, Projection
  (reduce), Rendering (XML RC), Pipeline manager, and EventStore persistence
- Change user message format from YAML front-matter to XML `<message>` tags
  with self-contained attributes (sender, channel, conversation, type)
- Merge CLI/Web dual API into single `/local/` endpoint, remove CLI handler
- Add `bot_session_events` table for event persistence and cold-start replay
- Add `discuss` session type (reserved for future Cahciua-style mode)
- Wire pipeline into HandleInbound: adapt → persist → push on every message
- Lazy cold-start replay: load events from DB on first session access

* feat: implement discuss mode with reactive driver and probe gate

Add discuss session mode where the bot autonomously decides when to speak
in group chats via tool-gated output (send tool only, no direct text reply).

- Add discuss driver (per-session goroutine, RC watch, step loop via
  agent.Generate, TR persistence, late-binding prompt with mention hints)
- Add system_discuss.md prompt template ("text = inner monologue, send = speak")
- Add context composition (MergeContext, ComposeContext, TrimContext) for
  RC + assistant/tool message interleaving by timestamp
- Add probe gate: when discuss_probe_model_id is set, cheap model pre-filters
  group messages; no tool calls = silence, tool calls = activate primary
- Add /new [chat|discuss] command: explicit mode selection, defaults to
  discuss in groups, chat in DMs, chat-only for WebUI
- Add ResolveRunConfig on flow.Resolver for discuss driver to reuse
  model/tools/system-prompt resolution without reimplementing
- Fix send tool for discuss mode: same-conversation sends now go through
  SendDirect (channel adapter) instead of the local emitter shortcut
- Add target attribute to XML message format (reply_target for routing)
- Add discuss_probe_model_id to bots table settings
- Remove pipeline compaction (SetCompactCursor) — reuse existing compaction.Service
- Persist full SDK messages (including tool calls) in discuss mode

* refactor: unify DCP event layer, fix persistence and local channel

- Fix bot_session_events dedup index to include event_kind so that
  message + edit events for the same external_message_id coexist.
- Change CreateSessionEvent from :one to :exec so ON CONFLICT DO NOTHING
  does not produce spurious errors on duplicate delivery.
- Move ACL evaluation before event ingest; denied messages no longer
  enter bot_session_events or the in-memory pipeline.
- Let chat mode consume RenderedContext from the DCP pipeline when
  available, sharing the same event-driven context assembly as discuss.
- Collapse local WebSocket handler to route through HandleInbound
  instead of directly calling StreamChatWS, eliminating the dual
  business entry point.
- Extract buildBaseRunConfig shared builder so resolve() and
  ResolveRunConfig() no longer duplicate model/credentials/skills setup.
- Add StoreRound to RunConfigResolver interface so discuss driver
  persists assistant output with full metadata, usage, and memory
  extraction (same quality as chat mode).
- Fix discuss driver context: use context.Background() instead of the
  short-lived HTTP request context that was getting cancelled.
- Fix model ID passed to StoreRound: return database UUID from
  ResolveRunConfig instead of SDK model name.
- Remove dead CLIAdapter/CLIType and update legacy web/cli references
  in tests and comments.

* fix: stop idle discuss goroutines after 10min timeout

Discuss session goroutines were never cleaned up when a session became
inactive (e.g. after /new). Add a 10-minute idle timer that auto-exits
the goroutine and removes it from the sessions map when no new RC
arrives.

* refactor: pipeline details — event types, structured reply, display content

- Remove [User sent N attachments] placeholder text from buildInboundQuery;
  attachment info is now expressed via pipeline <attachment> tags.
- Unify in-reply-to as structured ReplyRef (Sender/Preview fields) across
  Telegram, Discord, Feishu, and Matrix adapters instead of prepending
  [Reply to ...] text into the message body. Remove now-unused
  buildTelegramQuotedText, buildDiscordQuotedText, buildMatrixQuotedText.
- Make AdaptInbound return CanonicalEvent interface and dispatch to
  adaptMessage/adaptEdit/adaptService based on metadata["event_type"].
- Add event_id column to bot_history_messages (migration 0059) so user
  messages can reference their canonical pipeline event.
- PersistEvent now returns the event UUID; HandleInbound passes it through
  to both persistPassiveMessage and ChatRequest.EventID for storeRound.
- Add FillDisplayContent to message service: extracts plain text from
  event_data for clean frontend display.
- Frontend extractMessageText prefers display_content when available,
  falling back to legacy strip logic for old messages.
- Fix: always generate headerifiedQuery for storage even when usePipeline
  is true, so user messages are persisted via storeRound in chat mode.

* fix: use json.Marshal for pipeline context content serialization

The manual string escaping in buildMessagesFromPipeline only handled
double quotes but not newlines, backslashes, and other JSON special
characters, producing invalid json.RawMessage values. The LLM then
received empty/malformed context and complained about having no history.

* fix: restore WebSocket handler to use StreamChatWS directly

The previous refactoring replaced the WS handler with HandleInbound +
RouteHub subscription, which broke streaming because RouteHub events
use a different format (channel.StreamEvent) than what the frontend
expects (flow.WSStreamEvent with text_delta, tool_call_start, etc.).

Restore the original direct StreamChatWS call path so WebUI streaming
works again. The WS handler now matches the pre-refactoring behavior
while all other changes (pipeline, ACL, event types, etc.) are kept.

* feat: store display_text directly in bot_history_messages

Instead of computing display content at API response time by querying
bot_session_events via event_id, store the raw user text in a dedicated
display_text column at write time. This works for all paths including
the WebSocket handler which does not go through the pipeline/event layer.

- Migration 0060: add display_text TEXT column
- PersistInput gains DisplayText; filled from trimmedText (passive) and
  req.Query (storeRound)
- toMessageFields reads display_text into DisplayContent
- Remove FillDisplayContent runtime query and ListSessionEventsByEventID
- Frontend already prefers display_content when available (no change)

* fix: display_text should contain raw user text, not XML-wrapped query

req.Query gets overwritten to headerifiedQuery (with XML <message> tags)
before storeRound runs. Add RawQuery field to ChatRequest to preserve
the original user text, and use it for display_text in storeMessages.

* fix(web): show discuss sessions

* chore(feishu): change discuss output to stream card

* fix(channel): unify discuss/chat send path and card markdown delivery

* feat(discuss): switch to stream execution with RouteHub broadcasting

* refactor(pipeline): remove context trimming from ComposeContext

The pipeline path should not trim context by token budget — the
upstream IC/RC already bounds the event window. Remove TrimContext,
FindWorkingWindowCursor, EstimateTokens, FormatLastProcessedMs (all
unused or only used for trimming), the maxTokens parameter from
ComposeContext, and MaxContextTokens from DiscussSessionConfig.

---------

Co-authored-by: 晨苒 <16112591+chen-ran@users.noreply.github.com>
2026-04-06 21:56:25 +08:00
晨苒 830c521f11 feat(feishu): keep mention(at) target 2026-04-06 06:18:03 +08:00
Acbox Liu bca13a13fa feat(web): introduce a brand new web ui (#281)
* feat(web): introduce a brand new web ui

* refactor(ui): align chat sidebar and UI components with Figma design

- Restyle chat page sidebar: header with icon/title, search input,
  section labels, and "new session" footer button
- Simplify bot-sidebar and session-sidebar to card-based layout
  matching Figma session card design (58px height, 26px avatar, status dots)
- Update master-detail-sidebar-layout with bg-sidebar and 53px header
- Unify border-radius across UI components to rounded-lg (8px):
  Card, Toggle, Alert, Popover, Item; Dialog uses rounded-xl (12px)

* refactor(ui): move shared theme and design tokens from web to ui package

CSS variables, @theme inline mappings, @custom-variant, and base layer
styles now live in @memohai/ui/style.css. The web app imports them via
@import "@memohai/ui/style.css", keeping only the Tailwind entry point
and web-specific imports (markstream-vue, @source).

* refactor(ui): apply flat design system from Figma spec

Overhaul @memohai/ui component styles to match the new "high-contrast,
flat" design language defined in the Figma design spec (DESIGN.md).

Theme:
- --primary-foreground: pure white -> #fafafa
- --ring: purple -> foreground color (focus rings no longer use brand purple)

Atoms (zero shadow, monochrome):
- Button: default bg-primary -> bg-foreground; add explicit "primary" variant for Send CTA
- Badge: rounded-full -> rounded-sm; default bg-primary -> bg-foreground; add warning/outline/size variants
- Alert: rounded-lg -> rounded-[10px]; remove shadow-sm; destructive drops bg-red-50
- Card: add shadow-lg, rounded-lg -> rounded-xl, py-6 -> p-6
- Input/Textarea: remove shadow, text-sm -> text-[16px], focus ring non-purple
- Checkbox: checked bg-primary -> bg-foreground
- Switch: checked bg-primary -> bg-foreground
- RadioGroup: indicator fill-primary -> fill-foreground
- Slider: range/thumb border-primary -> border-foreground

Floating panels (shadow-md):
- DropdownMenu/Combobox/Select/ContextMenu Content: shadow-lg -> shadow-md
- Sheet: shadow-2xl -> shadow-lg
- MenuItem destructive focus: bg-red-50 -> bg-accent

Other:
- Pagination active: bg-foreground text-background (black, not purple)
- Item variants: bg-transparent -> bg-background/bg-accent
- Tabs active: shadow-sm -> border-border
- Toggle: remove shadow-xs, unify hover to accent
- SelectTrigger/NativeSelect: remove shadow, unify focus ring

Docs:
- Add packages/ui/DESIGN.md with full design system spec
- Simplify apps/web/AGENTS.md, remove duplicated design info, reference DESIGN.md

* refactor(chat-ui): restructure chat page components and styles (#288)

* refactor(chat-ui): restructure chat page components and styles

* feat(chat): add collapsible sidebar for both sides

* feat(ui): add PinInput and BadgeCount components, align styles with Figma spec

New components:
- PinInput (OTP input): PinInput, PinInputGroup, PinInputSlot, PinInputSeparator
  based on reka-ui PinInput primitives with flat border-stitching design
- BadgeCount: circular numeric counter with default/destructive/secondary variants

Style updates to match Figma design:
- Sonner: border-radius from 1rem to var(--radius-lg) (10px)
- Table: add border border-border rounded-sm to container
- TagsInput: remove shadow-xs, rounded-md -> rounded-lg, ring-[3px] -> ring-2

Updated DESIGN.md with all new component specifications.

* chore: move up css to ui package

* refactor: change npm package from @memoh to @memohai

* Feat/chat layout (#295)

* refactor(chat-ui): restructure chat page components and styles

* feat(chat): add collapsible sidebar for both sides

* fix: update chat page icon

* style: refine UI components appearance

* style: refine UI components appearance

* chore(ci): update lock file

* refactor: new layout

* chore: adjust style

* fix: tauri ui size

* chore: remove bot session metadata

* refactor: text size and muted color

* fix: indirect height of bot-details pages

* feat: add 5 icons

* refactor: polish chat flow and settings navigation labels

Persist chat selection across pages, simplify provider/settings sidebars, and refine chat/session UX so navigation and composer behavior feel consistent without extra session/provider jumps.

* docs(web): refresh AGENTS frontend architecture guide

Expand and align the web AGENTS documentation with the current route structure, component inventory, chat transport flow, and store responsibilities so implementation guidance matches the codebase.

---------

Co-authored-by: Quincy <69751197+dqygit@users.noreply.github.com>
2026-03-28 19:15:39 +08:00
Acbox dd1b588e95 chore: remove @memohai/cli 2026-03-24 21:34:04 +08:00
晨苒 e2e3b69acf feat(channel): add WeChat (weixin) adapter with QR code (#278)
* feat(channel): add WeChat (weixin) adapter with QR code

* fix(channel): fix weixin block streaming

* chore(channel): update weixin logo
2026-03-22 23:28:57 +08:00
AlexMa233 609ca49cf5 feat: matrix support (part 1) (#242)
* feat(channel): add Matrix adapter support

* fix(channel): prevent reasoning leaks in Matrix replies

* fix(channel): persist Matrix sync cursors

* fix(channel): improve Matrix markdown rendering

* fix(channel): support Matrix attachments and multimodal history

* fix(channel): expand Matrix reply media context

* fix(handlers): allow media downloads for chat-access bots

* fix(channel): classify Matrix DMs as direct chats

* fix(channel): auto-join Matrix room invites

* fix(channel): resolve Matrix room aliases for outbound send

* fix(web): use Matrix brand icon in channel badges

Replace the generic Matrix hashtag badge with the official brand asset so channel badges feel recognizable and fit the circular mask cleanly.

* fix(channel): add Matrix room whitelist controls

Let Matrix bots decide whether to auto-join invites and restrict inbound activity to allowed rooms or aliases. Expose the new controls in the web settings UI with line-based whitelist input so access rules stay explicit.

* fix(channel): stabilize Matrix multimodal follow-ups and settings

* fix(flow): avoid gosec panic on byte decoding

* fix: fix golangci-lint

* fix(channel): remove Matrix built-in ACL

* fix(channel): preserve Matrix image captions

* fix(channel): validate Matrix homeserver and sync access

Fail Matrix connections early when the homeserver, access token, or /sync capability is misconfigured so bot health checks surface actionable errors.

* fix(channel): preserve optional toggles and relax Matrix startup validation

* fix(channel): tighten Matrix mention fallback parsing

* fix(flow): skip structured assistant tool-call outputs

* fix(flow): resolve merged resolver duplication

Keep the internal agent resolver implementation after merging main so split helper files do not redeclare flow symbols. Restore user message normalization in sanitize and persistence paths to keep flow tests and command packages building.

* fix(flow): remove unused merged resolver helper

Drop the leftover truncate helper and import from the resolver merge fix so golangci-lint passes again without affecting flow behavior.

---------

Co-authored-by: Acbox Liu <acbox0328@gmail.com>
2026-03-22 21:55:34 +08:00
MoeMagicMango ebf238a568 fix(text): fix resolve emoji shown in telegram stream mode (#261)
* fix(text): resolve emoji shown in telegram stream mode

* chore(text): removing "reasoing" types in plain msg.

* feat(conversation): add function to check for tool call content in assistant outputs
2026-03-18 15:19:50 +08:00
Ringo.Typowriter ca598bb0a5 fix: align feishu webhook verification flow with sdk behavior (#250) 2026-03-15 19:39:13 +08:00
Menci be3d769013 feat(channel): redact credentials from IM error messages (#240) 2026-03-14 21:27:32 +08:00
BBQ 839e63acda feat(access): add guest chat ACL (#235) 2026-03-14 17:15:41 +08:00
Fodesu b46e494d3a feat(tts): introduce TTS system (#195) 2026-03-13 02:49:52 +08:00
Acbox 9b771acaa8 fix: slash commands in group chats trigger all bots instead of targeted one
- In group chats, only process slash commands when the message is
  directed at this bot (via @mention or reply-to-bot), preventing
  all bots from responding to the same command.
- Use raw_text metadata (before quote/forward context prepending)
  for command detection so quoted content like "/fs" doesn't
  accidentally match a command.
- Fix isTelegramBotMentioned text_mention entity check to verify
  the mentioned bot matches the current bot, not just any bot.
2026-03-12 20:08:55 +08:00
Acbox bb26d18757 fix(command): add missing command handler wiring and lint fixes
Wire SetCommandHandler into ChannelInboundProcessor so slash commands
are intercepted before reaching the LLM. Also apply lint fixes across
command package (strconv.Itoa, comment formatting, unused code removal)
and remove obsolete tool-call-browser.vue component.
2026-03-11 19:05:55 +08:00
Acbox a2e5c4f893 feat(channel): add quoted message context injection for Discord and Feishu
Prepend replied-to message text and attachments into the user query so
the LLM can see what is being replied to, matching the existing Telegram
behavior. Also set is_reply_to_bot metadata for Feishu reply-to-bot
detection in group chats.
2026-03-11 16:57:33 +08:00
BBQ 599bfb5ca8 fix(wecom): pass lint and typo checks
Fix WeCom adapter typos and strict Go lint findings (gosec/bodyclose/errcheck/revive) while keeping runtime behavior unchanged.
2026-03-11 02:14:00 +08:00
BBQ bc47655309 fix(wecom): align adapter with channel stream behavior
Migrate the imported WeCom adapter to current channel interfaces and stabilize stream delivery by preventing heartbeat/reply ACK timeout regressions and post-final overwrite updates.
2026-03-11 02:14:00 +08:00
Fodesu a2cb5939d7 fix(discord): rm reason in final message (#220) 2026-03-09 23:34:34 +08:00
Menci 36d50738b5 fix(channel): consistent markdown rendering across all Telegram paths (#210)
- Extract ContainsMarkdown to shared channel package
- Auto-detect markdown in normalizeOutboundMessage and MCP send tool
- Apply markdown-to-HTML conversion during streaming deltas, not just
  on the final message
- Remove resolveTelegramParseMode which incorrectly returned Telegram's
  native "Markdown" mode instead of converting to HTML
- Fix all 14 Telegram send/edit paths for consistent parse mode handling
- Reset parseMode for plain-text error messages to avoid HTML corruption
2026-03-09 13:06:44 +08:00
BBQ 3739def43f fix(text): avoid breaking UTF-8 during truncation
Use rune-aware truncation for user-facing text and log previews so multibyte content is not corrupted in memory context, Telegram messages, or diagnostics.
2026-03-09 12:43:57 +08:00
Menci 09cdb8c87f refactor(telegram): reduce code duplication and improve readability
- Extract parseTelegramTarget helper to consolidate duplicated @username
  vs numeric chat ID parsing from 6+ locations (builder functions,
  sendTelegramTextReturnMessage, sendTelegramAttachmentImpl)
- Extract Config.baseURL() to eliminate duplicate base URL resolution
  between apiEndpoint() and fileEndpoint()
- Refactor stream.go Push method: extract resetStreamState(),
  deliverFinalText(), and per-event-type sub-methods (pushDelta,
  pushFinal, pushToolCallStart, pushAttachment, pushPhaseEnd,
  pushError), reducing the 200-line switch-case to a clean dispatcher
- Use pushFinal's existing getBot() instead of duplicating parseConfig +
  getOrCreateBot
- Replace sort.SliceStable with slices.SortStableFunc + cmp.Compare
- Replace strings.Index + manual slicing with strings.Cut in
  decodeDataURLBytes, ResolveAttachment, and parseTelegramUserInput
2026-03-09 10:03:12 +08:00
Ringo.Typowriter e6a6dbe3f6 feat(channel): add QQ channel support and image message pipeline (#199)
* feat(channel): add qq adapter and outbound delivery

* feat(channel): ingest inbound qq messages

* feat(web): expose qq channel in management ui

* feat(channel): support qq attachment ingestion

* fix(mcp): fail read raw immediately for missing files

* fix(agent): parse inline image data into native image parts

* test(agent): align read_media tool tests with SDK options

* fix(channel): harden qq image delivery and reconnect loop

Avoid data URLs for qq channel images, reset reconnect backoff after healthy sessions, and fall back gracefully for malformed public image URLs.

* fix(channel): restore qq media delivery and target resolution

* fix(qq,mcp,agent): fix message/qq regressions and pass go lint

* fix(qq,agent): validate inline base64 and sync heartbeat seq

* fix(qq): validate remote voice mime for upload checks

* fix(qq): fall back intents and restore adapter wiring

* fix(qq): prevent final text leakage and dedupe persisted inbound query
2026-03-07 17:12:06 +08:00
BBQ 3feb03aca7 ci: add go lint and race test workflow (#187) 2026-03-05 11:25:33 +08:00
Fodesu 711cee7682 fix(channel): split long streaming messages at manager level (#168) (#182)
Split long AI responses into multiple platform messages during streaming
instead of truncating them. The manager counts accumulated delta runes
and opens a new stream when approaching the platform's TextChunkLimit.
Uses a soft/hard limit strategy that prefers splitting at sentence ends
or line breaks over cutting mid-sentence.

- Add pushDelta with soft (75%) / hard (100%) limit and natural break
  point detection
- Add splitStream, pushFinalAfterSplit, pushFinalWithChunking helpers
- Fix Discord adapter to use RuneCount Message Length
- Add tests for delta splitting, natural breaks, and final handling
2026-03-04 17:57:55 +08:00
Menci a124cde8e2 feat(telegram): add in-reply-to and forwarded-from header and clarify user name (#177) 2026-03-04 17:57:12 +08:00
Menci b1925bf2be feat(telegram): use sendMessageDraft for streaming in private chats (#174)
* feat(telegram): use sendMessageDraft for streaming in private chats

Use Telegram Bot API 9.3's sendMessageDraft to stream partial messages
with smooth animation in private chats, replacing the sendMessage +
editMessageText approach. Group/channel chats keep the existing
edit-based streaming.

- Add sendTelegramDraft() for the sendMessageDraft API
- Detect private chats via conversation_type metadata in OpenStream
- Use 300ms throttle for drafts (vs 5s for edits)
- Send permanent messages at tool call boundaries and on final event
- Reset buffer atomically in StreamEventFinal to prevent duplicate
  messages when multiple final events fire (one per assistant output)

* test(telegram): improve draft mode test assertions

Add sendTextForTest hook for sendTelegramTextReturnMessage to enable
direct assertion of send calls. Clean up residual unused variables
and replace indirect assertions with explicit mock-based verification.
2026-03-03 16:01:18 +08:00
BBQ 802dfd995f feat(telegram): support custom API base URL for reverse proxy setups (#160)
Allow configuring a custom Telegram Bot API base URL (`apiBaseURL`) per
channel, enabling users behind restricted networks to route requests
through a reverse proxy (e.g. Nginx, Cloudflare Workers).

Both API calls and file downloads respect the configured endpoint.
When omitted, the official https://api.telegram.org is used.

Closes #159
2026-03-02 15:04:20 +08:00
斬風千雪 b82444759a fix(telegram): several fixes of typing action in stream message (#136) 2026-03-01 14:11:32 +08:00
Fodesu 5aefccd7cc feat: support discord attacchment file, assetService 2026-02-25 16:30:43 +08:00
Fodesu 87a4be7439 fix: discord channel stream ignore Reasoning content 2026-02-25 16:29:57 +08:00
Ringo.Typowriter 29e76322cc feat(feishu): add webhook inbound mode, region support, and callback/attachment enhancements (#107) 2026-02-23 21:57:34 +08:00
Fodesu df12d94171 fix: replay methion 2026-02-23 18:38:07 +08:00
Acbox Liu 17cd077f34 feat: add thinking support (#100)
* feat: add thinking support

* feat: improve thinking block render in web and filter thinking content in channels

* fix: migrate
2026-02-23 14:41:27 +08:00
Ran 5a08b280ab fix: double reply bug 2026-02-23 05:55:20 +08:00
Fodesu 51acb4b546 feat: add replay Message 2026-02-23 05:55:20 +08:00
Fodesu 77ff24c6fd feat(platforms): add discord channel support 2026-02-23 05:55:20 +08:00
Acbox 44ab3792d2 fix(channel): create new message when tool calling 2026-02-23 00:31:59 +08:00
Acbox cb5d2c5fab fix: send message in group failed 2026-02-20 22:47:02 +08:00
Ringo.Typowriter 53f080503c fix(telegram): aggregate media_group inbound and preserve ordering with (#69)
attachment fallback query
2026-02-19 17:14:12 +08:00
BBQ bc374fe8cd refactor: content-addressed assets, cross-channel multimodal, infra simplification (#63)
* refactor(attachment): multimodal attachment refactor with snapshot schema and storage layer

- Add snapshot schema migration (0008) and update init/versions/snapshots
- Add internal/attachment and internal/channel normalize for unified attachment handling
- Move containerfs provider from internal/media to internal/storage
- Update agent types, channel adapters (Telegram/Feishu), inbound and handlers
- Add containerd snapshot lineage and local_channel tests
- Regenerate sqlc, swagger and SDK

* refactor(media): content-addressed asset system with unified naming

- Replace asset_id foreign key with content_hash as sole identifier
  for bot_history_message_assets (pure soft-link model)
- Remove mime, size_bytes, storage_key from DB; derive at read time
  via media.Resolve from actual storage
- Merge migrations 0008/0009 into single 0008; keep 0001 as canonical schema
- Add Docker initdb script for deterministic migration execution order
- Fix cross-channel real-time image display (Telegram → WebUI SSE)
- Fix message disappearing on refresh (null assets fallback)
- Fix file icon instead of image preview (mime derivation from storage)
- Unify AssetID → ContentHash naming across Go, Agent, and Frontend
- Change storage key prefix from 4-char to 2-char for directory sharding
- Add server-entrypoint.sh for Docker deployment migration handling

* refactor(infra): embedded migrations, Docker simplification, and config consolidation

- Embed SQL migrations into Go binary, removing shell-based migration scripts
- Consolidate config files into conf/ directory (app.example.toml, app.docker.toml, app.dev.toml)
- Simplify Docker setup: remove initdb.d scripts, streamline nginx config and entrypoint
- Remove legacy CLI, feishu-echo commands, and obsolete incremental migration files
- Update install script and docs to require sudo for one-click install
- Add mise tasks for dev environment orchestration

* chore: recover migrations

---------

Co-authored-by: Acbox <acbox0328@gmail.com>
2026-02-19 00:20:27 +08:00
斬風千雪 cd8cb59236 improvement(telegram): add ellipsis and "typing" action in streamed message (#59) 2026-02-17 19:13:55 +08:00
BBQ df7876a30c feat: add media asset system, channel lifecycle refactor, and chat attachments (#54) 2026-02-17 19:06:46 +08:00
Acbox 38753ef054 refactor: channel tools 2026-02-15 17:48:20 +08:00
Fodesu fc42e717a7 chore: fix typos 2026-02-13 22:06:56 +08:00
BBQ f3ea18a2da feat(telegram): add processing reaction and fix reply-to-bot trigger
Add ProcessingStatusNotifier implementation: show 👀 reaction while
processing, remove on completion/failure. Fix isReplyToBot to match
only the current bot (bot.Self.ID) instead of any bot, preventing
multiple bots from responding to the same reply. Add rune-safe text
truncation for Telegram message length limit.
2026-02-13 20:45:14 +08:00