Commit Graph

966 Commits

Author SHA1 Message Date
matevip
65cf53779a refactor(approval-grants-ui): paginated list, Element Plus icons, shorter sidebar label 2026-05-27 15:08:41 +08:00
matevip
2f5e06f286 fix(approval): three minor issues surfaced by end-to-end testing 2026-05-27 14:08:12 +08:00
matevip
fe072191ea feat(approval): REST surface for auto-grant strategies with tiered authorization 2026-05-27 14:07:56 +08:00
matevip
2653356613 feat(approval): record human-approval and timeout resolutions, retire grants on conversation delete 2026-05-27 14:07:48 +08:00
matevip
b7e923fac4 feat(approval): grant-based auto-approve with safety floor and resolution log 2026-05-27 14:07:39 +08:00
matevip
ac090afde3 fix(workflow): pre-check unique name on create/rename, return 409 instead of 500 (Fixes Gitee #IJPYWA) 2026-05-26 23:35:04 +08:00
matevip
b64a312994 fix(skill): harden GitHub token handling against credential leaks
The previous private-repo support inlined the access token into the
clone URL and then logged that URL on success — leaking the token to
log files, container stdout, and any IOException thrown when the clone
failed. The token also appeared in the process command line, visible
to anyone with shell access via `ps`.

Switch to git's GIT_CONFIG_COUNT/KEY/VALUE environment variables, which
inject `http.extraHeader: Authorization: Bearer <token>` into the child
process without ever touching argv or the repo URL. The URL stays
pristine, so the existing INFO log and error message are safe.

Other changes:
- Resolve token from `mateclaw.skill.github-token` property first, then
  fall back to GITHUB_TOKEN env var. Keeps the original deployment
  contract while letting admins manage the credential via configuration.
- Tighten the host check (prefix match on `https://github.com/` etc.)
  so a crafted URL like `https://evil.com/?u=github.com/...` cannot
  trick the fetcher into forwarding the token to a third party.
- Set GIT_TERMINAL_PROMPT=0 so a bad token fails fast instead of
  blocking on an interactive password prompt.
2026-05-26 23:11:46 +08:00
shenyuya
1c1409e309
feat(skill): support private GitHub repos and bump clone timeout
Adds GITHUB_TOKEN env var support for private GitHub repo cloning and raises the git clone timeout from 60s to 120s for slow networks.
2026-05-26 23:07:57 +08:00
matevip
c8b25e1bfb fix(agent): deny skill-discovery tools when skillsDisabled (#184 follow-up) 2026-05-26 22:16:51 +08:00
matevip
0ac325a337 feat(agent): explicit "no skills / no tools" opt-out flags (#184) 2026-05-26 22:07:12 +08:00
matevip
ff2620dfcf fix(wiki): harden kbName/kbId routing — ambiguous fail-closed, kbId param, prompt cleanup (#224) 2026-05-26 14:31:07 +08:00
matevip
d2b23c049c fix(wiki): let agents reach every visible KB via kbName + wiki_list_kbs (#224) 2026-05-26 13:53:32 +08:00
matevip
3ae4498f38 fix(channel,agent,chat): unify channel binding / conversation agent / model pin state sources 2026-05-26 09:40:49 +08:00
matevip
7f5652b2f0 fix(agent): replace per-loop head/tail trim with anchored token-budget budgeter 2026-05-26 07:34:25 +08:00
matevip
6c8c490bd3 chore(feishu): bump oapi-sdk to 2.7.1 and replace WS-close reflection with public API 2026-05-25 21:36:33 +08:00
matevip
e9ead959b8 fix(feishu): invoke SDK disconnect() directly and surface cleanup failures 2026-05-25 21:31:09 +08:00
倪程伟
54e3f7f3fa
fix(feishu): properly close WebSocket connection to prevent resource leak (#221)
stopWebSocket() only nullified the wsClient reference without calling
disconnect() on the SDK client. This left the old WebSocket connection's
pingLoop thread and ExecutorService running, leaking file descriptors
and threads on each reconnect. Over time, accumulated leaks prevented
new connections from being established, causing the Feishu channel to
silently stop receiving messages.

Fix: use reflection to access the SDK's protected `conn` field and call
close(1000) on the OkHttp WebSocket, triggering the SDK's onClosed →
disconnect() cleanup chain.

Note: oapi-sdk 2.7.1 adds a public close() method that would make this
reflection unnecessary. Consider upgrading as a follow-up.

Closes #220
2026-05-25 21:28:05 +08:00
matevip
a01f0354eb feat(conversation): introduce ChatResult to carry token usage through sync chat paths 2026-05-25 21:15:53 +08:00
matevip
8d01396130 fix(conversation): capture runtime model/provider with token usage in IM and webchat paths 2026-05-25 20:31:09 +08:00
倪程伟
727373f67c
fix(conversation): capture token usage in IM channel and webchat paths (#217)
IM channels (Feishu, DingTalk, WeCom, etc.) and the WebChat widget were
calling saveMessage without token usage parameters, causing promptTokens
and completionTokens to default to 0. This made the Token Statistics
module report significantly lower numbers than actual usage.

Root cause: the _usage_final event (containing promptTokens /
completionTokens) emitted by the agent graph at stream end was not being
captured in these paths, unlike ChatController's StreamAccumulator which
already handles it correctly.

Fix: capture _usage_final events in doOnNext handlers for:
- ChannelMessageRouter sync path (non-streaming IM adapters)
- ChannelMessageRouter streaming path (DingTalk, etc.)
- WebChatController SSE stream

Refs #214 (remaining String-API paths covered by follow-up).
2026-05-25 20:25:22 +08:00
matevip
07eb625d11 fix(agent): collapse SystemMessages at egress to fix LM Studio 400 (#218)
Some OpenAI-compatible providers (LM Studio's built-in server, certain
strict-mode vLLM / SGLang deployments) reject 400 "System message must
be at the beginning" when SystemMessages appear after user / assistant
/ tool messages. The reasoning loop currently emits four SystemMessage
segments — main prompt at index 0, skill catalog inserted at index 1,
progress-ledger snapshot and stale-reminder appended at the end of
nonHistoryPrefix after the runtime-context UserMessage. The latter two
violate the strict shape, so conversations on LM Studio 400 on the
first turn (reported in #218).

Add MessageNormalizer: collects every SystemMessage in the outbound
prompt regardless of position, joins their text with a blank-line
separator, and emits a single SystemMessage at index 0. Non-system
messages keep their relative order, so AssistantMessage(tool_calls) ↔
ToolResponseMessage adjacency is preserved verbatim (required by strict
pair validators).

Wire it into doStreamCall as the first pre-egress step so every node
(reasoning, step-execution, summarizing, plan-generation, limit-exceeded)
inherits the fix without per-node changes, and any future node that
emits multiple SystemMessages stays compliant.

The transformation is semantically equivalent on permissive providers
(OpenAI, DashScope, Ollama, DeepSeek, Kimi, Doubao, GLM) — the merged
token sequence matches what they would have seen across N SystemMessages
— and safe on non-OpenAI protocols (Anthropic, Vertex / Gemini), whose
adapters already extract SystemMessages into a top-level system field
and receive an identical payload.

Kill switch: -Dmateclaw.llm.message-normalizer.enabled=false reverts to
the prior behavior for emergency rollback.

Tests: 11 unit tests on MessageNormalizer cover empty / no-system /
canonical / mid-list / tail / blanks / tool-pair preservation / Prompt
option-reference preservation / kill switch. 1 wiring test pins the
call site in doStreamCall. Full vip.mate.agent.** suite (504 tests)
stays green.

Closes #218.
2026-05-25 17:57:03 +08:00
matevip
a37074a9a6 fix(tool): close three sandbox follow-up gaps surfaced by review
1. Relative parent traversal in shell commands (HIGH)

   validateShellCommand only scanned absolute path tokens, so commands
   like `cat ../mateclaw/CLAUDE.md`, `cd .. && cat foo`, or
   `ln -sf ../bar breakout` had no absolute path to trip the check.
   From a workspace cwd that's a real escape — `..` segments resolve
   against the JVM cwd at file-tool time and reach anywhere the user
   can read.

   Add a second pass: any token containing `..` as a path segment is
   resolved against the workspace root via root.resolve(token).
   normalize(); reject when the result falls outside. In-workspace
   traversal like `subdir/../sibling` normalizes back inside and
   passes. Identifiers without slashes (e.g. version strings with
   `1.2..3`) are not treated as paths.

2. Shell validation and process working directory used different
   context sources (MEDIUM)

   execute_shell_command validated with the explicit ToolContext, but
   buildShellProcess called WorkspacePathGuard.getWorkingDirectory()
   (no-arg), which only sees the ThreadLocal fallback. Today the
   ToolExecutionExecutor sets both so the discrepancy is latent, but
   a future direct Spring AI invocation passing only ToolContext would
   validate against one basePath and exec against another. Thread ctx
   through buildShellProcess and call getWorkingDirectory(ctx) so
   validation and execution agree on a single source of truth.

3. Absolute agent override could disable workspace scoping (MEDIUM)

   resolveAgentBasePath accepted an absolute override verbatim, even
   when it pointed outside the workspace root. An admin (or any
   account with agent-edit permission) could set workspaceBasePath="/"
   or another team's repo and bypass workspace boundaries entirely.

   When a workspace has its own basePath, require absolute overrides
   to sit underneath it. The caller in build() catches the rejection,
   logs WARN, and falls back to the workspace basePath so chat stays
   available rather than crashing agent construction. When the
   workspace has no basePath there's no boundary to enforce, so legacy
   behavior is preserved.

Test coverage: WorkspacePathGuardShellTest grows from 17 to 23 (six
new cases for `cd ..`, relative parent traversal, relative symlink
escape, deeper traversal, in-workspace normalization, and the
identifier false-positive guard). AgentGraphBuilderBasePathResolutionTest
grows from 7 to 10 (three new cases for in-workspace absolute,
outside-workspace absolute rejection, and no-workspace legacy
behavior). All 45 sandbox-area tests pass with no regressions.
2026-05-25 17:55:56 +08:00
matevip
7272d8f633 fix(tool): allow standard /dev/* character devices in shell sandbox 2026-05-25 16:38:31 +08:00
matevip
b09a220ec7 fix(tool): enforce workspace boundary on shell commands and file metadata tools 2026-05-25 16:26:16 +08:00
matevip
9e9a96f674 fix(agent): resolve relative workspaceBasePath under workspace root 2026-05-25 15:58:05 +08:00
倪程伟
cbdd70379b
feat(agent): optional agent-level workspace basePath override (#212)
* feat(agent): optional agent-level workspace basePath override

Add workspaceBasePath field to AgentEntity that optionally overrides
the workspace-level basePath. When set, the agent uses its own directory;
when null, it inherits the workspace's basePath (existing behavior).

- AgentEntity: new workspaceBasePath field with ALWAYS update strategy
- AgentGraphBuilder: agent-level override takes priority over workspace
- Flyway migration V121 for H2 and MySQL
- UI: form input in basic tab with i18n (zh-CN, en-US)

* fix(agent): rename migration V121→V125 to avoid Flyway conflict with upstream

Upstream already has V121__tool_disclosure_tier.sql. Rename our
migration to V125 (next available after V124).

* fix(agent): make MySQL V125 migration idempotent

Use INFORMATION_SCHEMA check before ADD COLUMN to avoid
"Duplicate column name" error on re-deploy.
2026-05-25 15:42:01 +08:00
matevip
692362e7a7 fix(channel): flush emergency save before SSE idle eviction disposes the run 2026-05-24 23:01:14 +08:00
matevip
f1d9104422 fix(channel): evict SSE RunState by inactivity, not wall-clock age 2026-05-24 23:01:04 +08:00
matevip
7f45b95432 feat(agent): include progress-ledger snapshot in limit-exceeded wrap-up 2026-05-24 23:00:49 +08:00
matevip
59d090ebb5 chore(agent): raise default max_iterations 100 -> 150 2026-05-24 23:00:42 +08:00
matevip
c36abf38b8 feat(agent): inject stale-ledger reminder when the model stops updating 2026-05-24 23:00:35 +08:00
matevip
8798668524 feat(agent): require ledger discipline in system prompt for multi-step tasks 2026-05-24 23:00:30 +08:00
matevip
9f2fb3db23 feat(agent): echo full ledger snapshot in progress_update tool result 2026-05-24 23:00:24 +08:00
matevip
7736f6b0ab fix(agent): serialise progress-ledger upsert per conversation 2026-05-24 23:00:17 +08:00
matevip
e953f8be5a feat(agent): age-based compaction of older tool-response bodies 2026-05-24 23:00:11 +08:00
matevip
05289e6bdb feat(agent): per-conversation progress ledger to survive context trims 2026-05-24 23:00:03 +08:00
matevip
fe9610dfa6 fix(dashboard): count tool calls from assistant.metadata, not role=tool rows 2026-05-24 22:58:02 +08:00
matevip
8030a41125 fix(api): return 400 on path-variable type mismatch instead of 500 2026-05-24 22:57:51 +08:00
matevip
c49b4a21bb fix(tool): force markdown-link reply for generated-file downloads 2026-05-24 22:57:41 +08:00
matevip
68effa3f90 fix(tool): retry browser_use eval with async IIFE on top-level return 2026-05-24 22:57:30 +08:00
matevip
c857d6dd45 fix(agent): retry empty LLM completion before treating it as final answer 2026-05-24 22:57:19 +08:00
matevip
481cece733 fix(channel): preserve queued chat inputs across turns 2026-05-24 22:57:08 +08:00
matevip
a9c2d45790 Harden goal approval and workspace flows 2026-05-23 22:55:16 +08:00
matevip
5cd6e841a4 feat(skill): broaden script security scan + GBK zip-import fallback 2026-05-23 22:55:10 +08:00
matevip
3fc121c6b7 fix(tool): show runtime names in tools admin 2026-05-23 22:54:57 +08:00
matevip
123d912f84 fix(skill): allow template file access 2026-05-23 09:08:01 +08:00
matevip
cef1730e6e feat(tool,skill,ui): progressive tool/skill disclosure (load_skill + enable_tool + tier UI) 2026-05-23 09:07:45 +08:00
matevip
773c64bfd7 chore(tool): localize send_file success message and use English Javadoc 2026-05-22 16:32:10 +08:00
倪程伟
f16021690f
feat(tool): add send_file tool for sending existing server files as IM attachments (#199)
* feat(tool): add send_file tool for sending existing server files as IM attachments

Adds a new built-in tool that reads a file from the server and stashes it
in GeneratedFileCache so the channel adapter (Feishu, DingTalk, etc.)
automatically sends it as a native attachment. This fills the gap where
agents had no way to send existing server files to users — ReadFileTool
only reads text, and render tools only generate new files.

- New SendFileTool with path validation, MIME detection, 20MB limit
- Added "send_file" to tool allowlist in AgentBindingService
- Added i18n error messages (zh-CN + en-US)

* fix(tool): send_file returns URL in scrubber-detectable format

The previous JSON return format caused the LLM to reply with just
"status: sent" without echoing the /api/v1/files/generated/{id} URL.
GeneratedFileScrubber only scans the LLM's final text output, so the
file was never delivered as a native attachment.

Changed to match GeneratedFileLink's format: returns a markdown link
with explicit instructions for the LLM to echo the URL verbatim.
2026-05-22 16:27:53 +08:00
倪程伟
6bf64449bd
fix(channel/feishu): register all IM event handlers to prevent HandlerNotFoundException (#196)
The Lark SDK throws HandlerNotFoundException for any event type without
a registered handler. This exception is caught internally by the SDK's
WebSocket client, which then sends a 500 response to the Feishu server.
The server may close the connection as a result, and the exception is
swallowed — never reaching the application layer.

Added empty handlers for all remaining IM event types:
- P2MessageReadV1 (read receipts)
- P2MessageRecalledV1 (message recall)
- P2ChatMemberBotDeletedV1 (bot removed from chat)
- P2ChatMemberUserAddedV1 / UserDeletedV1 / UserWithdrawnV1
- P2ChatUpdatedV1 (chat info update)
- P2ChatDisbandedV1 (chat disbanded)
- P2ChatAccessEventBotP2pChatEnteredV1 (bot entered p2p chat)

Also added explicit logback config for com.lark.oapi at WARN level
to ensure SDK internal errors are not silently filtered.

Refs: larksuite/oapi-sdk-java#185
2026-05-22 16:10:05 +08:00
matevip
5f571e86a2 feat(agent,ui): multi-level subagent delegation tree 2026-05-22 13:44:01 +08:00
matevip
8bd8a02cd0 feat(agent,ui): nested subagent timeline + always-on plan panel 2026-05-22 09:48:06 +08:00
matevip
66af70388b fix(goal): give reasoning models enough budget for the evaluator JSON 2026-05-21 22:27:43 +08:00
matevip
7d7ea99747 fix(goal): emit goal_evaluated on every GoalEvaluationNode skip path 2026-05-21 22:27:17 +08:00
matevip
81915ccfae feat(tool): read_file can page through an oversized single line via startColumn (#190) 2026-05-21 22:27:09 +08:00
matevip
9e93c52d9a fix(goal): real evaluator, retry refactor, hardened node + extra edges 2026-05-21 22:27:00 +08:00
matevip
c34e8290ac feat(goal,ui): inline set-goal prompt, terminal system-line, sidebar dot 2026-05-21 22:26:40 +08:00
matevip
9c5ad29d42 chore(llm): drop unused imports, add XIAOMI_MIMO cross-turn cache tests 2026-05-21 17:24:53 +08:00
倪程伟
7861f603eb
fix(llm): MiMo thinking 模式 reasoning_content 多轮对话兼容修复 (#189)
MiMo V2 系列在 thinking 模式下,assistant 消息携带 tool_calls 时必须同时包含 reasoning_content,否则提供方返回 400。

- ModelFamily 新增 MIMO_THINKING 族,detect() 添加 mimo* 匹配
- FallbackPolicy 新增 XIAOMI_MIMO(patchCrossTurn=true, patchNonToolCall=true)
- 新增 ReasoningContentCache,按 tool_call_ids 回放真实推理内容
- 缓存作用范围:所有 patchCrossTurn=true 的 thinking provider(MiMo + DeepSeek)
- NodeStreamingChatHelper 流式响应完成后写入缓存

Closes #188
2026-05-21 17:15:35 +08:00
matevip
e61b05bba0 fix(tool): read_file no longer returns empty content + infinite retry on oversized single lines (#190) 2026-05-21 16:26:24 +08:00
matevip
a910004b3b feat(agent): digital-employee builder skill to auto-create agents and chain them into a workflow (#165) 2026-05-21 16:26:04 +08:00
matevip
2b6a4c64c9 fix(agent): add data-fidelity rules to summarizer prompts + fix fallback cut (#187) 2026-05-21 15:13:15 +08:00
matevip
03e68d3c74 fix(agent): structure-aware truncation to stop mid-JSON cuts inducing hallucination (#187) 2026-05-21 15:13:07 +08:00
matevip
43bbe26ff9 fix(goal,agent): expose goal management tools to every agent by default 2026-05-21 14:44:06 +08:00
matevip
c9e54e820f fix(goal,ui): live ring update after agent-triggered setGoal / addGoalCriterion 2026-05-21 14:43:59 +08:00
matevip
89f8413db8 test(agent): sync LaneDPerformanceFixesTest with MAX_RETRIES bump 2026-05-21 14:43:52 +08:00
matevip
efce3bc209 fix(goal): pin JSON wire form for GoalStatus to lowercase 2026-05-21 14:43:40 +08:00
matevip
746ade0410 feat(goal): flip enabled flag on, forward completions to long-term memory 2026-05-21 14:43:34 +08:00
matevip
6646e91585 feat(goal): built-in tools for agent-driven goal management 2026-05-21 14:43:20 +08:00
matevip
ce74a0ae48 feat(goal): graph topology + evaluation node wired into ReAct + Plan-Execute 2026-05-21 14:43:13 +08:00
matevip
ac6c8a18e3 feat(goal): persistent cross-turn goal with self-evaluation scaffolding 2026-05-21 14:43:01 +08:00
matevip
51e6542a5a feat(channel/qq): add scan-to-bind onboarding via QQ Open Platform Lite portal 2026-05-20 21:47:49 +08:00
matevip
b26bca1584 fix(qq): handle non-Map data in DISPATCH events (#185) 2026-05-20 21:47:43 +08:00
matevip
2d3afa6550 feat(sessions): paginate admin list, add back-nav, redesign with depth 2026-05-20 20:58:05 +08:00
matevip
82540a5fcb docs(conversation): bilingualize ConversationService comments (en/zh) 2026-05-20 20:57:53 +08:00
matevip
6a4318c268 fix(channel): IM conversations respect per-conversation model selection (#183) 2026-05-20 17:49:03 +08:00
倪程伟
d7378273b2
fix(llm): classify "network connection error" as retryable SERVER_ERROR (#179)
Some providers (notably SiliconFlow) return "network connection error" in the response body when their backend is overloaded or the upstream model connection is disrupted. classifyError() had no pattern for this string, so it fell through to UNKNOWN (non-retryable), surfacing the raw error to the user on the first failure instead of running the exponential-backoff recovery. Adds the pattern to the SERVER_ERROR classifier and a friendly message mapping in extractUserFriendlyError(); bumps MAX_RETRIES from 5 to 10 so sustained wiki batch load can ride out provider flaps without surfacing an error to the channel user.

Closes #178
2026-05-20 16:49:54 +08:00
matevip
43136fc663 fix(channel): sweep orphan tool rows + guard rules; scope rule name to channel 2026-05-20 16:35:57 +08:00
matevip
12ff190392 feat(feishu): transcribe inbound voice messages via SttService 2026-05-20 16:35:46 +08:00
matevip
6b4456043e fix(feishu): outbound generated-file URLs become native attachments 2026-05-20 16:01:03 +08:00
matevip
6b397a10ed feat(feishu): inbound file/audio/video download — SDK path + cache push 2026-05-20 15:53:40 +08:00
matevip
71e08b015e sync: Feishu approval card 5-chain hotfix — verified end-to-end in production 2026-05-20 15:24:47 +08:00
matevip
090bb64c6a sync: Feishu channel-native tool provider + DbRuleGuardian generic guard 2026-05-20 12:29:29 +08:00
matevip
85d7ee23c4 sync: ChannelToolProvider SPI + node-local reconcile framework for channel-native tools 2026-05-20 12:18:51 +08:00
matevip
a9fa8e7fb1 sync: interactive approval card on Feishu via Schema-2.0 button + card.action callback 2026-05-20 12:05:47 +08:00
matevip
3554da8dbc sync: inject sender context into agent prompt + Feishu DONE ack hook 2026-05-20 11:51:58 +08:00
matevip
35f010d7a1 sync: Feishu CardKit streaming-card adapter via cardkit/v1 SDK 2026-05-20 11:37:12 +08:00
matevip
0e1b8ca564 sync: componentized media upload SPI + Feishu SDK-backed file sender 2026-05-20 11:23:33 +08:00
matevip
5cc567a689 refactor(llm): downgrade embedding connectivity test failure log to warn 2026-05-20 10:58:21 +08:00
matevip
eb827d8cc5 fix(feishu): three small follow-ups from the post-merge audit 2026-05-20 10:58:15 +08:00
倪程伟
2e4f88c612
fix(llm): switch slash-bearing modelId from path variable to query parameter (#177)
Closes #174

Model identifiers like 'Qwen/Qwen3-Embedding-8B' or
'Pro/deepseek-ai/DeepSeek-V3' carry forward slashes that Spring MVC
decodes from %2F before path matching, so even with the frontend's
encodeURIComponent the request never reaches the handler and 404s out.

The two affected endpoints take modelId as a request param instead:

  DELETE /{providerId}/models/{modelId}      -> DELETE /{providerId}/models?modelId=...
  POST   /{providerId}/models/{modelId}/test -> POST   /{providerId}/models/test?modelId=...

modelApi.removeProviderModel / testModel in the UI follow suit, passing
the id via axios params so axios handles the URL encoding consistently.
providerId stays as a path variable — provider ids are kebab-case and
never contain slashes.
2026-05-20 10:22:44 +08:00
倪程伟
461f81ccb5
fix(llm): add @Slf4j and log embedding test failures with full stack trace (#176)
Closes #175

ModelConfigController.testEmbedding() previously caught and stringified
the exception's getMessage() into the response body without writing
anything to the server log. Operators investigating an Embedding test
failure saw only the truncated client-side message — root causes like
the DashScope-native vs OpenAI-compat routing bug (#166) or the
requireApiKey gap (#167) were invisible server-side.

Add @Slf4j to the controller and log.error the full stack trace
alongside the failing modelId, so future Embedding test regressions are
diagnosable from the server log without redeploying with debug
breakpoints.
2026-05-20 10:18:48 +08:00
matevip
3340885da3 fix(llm): skip chat-style probe for embedding-prefix models in DashScope discovery 2026-05-20 10:14:25 +08:00
matevip
a6b5e3b515 refactor(llm): extract testable embedding protocol routing + drop dead fromProviderId 2026-05-20 10:14:12 +08:00
matevip
70a599e403 fix(embedding): use NoopApiKey for keyless OpenAI-compatible providers 2026-05-20 10:14:05 +08:00
matevip
2f90052430 refactor(channel): drop dead require_mention block in checkAccess 2026-05-20 10:13:59 +08:00
matevip
8d91197a46 refactor(feishu): drop dead helper, allow configurable card header, tidy regex 2026-05-20 10:13:53 +08:00
matevip
56ab7b3095 fix(feishu): fall back to text when Interactive Card payload exceeds Feishu limit 2026-05-20 10:13:47 +08:00
matevip
3c3c2a938f perf(feishu): prefetch bot open_id + DCL + 60s negative cache 2026-05-20 10:13:40 +08:00
matevip
73ab31a13c fix(feishu): fail open when bot open_id is unavailable 2026-05-20 10:13:34 +08:00
倪程伟
16b5d75d2c
fix(llm): exclude soft-deleted models from uniqueness check in validateModel (#173)
Closes #169

ModelConfigService.validateModel() flagged a duplicate when re-adding a
manually-typed (provider, modelName) pair that happened to match a row
with deleted=1 in mate_model_config. The user-visible symptom: adding
'dashscope/qwen3-plus' fails with 'model identifier already exists',
yet the management page shows no such model.

The project itself runs hard-delete via deleteById(), so the user-facing
delete path doesn't create deleted=1 rows. The stale rows come from
schema migrations (V44, V81) that intentionally tombstone bogus catalog
entries — for instance V81 sets deleted=1 on the non-existent
'qwen3-plus' (id=1000000172) so it stays out of routing but preserves
the id for audit. ModelConfigEntity has no @TableLogic, and the project
has no global logic-delete-field config, so LambdaQueryWrapper queries
do not auto-append the deleted filter; the migration tombstones leak
into the validate-model query.

Add an explicit .eq(getDeleted, 0) to the uniqueness check so migration
tombstones don't block legitimate re-adds.

Follow-up: several other queries in ModelConfigService share the same
oversight (list/get methods), and a future migration could drop the
tombstones entirely to align with the V20 hard-delete posture.
2026-05-20 09:33:33 +08:00
倪程伟
828ece526e
fix(llm): add text-embedding- prefix to DashScope native model allow-list (#172)
Closes #168

The native DashScope provider exposes both chat and embedding models, but
DASHSCOPE_NATIVE_ALLOW_PREFIXES only listed chat families
(qwen-/qwen2-/qwen3-/deepseek-/baichuan/yi-/llama). When a user manually
added text-embedding-v1/v2/v3/v4 to the dashscope provider,
assertModelIdAcceptable() rejected the id because no allow prefix matched.

Add 'text-embedding-' to the allow-list and broaden the doc comment from
"native chat protocol" to "native protocol (chat or embedding)" so the
intent is clear.

Discovery probing is chat-based and will still mark embedding entries
probeOk=false; surfacing them as discoverable embedding suggestions is a
separate follow-up.
2026-05-20 09:29:11 +08:00
倪程伟
e0f66eef25
fix(embedding): respect requireApiKey flag in OpenAI-compatible embedding factory (#171)
Closes #167

EmbeddingModelFactory.buildOpenAi() hard-failed on any provider whose API
key was empty or unusable, so keyless providers like Ollama and OpenCode
(declared with requireApiKey=false) could pass the chat connectivity test
but bounce when the same provider's embedding model was tested.

Mirror the chat path in OpenAiCompatibleChatModelBuilder.buildOpenAiApi:

- If requireApiKey is not explicitly false, an unusable key still throws.
- If requireApiKey == false, the key check is skipped and an empty string
  is passed to OpenAiApi.builder() so no Authorization: Bearer header is
  attached to the outgoing request.
2026-05-20 09:25:16 +08:00
倪程伟
8e00613e69
fix(embedding): use chatModel field for protocol routing instead of providerId matching (#170)
Closes #166

EmbeddingModelFactory used EmbeddingProtocol.fromProviderId() to pick the
embedding protocol, which substring-matches 'dashscope' / 'qwen' / 'aliyun'
in the providerId. The dashscope-compat provider carries 'dashscope' in its
id but runs in OpenAI compatible mode (chatModel='OpenAIChatModel',
baseUrl='https://dashscope.aliyuncs.com/compatible-mode/v1'). Routing it to
DASHSCOPE_EMBEDDING made DashScopeApi build its native path against the
compat base, producing 404s on every embedding call.

Switch to the chatModel column instead — the same signal ModelProtocol
.fromChatModel() uses for the chat path. chatModel='DashScopeChatModel'
takes the native protocol; everything else (including dashscope-compat)
takes OpenAI-compatible.

EmbeddingProtocol.fromProviderId() is retained for reference but is no
longer called; future callers should follow the chatModel pattern.
2026-05-20 09:22:06 +08:00
倪程伟
af3e68d271 fix(feishu): use SDK mentions field for require_mention group filtering (#163)
Closes #162

require_mention=true previously degraded to a no-op when botPrefix was unset:
shouldProcess() returned true for all messages and checkAccess() fell through
unconditionally, so any group message would be answered — including ones where
the @mention targeted another user.

FeishuChannelAdapter now consults the Feishu SDK's mentions field directly:

- WebSocket: read EventMessage.getMentions(); webhook: read mentions[] from the
  JSON payload. In both paths each mention's id.open_id is compared against the
  bot's own open_id.
- Bot open_id is fetched lazily via /open-apis/bot/v3/info and cached on the
  adapter instance. If the call fails the message is allowed through, matching
  the previous behaviour.
- The require_mention gate is applied at the top of handleFeishuMessage so 1:1
  chats are unaffected.

Tests: 15 unit cases covering null/empty inputs, bot mentioned, only-other
mentioned, bot among multiple mentions, and malformed payloads.
2026-05-20 08:49:11 +08:00
倪程伟
ee0c229f52
feat(feishu): support Interactive Card JSON for structured message rendering (#161)
Auto-route Agent replies to Feishu Interactive Card (schema 2.0) when the
content carries structure — JSON object / array, Markdown with code blocks
or headings, or long-form prose — and keep the original text path for
short plain replies.

- FeishuCardFormatter: package-private detect() + render() helper
  - JSON object → two-column summary card
  - JSON array (≤4 fields) → table component; (>4 fields) → div per item
  - Markdown → lark_md card with 'AI 助手' header
  - Long text (>300 chars with paragraph breaks) → plain_text card
  - JSON embedded in Markdown code blocks is recognised across all fences
- FeishuChannelAdapter
  - sendMessage() honours channel config 'card_format' (auto | always | never)
  - sendCard() POSTs interactive messages; ou_-prefixed targets use open_id
  - updateCard() PATCHes an existing message (streaming-update hook)
- Tests: 32 unit cases covering every detect path and render branch

Closes #141
2026-05-20 08:42:02 +08:00
matevip
db16ff02a5 chore: drop external project name references from code comments 2026-05-20 08:09:43 +08:00
matevip
c0f9d1d9fb test(wiki): add adversarial HTML normalization tests and harden skeleton-tag handling 2026-05-19 21:23:13 +08:00
matevip
9ffe7aa399 fix(wiki): strip script/style/nav/footer text from HTML-typed material 2026-05-19 21:23:07 +08:00
matevip
32d633ae4b fix(workspace): verify a path-bound agent belongs to the request workspace 2026-05-19 20:07:02 +08:00
matevip
651d53050e fix(workspace): return 403 instead of 500 for cross-workspace access 2026-05-19 20:06:50 +08:00
matevip
0a57cb3358 fix(workspace): harden workspace create and delete 2026-05-19 20:06:44 +08:00
matevip
b2f9976f44 fix(i18n): stop logging missing-key noise for optional tool descriptions 2026-05-19 20:06:38 +08:00
matevip
d629c945ee fix(wiki): return the new page id from the create-page tool 2026-05-19 20:06:33 +08:00
matevip
57ca69d674 fix(wiki): return HTTP 404 for missing knowledge bases and pages 2026-05-19 20:06:27 +08:00
matevip
c1b878b7e1 fix(agent): fail over to backup providers when the primary is rate-limited 2026-05-19 20:06:21 +08:00
matevip
95ce1bae24 docs(agent): describe binding-service helpers functionally 2026-05-19 16:53:24 +08:00
matevip
d1b7e76f5b fix(tool): support top-level await in the browser eval action 2026-05-19 16:53:18 +08:00
matevip
c6d51f8ced fix(wiki): resolve an agent's own knowledge base before shared ones 2026-05-19 16:53:12 +08:00
matevip
b0d9bde664 fix(agent): clamp DashScope max_tokens to the provider's 8192 ceiling 2026-05-19 16:53:06 +08:00
matevip
3d643f909d feat(skill): typed wrapper tools for declared script entrypoints 2026-05-19 09:56:39 +08:00
matevip
31d4d014ed fix(skill): normalize runSkillScript JSON args 2026-05-19 09:56:33 +08:00
matevip
4cd21056a0 feat(skill): automatic lifecycle archival for idle skills 2026-05-19 09:55:59 +08:00
matevip
fb4206e356 fix(wiki): count only the latest job per raw in KB failure stats 2026-05-18 22:00:36 +08:00
matevip
33a40ad9d9 feat(wiki): support HTML, Excel, PowerPoint and CSV raw materials 2026-05-18 21:57:43 +08:00
倪程伟
b763022810
fix(feishu): add handler for im.chat.member.bot.added_v1 event
Register a no-op handler for the bot-added-to-chat event on the Feishu WebSocket EventDispatcher. Without it, adding the bot to a group chat raises HandlerNotFoundException and drops the long connection. Mirrors the existing reaction-event handlers. Fixes #153.
2026-05-18 17:46:28 +08:00
matevip
cda001b818 fix(workflow): generate runnable drafts — snake_case names, correct output refs 2026-05-18 17:35:00 +08:00
matevip
4cf991851b fix(workflow): keep the editor canvas and status correct after publish 2026-05-18 17:34:53 +08:00
matevip
d53d66abe3 feat(chat): per-conversation model selection (#150) 2026-05-18 16:27:27 +08:00
matevip
0ff8da0caa fix(llm): stop MODEL_NOT_FOUND from evicting the whole provider (#150) 2026-05-18 16:27:20 +08:00
matevip
3157dde1f4 fix(memory): route long-term memory edits to the file being edited (#148) 2026-05-18 11:28:01 +08:00
matevip
75107ac815 feat(llm): native Gemini chat builder, Nano Banana image gen, xAI/Grok provider 2026-05-18 10:00:55 +08:00
matevip
a88edbdd07 refactor(llm): decouple model construction from the agent graph layer (#147) 2026-05-18 07:47:49 +08:00
matevip
b3e8d1e283 fix(chat): return 401/403 not 500 for chat auth and ownership failures 2026-05-17 17:56:03 +08:00
matevip
ff3e5b42f5 fix(memory): resolve morning-card user id from the JWT username 2026-05-17 17:55:56 +08:00
matevip
84d809f52b fix(wiki): show live page count in knowledge base list and detail 2026-05-17 17:55:29 +08:00
matevip
401c413a77 fix(chat): return 403 not 500 for conversation access failures 2026-05-17 17:55:21 +08:00
matevip
ce33c1c652 feat(llm): add UI to configure embedding models from any provider (#79) 2026-05-17 09:25:09 +08:00
matevip
7fb47390af feat(chat): pin, multi-select delete and agent filter for conversations (#144) 2026-05-17 09:25:02 +08:00
matevip
18df97a8e6 fix(agent): scope MCP tools to explicit per-agent selection (#117) 2026-05-17 09:24:56 +08:00
matevip
8c1eef00f2 build(deps): upgrade backend libraries and migrate to OpenPDF 3 package 2026-05-17 08:14:11 +08:00
matevip
e647f1d6cf fix(cron): isolate scheduled-job runs from shared conversation (#142) 2026-05-17 07:58:10 +08:00
matevip
b4add8b139 fix(agent): keep wiki tools available to skill-bound agents (#143) 2026-05-16 23:00:45 +08:00
matevip
033854a3ff fix(feishu): restore reaction-event no-op handlers dropped by an over-eager dedup revert 2026-05-16 17:39:19 +08:00
matevip
819c5d5c50 docs(memory): drop external-project attribution from javadoc 2026-05-16 17:30:49 +08:00
matevip
c8d908078a style(feishu): write reaction-handler comment in English
Project convention: all code comments must be in English.
2026-05-16 15:34:02 +08:00
倪程伟
a386b31e9b
fix(feishu): register no-op handlers for reaction events (#140)
The Feishu SDK EventDispatcher had no handler registered for im.message.reaction.created_v1 / deleted_v1, so adding or removing an emoji reaction raised HandlerNotFoundException and logged an ERROR stack trace. Register no-op handlers to silently ignore these events.
2026-05-16 15:28:59 +08:00
matevip
f56f4b059a fix(workspace): reject impossible calendar dates in memory snapshot import 2026-05-16 14:51:52 +08:00
matevip
69fb03968b fix(agent): expose memory-search and async-delegation tools to skill-bound agents 2026-05-16 14:51:47 +08:00
matevip
725fdd1e0f feat(workspace): memory snapshot export and import with whitelist 2026-05-16 14:51:25 +08:00
matevip
81c6488a3c feat(tool): async sub-agent delegation with attribution gate 2026-05-16 14:51:20 +08:00
matevip
419ee57cc8 feat(agent): structured compaction on prompt-too-long preserves prefix 2026-05-16 14:51:14 +08:00
matevip
b9c0ead024 feat(workspace): keyword search across agent memory files 2026-05-16 14:51:09 +08:00
matevip
d90591fa35 feat(task): generic one-shot Callable runner with parent-conv cancel 2026-05-16 14:51:04 +08:00
matevip
82594878a0 refactor(ui): fold the live runtime view into the Employees page 2026-05-16 14:50:58 +08:00
matevip
c320aab1ba fix(skill): synthesize SKILL.md body for ACP-derived virtual skills (#136) 2026-05-16 14:50:51 +08:00
matevip
130dfd5278 fix(skill): synthesize SKILL.md for MCP-derived virtual skills (#136) 2026-05-16 14:50:46 +08:00
matevip
51c922f395 fix(cron): include agent-less cron jobs in dashboard run views (#134) 2026-05-15 20:56:21 +08:00
matevip
3b9b4d79d5 feat(skill): scope skill catalog and runtime by workspace (#135) 2026-05-15 20:01:25 +08:00
matevip
38b1af11cd docs(skill): tighten skill_manage tool description to disambiguate from record_lesson 2026-05-15 15:21:28 +08:00
matevip
a4b8c00eba fix(orm): make updateTime auto-fill unconditional so rows actually move on update 2026-05-15 15:21:23 +08:00
matevip
1d1a818513 fix(skill): persist version on patch and rescan synchronously after edit/patch (#129) 2026-05-15 15:12:07 +08:00
matevip
26df2c94d0 fix(rbac): let viewers read active model so they can actually chat 2026-05-15 10:19:03 +08:00
matevip
9fc2e5f04f feat(memory,dashboard,channel,cron): raise read endpoints out of viewer 2026-05-15 10:18:20 +08:00
matevip
3ba22b99f3 feat(workspace,system): add @RequireGlobalAdmin and gate admin-only controllers 2026-05-15 10:18:10 +08:00
matevip
bba9975749 feat(llm,datasource): gate model/provider/oauth/datasource endpoints to admin 2026-05-15 10:18:03 +08:00
matevip
41f3408209 feat(skill,tool,plugin): gate skill/tool/plugin endpoints by workspace role 2026-05-15 10:17:56 +08:00
matevip
f4964bc6d8 feat(workspace): return memberRole on list and add access endpoint 2026-05-15 10:17:49 +08:00
matevip
65dd4c1f49 fix(skill): cascade-delete agent-skill bindings on skill removal (#127) 2026-05-15 10:17:41 +08:00
matevip
eb6badeb61 feat(ui): surface pending approvals + stuck agents as sidebar badges 2026-05-15 10:17:30 +08:00
matevip
adf5d93975 fix(wiki): align HTTP status with R envelope and harden config + edit paths 2026-05-14 23:01:00 +08:00
matevip
1bba59fc7e fix(skill): encode bridge virtual ids in the top two bits 2026-05-14 18:18:21 +08:00
matevip
139a478bcd fix(skill): give CJK-only MCP/ACP names a stable id-based slug 2026-05-14 18:18:15 +08:00
matevip
d1be02985c fix(agent): collapse model-predicted tool results that the actual tool replaced 2026-05-14 15:44:03 +08:00
matevip
f0f97232a8 fix(agent): pre-tool no-claim system rule + correct write/edit approval docs 2026-05-14 14:59:32 +08:00
matevip
bf3f4af913 fix(agent): structured tool-call replay + final-answer-only content (#120) 2026-05-14 07:46:30 +08:00
matevip
2bca55f5b4 fix(tool-guard): friendly duplicate rule-id rejection and client-side name/pattern validation (#88) 2026-05-13 09:55:30 +08:00
matevip
aed559d1cf docs(agent): update head-guard call-site comment to match the order-sensitive forward scan 2026-05-13 09:05:17 +08:00
matevip
1ea90c25cb fix(agent): make head-orphan repair order-sensitive — a later same-id assistant does not redeem an earlier orphan 2026-05-13 09:03:16 +08:00
matevip
bff57924d6 fix(agent): repair head-side orphan tool responses on the pagination cut 2026-05-13 09:03:10 +08:00
matevip
af56763156 fix(context): keep the latest summary even when its row sits outside the recent window; include summaryId in compact_status 2026-05-13 08:50:42 +08:00
matevip
faf7f98358 feat(context): consume compact_status on the frontend; load history from latest boundary; backfill summaryId 2026-05-13 08:50:35 +08:00
matevip
51b5eceb7d feat(context): preserve spill markers across compaction phases, persist enriched boundary, broadcast compact_status SSE (#110) 2026-05-13 08:50:28 +08:00
matevip
86a6829102 feat(executor): retention sweep and per-conversation purge for tool-result spill files 2026-05-13 08:50:22 +08:00
matevip
7015d1513c feat(context): anchor the first user message after compaction so long tasks remember the original goal 2026-05-13 08:50:15 +08:00
matevip
37894a6978 fix(context): never compact across a split tool_call ↔ tool_response pair 2026-05-13 08:50:08 +08:00
matevip
ae3d27922d fix(context): preserve raw older tool results instead of rewriting them to a lossy summary 2026-05-13 08:50:02 +08:00
matevip
8f49b0ec92 fix(executor): spill raw tool result before falling back to inline truncate 2026-05-13 08:49:56 +08:00
matevip
2ef806aa64 chore(deps): bump Spring AI 1.1.5 -> 1.1.6 and Spring AI Alibaba 1.1.2.2 -> 1.1.2.3 2026-05-13 07:39:27 +08:00
matevip
cc5d7d5ad0 fix(agent): auto-include enabled MCP tools in effective allowlist (#108) 2026-05-12 21:24:36 +08:00
matevip
aded30bbb1 feat(agent): auto-redirect skill-as-tool calls to readSkillFile 2026-05-12 17:20:19 +08:00
matevip
5789a28e19 feat(skill): boost newly installed skills + teach LLM to widen the catalog search 2026-05-12 17:20:09 +08:00
matevip
0b321dc903 fix(skill): repair zip install losses + persist scripts/refs to canonical store (#104) 2026-05-12 17:20:00 +08:00
matevip
691d2b867b fix(agent): hide disabled agents from the chat picker and reject chat calls against them (#105) 2026-05-12 14:53:14 +08:00
matevip
026afa2ba5 feat(wiki): optional JSON Schema on json-format transformations 2026-05-12 14:35:31 +08:00
matevip
465a727be2 feat(wiki): record per-run token usage so operators can see what each template burns 2026-05-12 14:35:17 +08:00
matevip
e911af2192 feat(wiki): JSON output mode — structured transformation output for programmatic downstream 2026-05-12 14:11:02 +08:00
matevip
90936dba4f feat(wiki): cross-material transformation aggregator — map-reduce all runs of a template into one KB page 2026-05-12 14:10:54 +08:00
matevip
a5b952050c feat(wiki): reverse-citation extractor — bind synthesis pages to the exact source chunks they cite 2026-05-12 14:10:47 +08:00
matevip
e1759e0e3c feat(wiki): page-input transformations — run a template against an existing page 2026-05-12 14:10:40 +08:00
matevip
bee39cc420 feat(wiki): page-level embedding so synthesis pages enter semantic search 2026-05-12 14:10:23 +08:00
matevip
8d01efbbb1 fix(agent): sanitize streamed tool-call arguments to valid JSON 2026-05-12 11:25:58 +08:00
matevip
62c9776b01 feat(wiki): clean conversational preamble and code fences from transformation output 2026-05-12 11:25:50 +08:00
matevip
f04ea58c8d feat(wiki): cancel a running transformation and re-run any past run 2026-05-12 11:25:44 +08:00
matevip
ef6ad1b91a feat(wiki): expose per-template model picker in the transformations modal 2026-05-12 11:25:38 +08:00
matevip
4098346fb7 fix(wiki): strip file extensions when deriving transformation page slugs 2026-05-12 11:25:32 +08:00
matevip
bae9f68261 feat(wiki): user-defined transformation templates with optional auto-save to synthesis pages 2026-05-12 10:49:58 +08:00
srant1
5f77434953
fix(llm): activate default model after OpenAI OAuth and gate provider checks by enabled
OAuth token save now promotes the first available chat model when no usable default exists. Default-model resolution and provider availability checks require Provider.enabled=true alongside credentials, so disabled Providers no longer return stale defaults. /models/enabled drops the single-Provider hard-code so OpenAI OAuth and other enabled chat models surface in selectors. Docker exposes the 1455 PKCE callback via MATECLAW_OAUTH_OPENAI_CALLBACK_BIND_HOST (default 127.0.0.1) and a port mapping; deployment mode stays Host-driven.
2026-05-12 09:58:30 +08:00
matevip
27f614d48a feat(wiki): enrich chunk embedding input with raw title and structural metadata 2026-05-12 08:44:26 +08:00
matevip
dac3f5be20 fix(extract): trigger OCR when text extraction returns unreadable bytes 2026-05-12 08:44:20 +08:00
matevip
071ccff9bf feat(tool): add render_html_image to deliver HTML artifacts as native IM images 2026-05-12 07:11:35 +08:00
matevip
b74176ef66 refactor(agent): tighten template pre-binding contract and prune product-assistant skills 2026-05-12 07:11:29 +08:00
matevip
201e510b17 feat(agent): pre-bind skills and tools when applying an agent template 2026-05-12 07:11:19 +08:00
matevip
08fcab12d1 fix(deploy): drop DASHSCOPE_API_KEY requirement so Docker boots with no LLM key configured (#99) 2026-05-11 20:44:31 +08:00
matevip
e6fd39f6ba fix(agent): raise parallel delegation timeout to 300s for thinking models 2026-05-11 17:23:26 +08:00
matevip
f25f910a82 feat(skill): support optional: true frontmatter for off-by-default bundled skills 2026-05-11 17:22:30 +08:00
matevip
6d5fb595d3 fix(agent): harden digital-employee isolation across creation, skill binding and prompt catalog 2026-05-11 16:10:28 +08:00
matevip
9fd5843dda fix(llm): pin OpenAI-compatible HTTP client to HTTP/1.1 (#89) 2026-05-11 11:22:23 +08:00
matevip
3f1f83bbfe fix(channel): gate WS/long-polling channels on a distributed leader lease (#85) 2026-05-11 10:50:00 +08:00
matevip
b7f69dbefe fix(skill): merge partial PUT into existing row so SKILL.md saves stop 500ing (#93) 2026-05-11 07:59:49 +08:00
matevip
9cac7a31d1 refactor(skill-mcp): expose tool display names via ResolvedSkill instead of Map view 2026-05-10 23:46:37 +08:00
czhcc
18148aa724
feat(skill-mcp): expose readable display name alongside prefixed MCP tool names
Add a parallel effectiveAllowedToolsDisplay field on the runtime status payload so the SkillMarket detail drawer can render mcp_<server>_<slug>_<hash> with the raw tool name appended in parentheses, while leaving the original prefixed list unchanged for any caller that needs the machine name. McpSkillBridge#decorateToolNameForDisplay reverses a prefixed name via the per-server cached tool list; the frontend prefers the new display field and falls back to effectiveAllowedTools when the field is absent.
2026-05-10 23:30:22 +08:00
matevip
f71f49bda1 fix(agent): dedup repetition output + mark INCOMPLETE instead of NORMAL 2026-05-10 19:16:16 +08:00
matevip
fb0ae2b6cc fix(agent): break out of self-arguing reasoning loops via content-repetition cap 2026-05-10 19:16:10 +08:00
matevip
a886447a39 fix(channel): drop stray 'group:' infix in wecom inbound media path 2026-05-10 19:16:04 +08:00
matevip
f18626304e feat(agent,ui): recovery affordance card for non-transient LLM errors 2026-05-10 19:15:57 +08:00
matevip
48510c9751 feat(channel): per-sender attribution + debounce boundary for group chats 2026-05-10 19:15:51 +08:00
matevip
00098fe5f1 feat(agent,channel): scrub fake generated-file URLs + paste-body hint for public-account articles 2026-05-10 19:15:44 +08:00
matevip
19a8c9e16c fix(agent): retry transient TLS / socket errors instead of failing the LLM call 2026-05-10 19:15:38 +08:00
matevip
6b3fba2ed4 feat(channel): wecom group reply fallback + upload size pre-check + appmsg parsing 2026-05-10 19:15:32 +08:00
matevip
63f14acb72 fix(settings): stop bulk save from clobbering multimodal sidecar config 2026-05-10 19:15:27 +08:00
matevip
4e1afa6b6f feat(channel): parse wecom quoted-message context for inbound replies 2026-05-10 19:15:20 +08:00
matevip
2b84d94897 fix(channel): adaptive debounce window for paste-split long messages 2026-05-10 19:15:15 +08:00
matevip
3d1f0469ca feat(channel): wecom group reply slot + upload size limits + appmsg inbound
Group-chat reply slot fallback:
- The platform blocks proactive sends in group chats; outbound paths
  (cron summaries, async-task completions, generated image/music/3D
  delivery, TTS audio) silently failed because they fell through to
  the proactive-send command. New bounded LRU maps each group chat
  to its most recent inbound frame id; the dispatcher prefers that
  reply slot and falls through to proactive only for single chats.
- Centralised text and media dispatch through a single helper so the
  group rule never has to be re-implemented per outbound path.

Upload size pre-check + auto-downgrade:
- Without client-side limits, oversized uploads streamed for ~1 minute
  before the server rejected at the finish step — users saw nothing
  arrive in their chat. The new decision layer mirrors the platform's
  hard limits and produces three outcomes: rejected with a friendly
  reason, downgraded to a generic file delivery with an inline note,
  or pass-through unchanged.
- Files over 20MB reject. Images / videos over their 10MB limit
  downgrade to file. Voice content that isn't AMR or exceeds 2MB
  downgrades. AMR voice within 2MB stays native.

appmsg inbound parsing:
- Forwarded complex messages (document transfers, article links,
  miniprogram cards) used to fall into the inbound switch's default
  branch and silently drop. The new branch flattens four sub-types
  into a text marker the agent reads plus any media that needs to
  reach downstream tools — document forwards reuse the same magic-
  byte sniff and per-conversation upload layout as native file
  inbound, so extension recovery and chat-uploads serving work
  identically.
- Article links produce "[链接] title\ndescription\nurl" so the agent
  can summarize without round-tripping. Miniprograms surface their
  title. Unknown sub-types still emit a generic marker so the agent
  is never blind.
2026-05-10 15:13:14 +08:00
matevip
9075abfef2 feat(channel + settings): wecom quote message parsing + sidecar config preservation
WeCom quoted-message context:
- Parse the body.quote field that arrives alongside any inbound message
  (text / image / voice / file / mixed sub-types). When a user long-
  presses a previous bot bubble and types a follow-up like "解释一下",
  the agent now sees both the user's new text and the referenced
  content as proper context — replies stay on topic instead of
  guessing what was being explained.
- Quoted images / files are downloaded through the same pipeline as
  inbound new media (magic-byte sniff, ZIP container peek for
  DOCX / XLSX / PPTX recovery, chat-uploads layout) so the vision
  sidecar and document tools can actually analyse what was quoted.
- Reading order in the assembled prompt: "[引用消息: ...]\n<user text>"
  first, then quoted media parts, then the user's own current-message
  media. Mixed quotes flatten into a space-joined summary.

Multimodal sidecar settings preservation:
- The bulk settings PUT used to unconditionally overwrite the vision /
  video sidecar model ids — null in a partial payload became "" in
  the DB, silently wiping the configured sidecar every time a user
  saved an unrelated settings page (System / Music / Image / etc.).
  Symptom: "I picked a vision model, saved a different settings tab,
  now the bot can't see images anymore."
- Bulk save now guards both keys with non-null checks, matching the
  pattern used for music / 3D / image / video / tts / stt blocks.
- A dedicated /settings/sidecar endpoint always writes both keys, so
  the sidecar UI can still explicitly clear via null without leaking
  the write-on-null semantics into every other settings save.
- Frontend sidecar card switches to the dedicated endpoint; other
  settings pages keep their existing partial-payload behaviour.
2026-05-10 11:38:35 +08:00
matevip
2e0a4674e5 feat(channel): IM channel media + paste-merge + native file delivery
Inbound (WeCom):
- Save uploaded media under data/chat-uploads/{conversationId}/ with full
  fileName/path/fileUrl/storedName/fileSize on the content part. Web mirrors
  of an IM conversation now show real thumbnails instead of "未命名".
- Magic-byte sniff (PDF / PNG / JPEG / GIF / Office / ODF / archives /
  audio / video) recovers a real extension when the platform omits filename
  for forwarded files — no more PDFs labelled "file.bin".
- ZIP container peek distinguishes DOCX / XLSX / PPTX / VSDX / ODT / ODS /
  ODP / EPUB / JAR from a plain zip via discriminator paths and the OASIS
  mimetype entry.

Outbound (WeCom):
- Chunk upload field name corrected so server-side actually stores the
  bytes — file messages used to arrive with correct filename/size but
  empty content, breaking every PDF / DOCX / PPTX recipient.
- Scan agent text for served-file URLs in both the text-reply and
  content-parts paths; fetch bytes from the in-memory generated-file
  cache and dispatch through the native chunk upload + media message
  protocol so users receive a tappable file card instead of an
  unopenable markdown link. Cache miss surfaces a clear retry hint.

Async tool result forwarding:
- New AsyncTaskMediaDispatcher routes generation completions (image,
  video, music, 3D model) to whichever IM channel the conversation is
  bound to via ChannelSessionStore + ChannelManager. Web / webchat
  conversations are intentionally skipped — their SSE stream already
  renders the result.
- Wired into all four generation services so IM users actually receive
  generated media as native attachments. Each part now carries an
  absolute disk path so adapters read bytes locally instead of round-
  tripping through an authenticated served URL.

Slack native file upload:
- SlackChannelAdapter overrides the content-parts dispatch. Image /
  audio / video / file / model3d parts ride filesUploadV2 so users see a
  file card with preview thumbnail bound to the same thread as the
  originating message. Text parts continue through chat.postMessage.
- Resolves bytes from the part's local path, falls back to an HTTP fetch
  of fully-qualified URLs.

IM approval hint visibility:
- IM-driven approve / deny / auto-cancel / replay-error hints now go
  through saveMessage + tracker broadcast in addition to the channel
  adapter, so a Web mirror viewing the same conversationId sees the
  resolution. Previously hints reached only the IM channel; the Web
  admin console had no record of the outcome.

Adaptive paste-merge debounce:
- WeCom and other IM clients silently split long pasted prompts into
  fragments that arrive 0.5-2 seconds apart, missing the existing 500ms
  merge window. The agent then saw torn context and emitted multiple
  conflicting replies.
- When the merged buffer crosses a content-length threshold, extend the
  debounce window so subsequent fragments arrive in time. Default
  500ms unchanged for normal short messages.
2026-05-10 09:00:22 +08:00
matevip
ee48b114f6 chore: scrub external project references from wecom comments 2026-05-10 08:28:26 +08:00
matevip
f6b4f7e402 feat(channel): forward async tool results to IM channels + slack file upload 2026-05-10 08:28:20 +08:00
matevip
134fa1a975 fix(channel): wecom file pipeline + IM approval hint visibility 2026-05-10 08:28:14 +08:00
matevip
225d14026b feat(channel): wecom approval card + keepalive + chunk dedup 2026-05-10 08:28:07 +08:00
matevip
20393ea679 test(channel): wecom reply queue concurrency stress suite 2026-05-10 08:28:01 +08:00
matevip
78f6ca8d33 feat(channel): reply queue foundation + lifecycle gate 2026-05-10 08:27:48 +08:00
matevip
ae4d432ee2 fix(security): root-cause guards against blank tool guard rule rows 2026-05-10 08:27:42 +08:00
matevip
4567bd6019 fix(security): require tool guard rule id (#88) 2026-05-10 08:02:15 +08:00
matevip
f7c1af80a4 fix(wiki): preserve user-owned files when deleting directory-scanned raws (#84) 2026-05-09 17:15:57 +08:00
matevip
c2aecf18ef feat(agent,llm): multimodal sidecar routing for unsupported attachments (#87) 2026-05-09 16:41:26 +08:00
matevip
97e2b12f71 feat(chat): show reply model attribution in assistant message bubbles 2026-05-09 16:41:18 +08:00
matevip
d2886bdaa3 fix(llm): let custom OpenAI-compatible providers opt out of API Key requirement (#89) 2026-05-09 14:26:35 +08:00
matevip
88fb28a22a feat(stt): route OpenAI Whisper provider to any OpenAI-compatible endpoint (#76) 2026-05-09 13:57:52 +08:00
matevip
d5969bb32c fix(chat,llm): liveness-aware model popup with row-based configured check (#81) 2026-05-09 11:07:08 +08:00
matevip
52e06afec4 feat(llm,tool/image): dashscope-compat provider + media-gen test profile 2026-05-09 06:28:38 +08:00
matevip
3f289da6b7 feat(tool/image,video): image edit support + new DashScope models (#75) 2026-05-08 22:05:06 +08:00
matevip
1605146931 fix(workflow/draftgen): teach prompt Pebble logical operator syntax 2026-05-08 15:09:29 +08:00
matevip
2804905500 fix(workflow,trigger): template preview-compile + 3 alpha consistency cleanups 2026-05-08 15:09:14 +08:00
matevip
2416d603c4 fix(workflow,trigger): generator triggerDrafts loop closure + 5 paper cuts 2026-05-08 15:09:06 +08:00
matevip
826c20639a feat(workflow): natural-language → workflow draft generator + agent tool 2026-05-08 15:08:58 +08:00
matevip
dcf1f56944 fix(workflow,trigger): fan_out isolation + workspace spoof + approval→resume bridge + payload strict + draft clear 2026-05-08 15:08:41 +08:00
matevip
3ed540e6eb feat(workflow,trigger): async dispatch + GC schedulers + trigger list polish 2026-05-08 15:07:55 +08:00
matevip
ed64cda043 fix(workflow): PublishContext arg order + drop workspace-agnostic agent fallback 2026-05-08 15:07:32 +08:00
matevip
231724d6a9 feat(agent,trigger): wire agent_lifecycle as a real event source 2026-05-08 15:07:18 +08:00
matevip
dae74cf26c feat(channel,trigger): wire channel_message + content_match event sources 2026-05-08 15:07:11 +08:00
matevip
87727414d5 feat(workflow,trigger): wire workflow_completion as a real event source 2026-05-08 15:06:53 +08:00
matevip
e3af03645b feat(workflow): wire await_approval into the approval inbox 2026-05-08 15:06:46 +08:00
matevip
2a803d2904 fix(trigger): honest fireCount + last_error via DispatchResult 2026-05-08 15:06:28 +08:00
matevip
dac774f15e feat(workflow): trigger workspace isolation + step property editor + custom dialogs 2026-05-08 15:06:16 +08:00
matevip
844a64ad37 fix(workflow): hard-delete + blank-draft compile + canvas blank pane 2026-05-08 15:05:53 +08:00
matevip
20884c1773 feat(workflow): payload fs fallback for medium-size payloads 2026-05-08 15:05:28 +08:00
matevip
e09c62b529 feat(trigger): match pattern_json before fan-out + periodic sync 2026-05-08 15:05:20 +08:00
matevip
05a2caa47c fix(workflow): tighten workspace isolation across CRUD / run / resume 2026-05-08 15:05:13 +08:00
matevip
67c0efe8ae feat(workflow): add publish API + workflow / trigger REST controllers 2026-05-08 15:04:55 +08:00
matevip
ce08b311d3 feat(trigger): add event ingest pipeline with dedup / rate limit / bot-self 2026-05-08 15:04:48 +08:00
matevip
54b12f5270 feat(trigger): add cron trigger engine with lamport coordination 2026-05-08 15:04:41 +08:00
matevip
67279bb9c9 feat(workflow): add write_memory adapter with four merge strategies 2026-05-08 15:04:33 +08:00
matevip
7c3c68bcfe feat(workflow): add dispatch_channel adapter for multi-channel delivery 2026-05-08 15:04:26 +08:00
matevip
48559343e2 feat(workflow): add await_approval pause / resume to the runtime 2026-05-08 15:04:16 +08:00
matevip
4e457c2e8b feat(workflow): add linear runtime for the four base step modes 2026-05-08 15:04:07 +08:00
matevip
3065d095fd feat(workflow): add publish-time compiler for the linear step DSL 2026-05-08 15:03:53 +08:00
matevip
f3b1cba6dc fix(conversation): render image/video/audio attachment markers for LLM (#66) 2026-05-08 15:03:42 +08:00
matevip
d87ed0365a feat(workflow): add foundational schema for workflow runtime 2026-05-08 15:03:34 +08:00
matevip
91e93e8831 feat(wiki): circuit-breaker for failing embedding provider (#72) 2026-05-08 15:03:19 +08:00
matevip
0357c9891d feat(wiki): user-initiated cancel for in-progress raw material processing (#72) 2026-05-08 15:03:10 +08:00
matevip
2af2620b4c fix(tool/mcp): split full URL into base + endpoint for HTTP transports (#70) 2026-05-08 15:03:03 +08:00
matevip
d13637f778 fix(skill/mcp): preserve Chinese MCP server names in zh-CN skill list (#65) 2026-05-08 15:02:56 +08:00
matevip
96840b0126 chore: bump version to 1.3.0-SNAPSHOT 2026-05-08 15:02:08 +08:00
matevip
023f0cfb06 fix(conversation): render image/video/audio attachment markers for LLM (#66) 2026-05-07 17:13:17 +08:00
matevip
b63d83a596 feat(wiki): circuit-breaker for failing embedding provider (#72) 2026-05-07 16:39:39 +08:00
matevip
76f5b3ca94 feat(wiki): user-initiated cancel for in-progress raw material processing (#72) 2026-05-07 16:32:35 +08:00
matevip
274aeb7695 fix(tool/mcp): split full URL into base + endpoint for HTTP transports (#70) 2026-05-07 14:39:27 +08:00
matevip
c830dd902c fix(skill/mcp): preserve Chinese MCP server names in zh-CN skill list (#65) 2026-05-07 14:25:32 +08:00
matevip
c82bf4d18f feat(tool/document): generate xlsx, pptx, pdf documents from markdown 2026-05-07 13:59:28 +08:00
matevip
a65aaa7aa4 fix(tool/extract): route xlsx/pptx extraction directly through Tika 2026-05-07 13:59:20 +08:00
matevip
ad1264ff51 fix(skill): refuse mutating MCP/ACP virtual skills and hide their card-level edit affordance 2026-05-07 08:18:00 +08:00
matevip
5cb82ed8f8 feat(agent,mcp): validate tool bindings on save and keep returnDirect raw-name config working 2026-05-07 08:17:53 +08:00
matevip
9adcc0a2a8 feat(skill,tool): expose MCP-derived skills and tools through the agent picker endpoints 2026-05-07 08:17:38 +08:00
matevip
845b5bb1b1 feat(mcp): stable prefixed callback names and persisted per-server tool cache 2026-05-07 08:17:30 +08:00
matevip
d58fd0c3fd fix(task): gate every worker completion branch with isConversationCanceled 2026-05-06 17:40:47 +08:00
matevip
01c8765c96 fix(task): cancel running async workers on conversation delete to prevent orphan writes 2026-05-06 16:45:31 +08:00
matevip
bf8a533b19 fix(approval): clear in-memory pendingMap on conversation delete via domain event 2026-05-06 16:45:23 +08:00
matevip
73d69c1dfe fix(conversation): cascade-clean orphan rows on conversation delete 2026-05-06 16:45:16 +08:00
matevip
0665a6be09 fix(mcp): expand JVM system properties in stdio args/env/cwd 2026-05-05 15:21:20 +08:00
matevip
fd05c3c9e1 fix(llm): record token usage on ChatGPT OAuth chat turns 2026-05-05 15:21:14 +08:00
DayByDay
a77f632c0a
fix(mcp): support Java system property placeholders in env expansion
Resolve ${user.home} and other JVM system properties in MCP server env, headers, and cwd — previously only OS env vars were expanded, causing the filesystem MCP server to fail on Windows where $HOME isn't set.
2026-05-05 14:54:54 +08:00
matevip
10fe511d48 chore(mcp): revert McpClientManager polish to enable PR #60 merge
Temporarily restore McpClientManager.java to its pre-#60 state so the
contributor's PR can squash-merge cleanly with their authorship preserved.
The args-expansion follow-up will land as a separate commit right after.
2026-05-05 14:53:58 +08:00
matevip
609da7029f fix(mcp): expand JVM system properties in stdio args/env/cwd 2026-05-05 14:46:08 +08:00
matevip
bbf978ed89 fix(image): handle data: URLs and broadcast sync image-gen completion 2026-05-05 13:06:53 +08:00
matevip
66510c96fa fix(image): set stream=true on ChatGPT OAuth /codex/responses request 2026-05-05 13:06:46 +08:00
matevip
80cb3c84eb fix(agent): decouple framework recursion limit from per-agent max_iterations 2026-05-05 13:06:40 +08:00
matevip
0f388382a2 feat(image): gpt-image-2 generation through ChatGPT subscription OAuth 2026-05-05 13:06:26 +08:00
matevip
704c6317e3 feat(llm): live model discovery for ChatGPT OAuth provider 2026-05-05 13:06:19 +08:00
matevip
bf7bc73f5f fix(llm): JSON body + 403/404 status mapping for OpenAI device code 2026-05-05 13:06:11 +08:00
matevip
e9a11f8db4 fix(chat): persist empty assistant turns 2026-05-05 11:12:45 +08:00
matevip
2ab0bd000b fix(chat): suppress duplicate assistant row on force-recycle 2026-05-05 10:39:54 +08:00
matevip
be2235a493 feat(llm): OAuth device authorization grant for ChatGPT remote deploy 2026-05-05 10:39:47 +08:00
matevip
eb64556636 fix(wiki): skip per-turn retrieval for continuation messages + drop weak tail hits 2026-05-04 20:54:44 +08:00
matevip
2e0ff8f90f fix(agent): unblock multi-role parallel delegation + propagate force-stop 2026-05-04 20:54:38 +08:00
matevip
be1fc86836 feat(agents): pixelart icons, per-role colors, runtime identity merge, locale templates 2026-05-04 15:26:02 +08:00
matevip
2a8f6774e7 feat(agents): role/goal/backstory profile, employee-card tagline, 5 career templates 2026-05-04 15:25:53 +08:00
DayByDay
1aa7787ced
fix(mcp): clear stale MCP connection errors
Update MCP server status with an explicit wrapper so successful reconnects can persist lastError=null while keeping updateTime refreshed.
2026-05-04 14:16:43 +08:00
matevip
42d406ffc8 fix(agent): drop brittle output policing, add evidence-grounded long-task safeguards 2026-05-04 11:55:44 +08:00
matevip
3d50b9c132 feat(skill): catalog sort + usage stats 2026-05-04 11:55:35 +08:00
matevip
68e1c6f50d fix(skill): harden ckjia skill binding and virtual dedup 2026-05-03 20:32:23 +08:00
matevip
6fe9b6e5f7 fix(llm): skip Ollama auto-discovery network probe when provider disabled 2026-05-03 20:32:17 +08:00
matevip
7d02841d1d fix(chat): per-event SSE ids for safe reconnect dedup + queue race recovery 2026-05-03 19:57:06 +08:00
matevip
a2c96946e3 fix(repetition-detector): catch markdown-list duplication separated by transition paragraph 2026-05-03 17:15:33 +08:00
matevip
a5e7f82a69 fix(cron): self-heal missing tasks_<wsId> conversation row before saving messages 2026-05-03 17:15:25 +08:00
matevip
74edc09b2b feat(backstage): admin runtime console for live agent visibility + force-recycle 2026-05-03 17:15:17 +08:00
matevip
66f09a968a feat(chat-stream): streaming UX overhaul + multi-agent stability layer 2026-05-03 17:15:02 +08:00
matevip
c07048f258 feat(skill,mcp): ckjia-shopping skill bundle + same-name dedup against bridged MCP/ACP virtual skills 2026-05-03 17:14:19 +08:00
matevip
03a82ece93 fix(wiki): retry route LLM call once with strict-JSON correction when first response isn't JSON 2026-05-03 09:12:51 +08:00
matevip
912a6336d3 fix(wiki): also remove the upload file from disk when a raw material is deleted 2026-05-03 09:12:44 +08:00
matevip
b0ca4c06d4 fix(wiki): cascade-delete pages + chunks when a raw material is removed 2026-05-03 09:12:32 +08:00
matevip
41ed929882 fix(wiki): deterministic, health-aware chat-LLM fallback 2026-05-02 23:31:48 +08:00
matevip
29d2d49d25 fix(wiki): widen mime_type to VARCHAR(255) so Office uploads don't blow the column 2026-05-02 23:11:40 +08:00
matevip
c60fc4cf83 feat(wiki): one-hop chat-LLM fallback when the primary provider is wedged 2026-05-02 21:46:22 +08:00
matevip
ac654f3957 feat(wiki): add Zhipu GLM-V and Volcano Doubao vision providers 2026-05-02 21:46:15 +08:00
matevip
2ae8758cd1 feat(wiki): admin endpoints for hot cache — get / regenerate / reset 2026-05-02 21:46:02 +08:00
matevip
5e936fd50a feat(wiki): hot cache rebuild engine — LLM updater, scheduler, conversation-end listener 2026-05-02 21:45:55 +08:00
matevip
93807d85ef feat(wiki): inject KB hot cache into agent system prompt via MemoryProvider SPI 2026-05-02 21:45:49 +08:00
matevip
a8f0998c24 feat(wiki): hot cache storage skeleton — table, entity, value object, read service 2026-05-02 21:45:43 +08:00
matevip
062b5169fa fix(llm): drop bogus qwen3-plus seed and gate enable_search to supporting models (issue #53) 2026-05-02 19:40:03 +08:00
matevip
44ea8c784b fix(wiki): skip vision pipeline entirely when wiki.ocr.enabled is off 2026-05-02 19:05:05 +08:00
matevip
f910d762a3 feat(wiki): feature-flag toggle UI + defer extracted_text cache when vision unavailable 2026-05-02 19:04:58 +08:00
matevip
bcefe43234 feat(wiki): inline image refs in search results + click-to-zoom lightbox 2026-05-02 19:04:51 +08:00
matevip
51995275bd feat(wiki): caption inline PDF images via the vision-in pipeline 2026-05-02 19:04:45 +08:00
matevip
4dc280a3d1 feat(wiki): wire image uploads to vision-in pipeline 2026-05-02 19:04:39 +08:00
matevip
088ffcaee1 feat(wiki): add image-to-text vision SPI with DashScope provider 2026-05-02 19:04:32 +08:00
matevip
b26a4ee4d1 feat(wiki): add SHA-256 keyed image caption cache 2026-05-02 19:04:25 +08:00
matevip
6f0ead162e feat(wiki): instrument compile / relation / retrieval with WikiMetrics 2026-05-02 19:04:19 +08:00
matevip
4f96a46900 feat(system): add runtime-toggleable feature flag store 2026-05-02 19:04:12 +08:00
matevip
d0706239ea feat(wiki): add mate_wiki_relation cache table for page-to-page edges 2026-05-02 19:04:06 +08:00