Commit Graph

860 Commits

Author SHA1 Message Date
matevip
94cf812207 feat(tool): add execute_code for running agent-authored code (#257)
Add an execute_code built-in tool that runs python/bash/node code the agent
writes on the fly, so a documentation-only skill (a SKILL.md with no bundled
scripts) can be acted on. Scoped runs inject the skill's secrets and run in
the skill directory; otherwise a private scratch directory is used. Host
secret env vars are scrubbed from the subprocess. execute_code is an
agent-wide capability, registered in the tool catalog (V143), and screened
by the tool guard with a dedicated set of destructive-pattern rules.

Tests cover python/bash/node execution, scratch-dir fallback, env scrubbing,
argument decoding, and guard gating.
2026-06-09 08:05:50 +08:00
matevip
b95ee90c64 fix(wiki,agent): tidy up post-merge review nits
- WikiPageTypeProfile: normalise pageType keys to lowercase on set, so a
  user-authored profile with an uppercase key still matches the
  case-insensitive hasPageType/get lookups.
- WikiDirectoryScanService: normalise the symlink-resolved glob base to
  forward slashes so directory-scan globs work on Windows paths.
- Agents roster tag filter: keep selected tags that no longer exist on any
  agent visible and deselectable (and show the filter bar when only such
  orphan selections remain) instead of silently filtering with no way to clear.
2026-06-08 22:48:53 +08:00
matevip
21798d6be5 fix(wiki): guard page reclassification against concurrent re-trigger
A second POST to /reclassify on the same KB spawned an independent pass over
the same pages, doubling LLM spend and racing the first pass's page-type
writes. Add a per-KB in-flight guard that rejects a concurrent run with a
friendly message (409 via R.fail rather than a generic 500), released in a
finally once the async pass completes. Also count and broadcast per-page
failures so an all-failing run is visible instead of reporting changed=0, and
type the api modelId param as string|number per the snowflake ID convention.
2026-06-08 21:59:39 +08:00
matevip
71ddade3bd fix(mcp): terminate stdio child process on shutdown and fail loud on SDK drift
The resilient connect() override started the child process into a local
variable and never set the parent's private process field, so the parent's
closeGracefully() logged "Process not started" and left the child running on
every reconnect/disable. Retain the started process and override
closeGracefully() to destroy it and interrupt the spawned I/O threads.

Also make the inboundSink/errorSink reflection fail loud (the transport is
useless without them — a silent null yielded a connected transport that timed
out on every call), force UTF-8 on the stdio reader/writer, honour a
cooperative closing flag in the reader loops, and drop duplicate PATH entries.
2026-06-08 21:59:39 +08:00
倪程伟
e3ddea9a70 feat(wiki): reclassify existing pages against the current pageType profile
Add a backfill path so pages created before a KB's pageType profile changed
can be migrated into newly-added types. A per-page classify-only LLM call
(title + summary in, single page_type out) is normalised through the profile
and written back via a partial update that never touches page content.

Exposed as POST /knowledge-bases/{id}/reclassify (admin) and a "re-classify
existing pages" action in the Wiki advanced panel.
2026-06-08 21:04:24 +08:00
倪程伟
28f2ba973d feat(wiki): honour KB pageType profile in transformations, agent pages & UI
Wiki page classification was only profile-aware in the main ingest pipeline.
Transformation outputs hard-coded "synthesis", agent-created pages were left
untyped, and the frontend hard-coded the built-in ten types for ordering,
colouring and labels — so custom/synthesis types sank to the bottom, rendered
grey and showed raw keys.

Backend:
- Add nullable target_page_type column to mate_wiki_transformation (V142,
  mysql + h2) plus the entity field and CRUD normalization (blank = use
  profile fallbackType; membership validated at save time, not edit time).
- Route transformation single-run + KB-aggregate page saves through
  WikiPageTypeProfileService.normalizePageType so output joins the KB
  classification; agent wiki_create_page now lands on the profile fallbackType
  instead of an untyped page.

Frontend:
- Load + parse the KB pageType profile into the wiki store (order, labels,
  fallbackType) on KB select / refresh.
- New useWikiPageType composable: profile-driven label (3-tier fallback) and
  colour (built-in fixed + deterministic hash palette for custom types).
- Sidebar grouping order, graph colouring, node panel, graph filter and the
  page header badge now follow the profile; transformation editor gains a
  target-type dropdown sourced from the profile when output target is a page.

Refs #292
2026-06-08 21:02:50 +08:00
倪程伟
cd3ae0c001 feat(agent): append static About You identity block to system prompt 2026-06-08 20:53:39 +08:00
倪程伟
2e1ef6b4b1 feat(agent): thread runtime model/provider into per-turn context injection 2026-06-08 20:53:39 +08:00
倪程伟
bd1ceace74 feat(agent): render runtime model identity line in RuntimeContextInjector
Add 5-arg buildContextMessage overload that emits [system-context] Model:
for every origin (web/cron/IM). Legacy 3/4-arg overloads delegate to the
new one with null model args, keeping their output byte-identical.

Also fix pre-existing FeishuMentionTest compile error caused by removed
mentionMatchesAnyAlias/collectMentionIdentifiers methods.
2026-06-08 20:53:39 +08:00
倪程伟
453997eb9c fix(cron): count silent-run token usage in settings total
A silent (no-op) cron run still makes a full LLM call, but the marker
message was persisted via the token-free saveMessage overload, so the
settings-page Token total (which aggregates MessageEntity token columns)
under-counted cron spend by exactly the silent runs.

Carry chatResult's prompt/completion tokens onto the marker message,
matching the non-silent branch. Closes #284.
2026-06-08 20:52:09 +08:00
倪程伟
37e9afa9de fix(wiki): match glob against symlink-resolved scan root
The base directory is canonicalized via toRealPath before walking, so the
walked files carry the symlink-resolved prefix. The PathMatcher was built
from the literal pattern, so a symlinked base never matched and files were
silently dropped. Rebuild the glob against the resolved scan root, escaping
glob metacharacters in the base so a real directory name containing */?/{}/[]
is treated literally.
2026-06-08 20:51:03 +08:00
倪程伟
7f4d3e3ab9 fix(mcp): resilient inbound processing for non-JSON stdout from MCP servers
The upstream StdioClientTransport breaks out of its inbound read loop on
any JSON parse error, permanently killing the reader thread. Some MCP
servers write non-JSON debug output to stdout (e.g.
"=== Document parser messages ==="), which triggers this and causes all
subsequent valid JSON-RPC responses to be lost → 30s timeout → agent stuck.

Override connect() in CwdAwareStdioClientTransport to skip non-JSON lines
(log at DEBUG) instead of breaking, keeping the inbound thread alive.

Closes #226
2026-06-08 20:48:24 +08:00
matevip
3c87efdb15 feat(chat): 购物推荐结果渲染为可点击商品卡片
- 聊天 markdown 渲染器解析 product-cards 围栏为卡片网格(图片/价格/平台/去购买按钮),整卡可点跳购买页
- DOMPurify afterSanitizeAttributes 钩子补回被 ALLOWED_URI_REGEXP 剥掉的 target/referrerpolicy,确保防盗链图片加载与新标签打开
- 后端在购物推荐工具结果尾部追加卡片渲染指令,保证模型稳定输出卡片而非表格
- 技能:购物意图优先调用参考价工具并约定 product-cards 输出格式
2026-06-08 15:11:56 +08:00
matevip
be7bbd9644 fix(skill): persist skill workspace on the existing data volume; skip binary entries in ZIP packages (#273) 2026-06-07 23:09:42 +08:00
matevip
5ebdccb1f6 feat(agent): scope agent knowledge base access to a bound subset (#261) 2026-06-07 22:41:14 +08:00
matevip
86cb449bd5 fix(skill): keep skill workspace paths stable while fixing non-ASCII collision
The #254 fix changed resolveConventionPath to {name}-{hashCode} and folded
hyphens to underscores, which re-pathed every existing skill (browser-cdp ->
browser_cdp-<hash>) with no migration, orphaning already-created workspaces and
breaking SkillWorkspaceManagerApplyBundleTest. Drop the hash suffix and keep
hyphens: the bare Unicode-preserving sanitized name already prevents the
non-ASCII collision (distinct CJK names map to distinct dirs) and leaves ASCII
kebab-case paths identical to the legacy scheme. Add path regression tests.
2026-06-07 20:06:19 +08:00
倪程伟
00b87a4325
fix(skill): prevent directory collision for non-ASCII skill names (#255)
Skills with non-ASCII (e.g. Chinese) names collapsed to underscores in the workspace path, so two such skills resolved to the same directory and overwrote each other. Preserve Unicode letters/digits when sanitizing the path so distinct names map to distinct directories. Also write SKILL.md with CREATE_NEW to avoid a TOCTOU race on concurrent uploads, and mount the skills workspace as a named Docker volume so packages survive container restarts.

Closes #254
2026-06-07 20:00:18 +08:00
matevip
f238959856 refactor(wiki): drop ineffective @Transactional on self-invoked scan-update methods
updateTextContentFromScan / updateBinaryFileFromScan are only reached via
self-invocation from the ingest* methods, so the proxy-based @Transactional
never applied. Each runs a single atomic updateById; remove the misleading
annotation and document why.
2026-06-07 19:53:12 +08:00
倪程伟
d3a432d8e3
fix(wiki): dedup directory-scanned files by source path, not just content hash (#272)
Directory-scan ingestion deduped only by content hash, so when a file at a known path changed, the new hash missed the existing raw and a second row was inserted for the same source_path — both rows then generated wiki pages, accumulating duplicates. Make source path the primary dedup key: same path + same hash skips, same path + changed hash updates the existing raw in place (reset to pending, re-process), falling back to the content-hash check only for genuine copies at new paths. Reprocessing reuses the same rawId, so deleteExclusiveBySourceRawId cleans the old pages before regeneration — no duplicate rows and no duplicate pages. findBySourcePath gains LIMIT 1 to tolerate pre-existing duplicates; docs/fix-duplicate-raws.sql remediates existing data.

Closes #271
2026-06-07 19:50:21 +08:00
matevip
46f3d425e0 feat(agent): add kill-switch for final-answer Markdown normalization
Gate MarkdownNormalizer behind mate.agent.markdown-normalize-enabled (default
true) so operators can disable the rewrite verbatim if a normalization edge
case ever mangles a legitimate answer.
2026-06-07 19:15:14 +08:00
倪程伟
398d7a2d80
feat(agent): deterministic Markdown normalization for final answers (#275)
LLMs routinely emit malformed Markdown (missing heading spaces, glued `---`, unaligned table pipes) that prompt rules cannot reliably prevent. Add a zero-token, regex-only MarkdownNormalizer applied on the FinalAnswerNode convergence path before persistence / channel delivery. It is code-fence aware, idempotent, and conservative (em-dash `---`, `#5`-style refs, stray prose pipes are left untouched). RETURN_DIRECT verbatim output and approval-wait paths return earlier and are unaffected.

Closes #274
2026-06-07 19:10:53 +08:00
倪程伟
a9698dbed3
fix(channel): point Feishu attachment part path to the staged chat-uploads copy (#279)
Same-message attachment reads failed on Feishu: the content part carried the sandbox-external ~/.mateclaw/media/ path, so read_file/extract_document_text were rejected by WorkspacePathGuard and ChatUploadResolver's basename fallback missed (media names {messageId}_{key} vs chat-uploads {millis}_{fileName}). cacheRecentFile already copies the attachment into the per-conversation data/chat-uploads/ dir; return that absolute path and stamp it onto the current message's image/file/audio/media parts so the path surfaced to the LLM is resolver-reachable. Reuses the existing copy — no extra I/O, no new state. Rich-text post multi-image and other channels sharing the media dir are noted as follow-ups.

Closes #278
2026-06-07 18:52:00 +08:00
倪程伟
d2e1f9eb9c feat(wiki): support multi-path and glob patterns for KB source directory
Allow knowledge base source paths to be configured as a newline-separated
list of absolute paths or glob patterns rather than a single directory.

- Each non-blank, non-# line is treated as one path or glob pattern
- Plain paths (no wildcards) retain the existing recursive-scan behaviour
  filtered by SUPPORTED_EXTENSIONS
- Glob patterns (e.g. /data/ocr/**/*.txt) walk from the fixed-prefix base
  and apply Java's PathMatcher against each candidate's absolute path
- Patterns whose filename segment explicitly specifies an extension
  (*.txt, *.{xlsx,csv}) skip the SUPPORTED_EXTENSIONS secondary filter,
  respecting the user's explicit choice (key for OCR pipelines that
  produce .txt output and should ignore the original PDF scans)
- Candidates collected across multiple patterns are deduplicated by
  resolved absolute path so overlapping patterns don't double-count
- Symlink-escape check uses each pattern's own validated scan root
- parseSourcePatterns / extractBasePath moved into WikiSourcePathValidator
  to eliminate a static circular reference between the two services
- Frontend watcher panel: single-line <input> replaced with <textarea>
  supporting multiline editing; i18n updated with example patterns
- No DB schema change; fully backward-compatible with existing single-path
  configs stored in sourceDirectory

Closes #(pending issue)
2026-06-07 18:05:04 +08:00
倪程伟
9baef57871
fix(cron): persist token usage to the run row on completion (#263)
finishRunAndPublish updated the run row with status and finished_at but dropped the LLM token usage, so mate_cron_job_run.token_usage stayed NULL and the scheduler history always showed 0. Write the prompt+completion token total (already computed for the assistant message row) to the run row, guarded so non-LLM paths (reminder direct-push with a null ChatResult) leave the column untouched.

Closes #262
2026-06-07 17:51:08 +08:00
matevip
39b6bdc522 test(skill): fix listAvailableSkills arity and cover agent-binding enforcement 2026-06-07 17:34:43 +08:00
倪程伟
3d8d266e3b
fix(skill): enforce agent skill bindings in skill meta-tools at runtime (#265)
The skill meta-tools (listAvailableSkills, readSkillFile, listSkillFiles, load_skill, runSkillScript) queried the full skill catalog without checking the calling agent's bindings, so an agent scoped to a subset of skills could still read, load, or execute any skill via direct tool calls. Resolve the agent's bound skill ids from the tool context and filter/deny accordingly: a null binding set means no restriction (backward compatible), a non-null set (including empty) restricts access to that set — matching the system-prompt catalog filtering.

Closes #264
2026-06-07 17:30:41 +08:00
倪程伟
29fcfb5572
fix(wiki): surface scan errors and fix Chinese path handling in Docker (#260)
Surface directory-scan failures to the user via toast and render ScanResult.errors[]; expose MATE_WIKI_ALLOWED_SOURCE_ROOTS as a Docker env entry with blank-entry filtering in the path validator; set C.UTF-8 locale in the runtime image so non-ASCII file names decode correctly during scans.

Fixes #259
2026-06-07 16:18:49 +08:00
matevip
808047d723 feat(proxy): global outbound HTTP/SOCKS proxy with settings page (#109)
Add a single global-proxy switch that routes the backend's outbound traffic
through a configured HTTP/HTTPS/SOCKS proxy, for deployments that cannot reach
overseas APIs directly or must use a unified egress.

- ProxyManager installs the proxy via a default ProxySelector (honored by
  java.net.http and HttpURLConnection), the proxy system properties, and a
  --proxy-server arg for the browser tool; restores direct egress when
  disabled. One switch covers LLM, web search, media generation, channels,
  MCP and the browser.
- SOCKS applies to the HttpURLConnection-based egress only; the java.net.http
  LLM/streaming path uses an HTTP proxy, and the UI states this.
- New Settings -> Network Proxy page: enable toggle, address, bypass list,
  test-connection, and a coverage summary. Config stored as key/value in
  mate_system_setting (no migration).

refs #109
2026-06-07 16:12:02 +08:00
matevip
7894a50067 fix(tool): emit absolute download URLs for streaming-generated files (#164)
Streaming chat ran render tools on an async thread with no bound request,
so download links lost their host and arrived without a domain. Resolve the
host on the request thread and carry it through ChatOrigin/ToolContext;
falls back to a configurable public-base-url, then a relative path.
2026-06-07 15:03:48 +08:00
matevip
86670312e4 release: v1.5.0 2026-06-05 07:53:15 +08:00
matevip
76c6504527 fix(llm): preferred provider now drives primary model selection — per-provider model resolution + unconfigured-provider skip 2026-06-04 07:36:31 +08:00
matevip
b22462105c test(agent): exercise real capability gate in ProviderRouter primary-selection tests 2026-06-03 23:39:19 +08:00
倪程伟
5746bcf8cc
feat(agent): 偏好提供商作为主模型选择依据 (#223)
偏好提供商从「仅 capability 触发」改为两轮筛选,使 Agent 偏好提供商能决定主模型选择;并在 Agent 显式配置 modelName 时优先 honour,不被偏好提供商覆盖。

Closes #222
2026-06-03 23:33:14 +08:00
matevip
6445f082a6 fix(tool): qualify Entry type in GeneratedFileCache LRU to fix compile error (closes #250) 2026-06-03 23:21:31 +08:00
matevip
3883b68cca fix(goal): drop score gate, bill failed evaluator calls, fix Evaluator SPI + tool prompt + bootstrap cap 2026-06-03 21:19:27 +08:00
matevip
a756da7817 test(goal): rewrite evaluator tests for bootstrap/verdict modes 2026-06-03 21:19:27 +08:00
matevip
77196acbcd test(goal): checklist codec + service coverage; final internal-ref sweep 2026-06-03 21:19:27 +08:00
matevip
c5ccce8ebc feat(goal): deterministic completion + remaining-criteria followup + auto-followup gates 2026-06-03 21:19:26 +08:00
matevip
0fc8579a3e feat(goal): persist checklist end-to-end + GoalResponse wire DTO 2026-06-03 21:19:26 +08:00
matevip
b65887f93d feat(goal): dual-mode checklist evaluator with structured output + Evaluator SPI 2026-06-03 21:19:26 +08:00
matevip
cd1c66fc0e feat(goal): structured checklist data model — criteria column, criterion records, dual-carrier evaluation result 2026-06-03 21:19:26 +08:00
matevip
c92bfa1f12 fix(mcp): raise default tool read timeout from 30s to 60s (#247) 2026-06-03 09:47:18 +08:00
matevip
9f02a0a221 fix(agent): unbreak DashScope tool calls, sharpen error class, rebalance plan triage (refs #246) 2026-06-03 08:38:48 +08:00
matevip
18f3675fd3 docs(api): align workflow & trigger endpoint reference with actual controllers (#248) 2026-06-03 07:32:20 +08:00
matevip
48611a6f4d feat(memory): per-owner memory isolation with owner_key + visibility scope (#235) 2026-06-02 17:04:06 +08:00
matevip
40ce1c67ac fix(tool): persist generated files to disk so download links survive restart and 10-min window (#243) 2026-06-01 21:35:53 +08:00
matevip
1388b6eec8 feat(wiki): add wiki_update_page (in-place edit) and wiki_stale_pages tools 2026-05-31 08:00:19 +08:00
matevip
e18e0f1029 feat(wiki): per-agent pageType permission config API and pending-approval recording 2026-05-31 07:59:48 +08:00
matevip
7363ee8668 feat(wiki): pipeline definition CRUD/YAML API, run query API, page-created trigger 2026-05-31 07:59:41 +08:00
matevip
c627f898ec feat(wiki): pluggable ingest-source SPI + source-watcher status API 2026-05-31 07:59:34 +08:00