Sprint 2: 产品体验打磨 + 行业模板
- Create PipelineResultPreview component with tab-based output switching
- Connect workflow/hand messages to PresentationContainer in ChatArea
- Add auto-trigger first Hand after onboarding (industry-specific queries)
- Seed 3 industry agent templates (education, healthcare, design-shantou)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Model Groups provide logical model names that map to multiple physical
models across providers, with automatic failover when one provider's
key pool is exhausted.
Backend:
- New model_groups + model_group_members tables with FK constraints
- Full CRUD API (7 endpoints) with admin-only write permissions
- Cache layer: DashMap-backed CachedModelGroup with load_from_db
- Relay integration: ModelResolution enum for Direct/Group routing
- Cross-provider failover: sort_candidates_by_quota + OnceLock cache
- Relay failure path: record failure usage + relay_dequeue (fixes
queue counter leak that caused connection pool exhaustion)
- add_group_member: validate model_id exists before insert
Frontend:
- saas-relay-client: accept getModel() callback for dynamic model selection
- connectionStore: prefer conversationStore.currentModel over first available
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Kernel orchestration bridge: execute_orchestration, auto_compose_skills,
validate_orchestration methods on Kernel struct
- True parallel execution: replace sequential for-loop with tokio::JoinSet
for concurrent node execution within parallel groups
- Tauri commands: orchestration_execute (auto-compose or pre-defined graph),
orchestration_validate (dry-run validation)
- Full type conversions: OrchestrationRequest/Response with camelCase serde
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SemanticSkillRouter: add RuntimeLlmIntent trait and with_llm_fallback() builder
- route(): call LLM fallback when TF-IDF/embedding confidence < threshold
- CJK tokenization: generate bigrams for Chinese/Japanese/Korean text
- Fix: previous tokenizer treated entire CJK string as one huge token
- SemanticSkillRouter: add RuntimeLlmIntent trait and with_llm_fallback() builder
- route(): call LLM fallback when TF-IDF/embedding confidence < threshold
- CJK tokenization: generate bigrams for Chinese/Japanese/Korean text
- Fix: previous tokenizer treated entire CJK string as one huge token
- LlmSkillFallback: concrete RuntimeLlmIntent using LlmDriver
- Asks LLM to pick best skill from ambiguous candidates list
- Parses structured JSON response from LLM output
- Includes tests for LLM fallback and CJK tokenization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MCPServices.tsx now calls real Tauri commands (start/stop/list)
instead of only toggling config flags
- Show running service count, discovered tools per service
- Expand/collapse tool list for each running MCP service
- Extended QuickConfig mcpServices type with command/args/env/cwd
- Config change persists enabled state, MCP start/stop happens live
- get_messages_paginated(session_id, limit, offset) for batch loading
- count_messages(session_id) for total count queries
- Enables frontend to load messages progressively instead of all-at-once
- Schema: migrations now execute ALTER TABLE ADD COLUMN for state/message_count
- MemoryStore: add update_agent_runtime() and list_agents_with_runtime()
- Registry: add register_with_runtime() to accept persisted state/message_count
- Kernel boot: restore agents with their persisted state (not always Running)
- Kernel shutdown: persist all agent states/message_counts before terminating
Agents that were suspended stay suspended after restart. Message counts
survive restarts instead of resetting to 0.
- Add effective_importance() with exponential time decay (30-day half-life)
and access count boost for fair scoring of stale vs fresh memories
- Add SqliteStorage::decay_memories() for periodic maintenance:
reduces stored importance per interval, archives (deletes) below threshold
- Update find() scoring to use time-decayed importance in sort
- Add DecayResult type and effective_importance re-export in lib.rs
- Remove dead frontend active-learning.ts (370 lines, zero imports)
- saas test harness: align WorkerDispatcher::new and AppState::new
signatures with SpawnLimiter addition and init_db(&DatabaseConfig)
- growth sqlite: add CJK fallback (LIKE-based) when FTS5 unicode61
tokenizer fails on Chinese queries (unicode61 doesn't index CJK)
Full E2E test suite for agent chat functionality including:
- Message send/receive flow
- Streaming response verification
- Model switching behavior
- Error handling scenarios
- Multi-turn conversation context
Includes test report documenting coverage and known gaps.
S6 MCP Protocol:
- Fix McpTransport::initialize() — store actual server capabilities instead
of discarding them and storing empty ServerCapabilities::default()
- Add send_notification() method to McpTransport for JSON-RPC notifications
- Send notifications/initialized after MCP handshake (spec requirement)
- Add McpToolAdapter: bridges MCP server tools into the tool execution path
- Add McpServiceManager: lifecycle management for MCP server connections
- Add 4 Tauri commands: mcp_start_service, mcp_stop_service,
mcp_list_services, mcp_call_tool
- Register zclaw-protocols dependency in desktop Cargo.toml
New files:
- crates/zclaw-protocols/src/mcp_tool_adapter.rs (153 lines)
- desktop/src-tauri/src/kernel_commands/mcp.rs (145 lines)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ChatArea.tsx: remove duplicate useState(searchOpen) declaration on line 70
- scheduled_task/mod.rs: fix route from /api/scheduler/tasks to /api/v1/scheduler/tasks
(matches admin-v2 service baseURL pattern and all other modules)
- scheduled_task/handlers.rs: remove @reserved annotations (now has Admin V2 frontend)
- scheduled_task/handlers.rs: update doc comments with correct /api/v1/ paths
- Add welcomeMessage/quickCommands fields to Clone interface
- Persist template welcome/quick data via updateClone after creation
- FirstConversationPrompt: prefer template-provided welcome message
over dynamically generated one
- FirstConversationPrompt: render template quick_commands as chips
instead of hardcoded QUICK_ACTIONS when available
- Tighten assign/unassign template endpoint permissions from model:read
to relay:use (self-service operation for all authenticated users)
- Fix seed template tools to match actual runtime tool names
(file_read/file_write/shell_exec/web_fetch)
- Persist system_prompt/temperature/max_tokens via identity system
in agentStore.createFromTemplate()
- Fire-and-forget assignTemplate() in AgentOnboardingWizard
- Fix saas-relay-client unused variable warning
- Make pgvector extension optional in knowledge_base migration
- Increase StreamBridge timeout from 30s to 90s for thinking models
Root cause: ChatArea.tsx called listen() from @tauri-apps/api/event
directly on component mount without checking isTauriRuntime(). When
accessed from a regular browser (not Tauri WebView), window.__TAURI_INTERNALS__
is undefined, causing "Cannot read properties of undefined (reading 'transformCallback')".
Solution:
- Created lib/safe-tauri.ts with safe wrappers (safeInvoke, safeListen,
safeListenEvent, requireInvoke) that gracefully degrade when Tauri
IPC is unavailable
- Replaced direct listen() call in ChatArea.tsx with safeListenEvent()
Step 5 (embedding config) and Step 5b (summary driver) in App.tsx
bootstrap called invoke() without checking if Tauri IPC is available.
When accessing http://localhost:1420/ in a regular browser, this caused
"Cannot read properties of undefined (reading 'transformCallback')".
Also added __TAURI_INTERNALS__ guard in saasStore kernel config sync.
SessionStreamGuard and StreamCancelFlags were type aliases to the same
Arc<DashMap<String, Arc<AtomicBool>>> type. Tauri distinguishes managed
state by Rust type, so registering both caused a runtime panic:
"state for type ... is already being managed".
Changed to newtype structs with Deref impl to the inner Arc<DashMap>,
keeping all call sites compatible without changes.
- STABILIZATION_DIRECTIVE.md: feature freeze rules, banned actions, priorities
- TRUTH.md: single source of truth for system state (crate counts, store counts)
- AI_SESSION_PROMPTS.md: three-layer prompt system for AI sessions
- Industry agent delivery design spec
- Stabilization test suite for regression prevention
- Delete stale ISSUE-TRACKER.md
- Add .dockerignore for container builds
- Add brainstorm session artifacts
Replace error-swallowing let _ = patterns with tracing::warn! in browser,
classroom, gateway, intelligence, memory, pipeline, secure_storage, and
viking command handlers. Ensures errors are observable in production logs.
- create_item: wrap item + version INSERT in transaction for atomicity
- update_item handler: validate content length (100KB) before DB hit
- KnowledgeChunk: document missing embedding field, safe per explicit SELECT usage
Server side:
- POST /api/v1/billing/usage/increment endpoint with dimension whitelist
(hand_executions, pipeline_runs, relay_requests) and count validation (1-100)
- Returns updated usage quota after increment
Desktop side:
- New saas-billing.ts mixin with incrementUsageDimension() and
reportUsageFireAndForget() (non-blocking, safe for finally blocks)
- handStore.triggerHand: reports hand_executions after successful run
- PipelinesPanel.handleRunComplete: reports pipeline_runs on completion
- SaaSClient type declarations for new billing methods
Billing pipeline now covers all three dimensions:
relay_requests → relay handler (server-side, real-time)
hand_executions → handStore (client-side, fire-and-forget)
pipeline_runs → PipelinesPanel (client-side, fire-and-forget)
- Wire relay handler to increment_usage() for JSON responses (tokens + relay_requests)
- Wire relay handler to increment_dimension("relay_requests") for SSE streams
- Add increment_dimension() function for hand_executions/pipeline_runs dimensions
- Schedule AggregateUsageWorker hourly for reconciliation (run_on_start=true)
- Mount mock payment routes in dev mode (ZCLAW_SAAS_DEV=true)
Previously the quota middleware always allowed requests because usage
counters were never incremented. Now relay requests update billing_usage_quotas
in real-time, with the aggregator providing hourly reconciliation.
Replace the inline ResultModal with the full-featured
PipelineResultPreview component. This gives users JSON/Markdown/
Classroom mode switching, file download cards, and classroom export
support instead of the previous basic PresentationContainer wrapper.
Remove unused ResultModal component and PresentationContainer import.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `facts` table to schema with columns for id, agent_id, content,
category, confidence, source_session, and created_at. Implement
store_facts() and get_top_facts() on MemoryStore using upsert-by-id
and confidence-desc ordering. Facts extracted from conversations are
now durable across sessions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. MemoryMiddleware: replace byte-length check (query.len() < 4) with
char-count check (query.chars().count() < 2). Single CJK characters
are 3 UTF-8 bytes but 1 meaningful character — the old threshold
incorrectly skipped 1-2 char Chinese queries like "你好".
2. QueryAnalyzer: add Chinese synonym mappings for 13 common technical
terms (错误→bug, 优化→improve, 配置→config, etc.) so CJK queries
can find relevant English-keyword memories and vice versa.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover config defaults, 13 action types deserialization, serialization
roundtrip, credential management, and data type parsing. Also add
PartialEq derive to HandStatus for test assertions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>