release: v1.5.0

This commit is contained in:
matevip 2026-06-05 07:53:15 +08:00
parent 76c6504527
commit 86670312e4
40 changed files with 1952 additions and 1687 deletions

View File

@ -153,11 +153,15 @@ curl http://localhost:18088/api/v1/cron-jobs \
-H "Authorization: Bearer <token>"
# Run once now (doesn't affect the next scheduled run)
curl -X POST http://localhost:18088/api/v1/cron-jobs/{id}/run-now \
curl -X POST http://localhost:18088/api/v1/cron-jobs/{id}/run \
-H "Authorization: Bearer <token>"
# View execution history
curl http://localhost:18088/api/v1/cron-jobs/{id}/runs \
# View execution history for one cron job
curl http://localhost:18088/api/v1/dashboard/cron-runs/{id} \
-H "Authorization: Bearer <token>"
# View recent execution history in the current workspace
curl http://localhost:18088/api/v1/dashboard/cron-runs \
-H "Authorization: Bearer <token>"
```

File diff suppressed because it is too large Load Diff

View File

@ -151,7 +151,7 @@ This is the most important thing to know if you're contributing to the backend.
### Goal-evaluation node (1.4.0+)
The graph (both ReAct and Plan-Execute) now runs a `GoalEvaluationNode` after `FinalAnswerNode` has streamed the final answer: it scores how completely the goal was met and can optionally inject an auto-followup message to keep pushing any unmet goals forward.
The graph (both ReAct and Plan-Execute) now runs a `GoalEvaluationNode` after `FinalAnswerNode` has streamed the final answer: since 1.5.0 it judges the goal's checklist criterion by criterion (bootstrap / verdict modes), treats the goal as complete **only when every criterion passes**, and can optionally inject an auto-followup message targeting the remaining criteria to keep pushing any unmet goal forward.
### Other 1.4.0 runtime changes

View File

@ -35,6 +35,11 @@ v1.4.0 makes Feishu a first-class channel — interactive cards, streaming cards
Feishu specifics are spelled out in the [Feishu](#feishu-lark) section below.
:::
::: tip 1.5.0 channel improvements
- **Shared inbound media pipeline****WeChat and WeCom** are currently wired onto a shared inbound-media downloader + magic-byte type detection + exponential-backoff retry (other IM channels to follow). File types are decided from content bytes (no more hardcoded `image/*`); HEIC / WEBP / DOCX / XLSX and friends are detected correctly, with automatic retry on download failure.
- **Feishu: follow-up text auto-carries recent files (#201)** — send a file in a Feishu chat first (even without @-mentioning the employee), then a text message, and the cached files are auto-attached as content parts for the employee — 5 files per chat, 60-minute TTL.
:::
---
## The nine channels

View File

@ -50,6 +50,8 @@ One of the questions MateClaw tries to answer with its chat UI is: **should you
Trust is earned by showing the work. MateClaw shows the work.
**Execution-plan & tool-call detail viewer (1.5.0).** Every plan step and every tool-call row gets a "view details" icon on the right. Click it for a frosted-glass dialog showing the **full request arguments and response output** — the parts the inline preview truncates — with copy buttons for request and response, and a status badge (in progress / completed / failed / pending). The data lives in message metadata, so plan steps and tool calls stay readable after a page reload.
---
## Multi-channel realtime sync
@ -86,6 +88,10 @@ Upload limits, default:
Images handed to a vision-capable model get attached for visual understanding. PDFs and DOCX files go through text extraction (with OCR fallback for scanned material). Everything the agent reads lands in its context for that turn.
::: tip Tool-generated files: download links survive restarts (1.5.0, #243)
Files a worker generates via tools (documents / images / audio…) are now **persisted to disk** under `data/generated-files/`, with a 7-day retention window + a 6-hour cleanup sweep and an in-memory LRU on top — download links keep working after a restart and are no longer bounded by the old 10-minute in-memory window. The frontend intercepts `/api/v1/files/generated/{id}` downloads via a global click delegator: success goes through an authenticated fetch → blob download; failure (404/410/expired) just shows a toast, **so a dead link no longer wedges the whole page**.
:::
### Primary model can't see images? "Multimodal sidecar" routing
::: tip Added in 1.3.0

View File

@ -111,7 +111,7 @@ Features:
- `POST /api/v1/chat/stream` — SSE streaming (native fetch)
- `POST /api/v1/chat/upload`
- `POST /api/v1/chat/{conversationId}/stop`
- `POST /api/v1/approvals/{id}/resolve`
- approval resolution is sent as `/approve` or `/deny` through `POST /api/v1/chat/stream`
- `GET /api/v1/chat/{conversationId}/pending-approvals`
- `GET /api/v1/conversations` — list
- `GET /api/v1/conversations/{id}/messages`

View File

@ -147,7 +147,8 @@ cd ../mateclaw-server
mvn clean package -DskipTests
# 3. Copy JAR to desktop resources
cp target/mateclaw-server.jar ../mateclaw-desktop/resources/app.jar
JAR_FILE=$(ls -1 target/mateclaw-server-*.jar | grep -v sources | head -n 1)
cp "$JAR_FILE" ../mateclaw-desktop/resources/app.jar
# 4. Download platform-specific JRE
cd ../mateclaw-desktop

View File

@ -1,233 +1,71 @@
# Doctor
**The Doctor page answers one question: is this thing actually working right now?**
Doctor is the in-app health drawer. It reports the current local instance status from the backend health service; it is not a separate scheduled diagnostics subsystem.
MateClaw has a lot of moving parts — the backend, the database, model providers, MCP servers, IM channels, cron jobs, memory consolidation, wiki digestion. When something goes sideways, the symptom ("my agent isn't responding") usually has a specific cause ("the DashScope API key expired yesterday") buried several layers away from where you'd notice. Doctor is a single page that runs every check at once and tells you what's green, what's yellow, and what's red.
Open it from the layout status button / Settings area. The drawer calls the backend each time it opens or when you click refresh.
Open it with `Settings → Doctor` or just navigate to `/doctor`.
## Current Backend API
---
## What it checks
Each check runs independently and reports one of three states:
- **✅ OK** — everything is working as expected
- **⚠️ Warning** — working but degraded (e.g., using a fallback provider, nearing a quota, a non-critical cron job is paused)
- **❌ Error** — broken in a way you need to fix
### Core infrastructure
| Check | What it verifies |
|-------|-----------------|
| **Backend version** | MateClaw is running and reports its version |
| **Database connection** | The configured datasource is reachable and queries succeed |
| **Database schema** | All expected `mate_*` tables exist; migration state is clean |
| **Disk usage** | The data directory has enough free space (warns under 20%, errors under 5%) |
| **H2 console exposure** | Warns if the H2 console is enabled in production profile |
| **JWT secret strength** | Warns if the default JWT secret is still in use |
### Models
| Check | What it verifies |
|-------|-----------------|
| **Active model** | A default model config exists and is enabled |
| **Provider connectivity** | Each enabled provider has passed a recent connection test |
| **API key presence** | Keys are configured for every cloud provider marked enabled |
| **Ollama reachability** | If Ollama is configured, the local instance is reachable |
### Agents & tools
| Check | What it verifies |
|-------|-----------------|
| **Tool registry** | Built-in and MCP tools are loaded without errors |
| **Tool Guard config** | At least one Tool Guard rule exists (warns if `default-policy: allow` is used) |
| **Default agent** | The default agent exists and is enabled |
| **Agent templates** | Built-in templates are present and loadable |
### Memory & wiki
| Check | What it verifies |
|-------|-----------------|
| **Memory consolidation cron** | Per-agent consolidation cron jobs exist and are enabled |
| **Last consolidation run** | Warns if no consolidation has run in the past 7 days |
| **Wiki digestion queue** | No stuck `pending` or `processing` raw materials |
| **Wiki schema** | `mate_wiki_*` tables exist and are queryable |
### Channels
| Check | What it verifies |
|-------|-----------------|
| **Channel health monitor** | Every enabled channel reports `connected` or is actively reconnecting |
| **Per-channel status** | For each IM channel, connection state and last error |
| **Webhook URL reachability** | Warns if a webhook-mode channel has no public URL configured in production |
### MCP
| Check | What it verifies |
|-------|-----------------|
| **Enabled MCP servers** | Every enabled MCP server is `connected` |
| **Tool count** | Each connected server reports at least one tool |
| **Orphaned subprocesses** | No stdio subprocesses outlive their parent client |
### Cron & async
| Check | What it verifies |
|-------|-----------------|
| **Cron engine** | The scheduled-task executor is running |
| **Overdue jobs** | Warns if any job is more than 24 hours overdue |
| **Async task queue** | `mate_async_task` queue length is within normal bounds |
---
## How checks run
Doctor runs two ways:
### On demand
Click **Run All Checks** on the Doctor page. The button fires off every check in parallel; the UI streams results back as each finishes. Most checks complete in under a second; the slowest (MCP server connection tests) can take 1030 seconds.
### On a schedule
Doctor also runs **automatically every 15 minutes** in the background. Results are cached in memory and persisted to `mate_doctor_check` so the page loads instantly when you open it — you're seeing the last cached state until you click **Run All Checks**.
You can tune the schedule in `application.yml`:
```yaml
mateclaw:
doctor:
enabled: true
schedule-minutes: 15
cache-ttl-minutes: 10
```bash
curl http://localhost:18088/api/v1/system/health \
-H "Authorization: Bearer <token>"
```
---
## Reading results
Each check returns:
Response shape:
```json
{
"name": "DashScope Provider Connectivity",
"category": "Models",
"status": "ok",
"message": "Connection test succeeded (latency: 240ms)",
"lastChecked": "2026-04-11T14:30:22",
"details": {
"provider": "dashscope",
"baseUrl": "https://dashscope.aliyuncs.com",
"latencyMs": 240
},
"fixUrl": "/settings/models"
"code": 200,
"msg": "success",
"data": {
"overall": "healthy",
"checks": [
{
"name": "default-model",
"status": "healthy",
"message": "Default model: qwen-plus",
"action": null
}
]
}
}
```
The UI renders:
`overall` is one of `healthy`, `warning`, or `error`. Each check has:
- **Category tabs** at the top — Infrastructure, Models, Agents, Memory, Wiki, Channels, MCP, Cron
- **Status counters** — green / yellow / red
- **Check list** — name, status, message, time since last check, "View details" expand, optional "Fix" button that navigates to the relevant settings page
- **History graph** — (for each check) a sparkline of the last 50 runs so you can see flapping checks at a glance
| Field | Meaning |
|---|---|
| `name` | Stable check key such as `default-model`, `database`, `browser`, `provider:<id>`, `mcp:<name>` |
| `status` | `healthy`, `warning`, or `error` |
| `message` | Short diagnostic text shown in the drawer |
| `action` | Optional `{ label, route }` hint for where to fix the issue |
---
## What It Checks Today
## Fix buttons
The current `SystemHealthService` checks:
For actionable checks, the Doctor row includes a **Fix** button that navigates directly to the relevant settings page:
| Check | What it verifies | Typical action |
|---|---|---|
| Default model | A default model is configured and loadable | `/settings/models` |
| Providers | API-key providers are configured when required | `/settings/models` |
| Enabled MCP servers | Enabled MCP servers have a successful connection result | `/settings/mcp-servers` |
| Database initialization | First-run bootstrap has completed | `/setup` |
| Browser diagnostics | Browser launch pre-flight for browser tooling | `/api/v1/system/browser-health` |
- Model provider failure → `Settings → Models`
- Tool Guard `default-policy: allow``Settings → Security & Approval`
- H2 console in production → `Settings → System` (or show a config snippet to copy)
- JWT default secret → `Settings → System` (or show a config snippet)
- MCP server disconnected → `Tools → MCP Servers`
- Stuck wiki digestion → `Wiki → [KB] → Raw Material`
Clicking Fix takes you to the exact page where you can address the issue. When possible, the target page is pre-filtered to highlight the failing item.
---
## Doctor API
There is also a direct browser diagnostics endpoint:
```bash
# Run all checks (synchronous)
curl http://localhost:18088/api/v1/doctor/run \
-H "Authorization: Bearer <token>"
# Get the cached check results
curl http://localhost:18088/api/v1/doctor/checks \
-H "Authorization: Bearer <token>"
# Run a specific category only
curl http://localhost:18088/api/v1/doctor/run?category=models \
-H "Authorization: Bearer <token>"
# Historical results
curl "http://localhost:18088/api/v1/doctor/history?check=dashscope-connectivity&limit=50" \
curl http://localhost:18088/api/v1/system/browser-health \
-H "Authorization: Bearer <token>"
```
---
## Not Implemented In The Current Source Tree
## Using Doctor in operations
Older docs mentioned `/api/v1/doctor/run`, `/api/v1/doctor/checks`, `/api/v1/doctor/history`, scheduled background Doctor runs, `mate_doctor_check`, and `mate_doctor_check_history`. Those endpoints and tables are not present in the current backend source. Use `/api/v1/system/health` for the current health surface.
### As a health endpoint for uptime monitoring
## Related Pages
Point your external uptime monitor (UptimeRobot, Pingdom, internal Prometheus) at:
```
GET /api/v1/doctor/checks
```
The endpoint returns HTTP 200 with JSON summary — aggregate pass/fail counts and per-category breakdown. Your monitor should alert when `errorCount > 0`.
For a simpler health check, use:
```
GET /actuator/health
```
which follows Spring Boot's standard format.
### During upgrades
After deploying a new MateClaw version, run Doctor to verify nothing regressed:
1. Open `/doctor`
2. Click **Run All Checks**
3. Look for any yellows or reds that weren't there before
4. Pay special attention to **Database schema** — a mismatched schema after an upgrade usually means a migration didn't run
### When something's broken
Doctor is the first place to look when a user reports "it's not working". Open the page, see which check is red, click **Fix**, solve the problem. If no check is red but the user still has an issue, it's probably something Doctor doesn't cover yet — file it as a [GitHub issue](https://github.com/matevip/mateclaw/issues) so we can add a check.
---
## Data model
**`mate_doctor_check`**
| Column | Purpose |
|--------|---------|
| `id` | Primary key |
| `name` | Check name |
| `category` | Check category |
| `status` | `ok` / `warning` / `error` |
| `message` | Human-readable message |
| `details` | JSON blob of extra detail |
| `last_checked` | When it last ran |
| `run_duration_ms` | How long the check took |
| `workspace_id` | Scoping (nullable for global checks) |
Historical results go into `mate_doctor_check_history` with the same columns plus a retention cleanup job.
---
## Next
- [Admin Console](./console) — the UI Doctor lives in
- [Configuration](./config) — things you might configure based on Doctor warnings
- [Security & Approval](./security) — what Doctor checks in Tool Guard
- [Contributing](./contributing) — add a new Doctor check if something's missing
- [API Reference](./api) - source-aligned route inventory
- [Models](./models) - model/provider setup
- [MCP](./mcp) - MCP server setup
- [Security & Approval](./security) - Tool Guard and approval behavior

View File

@ -215,9 +215,10 @@ Edit `PROFILE.md` or `MEMORY.md` directly in the agent workspace view. Lock page
### I approved a tool call but the agent didn't resume
1. Is `AWAITING_APPROVAL` still set? (`GET /api/v1/agents/{id}`)
2. Did the approval actually persist? (`GET /api/v1/approvals/{id}`)
2. Does the waiting conversation still have a pending approval? (`GET /api/v1/chat/{conversationId}/pending-approvals`)
3. Are there errors in the agent log around the replay attempt?
4. If replay failed, the agent should surface an error in the chat
4. Did the approve/reject message go through the same conversation via `POST /api/v1/chat/stream`?
5. If replay failed, the agent should surface an error in the chat
### I want to batch-approve future tool calls from this agent

View File

@ -65,8 +65,6 @@ For automation and external scripts, the endpoint is direct:
POST /api/v1/goals
{
"conversationId": "conv-xxx",
"agentId": "1000000001",
"workspaceId": 1,
"title": "Deploy blog to fly.io",
"description": "...",
"exitCriteria": "DNS + SSL + healthcheck + tests pass",
@ -76,7 +74,7 @@ POST /api/v1/goals
}
```
Full surface in the [API reference](./api).
> `agentId` / `workspaceId` are derived server-side from `conversationId`**don't send them** (they're ignored if you do). Full surface in the [API reference](./api).
---
@ -114,13 +112,59 @@ After every turn, a backend evaluator node runs:
When `autoFollowupEnabled=true` and this turn's evaluator decision is "continue", the backend:
1. Writes a `followup_injected` event to the timeline
2. APPENDs a user message to the conversation: *"Continue working on the goal. Still missing: {gap}. Take the next concrete step."*
2. APPENDs a user message to the conversation. **Since 1.5.0, if the goal has a checklist, that message explicitly lists the criteria still open***"5/8 done, remaining: ① … ② …, take the next step on these"*; with no checklist it falls back to the generic *"Continue working on the goal. Still missing: {gap}."*
3. Re-enters the reasoning loop — the next assistant reply lands right after the first
Feels like: the worker answers a segment → pauses a beat → **keeps going** — like a person who finished one step, thought for a second, and continued.
---
## A goal is a checklist (1.5.0+)
In 1.4.0 the evaluator gave a completion score (01) and a one-line "what's missing" each turn. The problem: **what does 0.8 mean** — which boxes are done, which aren't? You couldn't see it.
1.5.0 replaces that with a **checklist**: a goal = a set of **independently verifiable** criteria.
**The evaluator has two modes:**
| Mode | When | What it does |
|---|---|---|
| **bootstrap** | No criteria yet | Decomposes the goal into a checklist; each starts "not passed" |
| **verdict** | Criteria exist | Judges each one: satisfied? with evidence |
Both modes use **structured output** — the evaluator returns a typed object (criterion `id` + `passed` + `evidence`), not free text we have to parse.
**Completion is deterministic.** Only when **every criterion passes** is the goal done. 19 of 20 passed (a 0.95 score) is still "continue" — miss one and one is missing, no fuzzy threshold.
**Three ways to add a checklist:**
- **At creation** — pass `criteria: ["DNS resolves", "SSL valid", "tests green"]` to the `setGoal` tool, or `criteria` to `POST /api/v1/goals`. Skips the bootstrap round.
- **Let the evaluator decompose** — pass no criteria and the first evaluation bootstraps the checklist.
- **Append at runtime** — the `addGoalCriterion` tool or `POST /api/v1/goals/{id}/criteria` adds one to a live goal without restarting.
**What a criterion looks like:**
```json
{ "id": "C1", "text": "DNS resolves to fly.io", "passed": false, "evidence": "" }
```
`id` is server-assigned (C1, C2…), `text` is a sentence a human reads and an LLM judges, `passed` is the evaluator's verdict, `evidence` is the justification it gives. The checklist lives in the `mate_agent_goal.criteria` column (JSON) and is delivered parsed as `GoalResponse.criteria`, never as a raw JSON string.
### The ring, on hover, is a checklist card
- **No checklist** — a one-line tooltip: title + the gap text the evaluator wrote.
- **With a checklist** — a card: title + `X/Y` progress, then each criterion prefixed by `○` (open) or `✓` (green, done, struck through).
While evaluating, a sand-gold breathing halo surrounds the avatar; on completion a green ring shows briefly then disappears; on budget exhaustion the ring turns rust.
### Evaluator SPI
The evaluation logic implements Spring AI's `Evaluator` interface: it does goal-specific checklist verdicts (bootstrap / verdict) and can be reused as a generic evaluator (wrapping a single objective as one criterion in verdict mode). Failed evaluator calls **still count against the LLM budget**, so the accounting stays honest.
> The 1.4.0 goal was "the worker remembers what it's doing." The 1.5.0 goal is "the worker knows **exactly which boxes are still open**." From a score to a checklist you can tick.
---
## Four built-in tools (worker-callable)
These four ship as agent-wide system tools — no binding setup needed:
@ -132,7 +176,7 @@ These four ship as agent-wide system tools — no binding setup needed:
| **completeGoal** | Explicitly mark done | "All items done — call completeGoal" |
| **getGoalStatus** | Inspect current state | "How are we doing?" |
On completion (`completeGoal` or evaluator score ≥ 0.95), the worker forwards a summary to its [long-term memory](./memory) so future conversations can recall it.
On completion (`completeGoal`, or the evaluator judging **every criterion passed**), the worker forwards a summary to its [long-term memory](./memory) so future conversations can recall it.
---
@ -168,7 +212,7 @@ Your options:
↓ ↑
paused
active ──evaluator score≥0.95 / completeGoal──→ completed (terminal)
active ──all criteria passed / completeGoal──→ completed (terminal)
active ──turns_used / llm_calls exhausted ────→ exhausted (terminal)
@ -188,7 +232,7 @@ A few deliberate non-features:
- **No nested goals / goal trees** — one goal per conversation, no OKR stack
- **No "goal templates"** — every goal is hand-written
- **No cross-conversation goal migration** — use a [workflow](./workflow) for that
- **No completion score in the UI**`completionScore` is an internal engineering protocol, not user vocabulary. The UI speaks via a ring; hover reveals the natural-language gap the evaluator wrote. The numeric score stays in logs and the API for debugging
- **No completion score in the UI**`completionScore` is an internal engineering protocol, not user vocabulary. The UI speaks via a ring; on hover it shows the box-by-box checklist card when there's a checklist, or the natural-language gap text the evaluator wrote when there isn't. The numeric score stays in logs and the API for debugging
---
@ -219,12 +263,18 @@ mateclaw:
goal:
# Master switch; when off, the graph node passes through for every call.
enabled: true
# Create-time default for autoFollowupEnabled when the caller leaves it unset.
default-auto-followup: true
# Runtime master switch; when off, no goal injects a followup regardless of its per-goal flag.
allow-auto-followup: true
# Default turn budget when the user doesn't override.
default-turn-budget: 20
# Default combined (agent + evaluator) LLM call budget.
default-llm-call-budget: 200
# Minimum seconds between two consecutive auto-followups.
auto-followup-cooldown-seconds: 0
# Hard cap on auto-followups within a single graph run (per-message safety net; overall budget is turnBudget).
max-followups-per-run: 8
# Model used by the evaluator. Empty = same model as the chat agent.
# Recommended: a cheap model like qwen-turbo / glm-4-flash.
evaluator-model: ""

View File

@ -101,7 +101,7 @@ Earlier HTTP transport using SSE for server-to-client push. Legacy compatibility
- **URL** (streamable_http/sse) — server endpoint
- **HTTP Headers** (streamable_http/sse) — JSON object (e.g., `{"Authorization": "Bearer token"}`)
- **Connect timeout** — default 30s
- **Read timeout** — default 30s
- **Read timeout** — default **60s** (raised from 30s in 1.5.0, #247; a single callTool round-trip that legitimately runs longer no longer gets cut off. Each server is tunable 5300s)
Save. If enabled, MateClaw auto-attempts to connect and discover tools.
@ -361,7 +361,7 @@ After each connection operation, results persist:
| `cwd` | VARCHAR(512) | NULL | Working directory |
| `enabled` | BOOLEAN | TRUE | On/off |
| `connect_timeout_seconds` | INT | 30 | HTTP connect timeout |
| `read_timeout_seconds` | INT | 30 | Request response timeout |
| `read_timeout_seconds` | INT | 60 | Request response timeout (default 60 since 1.5.0, was 30) |
| `last_status` | VARCHAR(32) | `disconnected` | Last connection status |
| `last_error` | TEXT | NULL | Last error message |
| `last_connected_time` | DATETIME | NULL | Last successful connection |

View File

@ -65,6 +65,55 @@ Each layer operates at a different timescale. Short-term is *this turn*. Extract
---
## Memory knows who's who: per-owner isolation (1.5.0)
Before, an employee's memory was **shared**: whether it was you logged into the web, a colleague in a Feishu group, or an end user coming in through a third-party API, the memory piled into the same `MEMORY.md`. One employee serving multiple people would cross wires.
1.5.0 gives every memory an **owner** and a **visibility scope**.
### A unified owner_key
Whatever the identity source, it normalizes to one prefixed string:
| Source | owner_key |
|---|---|
| Web console | `user:<user id>` |
| IM channel (Feishu / DingTalk / WeCom…) | `<channel>:<sender id>` |
| Third-party API (with endUserId) | `api:<endUserId>` |
| System / cron | `system` |
### Three visibility scopes
| scope | Who reads it | Typical content |
|---|---|---|
| **PERSONAL** | Only the matching owner | Memory extracted from conversations defaults here |
| **TEAM** | Everyone using this employee | Agent config files (AGENTS.md / SOUL.md / PROFILE.md), backfilled legacy data |
| **GLOBAL** | Always visible across employees / workspaces | Preset facts, system reference material |
### Recall prefers personal memory
The system prompt bakes in only the shared TEAM/GLOBAL memory (cacheable); each turn then **prefetches** that owner's personal memory by owner_key. So when someone asks "what stack does my project use," the employee recalls *that person's* private memory files first, not generic KB material.
> On the structured "fact" layer: the **fact recall query itself supports owner-visibility filtering** (PERSONAL is owner-only, TEAM/GLOBAL shared). But the current **automatic fact projection** is built mainly from shared memory files and doesn't set `ownerKey/scope` on insert — so personalization shows up more in the personal-memory-file prefetch; per-owner facts are still being filled in.
### Third-party APIs pass through an end-user identity
`/api/v1/chat` and `/api/v1/chat/stream` request bodies gain an optional **`endUserId`** field (a string, to preserve large-integer precision). One PAT-authenticated integration represents one MateClaw user but can pass a distinct `endUserId` per end user, and memory isolates per end user automatically.
### It's a feature flag
The master switch is `mate.memory.lifecycle-mediator-enabled`.
::: warning Mind the default
The Java property's bare default is `false`, but the `application.yml` **shipped with the release sets it to `true`** — so per-owner isolation is **on by default in a default install**. To go back to the old shared behavior (all writes to TEAM), set it to `false` explicitly in your config.
:::
When on: conversation extraction writes to the owner's PERSONAL memory and recall filters by owner_key; when off, all writes fall back to shared TEAM. Multi-tenant instances stay on; single-user deployments can turn it off.
Under the hood: migration `V137` adds `owner_key` + `scope` columns to `mate_workspace_file` / `mate_memory_recall` / `mate_fact`, backfilling legacy rows as `TEAM` (so no memory gets hidden on upgrade). Memory tools like `remember` resolve owner_key from the current request context — when the flag is on they write to that owner's PERSONAL memory, when off they fall back to shared writes.
---
## Multi-layer memory with pluggable providers
The memory layer is not one hard-coded implementation. It's an **interface** — the multi-layer architecture lets you stack providers:
@ -415,6 +464,12 @@ mate:
# --- Consolidation / dreaming ---
emergence-enabled: true
emergence-day-range: 7
# --- per-owner memory isolation (1.5.0) ---
# The value shipped with the release is true (on): conversation extraction writes to the owner's
# PERSONAL memory and recall filters by owner_key. Set false for the old shared behavior (all writes
# to TEAM). The bare Java-property default is false.
lifecycle-mediator-enabled: true
```
Prefix: `mate.memory`.

View File

@ -17,8 +17,8 @@ MateClaw doesn't care which LLM you use. It talks to every mainstream provider t
| **Bailian Token Plan** | Bailian token-bundle plan | dashscope | 7 seeded models; long tokens supported |
| **OpenAI** | GPT-4o, GPT-4o-mini, GPT-5.5, o1, o3, o4-mini | openai | Standard OpenAI API |
| **OpenAI OAuth (ChatGPT Plus/Pro)** | GPT-4o, o3, o4-mini via subscription | openai | Browser-based OAuth — no API key |
| **Anthropic** | Claude 4.7, Claude 4.6 Sonnet, Claude 4.5 Haiku | anthropic | Native Messages API |
| **Anthropic Claude Code OAuth** | Claude 4.7 / 4.6 via Claude Pro/Max/Team subscription | anthropic | Browser OAuth + manual-paste flow — no API key |
| **Anthropic** | **Claude Opus 4.8 / 4.8 Fast** (1.5.0+), Claude 4.7, Claude 4.6 Sonnet, Claude 4.5 Haiku | anthropic | Native Messages API; both 4.8 variants support the `xhigh` thinking tier |
| **Anthropic Claude Code OAuth** | Claude Opus 4.8 / 4.7 / 4.6 via Claude Pro/Max/Team subscription | anthropic | Browser OAuth + manual-paste flow — no API key |
| **Google Gemini** _(native)_ | gemini-2.5-flash, gemini-3-pro-image-preview, gemini-2.5-flash-image | gemini | Native `generateContent` API (not OpenAI-compatible) — see "Native Gemini" below |
| **xAI / Grok** | Grok 3, Grok 4 | openai | OpenAI-compatible (base URL + API key); xAI brand icon in the UI |
| **DeepSeek** | deepseek-chat, deepseek-coder, **DeepSeek V4 flash + pro** (thinking-mode) | openai | OpenAI-compatible |
@ -161,13 +161,13 @@ Enter the user code in your browser, authorize, and the dialog closes itself the
If `local` mode can't bind a loopback port (port in use, sandbox refused), it falls through to `manual_paste` automatically.
**Backend endpoints** (`/api/v1/oauth/openai/device`):
**Backend endpoints:**
| Method | Path | Purpose |
|---|---|---|
| `POST` | `/start` | Begin a session — returns `deviceAuthId`, `userCode`, `verificationUrl`, `intervalSeconds`, `expiresInSeconds` |
| `POST` | `/poll` | Poll one session by `deviceAuthId` — returns `PENDING` / `COMPLETED` / `EXPIRED` |
| `POST` | `/cancel` | Drop the session (e.g. user closed the dialog) |
| `POST` | `/api/v1/oauth/openai/device/start` | Begin a session — returns `deviceAuthId`, `userCode`, `verificationUrl`, `intervalSeconds`, `expiresInSeconds` |
| `POST` | `/api/v1/oauth/openai/device/poll` | Poll one session by `deviceAuthId` — returns `PENDING` / `COMPLETED` / `EXPIRED` |
| `POST` | `/api/v1/oauth/openai/device/cancel` | Drop the session (e.g. user closed the dialog) |
The frontend respects the `intervalSeconds` OpenAI returns (typically 5 s); the server enforces a min poll interval (default 3 s) to keep load bounded. Expired sessions are swept every 5 minutes.
@ -392,6 +392,17 @@ Every provider you add joins an `AvailableProviderPool` that's probed at startup
- **Egress sanitizer** — provider-specific options (e.g., `reasoning_effort` for OpenAI reasoning models) are stripped at egress when failing over to a provider that doesn't support them, so leaked options can't 400 the fallback
- **UI distinguishes 401 from session expiry** — provider auth errors and user session expiry now show different messages with different remediation
### Preferred provider drives the primary model (1.5.0)
Before 1.5.0, "per-agent priority" only affected the **failover order** — the primary model was still the global default. 1.5.0 makes that preference **actually decide primary-model selection**. The full precedence is:
1. **A conversation-pinned model wins** — the chat-header ModelSelector bound a model to this conversation, so it's used (see [per-conversation model selection](./chat#per-conversation-model-selection))
2. **then the per-agent model override (`modelName`)** — the employee has a model pinned on it
3. **then the global default model**
4. **only when none of those are set does preferred-provider routing kick in** — picking the preferred provider's primary model
Preferred-provider routing has a **capability gate**: if the employee's bound skills declare a need like `requires-model: vision`, routing first picks a provider that can satisfy those modalities; only if none can does it fall back unconstrained. Preferences are stored in `mate_agent_provider_preference` (ascending `sortOrder` = higher priority).
---
## Configuration via API

View File

@ -10,6 +10,7 @@ For historical diffs, check the corresponding git tag. For the "why" behind a fe
| Version | Date | Highlights |
|---------|------|------------|
| [v1.5.0](./releases/1.5.0) | 2026-06-04 | Goals grew a checklist — from "a score" to "ticked boxes" (checklist + Evaluator SPI + deterministic completion) · The Wiki learned to maintain itself (`[[wikilinks]]` + cascade rename/delete link-fix + broken-link lint · fact/experience layers + staleness propagation · pageType profiles & per-agent permissions · processing pipelines · local-directory knowledge source with scheduled incremental sync) · Per-owner memory isolation (owner_key + personal/team/global scopes + third-party endUserId passthrough) · Each employee binds a primary KB · Preferred provider drives the primary model + Claude Opus 4.8 |
| [v1.4.0](./releases/1.4.0) | 2026-05-23 | Persistent Goals — an employee locks a goal and follows it to done on its own · Subagent delegation became a tree (recursive 3 levels + async + digital-employee builder) · Progressive tool/skill disclosure (`enable_tool` + `load_skill`) · Workspace RBAC (4 roles + capability gating) · Feishu as a first-class citizen (interactive / approval / streaming cards + voice / file / audio / video + channel-native tools) |
| [v1.3.0](./releases/1.3.0) | 2026-05-13 | Year one of workflow — 7 step modes assemble employees into business processes · 6 trigger patterns make events drive workflows · Wiki promoted from search index to processing pipeline (user templates + cross-material aggregator + reverse citations) · Per-agent MCP tool binding + multimodal sidecar routing · 4 JVM-native document generation tools + image edit |
| [v1.2.0](./releases/1.2.0) | 2026-05-05 | Agents renamed "digital employees" (role / goal / backstory + 5 career templates) · Skills became the skeleton (manifest + template wizard + LESSONS self-evolution) · ACP integration: Claude Code / Codex now show up as your employees · Admin Runtime Console lets you see every employee working in real time |

View File

@ -88,8 +88,8 @@ mateclaw:
| Code | Meaning | Response |
|------|---------|----------|
| 401 | Token missing, expired, or invalid | `{"code": 401, "message": "Unauthorized"}` |
| 403 | Valid token but insufficient permissions | `{"code": 403, "message": "Forbidden"}` |
| 401 | Token missing, expired, or invalid | `{"code":401,"msg":"Token expired or invalid","data":null}` |
| 403 | Valid token but insufficient permissions | `{"code":403,"msg":"Forbidden","data":null}` |
Frontend handles both uniformly — redirect to login, clear stored tokens.
@ -100,8 +100,8 @@ MateClaw ships with `admin` / `admin123`. **Change this immediately in any deplo
### Spring Security config
- **Stateless sessions** — no server-side session; all state in the JWT
- **Public endpoints** — `/api/v1/auth/login`, `/h2-console/**`, `/swagger-ui/**`
- **Protected endpoints** — everything else under `/api/v1/**`
- **Public API endpoints** — `GET /api/v1/settings/language`, `/api/v1/auth/login`, `/api/v1/chat/stream`, `/api/v1/chat/*/stop`, `/api/v1/agents/*/chat/stream`, `/api/v1/setup/**`, `/api/v1/channels/webhook/**`, `/api/v1/channels/webchat/**`, `/api/v1/talk/ws`, `/api/v1/files/generated/**`
- **Protected endpoints** — everything else under `/api/**`
- **CSRF disabled** — not needed for stateless JWT
---
@ -247,7 +247,7 @@ Frontend shows approval card
User clicks Approve or Reject
POST /api/v1/approvals/{id}/resolve
POST /api/v1/chat/stream with /approve or /deny
├─ Approved → reload agent, replay tool call, continue reasoning
└─ Rejected → send rejection as observation, continue reasoning
@ -255,6 +255,8 @@ POST /api/v1/approvals/{id}/resolve
The "replay" mechanism is important. When the agent resumes, it **doesn't re-reason from scratch** — it skips straight to the approved tool call, executes it, and continues from the observation. No duplicate LLM calls, no wasted tokens.
The current web path has no write-style `POST /api/v1/approvals/{id}/resolve` endpoint. Approval and denial use the same SSE channel as normal chat so replay, persistence, and cancellation all stay on one lifecycle.
### The `mate_tool_approval` table
| Column | Purpose |
@ -283,24 +285,28 @@ Pending approvals expire after a configurable timeout (default: 10 minutes). Exp
MateClaw can notify through `channel/notification/` adapters — email, in-app alert, DingTalk/Feishu push. Configure in `Settings → Security & Approval → Notifications`.
### Resolving via API
### Current API surface
```bash
# List pending
curl http://localhost:18088/api/v1/approvals?status=pending \
# Hydrate pending approvals after a page refresh
curl http://localhost:18088/api/v1/chat/{conversationId}/pending-approvals \
-H "Authorization: Bearer <token>"
# Approve
curl -X POST http://localhost:18088/api/v1/approvals/123/resolve \
# Approve in the waiting conversation
curl -N -X POST http://localhost:18088/api/v1/chat/stream \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"decision": "approved"}'
-d '{"agentId":"1","conversationId":"conv-abc123","message":"/approve"}'
# Reject with reason
curl -X POST http://localhost:18088/api/v1/approvals/123/resolve \
# Reject in the waiting conversation
curl -N -X POST http://localhost:18088/api/v1/chat/stream \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"decision": "rejected", "notes": "Not appropriate for this workspace"}'
-d '{"agentId":"1","conversationId":"conv-abc123","message":"/deny"}'
# Manage auto-approval grants
curl http://localhost:18088/api/v1/approval/grants \
-H "Authorization: Bearer <token>"
```
---

View File

@ -532,6 +532,18 @@ When disabled, the catalog guidance points at `readSkillFile` instead and `load_
---
## The `/skill` slash menu in chat (new in 1.5.0)
Don't want to prompt the employee in natural language about which skill to use? Type `/` in the chat composer to open a **searchable skill picker**:
- ↑↓ to move, Enter/Tab to select, Esc to close; typing filters the enabled skills live (up to 8 shown).
- The list comes from `GET /api/v1/skills/enabled` — real skills plus MCP/ACP-derived virtual skills (a real skill shadows a same-named virtual one). Cached per workspace for 30 seconds so reopening doesn't re-fetch.
- Selecting a skill inserts a directive into the box: `Use the "skill name" skill: `, cursor at the end, ready for you to add context and send. The employee sees the directive in message history and runs `load_skill` to pull it.
The menu shows whenever **an employee is selected and that employee hasn't disabled skills** (the frontend checks `currentAgent && !skillsDisabled`) — it is unrelated to the global progressive-disclosure switch. Setting `mateclaw.skill.disclosure.load-skill-tool.enabled` to `false` globally only stops the backend from registering the `load_skill` tool; the menu still opens (the employee just falls back to pulling skills via `readSkillFile` and similar).
---
## Skill lifecycle curator (new in v1.4)
Agents that synthesize skills accumulate cruft — a one-off skill from three weeks ago is still in the catalog, eating a slot. The **curator** is a daily sweep that ages idle, **agent-created** skills through `active → stale → archived` and gets them out of the way without deleting anything.

View File

@ -324,14 +324,14 @@ curl -X PUT http://localhost:18088/api/v1/tools/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"enabled": false}'
# Test a tool directly
curl -X POST http://localhost:18088/api/v1/tools/WebSearchTool/test \
# Set disclosure tier for a builtin or channel tool
curl -X PUT http://localhost:18088/api/v1/tools/1/disclosure-tier \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"query": "Spring AI"}'
-d '{"tier": "core"}'
```
Every provider-backed tool has a test button in the Tools page so you can verify API keys before shipping.
The current REST API manages tool rows, enabled state, and disclosure tier. Direct execution of builtin tools happens through the agent runtime, not through a `/tools/{name}/test` endpoint.
---

View File

@ -258,6 +258,8 @@ Bind an agent to a knowledge base from `Agents → [your agent] → Knowledge`.
| `wiki_related_pages` | Related-page discovery across four signals (shared chunks, shared raws, direct links, semantic neighbors). |
| `wiki_explain_relation` | Score breakdown for the relationship between two pages. |
| `wiki_create_page` / `wiki_delete_page` | Direct page management; deletion respects `locked` / `system`. |
| `wiki_update_page` | **1.5.0**: in-place edit of a page (keeps the slug), gated by the pageType "update" permission. |
| `wiki_stale_pages` | **1.5.0**: list every page currently flagged for review (`stale`). |
| `wiki_archive_page` / `wiki_unarchive_page` | Soft-archive: hide a page from default list/search/related results without destroying it. Citations and source lineage survive; recoverable. System pages can't be archived. |
| `wiki_list_transformations` | List the transformation templates available to this KB (name, intent, whether apply-default is on). |
| `wiki_apply_transformation` | Run a template against one **raw material**; returns the output, run id, and saved-page info. |
@ -299,13 +301,11 @@ The injection is gated by the `wiki.hot_cache.enabled` feature flag (off → emp
#### Operator endpoints
Base path `/api/v1/wiki/hot-cache`:
| Method | Path | What it does |
|---|---|---|
| `GET` | `/{kbId}` | Current snapshot + meta |
| `POST` | `/{kbId}/regenerate` | Manual rebuild (async, ignores debounce) |
| `DELETE` | `/{kbId}` | Soft-delete; rebuilds on next event |
| `GET` | `/api/v1/wiki/hot-cache/{kbId}` | Current snapshot + meta |
| `POST` | `/api/v1/wiki/hot-cache/{kbId}/regenerate` | Manual rebuild (async, ignores debounce) |
| `DELETE` | `/api/v1/wiki/hot-cache/{kbId}` | Soft-delete; rebuilds on next event |
The hot cache lives in `mate_wiki_hot_cache` — see the **Data model** section below for the exact columns.
@ -464,10 +464,86 @@ than getting silently redirected to a similarly-named page.
| 4 | Cascade delete and rename rewrite referrers in-transaction; audit log; feature flag |
| 5 | Analyze stage emits a `related_pages` slug whitelist (validated server-side); enrich applier skips code blocks and gates on the whitelist |
Full design + live verification: `rfcs/202605/55-wiki-link-resolution-overhaul.md`
and `mateclaw-server/src/test/resources/e2e/wiki-link-overhaul-verification.md`
(6 e2e passes, 50+ live assertions, 3 bugs caught and fixed during the
test).
Full design and live verification live in the matching design doc and
end-to-end verification record in the repository.
---
## The knowledge base maintains itself (1.5.0)
1.5.0 pushes the Wiki from "a searchable knowledge base" into "a knowledge engine that maintains its own consistency, layers itself, runs its own pipelines, and can mount a local directory." The management surface for all of this is the **Wiki advanced panel** in the admin console (five sub-pages: page-type profile / layers & staleness / permissions / source watcher / pipelines).
### Knowledge layers: fact vs experience
Each page can carry a **knowledge layer**:
- **`fact`** — "what is": foundational fact pages. Unlabeled defaults to fact.
- **`experience`** — "what it means": synthesis, analysis, insight, which **depends on** a set of fact pages.
**Staleness propagates.** An experience page declares which fact pages it depends on (edges stored by page **id**, so renames don't break them). When a fact page is updated during ingest, every experience page depending on it is auto-marked `stale` (needs review) + a reason. The `wiki_stale_pages` tool lists everything currently flagged; search can **filter by knowledge layer** (facts only / experience only / all).
Under the hood: `mate_wiki_page` gains `knowledge_layer` / `depends_on_json` / `stale` / `stale_reason_json` columns (migration V135), with dependency edges in `mate_wiki_page_dependency` and a reverse index dedicated to stale propagation.
### Page-type profiles (pageType profile)
Define which **page types** a KB has (e.g. "concept / tutorial / decision record"), each carrying:
- A structured-field **schema** — page metadata is validated against it on save, with the validation status recorded (valid / invalid + details)
- **route / create / merge**-stage prompts — injected into the corresponding LLM call
- A **Markdown template** — the skeleton used when generating the page
At most one **enabled** profile per KB; unconfigured KBs use a **built-in default**. Profiles are written in YAML or JSON, with "validate (no save)" and "reset to default" actions. Stored in `mate_wiki_page_type_profile` (migration V134); the page metadata columns (`metadata_json` / `metadata_validation_status` / `template_key` / `profile_version`) are added to `mate_wiki_page` in the same migration.
### Page-type permissions (per-agent)
For "**this agent + this KB + this page type**" you can set read / create / update / delete flags plus a **write policy**:
| Write policy | Meaning |
|---|---|
| `allow` | Write immediately |
| `approval_required` | Write is held pending [approval](./security) |
| `deny` | Blocked |
`page_type='*'` is the KB-wide default; **exact matches beat the wildcard**.
**Read and write fall back differently** — keep them distinct:
- **Read** — when no rule matches, read falls back to the **KB-level default read policy** `defaultReadPolicy` (`allow_all` unless the KB sets `deny_all`). So existing KBs stay fully readable after upgrade. Read gating filters lists and search results; an unreadable type is treated as nonexistent (no existence leak).
- **Write** — write is opt-in tightened. An agent with **no rules** for a KB writes `allow` (old behavior); add **any** rule and that KB enters "locked down" mode — page types with no matching rule resolve to `deny` (fail-safe).
Stored in `mate_wiki_agent_page_type_permission` (migration V133).
### Processing pipelines (Wiki Pipeline)
Define a processing flow for a KB, fired automatically by **page events**:
- **Triggers**: `page_type_count` (a page-type count crosses a threshold), `page_created` (a page of a given type is created), `stale_marked` (pages get flagged stale)
- **Step executors**:
- `llm` — run input through the model; the output becomes the step result
- `skill` — run a skill from a **restricted set**, as the owner agent
Definitions are written in YAML or JSON, with CRUD + validate endpoints. Every run and every step is persisted and queryable, deduplicated by `(definition, trigger, subject, bucket)` for idempotency. Tables: `mate_wiki_pipeline_definition` / `mate_wiki_pipeline_run` / `mate_wiki_pipeline_step_run` (migration V136).
### Mount a local directory as a knowledge source — pluggable + scheduled incremental
Knowledge sources are a **pluggable SPI** (`WikiIngestSourceProvider`) with a built-in filesystem provider: give a KB a `source_directory` and files in it get ingested.
- **Scheduled incremental sync** — a background scheduler (with a distributed lock so only one node runs per cycle) scans periodically, detects changes **by content hash**, and re-ingests only new/modified files (text and binary).
- **Fail-closed security** — paths are normalized then symlink-resolved (closing TOCTOU) and validated against an allowed-roots allowlist; under the production profile an empty allowlist rejects everything. Set the `mate.wiki.allowed-source-roots` allowlist.
- **Status + manual trigger**`GET .../source-watcher` shows status, `POST .../source-watcher/scan` runs a scan immediately.
Relevant config (`application.yml`):
```yaml
mate:
wiki:
watcher-enabled: false # master switch for the source watcher
watcher-interval-ms: 300000 # scan interval (default 5 min)
allowed-source-roots: [] # allowed source-directory roots (allowlist)
require-allowed-roots: false # production: set true so an empty allowlist rejects everything
```
All new REST endpoints are in the [API Reference](./api#llm-wiki).
---

View File

@ -261,7 +261,7 @@ curl -X POST http://localhost:18088/api/v1/workspaces/1/members \
curl -X DELETE http://localhost:18088/api/v1/workspaces/1/members/42 \
-H "Authorization: Bearer <token>"
curl -X PUT http://localhost:18088/api/v1/workspaces/1/members/42/role \
curl -X PUT http://localhost:18088/api/v1/workspaces/1/members/42 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'

View File

@ -153,11 +153,15 @@ curl http://localhost:18088/api/v1/cron-jobs \
-H "Authorization: Bearer <token>"
# 立刻试跑一次(不影响下次定时触发)
curl -X POST http://localhost:18088/api/v1/cron-jobs/{id}/run-now \
curl -X POST http://localhost:18088/api/v1/cron-jobs/{id}/run \
-H "Authorization: Bearer <token>"
# 看历史执行
curl http://localhost:18088/api/v1/cron-jobs/{id}/runs \
# 查看单个定时任务的执行历史
curl http://localhost:18088/api/v1/dashboard/cron-runs/{id} \
-H "Authorization: Bearer <token>"
# 查看当前工作区最近执行历史
curl http://localhost:18088/api/v1/dashboard/cron-runs \
-H "Authorization: Bearer <token>"
```

File diff suppressed because it is too large Load Diff

View File

@ -151,7 +151,7 @@ mateclaw/
### 目标评估节点1.4.0+
ReAct 和 Plan-Execute 都有)现在在 `FinalAnswerNode` 把最终答案流式输出之后再跑一个 `GoalEvaluationNode`它给目标完成度打分,并可选地注入一条自动跟进消息,把没达成的目标继续推进。
ReAct 和 Plan-Execute 都有)现在在 `FinalAnswerNode` 把最终答案流式输出之后再跑一个 `GoalEvaluationNode`1.5.0 起它逐条裁决目标的 checklist 准则bootstrap / verdict 两模式),**全部准则通过才算完成**,并可选地注入一条针对剩余准则的自动跟进消息,把没达成的目标继续推进。
### 其他 1.4.0 运行时变化

View File

@ -35,6 +35,11 @@ v1.4.0 把飞书做成了"一等公民"渠道——交互卡片、流式卡片
飞书的细节全部在下面[飞书](#飞书)一节里展开。
:::
::: tip 1.5.0 渠道改进
- **统一的入站媒体管线**:目前**微信和企业微信**已接入这层共用的入站媒体下载器 + 魔数magic-byte类型识别 + 指数退避重试(其它 IM 渠道后续接入)。文件类型从内容字节判定(不再硬编码 `image/*`HEIC / WEBP / DOCX / XLSX 等都能正确识别,下载失败自动重试。
- **飞书:跟进文本自动带上最近的文件(#201**:飞书群里先发一个文件(哪怕没 @ 员工),再发一句文字,缓存的文件会自动作为内容片段塞给员工——每群 5 个文件、60 分钟 TTL。
:::
---
## 九个渠道

View File

@ -50,6 +50,8 @@ MateClaw 的聊天 UI 在试着回答一个问题:**AI 刚刚告诉你的事
信任是靠"把过程摊开"挣来的。MateClaw 把过程摊开。
**执行计划 & 工具调用详情查看器1.5.0)。** 计划面板每个步骤、每个工具调用行,右侧多了一个"查看详情"图标。点开是一个带毛玻璃背景的弹窗,显示**完整的请求参数和响应输出**——内联预览会截断的部分这里都在,带请求/响应各自的复制按钮,状态徽标标"进行中 / 已完成 / 失败 / 等待中"。这些数据存在消息元数据里,所以刷新页面后计划步骤和工具调用照样可读。
---
## 多渠道实时同步
@ -86,6 +88,10 @@ ChatConsole 不只是你自己聊天的地方。它是一个**运营控制台**
图片递交给支持视觉的模型做视觉理解。PDF 和 DOCX 走文本抽取(扫描件自动降级到 OCR。Agent 在本回合读到的所有内容,都会进它的上下文。
::: tip 工具生成的文件下载链接扛得住重启1.5.0#243
员工调工具生成的文件(文档 / 图片 / 音频…)现在**落盘**到 `data/generated-files/`,带 7 天保留窗口 + 6 小时定时清理,内存里再放一层 LRU——下载链接重启后依然有效不再受原来 10 分钟内存窗口限制。前端用一个全局点击代理拦截 `/api/v1/files/generated/{id}` 下载:成功走鉴权 fetch → blob 下载失败404/410/过期)只弹一个 toast**不再因为一个失效链接把整个页面卡死**。
:::
### 主模型不支持图片?走"多模态旁路"
::: tip 1.3.0 新增

View File

@ -111,7 +111,7 @@
- `POST /api/v1/chat/stream`——SSE 流式(原生 fetch
- `POST /api/v1/chat/upload`
- `POST /api/v1/chat/{conversationId}/stop`
- `POST /api/v1/approvals/{id}/resolve`
- 审批结果通过 `POST /api/v1/chat/stream` 发送 `/approve``/deny`
- `GET /api/v1/chat/{conversationId}/pending-approvals`
- `GET /api/v1/conversations`——列表
- `GET /api/v1/conversations/{id}/messages`

View File

@ -262,7 +262,7 @@ Manifest 最好放在稳定的静态地址,不要依赖 GitHub API 动态查
建议机制:
- `current.json` 记录当前版本、上一版本、状态
- UI 启动成功后,前端调用 `/api/v1/system/ui/boot-ok` 或通过 preload IPC 上报“本次版本已健康启动”
- UI 启动成功后,通过 preload IPC 上报“本次版本已健康启动”;如果后续选择 REST 方案,可新增 `/api/v1/system/ui/boot-ok`(当前源码尚未提供该端点)
- 若启动后短时间内崩溃或白屏,下次启动自动回滚上一版本
### 7. 安全要求
@ -437,9 +437,9 @@ UI 热更新本质上是在本地执行新的前端资源,必须做完整校
- `uiUpdater.download()`
- `uiUpdater.applyOnRestart()`
后端接口建议新增:
后端接口建议新增(当前源码尚未提供)
- `GET /api/v1/runtime/version`
- 建议新增:`GET /api/v1/runtime/version`(当前源码尚未提供)
返回示例:

View File

@ -147,7 +147,8 @@ cd ../mateclaw-server
mvn clean package -DskipTests
# 3. 把 JAR 拷到桌面项目
cp target/mateclaw-server.jar ../mateclaw-desktop/resources/app.jar
JAR_FILE=$(ls -1 target/mateclaw-server-*.jar | grep -v sources | head -n 1)
cp "$JAR_FILE" ../mateclaw-desktop/resources/app.jar
# 4. 下载平台特定的 JRE
cd ../mateclaw-desktop

View File

@ -1,233 +1,71 @@
# Doctor
**Doctor 页面回答一个问题:这个东西现在是不是真的在正常工作?**
Doctor 是应用内的健康抽屉。它从后端健康服务读取当前本机实例状态;当前实现不是一个独立的定时诊断系统。
MateClaw 有很多活动部件——后端、数据库、模型供应商、MCP 服务、IM 渠道、cron 任务、记忆整合、wiki 消化。出问题时,**症状**"我的 Agent 不响应")通常有一个**具体的原因**"DashScope API Key 昨天过期了"埋在离你能看到的地方好几层远的地方。Doctor 是一个单页,**一次性跑所有检查**,告诉你哪些是绿的、哪些是黄的、哪些是红的
可以从布局里的状态按钮 / 设置区域打开。抽屉每次打开或点击刷新时都会请求后端
通过 `设置 → Doctor` 打开,或者直接跳 `/doctor`
## 当前后端 API
---
## 它检查什么
每一项检查独立运行,报告三种状态之一:
- **✅ OK**——一切按预期工作
- **⚠️ 警告**——在工作但降级了(例如在用 fallback provider、接近配额、一个非关键的 cron 任务暂停了)
- **❌ 错误**——以一种你需要修的方式坏了
### 核心基础设施
| 检查 | 验证什么 |
|------|----------|
| **后端版本** | MateClaw 在跑,报告它的版本 |
| **数据库连接** | 配置的数据源可达,查询成功 |
| **数据库 schema** | 所有预期的 `mate_*` 表存在;迁移状态干净 |
| **磁盘使用** | 数据目录有足够空闲空间(低于 20% 警告,低于 5% 错误) |
| **H2 console 暴露** | 生产 profile 里启用了 H2 console 会警告 |
| **JWT secret 强度** | 还在用默认 JWT secret 会警告 |
### 模型
| 检查 | 验证什么 |
|------|----------|
| **活跃模型** | 默认模型配置存在且启用 |
| **供应商连通性** | 每个启用的供应商最近通过了连接测试 |
| **API Key 存在** | 每个标记为启用的云供应商都配了 key |
| **Ollama 可达** | 如果配了 Ollama本地实例可达 |
### Agent 和工具
| 检查 | 验证什么 |
|------|----------|
| **工具注册表** | 内置工具和 MCP 工具加载无错 |
| **Tool Guard 配置** | 至少存在一条 Tool Guard 规则(用 `default-policy: allow` 会警告) |
| **默认 Agent** | 默认 Agent 存在且启用 |
| **Agent 模板** | 内置模板存在且可加载 |
### 记忆和 Wiki
| 检查 | 验证什么 |
|------|----------|
| **记忆整合 cron** | 每个 Agent 的整合 cron 任务存在且启用 |
| **上次整合运行** | 过去 7 天没有跑过整合会警告 |
| **Wiki 消化队列** | 没有卡住的 `pending``processing` 原始材料 |
| **Wiki schema** | `mate_wiki_*` 表存在且可查询 |
### 渠道
| 检查 | 验证什么 |
|------|----------|
| **渠道健康监控** | 每个启用的渠道报告 `connected` 或正在主动重连 |
| **每渠道状态** | 每个 IM 渠道的连接状态和上次错误 |
| **Webhook URL 可达** | 生产环境下 webhook 模式的渠道没配公网 URL 会警告 |
### MCP
| 检查 | 验证什么 |
|------|----------|
| **启用的 MCP 服务** | 每个启用的 MCP 服务是 `connected` |
| **工具数** | 每个连接成功的服务报告至少一个工具 |
| **孤儿子进程** | 没有超过它父 client 存活的 stdio 子进程 |
### Cron 和异步
| 检查 | 验证什么 |
|------|----------|
| **Cron 引擎** | 计划任务执行器在运行 |
| **过期任务** | 任何任务超时超过 24 小时会警告 |
| **异步任务队列** | `mate_async_task` 队列长度在正常范围 |
---
## 检查怎么跑
Doctor 两种方式跑:
### 按需
点 Doctor 页面上的**运行所有检查**。按钮并行触发所有检查UI 在每项检查完成时流式返回结果。大多数检查在一秒内完成最慢的MCP 服务连接测试)可能要 1030 秒。
### 按计划
Doctor 也在后台**每 15 分钟自动跑一次**。结果缓存在内存里并持久化到 `mate_doctor_check`,这样打开页面时它**立刻加载**——你看到的是上次缓存的状态,直到你点**运行所有检查**。
`application.yml` 里调整计划:
```yaml
mateclaw:
doctor:
enabled: true
schedule-minutes: 15
cache-ttl-minutes: 10
```bash
curl http://localhost:18088/api/v1/system/health \
-H "Authorization: Bearer <token>"
```
---
## 读结果
每个检查返回:
响应结构:
```json
{
"name": "DashScope 供应商连通性",
"category": "Models",
"status": "ok",
"message": "连接测试成功延迟240ms",
"lastChecked": "2026-04-11T14:30:22",
"details": {
"provider": "dashscope",
"baseUrl": "https://dashscope.aliyuncs.com",
"latencyMs": 240
},
"fixUrl": "/settings/models"
"code": 200,
"msg": "success",
"data": {
"overall": "healthy",
"checks": [
{
"name": "default-model",
"status": "healthy",
"message": "Default model: qwen-plus",
"action": null
}
]
}
}
```
UI 渲染
`overall` 取值为 `healthy`、`warning`、`error`。每个检查项包含:
- 顶部的**分类 tab**——基础设施、模型、Agent、记忆、Wiki、渠道、MCP、Cron
- **状态计数器**——绿 / 黄 / 红
- **检查列表**——名字、状态、消息、距上次检查的时间、"查看详情"展开、可选的"修复"按钮跳到相关设置页
- **历史图**——(每个检查)最近 50 次运行的 sparkline一眼看出抖动的检查
| 字段 | 含义 |
|---|---|
| `name` | 稳定检查 key例如 `default-model`、`database`、`browser`、`provider:<id>`、`mcp:<name>` |
| `status` | `healthy`、`warning` 或 `error` |
| `message` | 抽屉里展示的简短诊断信息 |
| `action` | 可选 `{ label, route }`,提示去哪里修 |
---
## 当前检查项
## 修复按钮
当前 `SystemHealthService` 检查:
对可操作的检查Doctor 行包含一个**修复**按钮,直接跳到相关的设置页面:
| 检查 | 验证什么 | 常见修复入口 |
|---|---|---|
| 默认模型 | 是否配置并能加载默认模型 | `/settings/models` |
| Provider 配置 | 需要 API key 的 provider 是否已配置 | `/settings/models` |
| 已启用 MCP 服务 | 已启用 MCP 服务是否有成功连接结果 | `/settings/mcp-servers` |
| 数据库初始化 | 首次启动 bootstrap 是否完成 | `/setup` |
| 浏览器诊断 | 浏览器工具启动前置条件 | `/api/v1/system/browser-health` |
- 模型供应商失败 → `设置 → 模型`
- Tool Guard `default-policy: allow``设置 → 安全与审批`
- 生产环境的 H2 console → `设置 → 系统`(或显示一个可复制的配置片段)
- JWT 默认 secret → `设置 → 系统`(或显示一个配置片段)
- MCP 服务断开 → `工具 → MCP 服务`
- 卡住的 wiki 消化 → `Wiki → [KB] → 原始材料`
点修复带你到**你能解决问题的那个具体页面**。可能的话,目标页面会预过滤高亮失败的条目。
---
## Doctor API
浏览器诊断也有独立接口:
```bash
# 跑所有检查(同步)
curl http://localhost:18088/api/v1/doctor/run \
-H "Authorization: Bearer <token>"
# 获取缓存的检查结果
curl http://localhost:18088/api/v1/doctor/checks \
-H "Authorization: Bearer <token>"
# 只跑特定分类
curl http://localhost:18088/api/v1/doctor/run?category=models \
-H "Authorization: Bearer <token>"
# 历史结果
curl "http://localhost:18088/api/v1/doctor/history?check=dashscope-connectivity&limit=50" \
curl http://localhost:18088/api/v1/system/browser-health \
-H "Authorization: Bearer <token>"
```
---
## 当前源码没有实现的旧内容
## 在运维中使用 Doctor
旧文档曾提到 `/api/v1/doctor/run`、`/api/v1/doctor/checks`、`/api/v1/doctor/history`、Doctor 定时后台运行、`mate_doctor_check`、`mate_doctor_check_history`。这些端点和表在当前后端源码中不存在。当前健康检查请使用 `/api/v1/system/health`
### 作为 uptime 监控的健康端点
## 相关页面
把你的外部 uptime 监控UptimeRobot、Pingdom、内部 Prometheus指向
```
GET /api/v1/doctor/checks
```
端点返回 HTTP 200 带 JSON 汇总——聚合的通过/失败计数和按分类细分。你的监控应该在 `errorCount > 0` 时报警。
要更简单的健康检查,用:
```
GET /actuator/health
```
这遵循 Spring Boot 的标准格式。
### 升级时
部署新 MateClaw 版本之后跑 Doctor 验证没有回归:
1. 打开 `/doctor`
2. 点**运行所有检查**
3. 看有没有之前没有的黄或红
4. **特别注意数据库 schema**——升级后 schema 不匹配通常意味着某个迁移没跑
### 出问题时
用户报告"它不工作"时 Doctor 是第一个去看的地方。打开页面,看哪个检查是红的,点**修复**,解决问题。**如果没有检查是红的但用户仍然有问题**,大概率是 Doctor 还没覆盖的东西——开一个 [GitHub issue](https://github.com/matevip/mateclaw/issues) 让我们加一个检查。
---
## 数据模型
**`mate_doctor_check`**
| 列 | 用途 |
|----|------|
| `id` | 主键 |
| `name` | 检查名字 |
| `category` | 检查分类 |
| `status` | `ok` / `warning` / `error` |
| `message` | 人类可读的消息 |
| `details` | 额外细节的 JSON |
| `last_checked` | 上次运行时间 |
| `run_duration_ms` | 检查耗时 |
| `workspace_id` | 范围(全局检查为 null |
历史结果进 `mate_doctor_check_history`,同样的列加上一个保留期清理任务。
---
## 下一步
- [控制台](./console)——Doctor 所在的 UI
- [配置说明](./config)——你可能基于 Doctor 警告配置的东西
- [安全与审批](./security)——Doctor 在 Tool Guard 里检查什么
- [贡献指南](./contributing)——缺了什么就加一个 Doctor 检查
- [API 参考](./api) —— 源码对齐的路由索引
- [模型配置](./models) —— 模型 / provider 设置
- [MCP 协议](./mcp) —— MCP 服务配置
- [安全与审批](./security) —— Tool Guard 和审批行为

View File

@ -215,9 +215,10 @@ UI 里用 `工具 → MCP 服务`。三种传输模式stdio、streamable_http
### 我批准了一个工具调用但 Agent 没恢复
1. `AWAITING_APPROVAL` 还是 true 吗?(`GET /api/v1/agents/{id}`
2. 审批真的持久化了吗?(`GET /api/v1/approvals/{id}`
2. 等待中的会话还有 pending 审批吗?(`GET /api/v1/chat/{conversationId}/pending-approvals`
3. Agent 日志里 replay 尝试附近有错误吗?
4. Replay 失败的话Agent 应该在聊天里暴露一个错误
4. 批准/拒绝消息是否通过同一个会话的 `POST /api/v1/chat/stream` 发送?
5. Replay 失败的话Agent 应该在聊天里暴露一个错误
### 我想批量批准这个 Agent 未来的工具调用

View File

@ -65,8 +65,6 @@ Goal 把这件事翻过来。**你说一次,员工锁住目标,自己每轮
POST /api/v1/goals
{
"conversationId": "conv-xxx",
"agentId": "1000000001",
"workspaceId": 1,
"title": "部署博客到 fly.io",
"description": "...",
"exitCriteria": "DNS+SSL+健康检查+测试通过",
@ -76,7 +74,7 @@ POST /api/v1/goals
}
```
完整接口列表见 [API 参考](./api)。
> `agentId` / `workspaceId``conversationId` 在服务端派生,**请求体里不用传**(传了也会被忽略)。完整接口列表见 [API 参考](./api)。
---
@ -114,13 +112,59 @@ POST /api/v1/goals
如果 `autoFollowupEnabled=true` 且这一轮 evaluator 判 "continue",后台会:
1. 写一条 `followup_injected` 事件到时间线
2. 给对话末尾 APPEND 一条用户消息:"Continue working on the goal. Still missing: {gap}. Take the next concrete step."
2. 给对话末尾 APPEND 一条用户消息。**1.5.0 起,如果目标有清单,这条消息会明确列出还没通过的那几条准则**——"5/8 已完成,剩余① …… ② ……,去做剩下的";没有清单时回退到笼统的 "Continue working on the goal. Still missing: {gap}."
3. 让员工再跑一轮 reasoning**这一轮的回答就直接接在第一轮后面**
你的体感是:员工答完一段 → 停半拍 → **继续往下做** — 就像一个人做完一步停了一下想了想然后继续。
---
## 目标是一份清单checklist1.5.0+
1.4.0 里 evaluator 每轮给一个完成度分数0~1和一句"还差什么"。问题是 **0.8 到底是什么意思**——哪几条做完了、哪几条没做,你看不清。
1.5.0 把它换成**清单**:目标 = 一组**可以逐条独立验证**的准则。
**evaluator 有两种模式:**
| 模式 | 什么时候跑 | 干什么 |
|---|---|---|
| **bootstrap拆解** | 还没有准则时 | 把目标拆成清单,每条初始为"未通过" |
| **verdict裁决** | 已有准则时 | 逐条判:这条满足了吗?给出证据 |
两种模式都用**结构化输出**——evaluator 必须返回带类型的对象(准则 `id` + `passed` + `evidence`),而不是一段自由文本让我们去猜。
**完成判定是确定性的。** 只有当**每一条准则都通过**才判完成。20 条里过了 19 条0.95 分)依然是"继续"——差一条就还差一条,没有模糊阈值。
**怎么给目标加清单——三种途径:**
- **创建时直接带**——`setGoal` 工具传 `criteria: ["DNS 解析正确", "SSL 有效", "测试全绿"]`,或 `POST /api/v1/goals``criteria`。省去 bootstrap 那一轮。
- **让 evaluator 自己拆**——不传 criteria第一轮评估时 bootstrap 模式自动拆解。
- **运行中追加**——`addGoalCriterion` 工具或 `POST /api/v1/goals/{id}/criteria`,往进行中的目标补一条,不用重开。
**一条准则长什么样:**
```json
{ "id": "C1", "text": "DNS 解析指向 fly.io", "passed": false, "evidence": "" }
```
`id` 由服务端分配C1、C2…`text` 是人能看懂、LLM 能判的一句话,`passed` 是 evaluator 的裁决,`evidence` 是它给的依据(输出片段、文件摘录等)。清单存在 `mate_agent_goal.criteria`JSON通过 `GoalResponse.criteria` 解析后下发,从不以裸 JSON 字符串暴露。
### 头像旁的光hover 出来是一张清单卡
- **没有清单**时——一句话 tooltip标题 + evaluator 写的 gap 文本。
- **有清单**时——一张卡片:标题 + `X/Y` 进度,下面每条准则前一个 `○`(未完成)或 `✓`(绿色已完成,文字带删除线)。
评估中头像周围是沙金色呼吸光晕;完成短暂显示绿色环后消失;预算耗尽变红橙色环。
### Evaluator SPI
评估逻辑实现了 Spring AI 的 `Evaluator` 接口:既能做目标专用的 checklist 裁决bootstrap / verdict也能被当成通用评估器复用把单个目标包成一条准则跑 verdict。失败的 evaluator 调用**照样计入 LLM 预算**,所以预算账目是准的。
> 1.4.0 的目标是"员工记住它在干什么"。1.5.0 的目标是"员工知道**具体还差哪几条**"。从一个分数,到一份能逐条勾的清单。
---
## 4 个内置工具(员工可用)
员工的工具集里默认包含这 4 个(无需手动绑定,是 agent-wide 系统级工具):
@ -132,7 +176,7 @@ POST /api/v1/goals
| **completeGoal** | 显式标记完成 | "所有事项已做完,请 completeGoal" |
| **getGoalStatus** | 查询当前 goal 状态 | "我们现在进展到哪了?" |
完成时 (`completeGoal` 或 evaluator 判 score≥0.95),员工会把这个目标的总结同步到[长期记忆](./memory),后续对话能查得回来。
完成时`completeGoal`,或 evaluator 判定**每一条准则都通过**,员工会把这个目标的总结同步到[长期记忆](./memory),后续对话能查得回来。
---
@ -168,7 +212,7 @@ turnsUsed >= turnBudget 或 (agentLlmCallsUsed + evalLlmCallsUsed) >= llmCallB
↓ ↑
paused
active ──evaluator score≥0.95 / completeGoal──→ completed (终态)
active ──evaluator 全部准则通过 / completeGoal──→ completed (终态)
active ──turns_used/llm_calls 用完 ─────────→ exhausted (终态)
@ -188,7 +232,7 @@ turnsUsed >= turnBudget 或 (agentLlmCallsUsed + evalLlmCallsUsed) >= llmCallB
- **不做嵌套目标 / 目标树** — 一个 conversation 一个目标,不堆 OKR
- **不做"目标模板"** — 每个目标是手写的,不是从库里挑的
- **不做跨 conversation 迁移目标** — 想要那效果,请用[工作流](./workflow)
- **不暴露评估分数给用户** — 那个 `completionScore` 是工程内部协议不是用户语言。UI 用一圈光说话hover 显示 evaluator 写的 gap 文本(自然语言)。后端日志和 API 里仍可见数值,方便调试
- **不暴露评估分数给用户** — 那个 `completionScore` 是工程内部协议不是用户语言。UI 用一圈光说话hover 出来:有清单时是逐条勾的清单卡,没清单时是 evaluator 写的 gap 文本(自然语言)。后端日志和 API 里仍可见数值,方便调试
---
@ -219,12 +263,18 @@ mateclaw:
goal:
# 主开关;关闭后图节点对所有调用 pass-through
enabled: true
# 创建目标时 autoFollowupEnabled 的默认值(调用方未指定时)
default-auto-followup: true
# 运行期总开关;关掉则无论 per-goal 标志如何,都不注入自动延续
allow-auto-followup: true
# 默认 turn 预算
default-turn-budget: 20
# 默认 LLM 调用预算agent + evaluator 之和)
default-llm-call-budget: 200
# 自动延续之间至少隔多久(秒)
auto-followup-cooldown-seconds: 0
# 单次 graph 运行内自动延续的硬上限(每条消息的安全网;总预算仍由 turnBudget 管)
max-followups-per-run: 8
# 评估器使用的模型;空字符串 = 沿用对话当前模型便宜的小模型推荐qwen-turbo / glm-4-flash
evaluator-model: ""
# 评估 prompt 携带的历史消息条数上限

View File

@ -99,7 +99,7 @@ MateClaw ── HTTP POST ──► 远程 MCP 服务
- **URL**streamable_http / sse——服务端点
- **HTTP Headers**streamable_http / sse——JSON 对象
- **连接超时**——默认 30 秒
- **读取超时**——默认 30 秒
- **读取超时**——默认 **60 秒**1.5.0 起从 30s 提到 60s#247单次 callTool 往返合法地跑久一点的工具不再被掐断。每台服务可单独调 5300s
保存。启用状态时 MateClaw 自动尝试连接并发现工具。
@ -357,7 +357,7 @@ stdio 服务:禁用/删除、配置替换、应用关闭(`@PreDestroy`)、
| `cwd` | VARCHAR(512) | NULL | 工作目录 |
| `enabled` | BOOLEAN | TRUE | 开关 |
| `connect_timeout_seconds` | INT | 30 | HTTP 连接超时 |
| `read_timeout_seconds` | INT | 30 | 请求响应超时 |
| `read_timeout_seconds` | INT | 60 | 请求响应超时1.5.0 起默认 60旧为 30 |
| `last_status` | VARCHAR(32) | `disconnected` | 上次连接状态 |
| `last_error` | TEXT | NULL | 上次错误消息 |
| `last_connected_time` | DATETIME | NULL | 上次成功连接时间 |

View File

@ -64,6 +64,55 @@ MateClaw 里其他所有东西在你配置完之后就静止了。Agent、工
---
## 记忆认人per-owner 隔离1.5.0
以前一个员工的记忆是**共享**的:不管是网页登录的你、还是飞书群里的同事、还是第三方 API 接进来的终端用户,聊出来的记忆都堆进同一个 `MEMORY.md`。一个员工服务多个人时,记忆会串台。
1.5.0 给每条记忆加了**主人owner**和**可见范围scope**。
### 统一的 owner_key
不管身份从哪来,都归一成一个带前缀的字符串:
| 来源 | owner_key |
|---|---|
| 网页控制台 | `user:<用户id>` |
| IM 渠道(飞书 / 钉钉 / 企微…) | `<渠道>:<发送者id>` |
| 第三方 API带 endUserId | `api:<endUserId>` |
| 系统 / cron | `system` |
### 三档可见性
| scope | 谁能读 | 典型内容 |
|---|---|---|
| **PERSONAL个人** | 只有匹配的 owner | 对话里抽取出来的记忆默认进这档 |
| **TEAM团队** | 用这个员工的人都能读 | 员工配置文件AGENTS.md / SOUL.md / PROFILE.md、历史回填的数据 |
| **GLOBAL全局** | 跨员工 / 工作空间始终可见 | 预置事实、系统参考资料 |
### 召回偏好个人记忆
system prompt 里只烤进 TEAM/GLOBAL 的共享记忆(可缓存);每轮再按当前 owner_key **预取**他个人的记忆注入。所以问"我的项目用什么栈"时,员工优先回忆**这个人**的私人记忆文件,而不是知识库里的泛泛资料。
> 关于结构化"事实"层:**事实召回查询本身支持 owner 可见性过滤**PERSONAL 仅 owner 可见TEAM/GLOBAL 共享)。但当前的**自动事实投影**主要从共享记忆文件构建、插入时不写 `ownerKey/scope`——也就是说个人化更多体现在个人记忆文件的预取上,事实层的 per-owner 化还在补齐中。
### 第三方 API 透传终端用户身份
`/api/v1/chat``/api/v1/chat/stream` 的请求体新增可选字段 **`endUserId`**(字符串,保大整数精度)。一个 PAT 认证的接入方代表一个 MateClaw 用户,但可以为每个终端用户传不同的 `endUserId`,记忆按终端用户自动隔离。
### 这是一个可开关的特性
总开关是 `mate.memory.lifecycle-mediator-enabled`
::: warning 默认值要看清楚
Java 属性的裸默认值是 `false`,但**随发行版打包的 `application.yml` 把它设成了 `true`**——也就是说**默认安装下 per-owner 隔离是开着的**。要回到旧的共享行为(所有写入走 TEAM在你的配置里显式设为 `false`
:::
打开后:对话抽取写入 owner 的 PERSONAL 记忆,召回按 owner_key 过滤;关闭后所有写入回退到共享 TEAM。多租户实例保持开启单人部署可以关掉。
底层:迁移 `V137``mate_workspace_file` / `mate_memory_recall` / `mate_fact` 三张表加了 `owner_key` + `scope` 列,历史行回填为 `TEAM`(保证升级后没有记忆被藏起来)。`remember` 等记忆工具会按当前请求上下文解析 owner_key开关打开时写进该 owner 的 PERSONAL 记忆,关闭时回退共享写入。
---
## 多层记忆 + 可插拔 Provider
记忆这一层不是一个硬编码的实现。它是一个**接口**——多层架构允许你**堆叠 provider**
@ -414,6 +463,11 @@ mate:
# --- 整合 / dreaming ---
emergence-enabled: true
emergence-day-range: 7
# --- per-owner 记忆隔离1.5.0---
# 随发行版打包的默认值是 true对话抽取写入 owner 的 PERSONAL 记忆,召回按 owner_key 过滤。
# 设为 false 回到旧的共享行为(所有写入走 TEAM。Java 属性裸默认值为 false。
lifecycle-mediator-enabled: true
```
配置前缀:`mate.memory`。

View File

@ -17,8 +17,8 @@ MateClaw 不关心你用哪个 LLM。它通过五个协议适配器跟所有主
| **百炼 Token Plan** | 阿里百炼 token 包月套餐 | dashscope | 7 个种子模型;支持长 token |
| **OpenAI** | GPT-4o、GPT-4o-mini、GPT-5.5、o1、o3、o4-mini | openai | 标准 OpenAI API |
| **OpenAI OAuthChatGPT Plus/Pro** | 通过订阅用 GPT-4o、o3、o4-mini | openai | 浏览器 OAuth**不需要 API Key** |
| **Anthropic** | Claude 4.7、Claude 4.6 Sonnet、Claude 4.5 Haiku | anthropic | 原生 Messages API |
| **Anthropic Claude Code OAuth** | 通过 Claude Pro/Max/Team 订阅用 Claude 4.7 / 4.6 | anthropic | 浏览器 OAuth + 手动粘贴流,**不需要 API Key** |
| **Anthropic** | **Claude Opus 4.8 / 4.8 Fast**1.5.0+)、Claude 4.7、Claude 4.6 Sonnet、Claude 4.5 Haiku | anthropic | 原生 Messages API4.8 两个变体都支持 `xhigh` 思考档 |
| **Anthropic Claude Code OAuth** | 通过 Claude Pro/Max/Team 订阅用 Claude Opus 4.8 / 4.7 / 4.6 | anthropic | 浏览器 OAuth + 手动粘贴流,**不需要 API Key** |
| **Google Gemini** _(原生)_ | gemini-2.5-flash、gemini-3-pro-image-preview、gemini-2.5-flash-image | gemini | 原生 `generateContent` API非 OpenAI 兼容)——见下方"原生 Gemini" |
| **xAI / Grok** | Grok 3、Grok 4 | openai | OpenAI 兼容base URL + API KeyUI 带 xAI 品牌图标 |
| **DeepSeek** | deepseek-chat、deepseek-coder、**DeepSeek V4 flash + pro**(支持思考模式) | openai | OpenAI 兼容 |
@ -162,13 +162,13 @@ Gemini 不再走 OpenAI 兼容层——MateClaw 直接对接 Google 的**原生
如果 `local` 模式起不来 loopback 端口(端口被占、沙箱拒绝),会自动降级到 `manual_paste`
**后端端点**`/api/v1/oauth/openai/device`
**后端端点**
| Method | Path | 用途 |
|---|---|---|
| `POST` | `/start` | 开一个会话,返回 `deviceAuthId` / `userCode` / `verificationUrl` / `intervalSeconds` / `expiresInSeconds` |
| `POST` | `/poll` | 按 `deviceAuthId` 轮询,返回 `PENDING` / `COMPLETED` / `EXPIRED` |
| `POST` | `/cancel` | 丢弃会话(比如用户关了对话框) |
| `POST` | `/api/v1/oauth/openai/device/start` | 开一个会话,返回 `deviceAuthId` / `userCode` / `verificationUrl` / `intervalSeconds` / `expiresInSeconds` |
| `POST` | `/api/v1/oauth/openai/device/poll` | 按 `deviceAuthId` 轮询,返回 `PENDING` / `COMPLETED` / `EXPIRED` |
| `POST` | `/api/v1/oauth/openai/device/cancel` | 丢弃会话(比如用户关了对话框) |
前端按 OpenAI 返回的 `intervalSeconds`(一般 5 秒)轮询;服务端再设一个最小轮询间隔(默认 3 秒)兜底,避免被打。过期的会话每 5 分钟扫一次清掉。
@ -393,6 +393,17 @@ MateClaw 用一个**活跃模型**作为全局默认。没有指定自己模型
- **出口 sanitizer** —— provider 专属选项(如 OpenAI 推理模型的 `reasoning_effort`)在 failover 到不支持的 provider 时被剥离,泄漏的选项不会让 fallback 报 400
- **UI 区分 401 与会话过期** —— provider 认证错误和用户会话过期现在显示不同消息、不同处置
### 偏好提供商决定主模型1.5.0
1.5.0 之前,"每个 agent 自定义优先级"只影响 **failover 顺序**——主模型仍是全局默认。1.5.0 让这个偏好**真的决定主模型选择**。完整优先级链是:
1. **会话钉选模型最高优先**——聊天头部 ModelSelector 给这个会话单独绑了模型,就用它(见[按会话选模型](./chat#按会话选模型)
2. **其次是 per-agent 的模型覆盖(`modelName`**——员工自己钉死了某个模型
3. **再次是全局默认模型**
4. **以上都没有时,才进入偏好提供商路由**——按偏好挑提供商的主模型
偏好提供商路由里有一道**能力门禁**:如果员工绑定的技能声明了 `requires-model: vision` 这类需求,路由会先挑能满足这些模态的提供商;满足不了再无约束回退。偏好存在 `mate_agent_provider_preference` 表(按 `sortOrder` 升序,越小优先级越高)。
---
## API 配置

View File

@ -10,6 +10,7 @@
| 版本 | 日期 | 亮点 |
|------|------|------|
| [v1.5.0](./releases/1.5.0) | 2026-06-04 | 目标长出清单——从"打个分"到"逐条勾"checklist + Evaluator SPI + 确定性完成判定) · Wiki 学会自维护(`[[wikilink]]` 互联 + 改名/删页级联修链 + 坏链体检 · 事实/经验分层 + 失效传播 · pageType 档案与 per-agent 权限 · 处理流水线 · 本地目录知识源定时增量同步) · 记忆按主人隔离owner_key + 个人/团队/全局可见性 + 第三方 endUserId 透传) · 每个员工绑主知识库 · 偏好提供商决定主模型 + Claude Opus 4.8 |
| [v1.4.0](./releases/1.4.0) | 2026-05-23 | 持久化目标——员工锁住目标自己跟到完成 · 子员工委派变成一棵树(递归 3 层 + 异步 + 数字员工构建器) · 渐进式工具/技能披露(`enable_tool` + `load_skill` · 工作空间 RBAC四级角色 + 能力门禁) · 飞书做成一等公民(互动/审批/流式卡片 + 语音/文件音视频 + 渠道原生工具) |
| [v1.3.0](./releases/1.3.0) | 2026-05-13 | 工作流元年——7 种 step mode 把员工组装成业务流程 · 触发器 6 种 pattern 让事件自动启动流程 · Wiki 从搜索索引升级为处理流水线(用户模板 + 跨材料聚合 + reverse-citation · MCP per-agent 工具绑定 + 多模态旁路路由 · 4 个 JVM 原生文档生成工具 + 图像编辑 |
| [v1.2.0](./releases/1.2.0) | 2026-05-05 | 智能体改名"数字员工"(角色 / 目标 / 背景故事 + 5 职业模板) · 技能成了骨架manifest + 模板向导 + LESSONS 自我进化) · ACP 接入Claude Code / Codex 变成你的员工 · Admin 运行时控制台让你看见每个员工正在干什么 |

View File

@ -88,8 +88,8 @@ mateclaw:
| 状态码 | 含义 | 响应 |
|--------|------|------|
| 401 | Token 缺失、过期或无效 | `{"code": 401, "message": "Unauthorized"}` |
| 403 | Token 有效但权限不足 | `{"code": 403, "message": "Forbidden"}` |
| 401 | Token 缺失、过期或无效 | `{"code":401,"msg":"Token expired or invalid","data":null}` |
| 403 | Token 有效但权限不足 | `{"code":403,"msg":"Forbidden","data":null}` |
前端统一处理——跳登录页、清空存储的 token。
@ -100,8 +100,8 @@ MateClaw 出厂带 `admin` / `admin123`。**除了你自己笔记本之外的任
### Spring Security 配置
- **无状态会话**——服务端不存 session所有状态都在 JWT 里
- **公共端点**——`/api/v1/auth/login`、`/h2-console/**`、`/swagger-ui/**`
- **受保护端点**——`/api/v1/**` 下的其他所有路径
- **公共 API 端点**——`GET /api/v1/settings/language`、`/api/v1/auth/login`、`/api/v1/chat/stream`、`/api/v1/chat/*/stop`、`/api/v1/agents/*/chat/stream`、`/api/v1/setup/**`、`/api/v1/channels/webhook/**`、`/api/v1/channels/webchat/**`、`/api/v1/talk/ws`、`/api/v1/files/generated/**`
- **受保护端点**——`/api/**` 下的其他所有路径
- **CSRF 关闭**——无状态 JWT 不需要
---
@ -247,7 +247,7 @@ Tool Guardrequire_approval
用户点 Approve 或 Reject
POST /api/v1/approvals/{id}/resolve
POST /api/v1/chat/stream消息为 /approve 或 /deny
├─ Approved → 重新加载 Agentreplay 工具调用,继续推理
└─ Rejected → 把拒绝作为 observation 返回,继续推理
@ -255,6 +255,8 @@ POST /api/v1/approvals/{id}/resolve
"replay" 机制很重要。Agent 恢复时**不会从头重新推理**——它直接跳到已经批准的工具调用、执行、从观察继续。**没有重复的 LLM 调用,没有浪费的 token。**
当前 Web 路径没有写入型 `POST /api/v1/approvals/{id}/resolve` 端点。批准和拒绝走普通聊天同一条 SSE 通道,这样 replay、持久化和取消都在同一个生命周期里。
### `mate_tool_approval`
| 列 | 用途 |
@ -283,24 +285,28 @@ Pending approval 在一个可配置的超时后过期(默认 10 分钟)。
MateClaw 可以通过 `channel/notification/` 适配器通知——邮件、应用内提醒、钉钉/飞书推送。在 `设置 → 安全与审批 → 通知` 里配置。
### API 方式处理审批
### 当前 API 表面
```bash
# 列出 pending 审批
curl http://localhost:18088/api/v1/approvals?status=pending \
# 刷新页面后补水 pending 审批
curl http://localhost:18088/api/v1/chat/{conversationId}/pending-approvals \
-H "Authorization: Bearer <token>"
# 批准
curl -X POST http://localhost:18088/api/v1/approvals/123/resolve \
# 在等待中的会话里批准
curl -N -X POST http://localhost:18088/api/v1/chat/stream \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"decision": "approved"}'
-d '{"agentId":"1","conversationId":"conv-abc123","message":"/approve"}'
# 拒绝并带原因
curl -X POST http://localhost:18088/api/v1/approvals/123/resolve \
# 在等待中的会话里拒绝
curl -N -X POST http://localhost:18088/api/v1/chat/stream \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"decision": "rejected", "notes": "这个工作空间不适合"}'
-d '{"agentId":"1","conversationId":"conv-abc123","message":"/deny"}'
# 管理自动批准授权
curl http://localhost:18088/api/v1/approval/grants \
-H "Authorization: Bearer <token>"
```
---

View File

@ -531,6 +531,18 @@ mateclaw:
---
## 聊天里的 `/skill` 斜杠菜单1.5.0 新增)
不想用自然语言提示员工用哪个技能?在聊天输入框打一个 `/`,弹出一个**可搜索的技能选择器**
- ↑↓ 选、Enter/Tab 确认、Esc 关;打字实时过滤已启用的技能(最多显示 8 条)。
- 列表来自 `GET /api/v1/skills/enabled`——包含真实技能 + MCP/ACP 派生的虚拟技能同名时真实技能优先。30 秒按工作空间缓存,避免每次重开都拉取。
- 选中一个技能后,输入框里被插入一句指令:`Use the "技能名" skill: `,光标停在末尾,你接着补充上下文发出去。员工在消息历史里看到这条指令,就会调 `load_skill` 拉起这个技能。
这个菜单的显示条件只看**当前选中了员工、且该员工没有关闭技能**(前端 `currentAgent && !skillsDisabled`)——和全局的渐进式披露开关无关。全局把 `mateclaw.skill.disclosure.load-skill-tool.enabled` 设为 `false` 只会让后端不注册 `load_skill` 工具,菜单照样弹(员工会回退用 `readSkillFile` 之类的方式拉技能)。
---
## 技能生命周期管理员v1.4 新增)
会合成技能的 Agent 会攒下垃圾——三周前的一次性技能还在目录里占着位子。**管理员curator** 是一个每日扫描,把闲置的、**Agent 创建的**技能沿 `active → stale → archived` 老化,让它们退场而不删除任何东西。

View File

@ -319,14 +319,14 @@ curl -X PUT http://localhost:18088/api/v1/tools/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"enabled": false}'
# 直接测试一个工具
curl -X POST http://localhost:18088/api/v1/tools/WebSearchTool/test \
# 设置内置或渠道工具的披露分级
curl -X PUT http://localhost:18088/api/v1/tools/1/disclosure-tier \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"query": "Spring AI"}'
-d '{"tier": "core"}'
```
每个依赖 provider 的工具在 Tools 页面都有测试按钮
当前 REST API 管理工具行、启用状态和披露分级。内置工具的直接执行走 Agent runtime不存在 `/tools/{name}/test` 端点
---

View File

@ -258,6 +258,8 @@ UI 上能做:
| `wiki_related_pages` | 关联页面(共享 chunk / 共享原文 / 双向链 / 语义近邻) |
| `wiki_explain_relation` | 详细拆解两页之间的关联强度和原因 |
| `wiki_create_page` / `wiki_delete_page` | 直接维护页面(删除受 locked / system 保护) |
| `wiki_update_page` | **1.5.0**:就地编辑一页(保留 slug受 pageType "改" 权限门禁 |
| `wiki_stale_pages` | **1.5.0**:列出当前所有被标记"待复核stale"的页 |
| `wiki_archive_page` / `wiki_unarchive_page` | 软归档:从默认 list/search/related 隐藏,但保留页面与引文,可恢复。系统页不能归档。 |
| `wiki_list_transformations` | 列出当前 KB 可用的加工器模板(名称、用途、是否默认运行)|
| `wiki_apply_transformation` | 对一份**原始材料**运行一个模板返回输出runId / output / 落页信息)|
@ -299,13 +301,11 @@ UI 上能做:
#### 运维端点
基础路径 `/api/v1/wiki/hot-cache`
| Method | Path | 作用 |
|---|---|---|
| `GET` | `/{kbId}` | 拿当前快照 + 元数据 |
| `POST` | `/{kbId}/regenerate` | 手动重建(异步,跳过去抖) |
| `DELETE` | `/{kbId}` | 软删除;下次事件触发重建 |
| `GET` | `/api/v1/wiki/hot-cache/{kbId}` | 拿当前快照 + 元数据 |
| `POST` | `/api/v1/wiki/hot-cache/{kbId}/regenerate` | 手动重建(异步,跳过去抖) |
| `DELETE` | `/api/v1/wiki/hot-cache/{kbId}` | 软删除;下次事件触发重建 |
热缓存数据落在 `mate_wiki_hot_cache`——具体列见下面的 **底层数据** 一节。
@ -420,7 +420,85 @@ Chat 渲染 agent 回复时content 里的 `[[slug]]` / `[[slug|alias]]` 会
| 4 | 删除 / 重命名级联清理audit logfeature flag |
| 5 | analyze 阶段输出 slug 白名单 `related_pages`服务端二次校验enrich applier 跳代码块 + slug 白名单 gate |
完整设计 + 实测见 `rfcs/202605/55-wiki-link-resolution-overhaul.md` + `mateclaw-server/src/test/resources/e2e/wiki-link-overhaul-verification.md`6 个 e2e pass section、50+ 条 live 断言、3 个测试中发现并修复的 bug 完整记录)。
完整设计与实测见仓库内对应的设计文档与端到端验证记录。
---
## 知识库会自维护1.5.0
1.5.0 把 Wiki 从"一个能搜的知识库"推进成"一个会自己维护一致性、自己分层、自己跑流水线、能挂本地目录的知识引擎"。这一整块的管理入口在后台的 **Wiki 高级管理面板**(五个子页:页面类型档案 / 分层与失效 / 权限 / 知识源 watcher / 流水线)。
### 知识分层:事实 vs 经验
每页可以标一个**知识层**
- **`fact`(事实层)**——"是什么":基础事实页。不标的默认按事实处理。
- **`experience`(经验层)**——"意味着什么":综合、分析、个人洞见,**依赖**一组事实页。
**失效会传播。** 经验页声明它依赖哪些事实页(按页面 **id** 存边,所以改名不断链)。当某个事实页在 ingest 时被更新,所有依赖它的经验页自动被标记 `stale`(待复核)+ 一段失效原因。`wiki_stale_pages` 工具列出当前所有待复核的页;搜索可以**按知识层过滤**(只搜事实 / 只搜经验 / 全部)。
底层:`mate_wiki_page` 加了 `knowledge_layer` / `depends_on_json` / `stale` / `stale_reason_json` 列(迁移 V135依赖边存在 `mate_wiki_page_dependency` 表,带一个反向索引专门给失效传播用。
### 页面类型档案pageType profile
为一个知识库定义有哪些**页面类型**(如"概念 / 教程 / 决策记录"),每种类型可以带:
- 结构化字段 **schema**——新页落库时按它校验元数据并记下校验状态valid / invalid + 详情)
- **路由 / 创建 / 合并** 阶段的提示词——注入到对应阶段的 LLM 调用里
- **Markdown 模板**——生成页面时的骨架
每个 KB 至多一个**启用的** profile没配的 KB 用**内建默认档案**。profile 用 YAML 或 JSON 写,有"校验(不落库)"和"重置为默认"两个动作。存在 `mate_wiki_page_type_profile` 表(迁移 V134页面元数据列也在同一迁移里加到 `mate_wiki_page``metadata_json` / `metadata_validation_status` / `template_key` / `profile_version`)。
### 页面类型权限per-agent
可以为"**某个员工 + 某个 KB + 某种页面类型**"配读 / 增 / 改 / 删四个开关,外加**写策略**
| 写策略 | 含义 |
|---|---|
| `allow` | 立即写 |
| `approval_required` | 写入挂起,走[审批](./security)流程 |
| `deny` | 禁止 |
`page_type='*'` 是 KB 级默认,**精确匹配优先于通配**。
**读和写的默认回退不一样**,这点要分清:
- **读**——没匹配到规则时,回退到 **KB 级默认读策略** `defaultReadPolicy`(默认 `allow_all`,除非 KB 配成 `deny_all`)。所以升级后已有 KB 仍然全可读。读门禁过滤列表和搜索结果,不可读的类型直接当不存在(不泄露存在性)。
- **写**——是 opt-in 收紧的。一个员工对某 KB **没配任何规则**时,写默认 `allow`(旧行为不变);一旦配了**任意一条**规则,这个 KB 就进入"锁定"模式——没匹配到规则的页面类型按 `deny` 处理fail-safe
存在 `mate_wiki_agent_page_type_permission` 表(迁移 V133
### 处理流水线Wiki Pipeline
给知识库定义一段处理流程,由**页面事件自动触发**
- **触发器**`page_type_count`(某类页面数量达到阈值)、`page_created`(新建某类页面)、`stale_marked`(页面被标记失效)
- **步骤执行器**
- `llm`——把输入过一遍模型,模型输出作为本步结果
- `skill`——在**受限技能集**内跑一个技能,以 owner agent 身份执行
定义用 YAML 或 JSON 写,有 CRUD + 校验接口。每次运行run和每一步step run都有持久化记录可查`(definition, trigger, subject, bucket)` 去重保证幂等。表:`mate_wiki_pipeline_definition` / `mate_wiki_pipeline_run` / `mate_wiki_pipeline_step_run`(迁移 V136
### 本地目录挂成知识源——可插拔 + 定时增量
知识源做成了**可插拔 SPI**`WikiIngestSourceProvider`),内建一个文件系统实现:给 KB 配一个 `source_directory`,目录里的文件就被吸进知识库。
- **定时增量同步**——后台调度器(用分布式锁保证多节点只跑一份)周期扫描,**按内容哈希**检测变更,只重新吸入新增 / 改动的文件(文本和二进制都覆盖)。
- **安全 fail-closed**——路径先规范化再解析软链(堵 TOCTOU按允许根目录白名单校验生产 profile 下空白名单默认拒绝一切。配 `mate.wiki.allowed-source-roots` 白名单。
- **状态可查 + 手动触发**——`GET .../source-watcher` 看状态,`POST .../source-watcher/scan` 立即扫一次。
相关配置(`application.yml`
```yaml
mate:
wiki:
watcher-enabled: false # 知识源 watcher 总开关
watcher-interval-ms: 300000 # 扫描间隔(默认 5 分钟)
allowed-source-roots: [] # 允许的源目录根(白名单)
require-allowed-roots: false # 生产建议设 true空白名单则拒绝一切
```
全部新增 REST 端点见 [API 参考](./api#llm-wiki)。
---

View File

@ -1,6 +1,6 @@
{
"name": "mateclaw-ui",
"version": "1.5.0-SNAPSHOT",
"version": "1.5.0",
"private": true,
"type": "module",
"description": "MateClaw - Personal AI Assistant Web Console",

View File

@ -21,7 +21,7 @@
<properties>
<!-- MateClaw release version shared by all Maven modules. -->
<revision>1.5.0-SNAPSHOT</revision>
<revision>1.5.0</revision>
<!-- Java -->
<java.version>21</java.version>