Commit Graph

1518 Commits

Author SHA1 Message Date
倪程伟
fcb488c567
feat(workspace): chat-uploads 上传目录工作空间/Agent 感知化 (#422)
* feat(workspace): chat-uploads 上传目录工作空间/Agent 感知化 (#421)

把硬编码的 data/chat-uploads/{conversationId}/ 改为按工作空间/Agent 解析,
解析优先级:Agent workspaceBasePath → Workspace basePath → 可配置默认目录
(新配置 mateclaw.chat.upload.base-dir,默认 data/chat-uploads,保持现网零行为变化)。

- 新增 ChatUploadLocationResolver 中央解析器:写路径返回唯一根,读/清理
  路径返回候选根列表(工作空间根 + 默认根)做双重查找,保证迁移前旧附件
  仍可解析/清理;conversationId→ConversationEntity 查询带 5min 缓存。
- 新增 ChatUploadProperties + ChatUploadAutoConfiguration(启动建目录)。
- 复用 AgentGraphBuilder.resolveAgentBasePath(提升为 public)的优先级与
  安全规则(相对路径在 workspace 根下解析,绝对路径逃逸被拒)。
- 所有写入/读取点改为走 resolver;读取点走双重查找。
- 解决 Spring 循环依赖(resolver → agentService → ... → conversationService
  → resolver):resolver 的 AgentService 注入加 @Lazy。

向后兼容:默认目录不变;双重查找覆盖历史消息里的相对路径;
服务端点 URL 契约不变,前端无需改动。

测试:新增 ChatUploadLocationResolverTest(8 用例);修复受影响的现有测试构造。

* refactor(workspace): address review findings on chat-uploads resolver (#421)

应用 code review 的 4 项修复:

1. (correctness) ChatUploadLocationResolver 缓存新增 ConversationDeletedEvent
   监听器,删除会话时立即失效 conversationId→ConversationEntity 映射。
   否则备份恢复后用相同 id 重建会话,会继承最长 5 分钟的过期 workspace/agent
   映射,导致 cleanAttachmentFiles 走错(过期的)上传目录。复用既有
   @EventListener-on-bean 模式(与 AsyncTaskService / WorkspaceLookupCache 一致)。

2. 收紧 resolveWorkspaceScopedRoot 里 3 个过宽的 catch(Exception) →
   MateClawException + warn,让真正的 bug(NPE / DataAccessException)暴露
   而非被静默降级为 debug 日志。

3. 更新 ChatController.upload 过期注释:会话尚未创建时附件暂存默认目录,
   会话创建后读取走双重查找仍能命中。

4. 移除不可达分支(agentWorkspaceId != workspaceId)—— 会话的 agent 必然
   归属会话的 workspace(创建时强约束),直接用会话 workspace 即可,
   少一次冗余 DB 查询与一层推测性逻辑。

测试:ChatUploadLocationResolverTest (8) + ConversationServiceCleanAttachmentFilesTest (2) 全绿。
2026-06-26 14:20:54 +08:00
matevip
7705778903 fix(agent): normalize replayed tool-call arguments to valid JSON (#410)
Strict OpenAI-compatible providers reject the /chat/completions request
with HTTP 400 when an assistant message in history carries a tool call
whose function.arguments is not parseable JSON. Normalize blank or
non-JSON arguments to "{}" at the send chokepoint so streaming,
history-replay, and older-persisted tool calls all stay well-formed.
2026-06-26 11:19:19 +08:00
matevip
e0ce5e2ea7 fix(sso): gate SSO runtime beans on mateclaw.sso.enabled
SsoService / SsoStateService / SsoController / SsoProviderRegistry were
unconditional component-scanned beans, but their configuration
(SsoProperties) is only registered by the conditional auto-configuration.
With SSO disabled (the default) the services were still instantiated and
startup failed: "required a bean of type SsoProperties that could not be
found". Gate the four beans on the same mateclaw.sso.enabled=true
condition so the SSO stack loads as a unit — disabled = no beans and no
exposed endpoints; enabled = the auto-configuration provides SsoProperties
and everything wires.

Verified: backend starts clean with SSO disabled (default).
2026-06-26 10:27:16 +08:00
matevip
b048718298 fix(sso): inline FQN→import + harden auto-create orphan rollback
- Replace inline fully-qualified names with top-of-file imports across
  SsoService / SsoStateService / FeishuSsoProvider (ObjectMapper, Autowired,
  Map.of, Date, DuplicateKeyException, Mac, URLEncoder) per code style.
- createSsoUser: roll back the freshly inserted user on any non-duplicate
  identity-insert failure, preventing passwordless orphan accounts. The two
  inserts share no transaction — the method is self-invoked and the enclosing
  callback performs a network call, so a method-level @Transactional would not
  apply; an explicit rollback in the catch is the correct guard here.
2026-06-26 10:09:43 +08:00
倪程伟
03a6d61131
feat(sso): 飞书 OAuth2 单点登录 (ISSUE #405 P0) (#419)
* feat(sso): feishu OAuth2 single sign-on (ISSUE #405 P0)

Implements the SSO design (ISSUE #405) with feishu as the first IdP
and a generic OAuth2 provider abstraction for future dingtalk/wecom
extensions. SSO is disabled by default — existing deployments are
unaffected until mateclaw.sso.enabled=true.

Backend:
- SsoProvider interface + SsoUserInfo record: generic IdP abstraction
- FeishuSsoProvider: OAuth2 authorization-code flow (app_access_token
  with Caffeine cache → user_access_token → user info). apiBase switches
  between feishu.cn / larksuite.com by domain config.
- SsoProviderRegistry: conditional registration, lists enabled providers
- SsoStateService: HMAC-signed OAuth2 state + self-contained bind_token
  JWT, both persisted to sso_state DB table for multi-node correctness.
  State is one-time-consumable (conditional UPDATE), bind_token jti
  anti-replay via PK insert. Hourly ShedLock purge (LambdaQuery + Java
  time, works on all 3 dialects).
- SsoService: authorize/callback/bind, user mapping (union_id first →
  external_id fallback), auto-create with concurrent idempotency
  (DuplicateKeyException → rollback orphan user → re-query), link-only
  mode issues bind_token for existing-account binding.
- SsoController: 4 endpoints (/providers, /authorize, /callback, /bind)
  all permitAll.
- V159 migration (h2/mysql/kingbase): mate_user_external_identity,
  sso_state, ALTER mate_user.password NULL (SSO-only users).
- AuthService: generateToken promoted to public; login() guards
  password=null (SSO-only users cannot password-login).
- SecurityConfig: /auth/sso/** added to permitAll whitelist.
- LoginRateLimitFilter: expanded to cover /auth/sso/bind (brute-force
  surface equivalent to /auth/login).
- application.yml: mateclaw.sso.* config block (all env-var driven).

Frontend:
- Login.vue: dynamic SSO buttons (only shown when providers configured),
  OAuth2 callback detection (?sso=callback), link-only bind dialog,
  shared applyLogin flow (localStorage + workspace + route).
- api/index.ts: ssoApi (providers, authorize, callback, bind).

Tests: SsoStateServiceTest (11) — state issue/verify/replay/tamper,
bind_token issue/verify/anti-replay/garbage. Regression: PAT (23) +
Approval resolve (13) all green.

Not in scope (P1/P2): link-only bind/unbind management endpoints,
user enable/disable endpoint, dingtalk/wecom providers, admin SSO
config page. Workspace assignment for auto-created users remains a
product decision (design doc §12 item 2).

* fix(sso): self-review fixes — P0 security + P1 quality

P0-1 BindRequired serialization: replaced the R.fail(200, Map.toString())
hack with a structured SsoCallbackResponse record. Controller no longer
catches an exception for a non-error path; frontend reads bindRequired
flag directly instead of regex-parsing a stringified map.

P0-2 createSsoUser unbounded recursion: added a retry flag — second
DuplicateKeyException (extreme race where identity was concurrently
deleted) now throws a 503 instead of recursing to stack overflow.

P0-3 state TTL not enforced: verifyState's conditional UPDATE now
includes created_at > cutoff, so a state unused for 5+ min is rejected
at consumption time, not just at the 1h purge. Without this the 5-min
window was advisory only.

P1-5 SsoStateService unused ObjectMapper: removed dead injection.

P1-6 audit JSON string concat: replaced with ObjectMapper serialization
(provider/externalId no longer risk breaking the JSON structure).

P1-7 LoginRateLimitFilter shared counter: documented the intentional
decision that login + bind share a per-IP counter (same brute-force
surface) with guidance on switching to per-path if finer isolation
is needed.
2026-06-26 10:00:31 +08:00
Joe0720
e79fb00fec
feat(desktop): support remote lite build mode without bundled JRE/JAR (#417)
* feat(desktop): support remote lite build mode without bundled JRE/JAR

Add a dual packaging mode system controlled by the BUILD_MODE env var:

- **local** (default): Full build bundling JRE + Spring Boot JAR, identical
  to the previous behavior.  Supports both embedded local backend and
  remote server connection.

- **remote** (lite): Omits the ~530 MB JRE/JAR resources, producing an
  installer that is ~81% smaller (97 MB vs 523 MB on macOS arm64).  The
  app only supports connecting to a remote server; the "local" option is
  hidden from the splash connection chooser.

Changes:
- Replace static electron-builder.json with dynamic electron-builder.cjs
  that conditionally includes extraResources based on BUILD_MODE
- Add build mode detection at runtime (checks JAR existence) with graceful
  fallback to remote-only mode
- Add IPC handler app:get-build-mode and expose via preload
- Hide "本地运行" option in splash when running a remote build
- Ignore stale 'local' saved config in remote builds
- Add package scripts: package:mac:local, package:mac:remote, etc.
- Add missing build scripts: build.sh, download-jre.sh, build-all-platforms.sh
- Add no-op afterPack hook (trim-playwright-driver.cjs) placeholder
- Add cross-env devDependency for cross-platform BUILD_MODE support

* feat(desktop): add white-label branding system for build-time rebranding

Add a Vite plugin (scripts/branding.cjs) that replaces hardcoded "MateClaw"
strings at build time, enabling white-label/OEM rebranding without modifying
any source code.

Configuration:
- Edit branding.config.json (name, tagline, team, copyright, appId, githubUrl)
- Or set BRAND_* env vars (BRAND_NAME, BRAND_TAGLINE, BRAND_TEAM, etc.)

Usage:
  # Default build (MateClaw brand)
  npm run package:mac

  # Custom brand via env vars
  BRAND_NAME=MyAI BRAND_TAGLINE="Smart AI Helper" npm run package:mac:remote

  # Or edit branding.config.json and build normally
  npm run package:mac:remote

Replacements applied at build time:
- Brand name (window title, About dialog, error messages, console logs)
- Tagline, team name, copyright line
- GitHub repo/issues URLs
- Logo file path
- electron-builder config (productName, appId, artifactName, dmg title, publish repo)

The branding plugin runs in Vite's transform hook, covering the renderer
(App.vue, index.html), electron main process, and preload script.

Server-coupled strings (H2 database name, Spring Boot property names) are
intentionally NOT replaced to avoid breaking backend compatibility.

---------

Co-authored-by: qiaozhipeng <qiaozhipeng@daojia-inc.com>
2026-06-26 09:29:24 +08:00
matevip
d7d409245b docs: feature-page coverage for 1.7.0 (operational export, desktop remote, webchat approval, run overview, workflow notify) 2026-06-25 17:26:33 +08:00
matevip
6ecec63098 docs(release): add v1.7.0 changelog entry 2026-06-25 17:07:11 +08:00
matevip
b563f93c1d feat(desktop): open-source the Electron desktop app (build, electron main/preload, renderer, config) 2026-06-25 16:00:39 +08:00
matevip
e4e7b4c377 fix(chat): suppress 403 console spam from polling unpersisted conversations (ISSUE #408) 2026-06-25 14:36:59 +08:00
倪程伟
2f12c269f4
feat(webchat): API-Key 渠道补齐审批 resolve + replay (ISSUE #413 P1) (#415)
* feat(webchat): add approval resolve + replay for API-Key channel (ISSUE #413 P1)

Before this, a WebChat (API-Key) channel that hit a ToolGuard-protected
tool parked the turn in a pending approval the visitor could never
clear — it hung for 30 min until the GC timeout and the turn was
wasted. This PR closes the loop, mirroring the web ChatController.

A1 — no code change. tool_approval_requested already reaches the SDK
via ToolExecutionGuardHelper's streamTracker.broadcastObject (direct
SSE push, bypassing the StreamDelta path). Adding it to
forwardVisitorEvent would double-deliver; the default-drop is correct.

A2 — new /sessions/approve and /sessions/deny REST endpoints. Auth is
the existing visitorToken + conversationId ownership guard; the actor
is webchatUsername(visitorId), which resolves the 'no MateClaw
username' blocker noted in the old stopSession javadoc. Both broadcast
tool_approval_resolved so the SDK clears its banner in real time.

A3 — approve returns an SSE stream: resolveAndConsume (atomic DB +
metadata + memory), restoreChatOrigin (recovers the webchat origin
captured at createPending), then chatWithReplayStream replays the
tool call and continues the turn. Replay may re-trigger approvals,
which the existing tool_approval_requested direct push handles.

A4 — stopSession now sweeps pending approvals (denyAllByConversation)
and broadcasts each resolution, so stopping a stream no longer leaves
approvals lingering for the GC.

Tests: WebChatApprovalInteractionTest (7) — deny resolves + broadcasts,
deny auth/ownership guards, idempotent unknown-pending, stop sweep
clears pending, stop no-op when nothing pending.

Regression: WebChatStopStreamTest (5), WebChatArchivePinTest (6),
WebChatSchemaFieldsTest (5), WebChatWikiPageListTest (8),
ApprovalWorkflowServiceResolveTest (13), GcTest (7), RecoveryTest (7).

* fix(webchat): IDOR guard + SSE hang fix (PR #415 review)

Addresses all review feedback from mateaix:

P0 IDOR (security): /sessions/approve and /sessions/deny accepted a
client-supplied pendingId without cross-checking it belonged to the
caller's conversation. A visitor could resolve / replay another
visitor's guarded tool call. Fix: getPending(pendingId) then assert
conversationId matches before resolving. Added getPending delegate on
ApprovalWorkflowService so the webchat controller (which holds the
workflow facade) can do the precise lookup.

SSE hang: approveSession's already-resolved / error branches broadcast
'done' before streamTracker.register/attach, so the event had no
subscriber and the SSE hung to the 10-min timeout. Fix: register+attach
first, then resolveAndConsume. Removed the now-duplicate register/attach
in the replay branch.

Tests: +2 IDOR cases (cross-visitor pendingId rejected 404; mismatched
pendingId rejected 404). denyResolvesPending now asserts via getPending
(findPendingByConversation returns the earliest pending, polluted by
cross-test map state). denyUnknownPendingIsSafe updated to expect 404
(no longer leaks pendingId existence). Isolated IDOR victim/attacker
visitor IDs to avoid cross-test conversationId collisions.

Regression: WebChatStopStreamTest (5), WebChatArchivePinTest (6),
ApprovalWorkflowServiceResolveTest (13), GcTest (7), RecoveryTest (7).

* style(webchat): use simple ChatOrigin name in approveSession (PR #415 review)

Reviewer flagged fully-qualified inline types (ResolveOutcome was fixed
in the prior commit; ChatOrigin was missed). Add the import and switch
the 3 FQN references in approveSession to the simple name, matching the
ResolveOutcome cleanup. chatStream's pre-existing FQN usages are out of
this PR's scope and left untouched.
2026-06-25 11:18:52 +08:00
倪程伟
b478eef78c
feat(im): resolve workflow approvals via feishu/wecom card clicks (ISSUE #413 P2-B3) (#416)
Before this, a workflow await_approval step whose approverChannels
pointed at feishu/wecom was effectively dead for IM interaction. Even
after PR #414 (B1) pushed the notice to the IM group, clicking the
card's Approve/Deny buttons did nothing useful:

- Identity check (requester==clicker) failed-closed: wf- approvals
  have userId=null (system-initiated), so every click was rejected.
- Even if it passed, the synthetic /approve injection was a dead end:
  the router routes by conversationId, but wf- ids use a synthetic
  workflow:run:{runId} key that no IM conversation matches, so
  findPendingByConversation returned null and the /approve was fed
  to the LLM as plain text.

B3 fix: both ToolGuardCardHandlers now detect the wf- prefix and
resolve inline (approvalService.resolve), bypassing the synthetic
injection entirely. The WorkflowApprovalResolvedEvent published
inside resolve is picked up by ApprovalResumeBridge (activated in
PR #414 B2), which resumes the paused run. This mirrors the Web /
WebChat resolve path (PR #415).

Identity policy: any audience member may resolve a wf- approval.
The card only reaches channels declared in await_approval's
approverChannels, so whoever sees it is a designated approver.
Regular tool approvals keep the strict requester==clicker guard.

Tests:
- wecom ToolGuardCardHandlerTest: +2 wf- cases (inline resolve, no
  synthetic injection; already-resolved renders expired). Existing 6
  cases updated for the new 3-arg constructor.
- feishu FeishuCardDispatcherTest: updated for the new factory
  constructor signature.

Regression: ApprovalWorkflowServiceResolveTest (13), GcTest (7),
RecoveryTest (7), feishu dispatcher (4), button value (7),
renderer (3+3) — all green.
2026-06-25 09:56:36 +08:00
倪程伟
20014c72ff
fix(workflow): activate approval notify + resolve→resume bridge (ISSUE #413 P0) (#414)
Two P0 fixes from ISSUE #413 — both address workflow await_approval
approvals that silently failed in production:

B1 — AwaitApprovalStepAdapter now dispatches the approval notice to
every channel in approverChannels that carries a target. Previously
approverChannels was write-only metadata: a workflow that declared
["feishu:oc_xxx"] silently dropped the notice and the IM group never
learned an approval was waiting. Element format is "channelType"
(no push, operator uses admin console) or "channelType:targetId".
Each channel failure is logged and skipped — it must not fail the step.

B2 — requestWorkflowApproval now registers the wf- approval into the
in-memory map via registerRecovered. Previously it only did
approvalMapper.insert, so getPending("wf-...") returned null,
performResolve short-circuited at the not-pending guard, the
WorkflowApprovalResolvedEvent was never published, and
ApprovalResumeBridge was dead code. With this fix, resolving a wf-
approval walks the full two-phase contract and the bridge fires.

Tests:
- WorkflowApprovalResumeBridgeTest (3): map registration, event
  publish on resolve, safe no-op for unregistered wf- ids.
- AwaitApprovalNotifyTest (2): targeted channels dispatched, bare
  "web" skipped, channel failure non-fatal.

Regression: ApprovalWorkflowServiceResolveTest (13), AwaitApprovalRuntimeTest (3),
DispatchChannelRuntimeTest (3), GcTest (7), RecoveryTest (7) — all green.
2026-06-25 09:54:55 +08:00
MIST
f7f1c30557 feat(chat): floating back-to-bottom button with End-key shortcut
Add a floating back-to-bottom control to the chat message list that
appears when the user scrolls up away from the live bottom. The button
auto-docks to the right edge after 15s of inactivity (with a subtle
breathing pulse) and un-docks on mouseenter, keeping it unobtrusive
while reading history.

- End key jumps to the bottom, ignored when focus is in an input,
  textarea, or contentEditable field.
- Explicit jump (button click or End) forces past the stick-to-bottom
  escape lock and clears it so sticky auto-scroll resumes following new
  content; automatic scrolls still respect the escape lock so they do
  not fight the user reading history.
- Larger thumb-reach hit area and lower placement on mobile.
- New i18n key chat.scrollToBottom (zh-CN / en-US).
2026-06-25 09:44:43 +08:00
MIST
1d1c35aadf feat(cli): project-level CLI framework with operational data export command 2026-06-25 09:31:48 +08:00
matevip
9013f5d780 refactor(dashboard): componentize operational export, restore DB chip, polish export dialog 2026-06-24 18:38:01 +08:00
matevip
6b2024b71e fix(operational): guard blank provider/username keys to prevent export crash 2026-06-24 18:06:45 +08:00
matevip
9310335cc8 fix(operational): admin gate, atomic one-time download, lock safety and Excel ID precision 2026-06-24 17:48:42 +08:00
MIST
c2620720d2
feat(operational): one-click operational data export with 9-sheet Excel (#411)
Add an async export feature on the Dashboard page -- global admins can
generate and download a multi-sheet operational data report (.xlsx
packaged as .zip).  The export covers 9 sheets:

1. Overview - interval KPIs, system snapshot, 7-day trend, period comparison,
   model details (configured providers only), agent activity ranking top 10
2. Token Usage - daily breakdown by runtime_provider with avg tokens/msg
3. Skill Stats - skill list with usage count, last-call time, bound agents
4. User Stats - per-(workspace, user) aggregated tokens, duration, last active
5. User Conversations - detail rows pairing user-asst messages
6. Security and Audit - unified view across 6 sources (guard rules, audit logs,
   approvals, grants, config, business audit events)
7. Channel Stats - per-channel conversation count, tokens, unique users
8. Model Config - enabled plus API-key-configured models with parameters
9. Cron Jobs - execution records with duration and token usage

Backend highlights:
- generate/progress/download endpoints guarded by PreAuthorize hasRole ADMIN
- single AtomicBoolean lock (409 when busy), 90-day frontend cap, 5-min deadline
- metadata-based tool-call counting, deleted=0 filtering everywhere
- value label mapping (chat to dialogue, TRUE to enabled, etc.)
- one-time downloadToken, file auto-cleanup after 24h or download

Frontend highlights:
- SVG ring progress bar with smooth dashoffset transition plus slow rotation
- visibility gated by workspaceStore.isGlobalAdmin (v-if on button)
- 1-second polling driving progress state machine (idle/generating/done)
- Element Plus date-picker (30-day default, 90-day max)
2026-06-24 17:38:08 +08:00
matevip
93f40b6dac feat(chat): stabilize run overview rail with planning placeholder and responsive drawer 2026-06-24 17:13:31 +08:00
matevip
47dc5373d1 feat(chat): in-chat run overview side panel for live plan progress and sub-agent status 2026-06-24 15:53:58 +08:00
matevip
982c6c048c feat(security): gate Swagger/OpenAPI UI behind mateclaw.openapi.expose-ui flag
- explicit SecurityConfig authorization for /swagger-ui*, /v3/api-docs*, /webjars/**
- public for local/default profile; admin-only (ROLE_ADMIN) by default in production DB profiles
- override via MATECLAW_OPENAPI_EXPOSE_UI; add RANDOM_PORT integration tests and docs
2026-06-24 10:42:46 +08:00
倪程伟
865513a3b6
docs(api): 完善 WebAPI 文档与 OpenAPI / Swagger 配置 (#407)
Closes #406

- 新增 OpenApiConfig 全局配置 Bean:标题/描述/服务器 + bearerAuth 安全方案
  (覆盖 JWT 与 mc_ PAT,对齐 JwtAuthFilter 前缀分发)
- application.yml 增 springdoc default-flat-param-object + mateclaw.openapi.* 外置项
- api.md 中英双语补全「通用约定」(R<T> 信封、ResultCode、错误模型、IPage 分页、
  ID 约定、三态认证、X-Workspace-Id 机制)+ 9 个旗舰端点完整参考
- 新增 openapi.md 中英双语 Swagger 使用指南
2026-06-24 10:07:11 +08:00
matevip
903c7bd72e feat(agent): parallel delegation optional fail-fast and per-call timeout override 2026-06-23 18:23:07 +08:00
matevip
a94a756677 feat(agent): register send continuations in the sub-agent registry 2026-06-23 18:22:56 +08:00
matevip
dd3dcc55ee feat(agent): SessionListTool discovers persisted sub-agent sessions for send 2026-06-23 18:22:45 +08:00
matevip
acfac0b56f feat(agent): add SessionSendTool for multi-turn sub-agent follow-ups 2026-06-23 18:22:32 +08:00
matevip
0d9c2b3532 feat(agent): register SessionListTool as a built-in tool (three dialects) 2026-06-23 18:22:22 +08:00
matevip
d3d86d5481 feat(agent): add SessionListTool for enumerating live sub-agents 2026-06-23 18:22:10 +08:00
matevip
14ee45a30d feat(agent): introduce SubagentRunContext value object for delegation runtime identity 2026-06-23 18:21:59 +08:00
matevip
68ec8f04c0 chore: remove stray screenshot accidentally synced 2026-06-23 13:52:23 +08:00
matevip
f08abad076 feat(skill): self-evolving skills — out-of-band reflection, curator consolidation, agent-authored skill files 2026-06-23 13:51:04 +08:00
matevip
a366c66d23 chore: bump version to 1.7.0-SNAPSHOT 2026-06-23 10:20:47 +08:00
matevip
252a6fc425 fix(tool-guard): enforce workspace boundary for execute_code and trust spill roots (#403)
execute_code (bash/sh/shell) bypassed the workspace boundary guard, so shell
code run through it could read/write/delete paths outside the workspace sandbox
(e.g. cat /etc/passwd) while the same paths were blocked for read_file and the
shell tools. Bring execute_code under the guard (scan only shell-language code,
report the code param), and trust the tool-result spill roots so a legitimate
spilled result stays readable. Adds regression tests.
2026-06-23 10:11:54 +08:00
matevip
5ff58b00ad fix(plans): scrub injected context from persisted plan goal (#402) 2026-06-22 17:54:27 +08:00
matevip
1caa0dbece docs(readme): mark v1.6.0 as the latest stable release at the top 2026-06-22 17:51:02 +08:00
matevip
30252a377d feat(docs): structure the in-app help viewer to match the docs site 2026-06-22 17:28:35 +08:00
matevip
438a5e00d7 chore: point GitHub repo URL to mateaix/mateclaw 2026-06-22 16:23:49 +08:00
matevip
2cf08683a4 release: v1.6.0 2026-06-22 15:07:05 +08:00
matevip
2664b26763 fix(plans): parent delegated-step child conversations so they don't leak into the conversation list 2026-06-21 23:18:49 +08:00
matevip
eca4229751 feat(plans): per-step agent delegation + fix kanban pending column (issue #385) 2026-06-21 21:20:58 +08:00
matevip
1373b78b0a chore(repo): drop unused npm/yarn lockfiles and fix inline FQNs
- Remove mateclaw-ui/package-lock.json and yarn.lock. This is a pnpm
  monorepo where pnpm-lock.yaml is the only lockfile; the npm/yarn locks
  were stray duplicates. Add a .gitignore rule so they are not committed
  again by mistake.
- SourceEvidenceLedger: reference Pattern/Matcher by their imported
  simple names instead of inline fully-qualified names.
2026-06-21 10:06:28 +08:00
SuperCoderMan521
cb87569264 feat(wiki): make [n] citation markers clickable, linking to wiki pages (#305)
Backend (SourceEvidenceLedger):
- appendWikiSourceTable now normalizes existing source lines in-place to
  canonical "[N] Title - section - page N" format instead of skipping them
- Added replaceSourceLine helper that matches a full source line by regex
  and replaces it with the canonical form
- When source lines exist without a "来源:" header, automatically insert
  one so the frontend preprocessor can locate the source table

Frontend (useMarkdownRenderer):
- Added data-citation-index / data-citation-title to DOMPurify whitelist
- Added preprocessWikiCitations preprocessor: parses the canonical source
  table to build an index-to-title map, replaces [n] markers in the answer
  body with clickable <a> links, and wraps entire source-table rows so the
  full line is clickable
- Integrated into the render pipeline after wikilink substitution and
  before Marked parsing

Frontend (useGlobalWikilinkClick):
- Extended the click delegation selector to match both .wiki-link and
  .wiki-citation elements
- Title extraction falls back: data-citation-title || data-wiki-title

Tests: added three test cases for source-line normalization, idempotency,
and automatic header insertion
2026-06-21 09:58:41 +08:00
matevip
c656aff349 feat(plans): Kanban boards in the Agents workspace
Live lifecycle board (grid<->board toggle) plus an assignee-swimlane plan
board that groups follow-up re-runs of one goal into a single xN card.
Custom right-side detail/goal panels with markdown output. Fixes plans
being persisted under the per-run trace id so the board actually populates.

Closes #385
2026-06-20 17:52:29 +08:00
matevip
d6001e3e6f fix(db): correct V154 wiki_disabled migration for MySQL and KingbaseES
The cherry-picked V154 used 'ALTER TABLE ... ADD COLUMN IF NOT EXISTS' for
MySQL — invalid on MySQL 8.0.x (a MariaDB-only extension) which aborts Flyway
at startup. Switch to the INFORMATION_SCHEMA + PREPARE guard the other MySQL
migrations use. Also change the KingbaseES column from SMALLINT to BOOLEAN to
match the Java 'Boolean wikiDisabled' field and the existing skills_disabled /
tools_disabled flags (vanilla PostgreSQL is strict about boolean vs smallint).
H2 (already BOOLEAN) is unchanged.
2026-06-20 07:23:54 +08:00
倪程伟
22a212a2e6 feat(agent): add wiki_disabled opt-out flag for knowledge bases
Issue #304. Operators who want an agent with NO knowledge base had no
way to express it: leaving the KB picker empty fell through to "inherit
workspace-wide" (every KB visible), so the agent ended up ingesting
every KB's context. This adds the same opt-out toggle that
skills_disabled (V126) / tools_disabled already provide.

Backend:
- V154 migration (h2 + mysql + kingbase): mate_agent.wiki_disabled
  BOOLEAN/TINYINT/SMALLINT NOT NULL DEFAULT FALSE. Legacy agents stay
  bit-identical.
- AgentEntity.wikiDisabled: Boolean field, @TableField("wiki_disabled").
- AgentBindingService.getBoundKbIds: short-circuit at the top —
  wiki_disabled=true returns Set.of() regardless of binding rows. Mirrors
  the precedence contract of getBoundSkillIds vs skills_disabled.
- AgentBindingService.setKbBindings: a non-empty save auto-clears a
  stale wiki_disabled flag (same contract as setSkillBindings /
  setToolBindings on their respective flags). Empty saves leave the flag
  untouched — the UI toggle owns the bit, not the binding writer.
- AgentBindingServiceWikiDisabledTest: 5 cases covering all three
  return states + the stale-flag auto-clear + empty-save no-op.

Frontend:
- Agents.vue KB picker: add the "此智能体不使用任何知识库" /
  "This agent uses no knowledge bases" toggle, mirroring the skills /
  tools picker layout. Tab badge shows "Off" when the toggle is on.
- types/index.ts: add Agent.wikiDisabled?: boolean.
- Save logic: when wikiDisabled is on, send an empty KB list (the
  setKbs contract then leaves the flag alone server-side, exactly as
  setSkills / setTools behave for their opt-out flags).
- i18n (zh + en): new strings for toggle label, hint, badge, and the
  scope description shown when the toggle is on.

Stacked on top of #382 (which introduced AgentBindingResolver
.getBoundKbIds). No agent-runtime changes — wiki tools already degrade
cleanly when getBoundKbIds returns Set.of().
2026-06-20 07:21:07 +08:00
倪程伟
0ab11f8922 docs(webchat): document /wiki/pages endpoint and [[slug]] picker
Add /wiki/pages row to endpoint table and a new "Wiki knowledge-base
reference ([[slug]] picker)" section explaining the directive-text
mechanism, query parameters, visibility rules (synthesis excluded,
100-page cap, KB-scope fallback), and curl examples (zh + en).

Follow-up docs for the wiki picker endpoint shipped in this PR.
2026-06-20 07:21:07 +08:00
倪程伟
f6156f6093 feat(webchat): expose agent-bound wiki pages to API-Key callers
Add GET /api/v1/channels/webchat/wiki/pages mirroring /skills, so
downstream integrators can build a [[slug]] picker UI that points the
LLM at specific wiki pages. The picker token format is the universal
Obsidian/Wikipedia wikilink convention; the LLM consumes [[slug]] via
the existing wiki_read_page(slug=...) tool, so no agent-runtime changes
are needed.

- AgentBindingResolver.getBoundKbIds(agentId): three-state mirror of
  getBoundSkillIds. null = no rows (fall through to workspace-wide KBs),
  Set.of() = explicitly scoped to zero KBs, non-empty = explicit scope.
- WebChatController.listWikiPages: API Key + visitorToken auth chain,
  agentId workspace anti-escalation, visibility excludes pageType=
  synthesis (LLM intermediate artifacts), 100-page cap forces keyword
  filter, response carries only display-level metadata.
- WebChatWikiPageView DTO: kbId/kbName/slug/title/summary/pageType;
  content/embedding/sourceRawIds deliberately stay admin-console-only.
- WikiTool.wiki_read_page @Tool description: document the [[slug]]
  convention so the LLM treats each token as a wiki-page reference.
- WebChatWikiPageListTest: 8 cases covering happy path, keyword filter,
  synthesis exclusion, anti-escalation, auth failures, cap behavior,
  and the no-binding → workspace-wide fallback.

Closes #381.
2026-06-20 07:21:07 +08:00
倪程伟
a5e7060045 docs(webchat): polish /skills endpoint docs
Add /skills row to endpoint list table, note optional agentId on /stream,
and add a new "Skill invocation (slash picker)" section explaining the
directive-text mechanism with curl examples (zh + en).

Follow-up polish for the /skills endpoint shipped via PR #374.
2026-06-20 07:21:07 +08:00
matevip
4804954ad2 feat(wiki): configurable entity types, type legend filter & theme-aligned graph colors (#336)
- per-KB entity-type whitelist (config UI + persistence; empty = built-in defaults)
- entity graph: legend grouped by type with click-to-filter; nodes colored by type
- always show entity names on graph nodes (not only on hover)
- earthy categorical palette aligned to the app theme, shared by entity & page graphs
- theme-aware graph label color (resolve CSS var for canvas, light/dark correct)
- manual extract = full rebuild: idempotent force re-extraction + orphan pruning,
  guarded against data loss on a fully-failed run
- regression test for force re-extraction; zh/en i18n
2026-06-19 07:18:09 +08:00