refactor: 移除前端 Mock API,新增共享组件,优化认证流程
- 删除 mock_api_client、mock_calendar_service、mock_history_service - 新增 fixed_length_code_input、link_button、message_composer 共享组件 - 优化登录/注册/密码重置页面使用新组件 - 简化 injection.dart 移除 mock 分支 - 更新 env.dart 配置(BACKEND_URL 替换 API_URL) - 后端 agentscope 工具和测试更新 - 重构 AGENTS.md 文档结构 - 新增 deploy/ 目录和 protocol 文档
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# Auth Routes Protocol Notes
|
||||
|
||||
## POST `/api/v1/auth/verifications`
|
||||
|
||||
- `invite_code` is optional.
|
||||
- Recommended format is fixed `4` chars and pattern `^[ABCDEFGHJKMNPQRSTUVWXYZ23456789]{4}$`.
|
||||
- Backend normalizes invite codes to uppercase and validates in service logic.
|
||||
- Invalid invite code values are ignored (treated as empty), and signup verification email flow still continues.
|
||||
|
||||
## Verification Token Input Convention
|
||||
|
||||
- Verification token for signup/recovery uses fixed `6` digits.
|
||||
- Client UI should use fixed-length segmented input to reduce mistyped values.
|
||||
@@ -0,0 +1,466 @@
|
||||
# UI Schema Protocol
|
||||
|
||||
> **NOTE**: This document is the single source of truth. All implementations must follow this specification.
|
||||
|
||||
## Overview
|
||||
|
||||
A generic UI schema for rendering tool/agent execution results. Designed for AI Agent / Tool ecosystem with extensibility.
|
||||
|
||||
## Version
|
||||
|
||||
- **Current**: `1.0`
|
||||
- **Status**: Frozen (no new node types)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ UiSchemaDocument (root) │
|
||||
│ - version / schemaType / status / docId │
|
||||
│ - meta (protocol-level metadata) │
|
||||
│ - nodes (array of UiNode) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Field Layers: │
|
||||
│ 1. Public fields (all renderers must handle) │
|
||||
│ id / type / title / description / icon / status / │
|
||||
│ timestamp / actions │
|
||||
│ 2. meta (protocol-level, not rendered) │
|
||||
│ requestId / toolId / traceId / userId │
|
||||
│ 3. extensions (tool私有扩展, renderer透传) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Types
|
||||
|
||||
### SchemaType
|
||||
|
||||
```typescript
|
||||
type SchemaType = 'tool_result' | 'agent_response' | 'notification';
|
||||
```
|
||||
|
||||
### UiStatus
|
||||
|
||||
```typescript
|
||||
type UiStatus = 'info' | 'success' | 'warning' | 'error' | 'pending';
|
||||
```
|
||||
|
||||
### IconSource
|
||||
|
||||
```typescript
|
||||
type IconSource = 'icon' | 'emoji' | 'url';
|
||||
```
|
||||
|
||||
### ActionType
|
||||
|
||||
```typescript
|
||||
type ActionType = 'navigation' | 'url' | 'event' | 'tool' | 'copy' | 'payload';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Root Structure
|
||||
|
||||
```typescript
|
||||
interface UiSchemaDocument {
|
||||
// Protocol identifier
|
||||
version: string; // "1.0"
|
||||
schemaType: SchemaType; // tool_result | agent_response | notification
|
||||
|
||||
// Document metadata
|
||||
docId?: string; // For local refresh / diff / analytics
|
||||
timestamp?: string; // ISO 8601
|
||||
locale?: string; // "zh-CN"
|
||||
|
||||
// Unified status
|
||||
status: UiStatus;
|
||||
|
||||
// Render control
|
||||
renderer?: {
|
||||
renderer?: string; // Dedicated renderer name
|
||||
theme?: 'default' | 'dark' | 'light';
|
||||
};
|
||||
|
||||
// Protocol-level metadata (not rendered)
|
||||
meta?: {
|
||||
requestId?: string;
|
||||
toolId?: string;
|
||||
traceId?: string;
|
||||
userId?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
// Root nodes
|
||||
nodes: UiNode[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Node Types (v1 Whitelist)
|
||||
|
||||
```
|
||||
✅ Supported in v1:
|
||||
- card 卡片
|
||||
- list 列表
|
||||
- table 表格
|
||||
- text 文本/Markdown
|
||||
- kv 键值对
|
||||
- operation 操作结果
|
||||
- error 错误提示
|
||||
- container 容器
|
||||
|
||||
❌ Not supported in v1 (reserved for v2):
|
||||
- chart / metric / image / video / tabs / accordion / form
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Node Fields
|
||||
|
||||
All nodes share these fields:
|
||||
|
||||
```typescript
|
||||
interface UiBaseNode {
|
||||
id?: string; // For local refresh / action targeting
|
||||
type: string; // Node type identifier
|
||||
}
|
||||
|
||||
interface UiTitledNode extends UiBaseNode {
|
||||
title?: string;
|
||||
description?: string;
|
||||
icon?: UiIcon;
|
||||
status?: UiStatus;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
interface UiActionableNode extends UiTitledNode {
|
||||
actions?: UiAction[];
|
||||
}
|
||||
|
||||
interface UiExtendableNode extends UiActionableNode {
|
||||
extensions?: Record<string, any>; // Tool私有扩展, 通用renderer透传
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Node Definitions
|
||||
|
||||
### 1. Card Node
|
||||
|
||||
```typescript
|
||||
interface UiCardNode extends UiExtendableNode {
|
||||
type: 'card';
|
||||
children?: UiNode[]; // Nested nodes
|
||||
footer?: UiTextNode;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. List Node
|
||||
|
||||
```typescript
|
||||
interface UiListNode extends UiExtendableNode {
|
||||
type: 'list';
|
||||
items: ListItem[];
|
||||
pagination?: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
};
|
||||
emptyText?: string;
|
||||
}
|
||||
|
||||
interface ListItem {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
description?: string;
|
||||
icon?: UiIcon;
|
||||
badge?: { label: string; variant?: 'default' | 'success' | 'warning' | 'error' | 'info' };
|
||||
metadata?: Record<string, any>;
|
||||
actions?: UiAction[];
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Table Node
|
||||
|
||||
```typescript
|
||||
interface UiTableNode extends UiExtendableNode {
|
||||
type: 'table';
|
||||
columns: TableColumn[];
|
||||
rows: TableRow[];
|
||||
pagination?: Pagination;
|
||||
}
|
||||
|
||||
interface TableColumn {
|
||||
key: string;
|
||||
label: string;
|
||||
width?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}
|
||||
|
||||
interface TableRow {
|
||||
id: string;
|
||||
cells: Record<string, any>;
|
||||
metadata?: Record<string, any>;
|
||||
actions?: UiAction[];
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Text Node
|
||||
|
||||
```typescript
|
||||
interface UiTextNode extends UiBaseNode {
|
||||
type: 'text';
|
||||
content: string;
|
||||
format?: 'plain' | 'markdown';
|
||||
icon?: UiIcon;
|
||||
actions?: UiAction[];
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Key-Value Node
|
||||
|
||||
```typescript
|
||||
interface UiKvNode extends UiExtendableNode {
|
||||
type: 'kv';
|
||||
pairs: KeyValuePair[];
|
||||
layout?: 'vertical' | 'horizontal' | 'grid';
|
||||
}
|
||||
|
||||
interface KeyValuePair {
|
||||
key: string;
|
||||
label?: string;
|
||||
value: string | number | boolean;
|
||||
copyable?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Operation Node
|
||||
|
||||
```typescript
|
||||
interface UiOperationNode extends UiExtendableNode {
|
||||
type: 'operation';
|
||||
operation: 'create' | 'update' | 'delete' | 'execute';
|
||||
result: 'success' | 'failure' | 'partial';
|
||||
message?: string;
|
||||
affectedCount?: number;
|
||||
details?: UiNode;
|
||||
rollback?: UiAction;
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Error Node
|
||||
|
||||
```typescript
|
||||
interface UiErrorNode extends UiBaseNode {
|
||||
type: 'error';
|
||||
title?: string;
|
||||
icon?: UiIcon;
|
||||
errorCode: string;
|
||||
message: string;
|
||||
details?: string;
|
||||
stack?: string;
|
||||
retryable: boolean;
|
||||
suggestions?: string[];
|
||||
retry?: UiAction;
|
||||
support?: UiAction;
|
||||
actions?: UiAction[];
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Container Node
|
||||
|
||||
```typescript
|
||||
interface UiContainerNode extends UiBaseNode {
|
||||
type: 'container';
|
||||
direction: 'vertical' | 'horizontal';
|
||||
gap?: number;
|
||||
children: UiNode[];
|
||||
actions?: UiAction[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Action Structure
|
||||
|
||||
```typescript
|
||||
interface UiAction {
|
||||
id: string;
|
||||
label: string;
|
||||
icon?: UiIcon;
|
||||
style?: 'primary' | 'secondary' | 'ghost' | 'danger';
|
||||
disabled?: boolean;
|
||||
action: ActionSpec;
|
||||
confirm?: {
|
||||
title?: string;
|
||||
message?: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type ActionSpec =
|
||||
| { type: 'navigation'; path: string; params?: Record<string, any> }
|
||||
| { type: 'url'; url: string; target?: '_self' | '_blank' }
|
||||
| { type: 'event'; event: string; payload?: Record<string, any> }
|
||||
| { type: 'tool'; toolId: string; params?: Record<string, any> }
|
||||
| { type: 'copy'; content: string; successMessage?: string }
|
||||
| { type: 'payload'; payload: Record<string, any>; submitTo?: string };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Icon Structure
|
||||
|
||||
```typescript
|
||||
interface UiIcon {
|
||||
source: IconSource; // 'icon' | 'emoji' | 'url'
|
||||
value: string; // icon name / emoji / URL
|
||||
color?: string; // "#FF0000"
|
||||
size?: number; // pixels
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Rules
|
||||
|
||||
### operation vs error
|
||||
|
||||
| Scenario | Node Type |
|
||||
|----------|-----------|
|
||||
| Tool execution failed, system exception | `UiErrorNode` |
|
||||
| Business operation result (CRUD) | `UiOperationNode` |
|
||||
| Network error / permission denied | `UiErrorNode` |
|
||||
|
||||
### extensions Usage Constraints
|
||||
|
||||
1. ❌ NO: Any rendering-related style / text / layout
|
||||
2. ❌ NO: Any fields other renderers need to read
|
||||
3. ✅ YES: Business identifiers (eventId, orderId)
|
||||
4. ✅ YES: Dedicated renderer private config
|
||||
5. ✅ YES: Analytics / logging context data
|
||||
6. ✅ YES: Data that generic renderer doesn't care about
|
||||
|
||||
---
|
||||
|
||||
## JSON Examples
|
||||
|
||||
### Example 1: Success Card
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"schemaType": "tool_result",
|
||||
"docId": "doc_evt_001",
|
||||
"timestamp": "2026-03-12T10:30:00Z",
|
||||
"status": "success",
|
||||
"renderer": { "renderer": "calendar" },
|
||||
"meta": { "requestId": "req_abc", "toolId": "calendar.create_event" },
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node_card_1",
|
||||
"type": "card",
|
||||
"title": "日程已创建",
|
||||
"description": "会议日程创建成功",
|
||||
"icon": { "source": "icon", "value": "event_available" },
|
||||
"extensions": { "eventId": "evt_abc123", "color": "#3B82F6" },
|
||||
"children": [
|
||||
{
|
||||
"type": "kv",
|
||||
"pairs": [
|
||||
{ "key": "title", "label": "主题", "value": "Q1 规划会议" },
|
||||
{ "key": "time", "label": "时间", "value": "2026-03-15 14:00" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"id": "action_view",
|
||||
"label": "查看详情",
|
||||
"style": "primary",
|
||||
"action": { "type": "navigation", "path": "/calendar/evt_abc123" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: List Result
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"schemaType": "tool_result",
|
||||
"status": "success",
|
||||
"meta": { "toolId": "search.documents" },
|
||||
"nodes": [
|
||||
{ "type": "text", "content": "找到 **3** 个相关文档" },
|
||||
{
|
||||
"id": "node_list_1",
|
||||
"type": "list",
|
||||
"items": [
|
||||
{
|
||||
"id": "item_1",
|
||||
"title": "API 设计规范",
|
||||
"subtitle": "v2.1",
|
||||
"icon": { "source": "emoji", "value": "📄" },
|
||||
"actions": [
|
||||
{ "id": "a1", "label": "查看", "action": { "type": "url", "url": "/docs/api" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagination": { "page": 1, "pageSize": 10, "total": 3, "hasMore": false }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example 3: Error Result
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"schemaType": "tool_result",
|
||||
"status": "error",
|
||||
"meta": { "toolId": "user.delete" },
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node_error_1",
|
||||
"type": "error",
|
||||
"title": "删除用户失败",
|
||||
"icon": { "source": "icon", "value": "error_outline" },
|
||||
"errorCode": "PERMISSION_DENIED",
|
||||
"message": "您没有权限执行此操作",
|
||||
"details": "需要管理员权限",
|
||||
"retryable": true,
|
||||
"suggestions": ["联系管理员", "联系技术支持"],
|
||||
"retry": {
|
||||
"id": "action_retry",
|
||||
"label": "重试",
|
||||
"style": "primary",
|
||||
"action": { "type": "tool", "toolId": "user.delete", "params": { "userId": "u1" } }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Evolution (v2)
|
||||
|
||||
Reserved for future:
|
||||
|
||||
- Nested containers: `tabs`, `accordion`, `carousel`
|
||||
- Data visualization: `chart`, `metric`, `progress`
|
||||
- Rich media: `image`, `video`, `audio`, `file`
|
||||
- Internationalization: `i18nKey` field
|
||||
- Version migration: `deprecated` flag
|
||||
- Offline support: `offline` flag
|
||||
Reference in New Issue
Block a user