feat: MetonaAI Desktop 初始项目

- Electron + React + TypeScript 架构
- 三栏布局: Sidebar | ChatPanel | DetailPanel
- 9 个内置工具 (文件系统/网络/记忆/命令)
- SQLite 持久化 (better-sqlite3)
- MUI 暗色/亮色主题系统
- Agent Loop ReAct 状态机引擎
- DeepSeek / Agnes AI / Ollama Provider 适配器
- MCP 协议集成
- 系统托盘 + 全局快捷键
- Tailwind CSS v4 + Tailwind Merge
- 修复: Sidebar 缺失 TextField 导入导致黑屏
This commit is contained in:
thzxx
2026-06-27 21:33:27 +08:00
commit 1d185db6b3
109 changed files with 39155 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
/**
* Metona IR — 统一导出
*
* 所有 Metona IR 类型的单入口导出。
* 项目内部只从此文件导入类型,确保类型一致性。
*/
// ===== 请求类型 =====
export type {
MetonaRequest,
MetonaRequestMeta,
MetonaSystemPrompt,
MetonaGenerationParams,
MetonaConstraints,
MetonaMessage,
MetonaImageContent,
MetonaToolDef,
MetonaToolParams,
MetonaParamField,
MetonaToolCall,
MetonaToolResult,
} from './metona-request';
export { MetonaToolCategory, MetonaRiskLevel } from './metona-request';
// ===== 响应类型 =====
export type {
MetonaResponse,
MetonaResponseMeta,
MetonaTokenUsage,
MetonaStreamEvent,
MetonaThinking,
MetonaError,
} from './metona-response';
export {
MetonaFinishReason,
MetonaStreamEventType,
MetonaErrorCode,
} from './metona-response';
// ===== 上下文与记忆 =====
export type { MetonaContext, MetonaMemoryItem } from './metona-context';
// ===== 适配器接口 =====
export type { IMetonaProviderAdapter, AdapterConfig } from './metona-adapter';
+72
View File
@@ -0,0 +1,72 @@
/**
* Metona IR — Provider Adapter 接口定义
*
* 所有 LLM Provider 适配器必须实现此接口。
*
* @see docs/MetonaAI-Desktop 内部API请求与响应标准.html
*/
import type { MetonaRequest, MetonaResponse, MetonaStreamEvent } from './index';
/**
* Provider 适配器配置
*/
export interface AdapterConfig {
/** Provider 标识(如 'deepseek'、'agnes'、'ollama' */
provider: string;
/** API 基础 URL */
baseURL: string;
/** API 密钥(Ollama 等本地 Provider 可为空) */
apiKey?: string;
/** 默认模型名 */
defaultModel: string;
/** 请求超时(ms */
timeoutMs?: number;
/** 自定义请求头 */
headers?: Record<string, string>;
}
/**
* Provider 适配器统一接口
*
* 职责:
* 1. 将 MetonaRequest 转换为目标 Provider 原生请求格式
* 2. 调用外部 API
* 3. 将原生响应转换为 MetonaResponse / MetonaStreamEvent
*
* 铁律:外部 API 原始类型不得穿透到此接口之外
*/
export interface IMetonaProviderAdapter {
/** Provider 标识 */
readonly provider: string;
/** 支持的模型列表 */
readonly supportedModels: string[];
/** 是否支持 Tool Calling */
readonly supportsToolCalling: boolean;
/** 是否支持 Thinking / Reasoning 模式 */
readonly supportsThinking: boolean;
/**
* 发送非流式请求
*/
chat(request: MetonaRequest): Promise<MetonaResponse>;
/**
* 发送流式请求
* @returns AsyncIterable 逐事件推送 MetonaStreamEvent
*/
chatStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent>;
/**
* 检查 Provider 可用性
*/
healthCheck(): Promise<boolean>;
/**
* 列出可用模型(可选)
*/
listModels?(): Promise<string[]>;
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Metona IR — 上下文与记忆类型定义
*
* @see docs/MetonaAI-Desktop 内部API请求与响应标准.html
*/
import type { MetonaMessage, MetonaSystemPrompt, MetonaToolDef } from './metona-request';
// ===== 上下文标准 =====
export interface MetonaContext {
/** 上下文唯一标识 */
id: string;
/** 关联的会话 */
sessionId: string;
/** System Prompt 分区 */
systemPrompt: MetonaSystemPrompt;
/** 会话历史(最近 N 轮) */
history: MetonaMessage[];
/** 检索到的相关记忆 */
relevantMemories: MetonaMemoryItem[];
/** 当前任务信息 */
currentTask: {
userInput: string;
iteration: number;
taskGoal?: string;
};
/** 可用工具列表 */
availableTools: MetonaToolDef[];
/** 预估 Token 数 */
estimatedTokens: number;
/** 上下文使用率(estimatedTokens / contextWindow */
usageRatio: number;
/** 是否需要压缩 */
needsCompression: boolean;
}
// ===== 记忆格式 =====
export interface MetonaMemoryItem {
id: string;
type: 'episodic' | 'semantic' | 'working';
/** 可被 LLM 阅读的记忆内容 */
content: string;
/** 精简摘要(上下文紧张时使用) */
summary?: string;
/** 来源 */
source: 'user_input' | 'tool_result' | 'agent_thought' | 'imported';
/** 重要程度 0-1 */
importance: number;
/** 检索相关性分数(仅在检索结果中出现) */
relevanceScore?: number;
sessionId?: string;
createdAt: number;
expiresAt?: number;
}
+195
View File
@@ -0,0 +1,195 @@
/**
* Metona IR — 请求标准类型定义
*
* 本文件是项目内所有 AI 交互请求的统一类型定义。
* 无论底层对接 DeepSeek、Ollama、Agnes 还是其他 LLM
* Agent Loop、UI、记忆系统均读写此标准格式。
*
* @see docs/MetonaAI-Desktop 内部API请求与响应标准.html
*/
// ===== 请求元信息 =====
export interface MetonaRequestMeta {
/** 会话 ID */
sessionId: string;
/** 当前 ReAct 迭代轮次(从 1 开始) */
iteration: number;
/** 本次请求的唯一 ID */
requestId: string;
/** Unix 毫秒时间戳 */
timestamp: number;
/** Agent 引擎版本 */
agentVersion: string;
}
// ===== System Prompt =====
export interface MetonaSystemPrompt {
/** 角色定义(静态区,利用 LLM 缓存) */
roleDefinition: string;
/** 输出格式约束 */
outputConstraints: string;
/** 安全准则 */
safetyGuidelines: string;
/** 动态注入的尾部提醒 */
dynamicReminders?: string;
}
// ===== 生成参数 =====
export interface MetonaGenerationParams {
/** 最大生成 token 数 */
maxTokens?: number;
/** 温度(默认 0.0Agent 需要确定性) */
temperature?: number;
/** 核采样 */
topP?: number;
/** 是否流式输出 */
stream?: boolean;
/** 停止序列 */
stopSequences?: string[];
/** 是否启用思考模式 */
thinkingEnabled?: boolean;
/** 思考强度(替代 thinkingBudget,各 Provider 映射见 Adapter 规范) */
thinkingEffort?: 'low' | 'medium' | 'high' | 'max';
}
// ===== 安全约束 =====
export interface MetonaConstraints {
/** 本迭代允许使用的工具白名单 */
allowedTools?: string[];
/** 单轮最大工具调用数 */
maxToolCalls?: number;
/** 本请求整体超时 */
timeoutMs?: number;
}
// ===== 消息格式 =====
export interface MetonaMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
/** 文本内容(纯文本或 Markdown */
content: string;
/** (仅 assistant)思考/推理内容 */
reasoningContent?: string;
/** (仅 assistant)工具调用请求 */
toolCalls?: MetonaToolCall[];
/** (仅 tool)工具执行结果 */
toolResult?: MetonaToolResult;
/** 时间戳 */
timestamp: number;
/** 所属迭代轮次 */
iteration?: number;
/** 图片内容(可选,用于多模态) */
images?: MetonaImageContent[];
}
export interface MetonaImageContent {
/** 图片公网 URL 或 base64 data URI */
url: string;
detail?: 'low' | 'high' | 'auto';
}
// ===== 工具定义 =====
export interface MetonaToolDef {
/** 工具唯一名称 */
name: string;
/** 功能描述(供 LLM 阅读) */
description: string;
/** 参数 JSON Schema */
parameters: MetonaToolParams;
/** 分类 */
category: MetonaToolCategory;
/** 风险等级 */
riskLevel: MetonaRiskLevel;
/** 是否需要用户授权 */
requiresPermission: boolean;
/** 超时时间 */
timeoutMs: number;
}
export interface MetonaToolParams {
type: 'object';
properties: Record<string, MetonaParamField>;
required?: string[];
}
export interface MetonaParamField {
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
description: string;
enum?: string[];
items?: MetonaParamField;
}
export enum MetonaToolCategory {
FILESYSTEM = 'filesystem',
SEARCH = 'search',
CALCULATION = 'calculation',
CODE_EXECUTION = 'code_execution',
NETWORK = 'network',
DATABASE = 'database',
MCP = 'mcp',
CUSTOM = 'custom',
}
export enum MetonaRiskLevel {
SAFE = 'safe',
LOW = 'low',
MEDIUM = 'medium',
HIGH = 'high',
CRITICAL = 'critical',
}
// ===== 工具调用 =====
export interface MetonaToolCall {
/** 工具调用唯一 ID */
id: string;
/** 工具名称 */
name: string;
/** 调用参数 */
args: Record<string, unknown>;
/** 所属 ReAct 迭代 */
iteration: number;
timestamp: number;
}
export interface MetonaToolResult {
toolCallId: string;
toolName: string;
/** 工具原始返回值 */
result: unknown;
/** 人工可读摘要(用于 LLM 上下文注入) */
summary?: string;
success: boolean;
error?: string;
durationMs: number;
timestamp: number;
}
// ===== 完整请求 =====
export interface MetonaRequest {
/** 请求元信息 */
meta: MetonaRequestMeta;
/** System Prompt(行为宪法) */
systemPrompt: MetonaSystemPrompt;
/** 消息列表(含历史 + 当前用户输入 + 工具结果) */
messages: MetonaMessage[];
/** 本轮可用的工具定义列表 */
tools?: MetonaToolDef[];
/** 生成参数 */
params: MetonaGenerationParams;
/** 安全约束 */
constraints?: MetonaConstraints;
}
+187
View File
@@ -0,0 +1,187 @@
/**
* Metona IR — 响应标准类型定义
*
* @see docs/MetonaAI-Desktop 内部API请求与响应标准.html
*/
import type { MetonaToolCall, MetonaToolResult } from './metona-request';
// ===== 响应元信息 =====
export interface MetonaResponseMeta {
/** 对应的请求 ID */
requestId: string;
/** Provider 标识(如 'deepseek' */
provider: string;
/** 实际使用的模型名称 */
model: string;
/** 端到端延迟 */
latencyMs: number;
/** 响应时间戳 */
timestamp: number;
/** Provider 原生性能统计(可选,主要用于 Ollama) */
perfStats?: {
/** 模型加载耗时 */
loadDurationMs?: number;
/** Prompt 评估耗时 */
promptEvalDurationMs?: number;
/** 生成耗时 */
evalDurationMs?: number;
/** 生成速率 */
tokensPerSecond?: number;
};
}
// ===== Token 使用统计 =====
export interface MetonaTokenUsage {
inputTokens: number;
outputTokens: number;
totalTokens: number;
/** Thinking 模式专用 */
reasoningTokens?: number;
cacheHitTokens?: number;
cacheMissTokens?: number;
}
// ===== 停止原因 =====
export enum MetonaFinishReason {
STOP = 'stop',
LENGTH = 'length',
TOOL_CALLS = 'tool_calls',
CONTENT_FILTER = 'content_filter',
ERROR = 'error',
}
// ===== 完整响应 =====
export interface MetonaResponse {
/** 响应元信息 */
meta: MetonaResponseMeta;
/** 模型输出(完整文本) */
content: string;
/** 思考/推理内容(Thinking 模式) */
reasoningContent?: string;
/** 结构化输出(如果模型原生支持 JSON Schema */
structuredOutput?: unknown;
/** 工具调用请求列表 */
toolCalls?: MetonaToolCall[];
/** Token 使用统计 */
usage: MetonaTokenUsage;
/** 停止原因 */
finishReason: MetonaFinishReason;
/** 错误信息(如果出错) */
error?: MetonaError;
}
// ===== 流式事件 =====
export enum MetonaStreamEventType {
TEXT_DELTA = 'text_delta',
REASONING_DELTA = 'reasoning_delta',
TOOL_CALL_DELTA = 'tool_call_delta',
TOOL_CALL_COMPLETE = 'tool_call_complete',
THINKING_START = 'thinking_start',
THINKING_END = 'thinking_end',
ERROR = 'error',
DONE = 'done',
USAGE = 'usage',
}
export interface MetonaStreamEvent {
type: MetonaStreamEventType;
requestId: string;
sessionId: string;
iteration: number;
/** 序列号 */
seq: number;
timestamp: number;
/** 根据 type 使用不同字段 */
/** TEXT_DELTA / REASONING_DELTA */
delta?: string;
/** TOOL_CALL_DELTA */
toolCallDelta?: {
/** 工具调用索引(同一轮可能有多个) */
index: number;
/** 工具名称片段(首个事件携带) */
name?: string;
/** 参数 JSON 增量片段 */
argsDelta?: string;
};
/** TOOL_CALL_COMPLETE(拼接完成后的完整调用) */
toolCall?: MetonaToolCall;
/** USAGE */
usage?: MetonaTokenUsage;
/** ERROR */
error?: MetonaError;
}
// ===== 思考内容 =====
export interface MetonaThinking {
/** 思考内容文本 */
content: string;
/** 思考状态 */
status: 'thinking' | 'complete';
/** 思考耗时 (ms) */
durationMs: number;
/** 思考消耗的 token 数 */
tokensUsed: number;
}
// ===== 错误格式 =====
export interface MetonaError {
/** 错误码 */
code: MetonaErrorCode;
/** 人类可读的错误描述 */
message: string;
/** 出错的 Provider */
provider?: string;
/** Provider 原始错误码 */
providerCode?: string;
/** 是否可重试 */
retryable: boolean;
/** 建议重试等待时间 */
retryAfterMs?: number;
}
export enum MetonaErrorCode {
// 网络层
NETWORK_TIMEOUT = 'network_timeout',
NETWORK_ERROR = 'network_error',
// 认证层
AUTH_INVALID = 'auth_invalid',
AUTH_EXPIRED = 'auth_expired',
// 频率限制
RATE_LIMITED = 'rate_limited',
QUOTA_EXCEEDED = 'quota_exceeded',
// 模型层
MODEL_OVERLOADED = 'model_overloaded',
MODEL_NOT_FOUND = 'model_not_found',
CONTEXT_LENGTH_EXCEEDED = 'context_length_exceeded',
OUTPUT_LENGTH_EXCEEDED = 'output_length_exceeded',
// 内容层
CONTENT_FILTERED = 'content_filtered',
// 解析层
PARSE_ERROR = 'parse_error',
INVALID_RESPONSE = 'invalid_response',
// Agent 层
MAX_ITERATIONS = 'max_iterations',
USER_ABORTED = 'user_aborted',
TIMEOUT = 'timeout',
UNKNOWN = 'unknown',
}
+53
View File
@@ -0,0 +1,53 @@
/**
* Metona IR — 工具相关类型定义
*
* 独立于 metona-request.ts 中的工具定义,用于 Tool Registry 内部。
*
* @see docs/MetonaAI-Desktop 架构与交互设计.html
*/
import type { MetonaToolDef, MetonaToolCall, MetonaToolResult } from './metona-request';
/**
* 工具执行上下文(传递给工具的 execute 方法)
*/
export interface ToolExecutionContext {
/** 当前会话 ID */
sessionId: string;
/** 当前工作空间根路径 */
workspacePath: string;
/** 当前迭代轮次 */
iteration: number;
/** 请求 ID */
requestId: string;
}
/**
* 工具基类接口
* 所有内置工具和 MCP 工具都实现此接口
*/
export interface IMetonaTool {
/** 工具定义(供 LLM 阅读) */
readonly definition: MetonaToolDef;
/**
* 执行工具
* @param args 工具参数
* @param context 执行上下文
* @returns 工具执行结果
*/
execute(args: Record<string, unknown>, context: ToolExecutionContext): Promise<unknown>;
}
/**
* 工具注册表中存储的工具记录
*/
export interface ToolRegistryEntry {
tool: IMetonaTool;
/** 来源:'builtin' | 'mcp' */
source: 'builtin' | 'mcp';
/** MCP Server 名称(仅 mcp 来源) */
serverName?: string;
/** 是否已启用 */
enabled: boolean;
}