Add a dedicated cancel route so users can skip mid-conversation OAuth without
token verification, and restyle OAuth/approval cards to match AgentStreamDisplay
with compact inline actions.
OAuth authorization was only handled in MCPTool.Execute, so unauthenticated
services failed silently during RegisterMCPTools before any tool reached the
agent. Wire EventBus/session metadata into registration and reuse the same
getOrCreateMCPClientWithOAuthRetry path used at execution time.
Extract MCP OAuth session/wait/connect logic into mcp_oauth.go and remove the
duplicate waitForOAuthAndRetry + RegisterMCPContext layering in mcp_tool.go.
When an agent invokes an OAuth-enabled MCP service that the current user has
not authorized yet, the tool call now pauses and surfaces an "Authorize" card
in the chat instead of failing with an opaque error. After the user completes
the per-user OAuth popup, the backend verifies a token exists and resumes the
paused tool call automatically.
Backend:
- Add mcp_oauth_required / mcp_oauth_resolved events and response types.
- Add OAuthPendingRequest + Gate.RequestOAuthAndWait, reusing the existing
approval waiter, Resolve, Redis cross-instance fan-out and timeout.
- MCPTool.Execute pauses on an authorization-required error for OAuth services
and retries once (with a fresh ctx and recycled connection) after the user
authorizes.
- Forward the new events to the SSE stream.
- Add ResolveMCPOAuth handler + route; it verifies authorization before
unblocking so a premature resolve cannot resume into another failure.
Frontend:
- Add McpOAuthCard with an Authorize button reusing the popup + status-poll
flow, then resolve the paused run.
- Handle the new stream events (dedup multiple pending cards per service).
- Add resolveMCPOAuth API client and i18n keys (zh/en/ko/ru).
Select the auth header for the configured strategy only, instead of
emitting both X-API-Key and Authorization whenever the fields happened
to be set (which double-authed after switching strategies). AuthType=""
keeps the legacy infer-from-fields behavior for pre-existing rows.
Add a configurable api_key_header (non-secret, defaults to X-API-Key) so
services expecting the key in a different header work without plaintext
custom headers.
On connection test, upgrade a bare 401 that advertises RFC 9728
protected-resource metadata into an explicit OAuthRequired signal so the
UI can guide the user to switch to OAuth 2.0. Surface the server's
description from the initialize handshake in the test result.
- Introduced tests for buildMustUseBlock to validate multi-word service prefix handling and name sanitization.
- Ensured that service names are correctly sanitized to prevent newline injection in must_use blocks.
- Verified that the generated block includes the correct tool name prefixes and does not contain unwanted characters.
Fixing the knowledge_create_test compile error in this branch let the
internal/application/service test package run for the first time, which
exposed two latent failures introduced when GetKnowledgeByID started
eagerly loading tags:
- GetOwningKBCreatorID went through the service-level GetKnowledgeByID
(which now calls repo.GetKnowledgeTags); ownership resolution only
needs the knowledge -> kb_id link, so resolve via repo.GetKnowledgeByID
directly (tenant-scoped) and drop the extra tag query.
- createKnowledgeFileRepoStub lacked GetKnowledgeTags, which the create
path always invokes via setAndAttachKnowledgeTags; add the stub method.
- Fix grep_chunks dropping the full-KB scope when a turn mixes @KB with
@tag/@file: extract scopeClause and OR knowledge-ID and (kb,tenant)
predicates together, matching knowledge_search's fan-out.
- Consolidate the three scattered per-request @Skill/@MCP scope blocks in
buildAgentConfig into applyPerRequestSkillScope / applyPerRequestMCPScope,
each narrowing the whitelist and computing the must_use pin in one place.
- Dedupe the two identical selection-mode normalizers in Input-field.vue.
- Fix knowledge_create_test passing "" for the tagIDs []string parameter,
which made the whole service test package fail to compile.
- Add unit tests for scopeClause and the per-request scope helpers.
Tighten per-turn @MCP/@Skill/@tag handling after review: enforce agent
MCP/Skill whitelist, ignore mentions when selection mode is none, tag-scope
grep_chunks, and simplified English must_use lines without tool dumps.
Skip persisting scope envelopes to history rendered_content.
Wire per-turn @mentions for skills, MCP services, tags, and files through
QA handlers into agent config, runtime_context/must_use user blocks, and
tool registration. Fix tag-only scopes dropping KB tools, refresh suggested
questions on mention changes, and strengthen MCP must_use so @selected
services are invoked before local KB search.
Two pre-existing test failures on main, unrelated to any feature change:
- Doris VectorRetrieve/KeywordsRetrieve inline LIMIT as a literal (Doris
does not support placeholders for LIMIT/OFFSET), but the SQL-shape tests
still expected `LIMIT ?` with TopK as a bound arg. Match the literal
LIMIT and drop the extra argument.
- TestOssEnsureBucket_CreateFails used the bucket name "test-bucket",
which already exists globally on OSS, so IsBucketExist short-circuited
and the create path never ran. Use a non-existent bucket name so the
create-with-invalid-credentials path is actually exercised.
Align embedding outbound requests with chat module defenses by validating
base URLs at construction time and using SSRFSafeDialContext with redirect checks.
Fetch publish tokens when opening an embed channel drawer so deploy snippets
stay available after browser restarts, while list endpoints still omit secrets.
Split tag filtering from CRUD management on the knowledge base document
page, align copy to "tags" across locales, and polish hover/detail surfaces
with setting-drawer section styling. Return tags from GetKnowledge for the
detail drawer.
Clear tag relations on move/delete, validate tags on create, batch-load tags,
restore KB-scoped reference counts, dedupe SetKnowledgeTags, fix filter
double-fetch and ResizeObserver leaks, and regenerate Swagger for tag_ids.
* feat: support multi-tag for document knowledge
- Add knowledge_tag_relations join table for many-to-many tag-knowledge mapping
- Migrate existing single tag_id data to the new table
- Update Knowledge struct, filter, payload types (TagID -> Tags/TagIDs)
- Rewrite repository layer: SetKnowledgeTags, GetKnowledgeTags, ListIDsByTagIDs
- Update tag count references to query knowledge_tag_relations
- Update service/handler layers for multi-tag create/list/batch-update
- Frontend: multi-select tag sidebar, multi-tag chip display, tag toggle in popup
- FAQ tags remain single-tag model (unchanged)
* feat(knowledge): tag edit dialog with dynamic overflow and inline creation
- Add TagEditDialog component with search, multi-select, and confirm
- Replace inline t-popup tag selector with dialog-based editing
- Tags overflow with +N badge, visible count adapts to container width via ResizeObserver
- Date/card layout: fixed date width, tags flex to fill remaining space
- Add persistent new-tag input bar in dialog (Enter to create, auto-select)
- Tag creation emits callback to refresh parent tag list immediately
- Apply same overflow + dialog trigger to both card view and list view
- Add i18n keys: tagEditDialogTitle, tagEditSearch, tagSelectedCount, tagOverflowTip, tagNewPlaceholder
* fix: exclude soft-deleted knowledge from tag reference counts
CountReferences, BatchCountReferences, and DeleteUnusedTags now
JOIN with knowledges table to filter out soft-deleted records
when counting knowledge_tag_relations. Previously they queried
knowledge_tag_relations directly without the deleted_at check,
causing stale tag counts after knowledge deletion.
* fix: refresh tag counts after async knowledge deletion completes
Add a second delayed tag list refresh (2.5s) after tag deletion to capture
updated reference counts for remaining tags. The original immediate refresh
handles removing the deleted tag from the list, while this delayed refresh
ensures other tags' counts are correct after the async deletion finishes.
---------
Co-authored-by: lyingbug <xiayula2014@gmail.com>
Move batch reparse work to an asynq task to avoid HTTP timeouts, add i18n
for all locales, guard in-flight documents, and prevent concurrent delete/reparse.
This commit introduces the Batch Reparse feature for the Knowledge Base.
Users can now select multiple documents and trigger a re-parsing pipeline in batches, streamlining the recovery from parse failures.
Tighten shared-agent readiness checks, surface not-ready toasts in the
selector, validate chat-model overrides in resolveChatModelID, and
improve configure deep-links for shared agents.
Block chat when agents lack an explicit chat or rerank model, guide users
through the selector and editor with field highlight, and align session QA
checks with the same readiness rules on backend and frontend.
Let admins send real requests against configured chat, embedding, rerank,
VLM, and ASR models from settings, with thinking controls shown only when
the model supports them.
Add recent=true to knowledge search so the mention picker can list recent
files without a keyword, and consolidate KB/FAQ/file chip styling across
chat input and message history.
Add compact agent filter beside channel list titles with selected name
display, fix embed localStorage agentId read on rebind, add IM agent
rebind tests, and deep-link agent editor counts to the matching tab.
Return provider-specific diagnostics when connectivity tests return zero
results, including SearXNG unresponsive_engines details instead of a
misleading API key hint.
Add agent complete wait timeout and answer fallbacks for cancel paths,
restore Telegram Markdown on finalize with plain-text retry, and show
answer-only final IM frames instead of the collapsed think summary.
Wait for agent complete without a short timeout, unify Telegram stream
parse mode, remove obsolete FinalAnswer handling, isolate per-section
newline tracking, add lifecycle tests, and update IM streaming docs.
Mirror Web AgentStreamDisplay/RagPipelineProgress in IM text streams:
retract live answers into 思考过程, show Web-aligned tool titles with
query/summary from structured data, and collapse thinking on finalize
instead of dropping it entirely.
Default unset agent thinking to explicit false so thinking_type models
receive disabled/enabled wire params. Render reasoning inside the quick-answer
pipeline timeline only when thinking events arrive, avoiding a flash of the
thinking step when thinking mode is off.
The runnable_by_viewer field was never populated for built-in agents,
so TenantRoleViewer users always hit 403 on agent-chat. Drop the field
and RBAC check so agent-chat matches knowledge-chat access for viewers.
Scope auth headers to feed fetches, use feed-scoped external IDs and
content-based incremental fingerprints, tolerate partial feed failures,
store feed URLs in settings, and fix credential configured detection.
Support connecting to MCP servers that require OAuth2, with a Cursor-like
zero-config flow: the user provides only a URL and the system performs
metadata discovery, dynamic client registration (DCR), and the
authorization code flow with PKCE on first connect.
- Persist per-user access/refresh tokens and per-service DCR client
credentials, encrypted at rest (AES-256-GCM)
- Store the PKCE verifier/state during the flow via Redis with an
in-memory fallback for Lite mode
- Cache MCP clients per (service, user) so tokens stay user-isolated
- Add endpoints for authorize-url, callback, status, and token revoke;
the callback is allowlisted for unauthenticated access
- Surface a structured "authorization required" hint when a tool call
hits an unauthorized MCP service
- Frontend: add an auth-type selector with OAuth config, inline
authorization status/actions, and render the connection test result
inline in the editor drawer instead of a stacked centered dialog
When editing a data source whose tree loads one level at a time (Feishu
wiki), a saved deep-node selection was invisible: only top-level spaces
were listed, so the selected node could not be seen and its ancestor
space rendered as unchecked.
- Add Connector.ResolveResourceAncestors: given selected resource IDs,
return the ancestor IDs that must be expanded to reveal them. Feishu
walks up via GetWikiNode (O(depth) per selection, no full-tree walk);
Notion/Yuque return empty.
- Fix wikiNode.ParentNodeID JSON tag: the Feishu API field is
parent_node_token, not parent_node_id, so GetWikiNode can return the
parent and the ancestor walk works in production.
- Expose POST /datasource/{id}/resource-ancestors (service + handler +
route) and a matching frontend API client.
- Edit-mode picker reveals hidden selections by expanding and lazily
loading each ancestor level, so selections are visible and checked.
- Gate child fetching on treeFullyLoaded instead of a partial-children
heuristic, avoiding a redundant request for full-tree connectors.
Listing resources for a Feishu data source eagerly walked the entire wiki
tree (one API call per node across every space), which exceeded the request
timeout for large knowledge bases. ListResources now takes a parentID and
returns a single level at a time: empty lists the wiki spaces, a space/node
ID lists only its direct children. The frontend fetches children on demand
when a node is expanded. Notion (full tree in one call) and Yuque (flat)
keep their existing behavior by ignoring non-empty parentID.
Refs: https://github.com/Tencent/WeKnora/issues/1672
PaddleOCR-VL renders tables as HTML with per-cell text-align styles.
These waste tokens on layout markup and bypass the chunker's
table-protection logic (which only recognizes Markdown pipe tables),
causing large tables to be split mid-row.
Convert inline HTML <table> blocks to GFM Markdown tables when possible,
and strip presentational attributes (style/class/align/...) for tables
using rowspan/colspan that Markdown cannot express.
Refs: https://github.com/Tencent/WeKnora/issues/1725
GET with a JSON body was non-standard and confused integrators. Register
POST as the preferred method and keep GET for existing clients; update SDK,
MCP server, and API docs accordingly.
Refs: https://github.com/Tencent/WeKnora/issues/1727
- Add IM service dependency to CustomAgentHandler for managing agent-related IM channels.
- Implement DeleteChannelsByAgent method to stop and soft-delete IM channels associated with a custom agent upon its removal.
- Update ListChannelsByTenant to exclude soft-deleted custom agents, ensuring consistent overview lists after agent removal.
The angle-bracket image target parser declared a local variable named
`close`, which shadows Go's built-in `close` function and is flagged by
revive's redefines-builtin-id rule, breaking `make lint`. Rename it to
`closeIdx`.
DingTalk websocket (Stream) channels stopped delivering @-mentions after
a day or two and only recovered when the IM channel was toggled off/on.
The factory fired the SDK's Start once and relied entirely on the SDK's
internal auto-reconnect, with no liveness supervision on WeKnora's side.
On long-running connections the SDK can enter a zombie state where the
connection object stays alive but no longer receives messages, leaving no
way to recover automatically. The cancel func was also ineffective because
the SDK ignores the context after the initial dial, so stopping a channel
leaked the old connection.
Add a reusable RunSupervised helper that keeps a long connection alive and
proactively recycles it every 6h on top of the SDK's own reconnect, which
bounds the worst-case outage and clears stuck connections. Wire DingTalk to
it, add LongConnClient.Close (disables AutoReconnect before closing so the
SDK does not resurrect the connection), and make the cancel func actually
tear the connection down. Drop the now-unused stream client reference from
the adapter, which only sends replies via sessionWebhook or OpenAPI.
checkSQLInjectionRisks and InjectAndConditions compiled their regexps
with regexp.MustCompile on every invocation. Both run on the datasource
SQL path — InjectAndConditions is called once per security filter
(tenant, soft-delete, hidden-KB, search-scope), so a single secured query
recompiled ~20 patterns. InjectAndConditions also compiled the identical
tail-clause pattern twice.
Hoist them to package-level vars (compiled once at init). Behavior is
unchanged — same patterns, covered by the existing TestValidateSQL_*
and TestInjectAndConditions tests.
Benchmarks (apple M, -benchmem):
InjectAndConditions 4347 ns, 72 allocs -> 1023 ns, 7 allocs
checkSQLInjectionRisks 36074 ns, 560 allocs -> 12984 ns, 12 allocs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>