@hchuanz/pocket-core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants/pocket.ts","../src/adapters/loggers.ts","../src/adapters/node-json-storage.ts","../src/adapters/plain-text-secure-storage.ts","../src/adapters/node-json-config-store.ts","../src/adapters/memory-config-store.ts","../src/adapters/node-json-flip-source.ts","../src/adapters/memory-flip-source.ts","../src/adapters/openai-compatible-client.ts","../src/adapters/callback-stream-sink.ts","../src/tools/tool-registry.ts","../src/types/mirror.ts","../src/mirror/mirror-store.ts","../src/mirror/mirror-index.ts","../src/mirror/mirror-memory.ts","../src/mirror/group-chat/store.ts","../src/flip/flip-cache-service.ts","../src/pocket/client.ts","../src/mirror/mirror-constants.ts","../src/mirror/mirror-builder.ts","../src/mirror/mirror-chat.ts","../src/mirror/mirror-growth.ts","../src/tools/platform-tools.ts","../src/tools/mirror-tools.ts","../src/pocket/user.ts","../src/pocket/flip.ts","../src/pocket/member.ts","../src/pocket/room.ts","../src/tools/pocket-tools.ts","../src/pocket/im.ts","../src/llm/agent-loop.ts","../src/context.ts","../src/llm/llm-constants.ts","../src/mirror/group-chat/prompt.ts","../src/mirror/group-chat/engine.ts"],"sourcesContent":["// 类型(单一来源)\nexport * from './types'\n\n// 常量\nexport * from './constants/pocket'\n\n// 端口(接口,只定义不实现)\nexport * from './ports'\n\n// Node 参考适配器\nexport * from './adapters'\n\n// 组合根 + 高层 API\nexport * from './context'\n\n// 业务模块(按需导出)\nexport * from './llm/llm-constants'\nexport * from './mirror/mirror-constants'\nexport * from './mirror/mirror-store'\nexport * from './mirror/mirror-index'\nexport * from './mirror/mirror-memory'\nexport * from './mirror/mirror-builder'\nexport * from './mirror/mirror-chat'\nexport * from './mirror/mirror-growth'\nexport * from './mirror/group-chat/prompt'\nexport * from './mirror/group-chat/store'\nexport * from './mirror/group-chat/engine'\nexport * from './tools/tool-registry'\nexport * from './tools/platform-tools'\nexport * from './tools/mirror-tools'\nexport * from './tools/pocket-tools'\nexport * from './pocket/client'\nexport * from './pocket/user'\nexport * from './pocket/flip'\nexport * from './pocket/im'\nexport * from './pocket/member'\nexport * from './pocket/room'\nexport * from './flip/flip-cache-service'\nexport * from './llm/agent-loop'","/**\n * Pocket48 客户端常量\n *\n * 来自反编译的 Android APK(com.pocket.snh48),\n * 这些是客户端签名常量,不是用户密码或 API Key。\n *\n * ⚠ 安全提示:真实 token / apiKey / 用户密码不得写入此文件。\n * apiKey 仅由 ISecureStorage 管理,token 由 IConfigStore 管理。\n */\n\n/** 所有业务接口前缀 */\nexport const POCKET_API_BASE = 'https://pocketapi.48.cn'\n\n/** 头像/图片相对路径必须拼此前缀(如 /content/images/xxx.png → https://source.48.cn/content/images/xxx.png) */\nexport const AVA_BASE_URL = 'https://source.48.cn'\n\n/** 客户端描述 JSON(deviceId 为固定值,非用户设备) */\nexport const POCKET_APP_INFO = JSON.stringify({\n vendor: 'apple',\n deviceId: '2A01B81B-653A-43EC-9C58-95CB418A58AC',\n appVersion: '7.1.37',\n appBuild: '26020801',\n osVersion: '26.4.0',\n osType: 'ios',\n deviceName: 'iPhone17,1',\n os: 'ios',\n})\n\n/** 客户端签名 pa 头(静态值,非密码学签名) */\nexport const POCKET_PA =\n 'MTc4MjM3NDEwNjAwMCw3NzE1LEIwODM5QzdEMjZFQzJFOTFERTExNDVERDU2NTlBRjk5LA=='\n\n/** User-Agent */\nexport const POCKET_USER_AGENT =\n 'PocketFans201807/7.1.37 (iPhone; iOS 26.4; Scale/3.00)'\n\n/** token 本地过期时间 */\nexport const SESSION_EXPIRE_MS = 30 * 24 * 60 * 60 * 1000","import type { ILogger } from '../ports/logger'\n\n/** 直打 console,适用本地/CLI/MCP 场景 */\nexport class ConsoleLogger implements ILogger {\n debug(...args: unknown[]): void {\n console.debug(...args)\n }\n info(...args: unknown[]): void {\n console.info(...args)\n }\n warn(...args: unknown[]): void {\n console.warn(...args)\n }\n error(...args: unknown[]): void {\n console.error(...args)\n }\n}\n\n/** 空实现,测试或静默场景 */\nexport class NoopLogger implements ILogger {\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n}","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { IStorage } from '../ports/storage'\n\n/**\n * Node JSON 文件存储。\n *\n * 对齐原 hchuanz-pocket-helper 的 `mirrorStore` / `flipCache` 等 fs 读写习惯:\n * JSON 缩进 2 空格、不存在返回 null、读取失败返回 null。\n */\nexport class NodeJsonFileStorage implements IStorage {\n readonly root: string\n\n constructor(root: string) {\n this.root = root\n }\n\n private resolve(subpath: string): string {\n return path.join(this.root, subpath)\n }\n\n readJson<T = unknown>(subpath: string): T | null {\n const fp = this.resolve(subpath)\n if (!fs.existsSync(fp)) return null\n try {\n return JSON.parse(fs.readFileSync(fp, 'utf-8')) as T\n } catch {\n return null\n }\n }\n\n writeJson(subpath: string, data: unknown): void {\n const fp = this.resolve(subpath)\n fs.mkdirSync(path.dirname(fp), { recursive: true })\n fs.writeFileSync(fp, JSON.stringify(data, null, 2), 'utf-8')\n }\n\n exists(subpath: string): boolean {\n return fs.existsSync(this.resolve(subpath))\n }\n\n delete(subpath: string): void {\n const fp = this.resolve(subpath)\n if (fs.existsSync(fp)) fs.unlinkSync(fp)\n }\n\n list(dirSubpath: string): string[] {\n const dir = this.resolve(dirSubpath)\n if (!fs.existsSync(dir)) return []\n return fs.readdirSync(dir)\n }\n\n mkdir(dirSubpath: string): void {\n fs.mkdirSync(this.resolve(dirSubpath), { recursive: true })\n }\n}","import type { ISecureStorage } from '../ports/secure-storage'\n\n/**\n * 明文安全存储(dev/test 参考实现)。\n *\n * ⚠ 仅 base64 编码,**不是真加密**,禁止用于生产环境敏感数据。\n * 生产用 Electron 的 safeStorage 实现(留在原仓库)。\n */\nexport class PlainTextSecureStorage implements ISecureStorage {\n isAvailable(): boolean {\n return true\n }\n\n encryptString(plain: string): string {\n return Buffer.from(plain, 'utf-8').toString('base64')\n }\n\n decryptString(encoded: string): string {\n return Buffer.from(encoded, 'base64').toString('utf-8')\n }\n}","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { IConfigStore } from '../ports/config-store'\n\n/**\n * Node JSON KV 配置存储。\n *\n * 对齐原 hchuanz-pocket-helper 的 `storeService`:持久化到单个 `app-store.json`,\n * get 不存在返回 undefined。\n */\nexport class NodeJsonConfigStore implements IConfigStore {\n private readonly filePath: string\n private data: Record<string, unknown> = {}\n private loaded = false\n\n constructor(root: string, filename = 'app-store.json') {\n this.filePath = path.join(root, filename)\n }\n\n private ensureLoaded(): void {\n if (this.loaded) return\n this.loaded = true\n try {\n if (fs.existsSync(this.filePath)) {\n this.data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8')) as Record<string, unknown>\n }\n } catch {\n this.data = {}\n }\n }\n\n private save(): void {\n fs.mkdirSync(path.dirname(this.filePath), { recursive: true })\n fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf-8')\n }\n\n get<T = unknown>(key: string): T | undefined {\n this.ensureLoaded()\n return this.data[key] as T | undefined\n }\n\n set(key: string, value: unknown): void {\n this.ensureLoaded()\n if (value === undefined || value === null) {\n delete this.data[key]\n } else {\n this.data[key] = value\n }\n this.save()\n }\n\n remove(key: string): void {\n this.ensureLoaded()\n delete this.data[key]\n this.save()\n }\n\n has(key: string): boolean {\n this.ensureLoaded()\n return key in this.data\n }\n\n clear(): void {\n this.data = {}\n this.save()\n }\n}","import type { IConfigStore } from '../ports/config-store'\n\n/** 纯内存 KV 存储,给 MCP 无磁盘场景 / 测试 */\nexport class MemoryConfigStore implements IConfigStore {\n private data = new Map<string, unknown>()\n\n get<T = unknown>(key: string): T | undefined {\n return this.data.get(key) as T | undefined\n }\n\n set(key: string, value: unknown): void {\n this.data.set(key, value)\n }\n\n remove(key: string): void {\n this.data.delete(key)\n }\n\n has(key: string): boolean {\n return this.data.has(key)\n }\n\n clear(): void {\n this.data.clear()\n }\n}","import path from 'node:path'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { FlipUserCache } from '../types/pocket'\nimport type { IStorage } from '../ports/storage'\n\nconst CACHE_DIR = 'flip-cache'\n\n/** 兼容旧存储的读取器(allUserData/groupedDataById/dashboardInfo 迁移),可空 */\nexport type LegacyFlipReader = () => {\n allUserData?: Record<string, unknown[]>\n groupedDataById?: Record<string, unknown>\n dashboardInfo?: FlipUserCache['dashboardInfo']\n} | null\n\n/**\n * Node JSON 翻牌数据源。\n *\n * 对齐原 hchuanz-pocket-helper 的 `flipCacheService`:\n * `flip-cache/{userId}.json`,构造可注入 legacy 读取器做旧数据迁移。\n */\nexport class NodeJsonFlipDataSource implements IFlipDataSource {\n private readonly storage: IStorage\n private readonly readLegacy?: LegacyFlipReader\n\n constructor(storage: IStorage, readLegacy?: LegacyFlipReader) {\n this.storage = storage\n this.readLegacy = readLegacy\n }\n\n private filePath(userId: string): string {\n return path.join(CACHE_DIR, `${userId}.json`)\n }\n\n get(userId: string): FlipUserCache | null {\n if (!userId) return null\n const cached = this.storage.readJson<FlipUserCache>(this.filePath(userId))\n if (cached) return cached\n return this.migrateFromLegacy(userId)\n }\n\n set(cache: FlipUserCache): void {\n this.storage.writeJson(this.filePath(cache.userId), cache)\n }\n\n clear(userId: string): void {\n this.storage.delete(this.filePath(userId))\n }\n\n private migrateFromLegacy(userId: string): FlipUserCache | null {\n if (!this.readLegacy) return null\n const legacy = this.readLegacy()\n const records = legacy?.allUserData?.[userId]\n if (!Array.isArray(records) || records.length === 0) return null\n const cache: FlipUserCache = {\n userId,\n records: records as FlipUserCache['records'],\n groupedDataById: (legacy?.groupedDataById as FlipUserCache['groupedDataById']) || {},\n dashboardInfo: legacy?.dashboardInfo || {\n totalFlipCount: records.length,\n runningCount: 0,\n returnedCount: 0,\n costTotal: 0,\n },\n meta: { syncedAt: Date.now(), recordCount: records.length },\n }\n this.set(cache)\n return cache\n }\n}","import type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { FlipUserCache } from '../types/pocket'\n\n/** 纯内存翻牌数据源,给 MCP 无磁盘场景 / 测试 */\nexport class MemoryFlipDataSource implements IFlipDataSource {\n private data = new Map<string, FlipUserCache>()\n\n get(userId: string): FlipUserCache | null {\n return this.data.get(userId) ?? null\n }\n\n set(cache: FlipUserCache): void {\n this.data.set(cache.userId, cache)\n }\n\n clear(userId: string): void {\n this.data.delete(userId)\n }\n}","import type { ILlmClient, ChatCompletionOptions, ChatCompletionResult } from '../ports/llm-client'\nimport type { ILogger } from '../ports/logger'\nimport type { ChatCompletionChunk, ChatMessage, ModelConfig, ToolDefinition } from '../types/llm'\n\n/**\n * OpenAI 兼容 LLM 客户端。\n *\n * 对齐原 hchuanz-pocket-helper 的 `common/providers.ts`:\n * chatCompletion / chatCompletionStream / embed / testConnection,\n * 全部走原生 fetch,零依赖。\n */\nexport class OpenAICompatibleClient implements ILlmClient {\n private readonly logger: ILogger\n\n constructor(logger: ILogger) {\n this.logger = logger\n }\n\n async chatCompletion(\n config: ModelConfig,\n messages: ChatMessage[],\n tools?: ToolDefinition[],\n options?: ChatCompletionOptions,\n ): Promise<ChatCompletionResult> {\n const url = `${config.baseUrl}/chat/completions`\n const body: Record<string, unknown> = {\n model: options?.model || config.chatModel,\n messages,\n stream: false,\n }\n if (tools?.length) {\n body.tools = tools\n body.tool_choice = 'auto'\n }\n if (options?.maxTokens) body.max_tokens = options.maxTokens\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options?.timeoutMs ?? 120000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`LLM ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const json = await resp.json() as {\n choices?: Array<{\n message?: { content?: string; tool_calls?: ChatMessage['tool_calls'] }\n }>\n }\n const choice = json.choices?.[0]\n return {\n content: choice?.message?.content ?? '',\n toolCalls: choice?.message?.tool_calls,\n }\n }\n\n async chatCompletionStream(\n config: ModelConfig,\n messages: ChatMessage[],\n onChunk: (chunk: ChatCompletionChunk) => void,\n tools?: ToolDefinition[],\n options?: ChatCompletionOptions,\n ): Promise<ChatMessage['tool_calls']> {\n const url = `${config.baseUrl}/chat/completions`\n const body: Record<string, unknown> = {\n model: options?.model || config.chatModel,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n }\n if (tools?.length) {\n body.tools = tools\n body.tool_choice = 'auto'\n }\n if (options?.maxTokens) body.max_tokens = options.maxTokens\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options?.timeoutMs ?? 120000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`LLM stream ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const reader = resp.body?.getReader()\n if (!reader) throw new Error('No response body')\n\n const decoder = new TextDecoder()\n let buffer = ''\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>()\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') break\n\n try {\n const json = JSON.parse(data) as {\n choices?: Array<{\n delta?: {\n content?: string\n tool_calls?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n }\n }>\n }\n const delta = json.choices?.[0]?.delta\n if (!delta) continue\n\n const content = delta.content ?? ''\n\n // merge streaming tool calls\n for (const tc of delta.tool_calls ?? []) {\n const idx = tc.index ?? 0\n const existing = toolCallMap.get(idx) ?? { id: '', name: '', arguments: '' }\n if (tc.id) existing.id = tc.id\n if (tc.function?.name) existing.name += tc.function.name\n if (tc.function?.arguments) existing.arguments += tc.function.arguments\n toolCallMap.set(idx, existing)\n }\n\n onChunk({\n content,\n toolCalls: Array.from(toolCallMap.entries()).map(([index, tc]) => ({ index, ...tc })),\n isDone: false,\n })\n } catch {\n // skip unparseable lines\n }\n }\n }\n } finally {\n reader.releaseLock()\n }\n\n // final chunk\n const finalToolCalls = toolCallMap.size > 0\n ? Array.from(toolCallMap.values()).map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n : undefined\n\n onChunk({ content: '', toolCalls: [], isDone: true })\n\n return finalToolCalls\n }\n\n async embed(config: ModelConfig, texts: string[], model?: string): Promise<number[][]> {\n const url = `${config.baseUrl}/embeddings`\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify({\n model: model || config.embeddingModel,\n input: texts,\n encoding_format: 'float',\n }),\n signal: AbortSignal.timeout(60000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`Embedding ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const json = await resp.json() as {\n data?: Array<{ embedding: number[] }>\n }\n return (json.data ?? []).map((d) => d.embedding)\n }\n\n async testConnection(config: ModelConfig): Promise<{ ok: boolean; message: string }> {\n try {\n const result = await this.chatCompletion(\n config,\n [{ role: 'user', content: 'ping' }],\n undefined,\n { maxTokens: 10, timeoutMs: 15000 },\n )\n return {\n ok: result.content.length > 0 || (result.content.length === 0 && !result.toolCalls),\n message: result.content.slice(0, 100) || '(empty response)',\n }\n } catch (e) {\n this.logger.warn('[OpenAICompatibleClient] testConnection failed:', e)\n return {\n ok: false,\n message: e instanceof Error ? e.message : String(e),\n }\n }\n }\n}","import type { IChatStreamSink, IEventSink } from '../ports/stream-sink'\n\n/**\n * 回调流输出。\n *\n * 把 `IChatStreamSink` 桥接到回调函数,供测试或直接消费(非 IPC)。\n */\nexport class CallbackStreamSink implements IChatStreamSink {\n private readonly onChunkCb: (content: string, isDone: boolean, newSessionId?: string) => void\n\n constructor(cb: (content: string, isDone: boolean, newSessionId?: string) => void) {\n this.onChunkCb = cb\n }\n\n onChunk(content: string, isDone: boolean, newSessionId?: string): void {\n this.onChunkCb(content, isDone, newSessionId)\n }\n}\n\n/**\n * 事件推送空实现(MCP/测试场景无 UI 渲染进程)。\n */\nexport class NoopEventSink implements IEventSink {\n emit(): void {}\n}","import type { ILogger } from '../ports/logger'\nimport type { ToolDefinition } from '../types/llm'\n\n/**\n * Runtime tool descriptor with handler\n */\nexport interface RegisteredTool extends ToolDefinition {\n category: 'platform' | 'mirror' | 'user' | 'pocket'\n handler: (args: Record<string, unknown>, context: ToolContext) => Promise<unknown>\n}\n\nexport interface ToolContext {\n userId: string\n signal?: AbortSignal\n}\n\n/**\n * 工具注册中心。\n *\n * 负责注册、查询、执行 Agent 工具。通过依赖注入接收 ILogger,\n * 替代原 hchuanz-pocket-helper 中直接 import ee-core/log 的实现。\n */\nexport class ToolRegistry {\n private tools = new Map<string, RegisteredTool>()\n\n constructor(private logger: ILogger) {}\n\n /**\n * Register a tool (platform / agent / user MCP)\n */\n register(tool: RegisteredTool): void {\n if (this.tools.has(tool.function.name)) {\n this.logger.warn(`[toolRegistry] overwriting tool: ${tool.function.name}`)\n }\n this.tools.set(tool.function.name, tool)\n this.logger.info(`[toolRegistry] registered: ${tool.function.name}`)\n }\n\n /**\n * Bulk register tools from an array\n */\n registerAll(tools: RegisteredTool[]): void {\n for (const t of tools) this.register(t)\n }\n\n /**\n * Unregister a tool by name\n */\n unregister(name: string): void {\n this.tools.delete(name)\n }\n\n /**\n * Unregister all tools with a given prefix (e.g. MCP server name)\n */\n unregisterByPrefix(prefix: string): void {\n for (const name of this.tools.keys()) {\n if (name.startsWith(prefix)) this.tools.delete(name)\n }\n }\n\n /**\n * Get a single tool by name\n */\n get(name: string): RegisteredTool | undefined {\n return this.tools.get(name)\n }\n\n /**\n * Get all tools in OpenAI function-calling format\n */\n getOpenAIFormat(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => ({\n type: 'function' as const,\n function: { ...t.function },\n }))\n }\n\n /**\n * Get tools filtered by category\n */\n getByCategory(category: RegisteredTool['category']): ToolDefinition[] {\n return Array.from(this.tools.values())\n .filter((t) => t.category === category)\n .map((t) => ({ type: 'function' as const, function: { ...t.function } }))\n }\n\n /**\n * Execute a tool by name\n */\n async execute(name: string, args: Record<string, unknown>, context: ToolContext): Promise<unknown> {\n const tool = this.tools.get(name)\n if (!tool) throw new Error(`Tool not found: ${name}`)\n return tool.handler(args, context)\n }\n\n /**\n * List all registered tool names\n */\n listNames(): string[] {\n return Array.from(this.tools.keys())\n }\n\n /**\n * Clear all tools\n */\n clear(): void {\n this.tools.clear()\n }\n}","export interface MirrorPersona {\n personalityTraits: string[]\n speechPatterns: string[]\n signaturePhrases: string[]\n emotionalTone: string\n personaSummary: string\n relationship?: MirrorRelationship\n recentActivity?: MirrorRecentActivity\n}\n\nexport interface MirrorRelationship {\n dynamic: string\n closeness: number\n howSheAddressesMe: string\n topicsWeDiscuss: string[]\n evolution: string\n}\n\nexport interface MirrorRecentActivity {\n topics: string[]\n mood: string\n events: string[]\n}\n\nexport interface MirrorProfile {\n xoxId: number\n xoxNickname: string\n persona: MirrorPersona\n previousPersona?: MirrorPersona\n fewShotExamples: Array<{\n userQuestion: string\n idolReply: string\n relevance: number\n }>\n meta: {\n totalSamplesUsed: number\n lastUpdatedAt: number\n dataRangeMs: [number, number]\n maturityScore: number\n }\n growthLog?: GrowthEvent[]\n}\n\nexport interface GrowthEvent {\n timestamp: number\n reason: string\n changes: string[]\n}\n\nexport interface ChatSession {\n sessionId: string\n xoxId: number\n messages: Array<{ role: string; content: string }>\n createdAt: number\n lastActiveAt: number\n}\n\n/**\n * Normalize a persona from disk or LLM output.\n * Old profiles / incomplete LLM responses may miss fields; fill in safe defaults\n * so downstream consumers (chat prompt builder, frontend render) never crash.\n */\nexport function normalizePersona(persona: Partial<MirrorPersona> | null | undefined): MirrorPersona {\n const p = persona || {}\n const rel = p.relationship\n return {\n personalityTraits: Array.isArray(p.personalityTraits) ? p.personalityTraits : [],\n speechPatterns: Array.isArray(p.speechPatterns) ? p.speechPatterns : [],\n signaturePhrases: Array.isArray(p.signaturePhrases) ? p.signaturePhrases : [],\n emotionalTone: typeof p.emotionalTone === 'string' ? p.emotionalTone : '温柔真诚',\n personaSummary: typeof p.personaSummary === 'string' ? p.personaSummary : '',\n ...(rel\n ? {\n relationship: {\n dynamic: typeof rel.dynamic === 'string' ? rel.dynamic : '友好',\n closeness: typeof rel.closeness === 'number' ? rel.closeness : 0.5,\n howSheAddressesMe: typeof rel.howSheAddressesMe === 'string' ? rel.howSheAddressesMe : '你',\n topicsWeDiscuss: Array.isArray(rel.topicsWeDiscuss) ? rel.topicsWeDiscuss : [],\n evolution: typeof rel.evolution === 'string' ? rel.evolution : '',\n },\n }\n : {}),\n ...(p.recentActivity\n ? {\n recentActivity: {\n topics: Array.isArray(p.recentActivity.topics) ? p.recentActivity.topics : [],\n mood: typeof p.recentActivity.mood === 'string' ? p.recentActivity.mood : '',\n events: Array.isArray(p.recentActivity.events) ? p.recentActivity.events : [],\n },\n }\n : {}),\n }\n}\n\nexport interface MirrorConfig {\n /** LLM analysis: max flip records to sample for persona extraction */\n llmSampleSize: number\n /** LLM analysis: max_tokens for the analysis response */\n llmMaxTokens: number\n /** LLM analysis: temperature (0=deterministic, 1=creative) */\n analysisTemperature: number\n /** Chat: max rounds of conversation history to keep */\n chatHistoryRounds: number\n /** Chat: temperature (0=deterministic, 1=creative) */\n chatTemperature: number\n /** RAG: top-K results to retrieve from vector index */\n ragTopK: number\n /** RAG: minimum cosine similarity score to include a result */\n ragMinScore: number\n /** Episodic memory: max recent conversation summaries to inject */\n memoryEpisodes: number\n /** Embedding: max records to build vector index for (0 = unlimited) */\n embeddingMaxRecords: number\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport {\n type MirrorProfile,\n type ChatSession,\n normalizePersona,\n} from '../types/mirror'\n\nexport { normalizePersona }\nexport type { MirrorProfile, ChatSession }\n\nconst MIRROR_DIR = 'mirrors'\nconst CHAT_DIR = 'chat_sessions'\n\nfunction mirrorSubdir(userId: string): string {\n return `${MIRROR_DIR}/${userId}`\n}\n\nfunction chatSubdir(userId: string): string {\n return `${CHAT_DIR}/${userId}`\n}\n\nfunction profileSubpath(xoxId: number, userId: string): string {\n return `${MIRROR_DIR}/${userId}/${xoxId}.json`\n}\n\nfunction sessionSubpath(sessionId: string, userId: string): string {\n return `${CHAT_DIR}/${userId}/${sessionId}.json`\n}\n\nfunction normalizeProfile(raw: MirrorProfile): MirrorProfile {\n return {\n ...raw,\n persona: normalizePersona(raw.persona),\n previousPersona: raw.previousPersona ? normalizePersona(raw.previousPersona) : undefined,\n fewShotExamples: Array.isArray(raw.fewShotExamples) ? raw.fewShotExamples : [],\n meta: raw.meta || { totalSamplesUsed: 0, lastUpdatedAt: 0, dataRangeMs: [0, 0], maturityScore: 0 },\n }\n}\n\n/**\n * 镜像档案与聊天会话持久化存储。\n *\n * 通过 IStorage 抽象底层文件系统,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` 的直接调用。\n */\nexport class MirrorStore {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n ) {}\n\n // ═══ Mirror Profile CRUD ═══\n\n getProfile(xoxId: number, userId: string): MirrorProfile | null {\n const raw = this.storage.readJson<MirrorProfile>(profileSubpath(xoxId, userId))\n if (!raw) return null\n try {\n return normalizeProfile(raw)\n } catch (e) {\n this.logger.warn(`[mirrorStore] read profile failed for ${xoxId}`, e)\n return null\n }\n }\n\n saveProfile(profile: MirrorProfile, userId: string): void {\n this.storage.mkdir(mirrorSubdir(userId))\n this.storage.writeJson(profileSubpath(profile.xoxId, userId), profile)\n this.logger.info(`[mirrorStore] saved profile for ${profile.xoxNickname}(${profile.xoxId})`)\n }\n\n deleteProfile(xoxId: number, userId: string): void {\n this.storage.delete(profileSubpath(xoxId, userId))\n // also delete chat sessions for this xoxId\n const cd = chatSubdir(userId)\n if (this.storage.exists(cd)) {\n const files = this.storage.list(cd)\n for (const f of files) {\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session?.xoxId === xoxId) {\n this.storage.delete(`${cd}/${f}`)\n }\n } catch { /* skip */ }\n }\n }\n }\n\n listProfiles(userId: string): MirrorProfile[] {\n const dir = mirrorSubdir(userId)\n if (!this.storage.exists(dir)) return []\n const profiles: MirrorProfile[] = []\n const files = this.storage.list(dir)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n if (f.includes('_index')) continue // skip vector index files\n if (f.includes('_memory')) continue // skip episodic memory files\n try {\n const profile = this.storage.readJson<MirrorProfile>(`${dir}/${f}`)\n if (profile) profiles.push(normalizeProfile(profile))\n } catch { /* skip */ }\n }\n return profiles\n }\n\n // ═══ Chat Session CRUD ═══\n\n getSession(sessionId: string, userId: string): ChatSession | null {\n return this.storage.readJson<ChatSession>(sessionSubpath(sessionId, userId))\n }\n\n saveSession(session: ChatSession, userId: string): void {\n this.storage.mkdir(chatSubdir(userId))\n this.storage.writeJson(sessionSubpath(session.sessionId, userId), session)\n }\n\n /**\n * Find the most recently active chat session for an idol\n */\n getLatestSessionForXox(xoxId: number, userId: string): ChatSession | null {\n const cd = chatSubdir(userId)\n if (!this.storage.exists(cd)) return null\n let latest: ChatSession | null = null\n const files = this.storage.list(cd)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session && session.xoxId === xoxId) {\n if (!latest || session.lastActiveAt > latest.lastActiveAt) {\n latest = session\n }\n }\n } catch { /* skip */ }\n }\n return latest\n }\n\n deleteSession(sessionId: string, userId: string): void {\n this.storage.delete(sessionSubpath(sessionId, userId))\n }\n\n /**\n * Delete all chat sessions for an idol (keeps profile)\n */\n deleteSessionsForXox(xoxId: number, userId: string): void {\n const cd = chatSubdir(userId)\n if (!this.storage.exists(cd)) return\n const files = this.storage.list(cd)\n for (const f of files) {\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session?.xoxId === xoxId) {\n this.storage.delete(`${cd}/${f}`)\n }\n } catch { /* skip */ }\n }\n }\n}","import crypto from 'node:crypto'\nimport type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ModelConfig } from '../types/llm'\n\nexport interface FlipRecordVector {\n answerId: string\n content: string\n answerContent: string\n qtime: number\n hash: string\n vector: number[]\n}\n\nexport interface SearchHit {\n record: FlipRecordVector\n score: number\n}\n\ninterface IndexFile {\n xoxId: number\n model: string\n updatedAt: number\n vectors: FlipRecordVector[]\n}\n\nconst INDEX_DIR = 'mirrors'\nconst EMBED_BATCH_SIZE = 32\n\nfunction indexPath(xoxId: number): string {\n return `${INDEX_DIR}/${xoxId}_index.json`\n}\n\nfunction hashRecord(content: string, answerContent: string): string {\n return crypto.createHash('md5').update(`${content}||${answerContent}`).digest('hex').slice(0, 12)\n}\n\nfunction cosine(a: number[], b: number[]): number {\n let dot = 0\n let na = 0\n let nb = 0\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i]\n na += a[i] * a[i]\n nb += b[i] * b[i]\n }\n if (na === 0 || nb === 0) return 0\n return dot / (Math.sqrt(na) * Math.sqrt(nb))\n}\n\n/**\n * 向量索引服务。\n *\n * 通过 ILlmClient 调用 embedding API 构建/搜索翻牌记录的语义向量索引,\n * 支持 cosine + MMR 的 top-K 检索。\n * 底层持久化通过 IStorage,替代原 hchuanz-pocket-helper 中 `fs` + `app.getPath`。\n */\nexport class MirrorIndex {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private llmClient: ILlmClient,\n ) {}\n\n /**\n * Load index from disk (returns null if not exists)\n */\n load(xoxId: number): IndexFile | null {\n const fp = indexPath(xoxId)\n const raw = this.storage.readJson<IndexFile>(fp)\n if (!raw) return null\n return raw\n }\n\n /**\n * Build / incrementally update the vector index for an idol's flip records.\n * Records with unchanged hash reuse existing embeddings.\n */\n async buildIndex(\n xoxId: number,\n config: ModelConfig,\n records: Array<{ answerId?: string; content?: string; answerContent?: string; qtime?: number | string }>,\n ): Promise<{ indexed: number; reused: number }> {\n const existing = this.load(xoxId)\n const existingMap = new Map<string, FlipRecordVector>()\n if (existing && existing.model === config.embeddingModel) {\n for (const v of existing.vectors) existingMap.set(v.hash, v)\n }\n\n const toEmbed: Array<{ rec: { answerId: string; content: string; answerContent: string; qtime: number }; hash: string; text: string }> = []\n const reused: FlipRecordVector[] = []\n\n for (const r of records) {\n const content = String(r.content || '').trim()\n const answer = String(r.answerContent || '').trim()\n if (!content && !answer) continue\n const hash = hashRecord(content, answer)\n const cached = existingMap.get(hash)\n if (cached) {\n reused.push(cached)\n } else {\n toEmbed.push({\n rec: {\n answerId: String(r.answerId || ''),\n content,\n answerContent: answer,\n qtime: Number(r.qtime) || 0,\n },\n hash,\n text: `问:${content} 答:${answer}`,\n })\n }\n }\n\n // batch embed new records\n const newVectors: FlipRecordVector[] = []\n for (let i = 0; i < toEmbed.length; i += EMBED_BATCH_SIZE) {\n const batch = toEmbed.slice(i, i + EMBED_BATCH_SIZE)\n const texts = batch.map((b) => b.text)\n const vectors = await this.llmClient.embed(config, texts)\n batch.forEach((b, j) => {\n newVectors.push({ ...b.rec, hash: b.hash, vector: vectors[j] || [] })\n })\n }\n\n const indexFile: IndexFile = {\n xoxId,\n model: config.embeddingModel,\n updatedAt: Date.now(),\n vectors: [...reused, ...newVectors],\n }\n\n this.storage.mkdir(INDEX_DIR)\n this.storage.writeJson(indexPath(xoxId), indexFile)\n this.logger.info(`[mirrorIndex] built index for ${xoxId}: ${newVectors.length} new, ${reused.length} reused`)\n\n return { indexed: newVectors.length, reused: reused.length }\n }\n\n /**\n * Delete index for an idol\n */\n deleteIndex(xoxId: number): void {\n this.storage.delete(indexPath(xoxId))\n }\n\n /**\n * Search with cosine top-K + MMR dedup\n */\n async search(xoxId: number, config: ModelConfig, query: string, topK = 5): Promise<SearchHit[]> {\n const index = this.load(xoxId)\n if (!index || index.vectors.length === 0) return []\n\n const [qv] = await this.llmClient.embed(config, [query])\n if (!qv) return []\n\n // top-K cosine\n const scored: SearchHit[] = index.vectors\n .map((v) => ({ record: v, score: cosine(qv, v.vector) }))\n .sort((a, b) => b.score - a.score)\n .slice(0, Math.min(topK * 3, index.vectors.length))\n\n // MMR: balance relevance and diversity\n const selected: SearchHit[] = []\n const LAMBDA = 0.75\n const pool = [...scored]\n\n while (selected.length < topK && pool.length > 0) {\n let best: SearchHit | null = null\n let bestScore = -Infinity\n for (const cand of pool) {\n let maxSim = 0\n for (const sel of selected) {\n const sim = cosine(cand.record.vector, sel.record.vector)\n if (sim > maxSim) maxSim = sim\n }\n const mmr = LAMBDA * cand.score - (1 - LAMBDA) * maxSim\n if (mmr > bestScore) {\n bestScore = mmr\n best = cand\n }\n }\n if (!best) break\n selected.push(best)\n pool.splice(pool.indexOf(best), 1)\n }\n\n return selected\n }\n\n /**\n * Get index stats\n */\n getStats(xoxId: number): { total: number; updatedAt: number } | null {\n const index = this.load(xoxId)\n if (!index) return null\n return { total: index.vectors.length, updatedAt: index.updatedAt }\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ModelConfig } from '../types/llm'\n\nexport interface EpisodicEntry {\n id: string\n topic: string\n summary: string\n turns: number\n createdAt: number\n}\n\ninterface MemoryFile {\n xoxId: number\n totalTurns: number\n episodes: EpisodicEntry[]\n updatedAt: number\n}\n\nconst MEMORY_DIR = 'mirrors'\nconst MAX_EPISODES = 50\n\nfunction memoryPath(xoxId: number): string {\n return `${MEMORY_DIR}/${xoxId}_memory.json`\n}\n\n/**\n * 情节记忆服务。\n *\n * 记录对话轮次,定期调用 LLM 将对话压缩为情节记忆条目,提供检索和清理。\n * 底层持久化通过 IStorage,LLM 调用通过 ILlmClient。\n */\nexport class MirrorMemory {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private llmClient: ILlmClient,\n ) {}\n\n private ensureDir(): void {\n if (!this.storage.exists(MEMORY_DIR)) {\n this.storage.mkdir(MEMORY_DIR)\n }\n }\n\n /**\n * Load memory file\n */\n load(xoxId: number): MemoryFile {\n const mp = memoryPath(xoxId)\n const raw = this.storage.readJson<MemoryFile>(mp)\n if (raw) return raw\n return { xoxId, totalTurns: 0, episodes: [], updatedAt: Date.now() }\n }\n\n private save(mem: MemoryFile): void {\n this.ensureDir()\n mem.updatedAt = Date.now()\n this.storage.writeJson(memoryPath(mem.xoxId), mem)\n }\n\n /**\n * Increment turn counter (called after each chat round)\n */\n addTurn(xoxId: number): number {\n const mem = this.load(xoxId)\n mem.totalTurns += 1\n this.save(mem)\n return mem.totalTurns\n }\n\n getTotalTurns(xoxId: number): number {\n return this.load(xoxId).totalTurns\n }\n\n /**\n * Summarize a batch of conversation messages into an episodic memory entry.\n * Uses LLM to extract topic + summary.\n */\n async recordEpisode(\n xoxId: number,\n config: ModelConfig,\n messages: Array<{ role: string; content: string }>,\n ): Promise<EpisodicEntry | null> {\n if (messages.length < 4) return null // too short to be meaningful\n\n const convText = messages.slice(-30).map((m) => `${m.role === 'user' ? '粉丝' : '偶像'}: ${m.content}`).join('\\n')\n\n const systemPrompt = `你是对话记忆压缩器。请把以下对话压缩成一条记忆条目,输出 JSON:\n{\"topic\": \"话题关键词(2-6字)\", \"summary\": \"30-80字的对话摘要,包含关键事实、情绪和约定\"}\n只输出 JSON,不要其他内容。`\n\n try {\n const resp = await this.llmClient.chatCompletion(\n config,\n [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: convText },\n ],\n undefined,\n { model: config.lightweightModel }, // use lightweight model for summarization\n )\n\n const jsonMatch = resp.content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) return null\n const parsed = JSON.parse(jsonMatch[0]) as { topic: string; summary: string }\n\n const entry: EpisodicEntry = {\n id: `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n topic: parsed.topic || '未分类',\n summary: parsed.summary || '',\n turns: Math.floor(messages.length / 2),\n createdAt: Date.now(),\n }\n\n const mem = this.load(xoxId)\n mem.episodes.push(entry)\n // cap episodes count\n if (mem.episodes.length > MAX_EPISODES) {\n mem.episodes = mem.episodes.slice(-MAX_EPISODES)\n }\n this.save(mem)\n return entry\n } catch (e) {\n this.logger.warn('[mirrorMemory] recordEpisode failed:', e)\n return null\n }\n }\n\n /**\n * Get recent episodes for prompt injection\n */\n getRecentEpisodes(xoxId: number, count = 3): EpisodicEntry[] {\n return this.load(xoxId).episodes.slice(-count)\n }\n\n /**\n * Search episodes by keyword (simple substring match on topic + summary)\n */\n searchEpisodes(xoxId: number, keyword: string, count = 3): EpisodicEntry[] {\n const kw = keyword.toLowerCase()\n const mem = this.load(xoxId)\n return mem.episodes\n .filter((e) => e.topic.toLowerCase().includes(kw) || e.summary.toLowerCase().includes(kw))\n .slice(-count)\n }\n\n /**\n * Clear all episodic memory (keeps totalTurns for maturity continuity)\n */\n clearEpisodes(xoxId: number): void {\n const mem = this.load(xoxId)\n mem.episodes = []\n this.save(mem)\n }\n\n /**\n * Delete memory file entirely\n */\n deleteMemory(xoxId: number): void {\n this.storage.delete(memoryPath(xoxId))\n }\n\n getStats(xoxId: number): { totalTurns: number; episodeCount: number; updatedAt: number } {\n const mem = this.load(xoxId)\n return { totalTurns: mem.totalTurns, episodeCount: mem.episodes.length, updatedAt: mem.updatedAt }\n }\n}","import type { ILogger } from '../../ports/logger'\nimport type { IStorage } from '../../ports/storage'\n\n// ═══ Types ═══\n\nexport interface GroupChatMember {\n xoxId: number\n nickname: string\n}\n\nexport interface GroupChatMessage {\n msgId: string\n speakerId: number // -1 = user, 0 = system, >0 = mirror xoxId\n speakerName: string\n content: string\n timestamp: number\n}\n\nexport interface GroupChatSession {\n sessionId: string\n name: string\n members: GroupChatMember[]\n topic: string | null\n mode: 'observer' | 'participant'\n messages: GroupChatMessage[]\n createdAt: number\n lastActiveAt: number\n}\n\n// ═══ Store ═══\n\nconst GROUP_SESSIONS_DIR = 'group_sessions'\n\nfunction sessionSubpath(sessionId: string): string {\n return `${GROUP_SESSIONS_DIR}/session_${sessionId}.json`\n}\n\n/**\n * 群聊会话持久化存储。\n *\n * 通过 IStorage 抽象底层文件系统,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` 的直接调用。\n */\nexport class GroupChatStore {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n ) {}\n\n private ensureDir(): void {\n if (!this.storage.exists(GROUP_SESSIONS_DIR)) {\n this.storage.mkdir(GROUP_SESSIONS_DIR)\n }\n }\n\n saveSession(session: GroupChatSession): void {\n this.ensureDir()\n this.storage.writeJson(sessionSubpath(session.sessionId), session)\n }\n\n getSession(sessionId: string): GroupChatSession | null {\n const session = this.storage.readJson<GroupChatSession>(sessionSubpath(sessionId))\n if (!session) return null\n return session\n }\n\n listSessions(): GroupChatSession[] {\n if (!this.storage.exists(GROUP_SESSIONS_DIR)) return []\n const sessions: GroupChatSession[] = []\n try {\n const files = this.storage.list(GROUP_SESSIONS_DIR)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n try {\n const session = this.storage.readJson<GroupChatSession>(`${GROUP_SESSIONS_DIR}/${f}`)\n if (session) sessions.push(session)\n } catch { /* skip */ }\n }\n } catch { /* skip */ }\n return sessions\n }\n\n deleteSession(sessionId: string): void {\n this.storage.delete(sessionSubpath(sessionId))\n }\n\n /**\n * Append a message to a session (read → push → write back)\n */\n appendMessage(sessionId: string, msg: GroupChatMessage): void {\n const session = this.storage.readJson<GroupChatSession>(sessionSubpath(sessionId))\n if (!session) return\n session.messages.push(msg)\n session.lastActiveAt = Date.now()\n this.storage.writeJson(sessionSubpath(sessionId), session)\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { IConfigStore } from '../ports/config-store'\nimport type { DashboardInfo, FlipCardGroup, FlipRecord, FlipUserCache } from '../types/pocket'\n\nconst CACHE_DIR = 'flip-cache'\n\nfunction cacheSubpath(userId: string): string {\n return `${CACHE_DIR}/${userId}.json`\n}\n\n/**\n * 翻牌数据缓存服务。\n *\n * 通过 IStorage + IConfigStore 抽象底层持久化,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` + `storeService` 的直接调用。\n */\nexport class FlipCacheService {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private configStore: IConfigStore,\n ) {}\n\n /** 计算仪表盘数据(纯逻辑) */\n static calcDashboard(records: Array<{ status?: number; cost?: number }>): DashboardInfo {\n const info: DashboardInfo = { totalFlipCount: records.length, runningCount: 0, returnedCount: 0, costTotal: 0 }\n for (const card of records) {\n if (card.status === 3) info.returnedCount += 1\n else if (card.status === 1) info.runningCount += 1\n else if (card.status === 2) info.costTotal += Number(card.cost) || 0\n }\n return info\n }\n\n /** 按偶像分组(纯逻辑) */\n static groupDataById(records: Array<{ answerId?: string; cost?: number; answerType?: number; answerTime?: string; qtime?: string; type?: number; baseUserInfo?: { userId?: number; nickname?: string } }>): Record<string, FlipCardGroup> {\n const groupData: Record<string, FlipCardGroup> = {}\n for (const item of records) {\n const base = item.baseUserInfo\n const xoxId = base?.userId\n if (!xoxId) continue\n const cardInfo = {\n answerId: item.answerId || '',\n cost: item.cost || 0,\n answerType: item.answerType || 0,\n answerTime: item.answerTime || '',\n qtime: item.qtime || '',\n type: item.type || 0,\n }\n const key = String(xoxId)\n if (key in groupData) {\n groupData[key].cards.push(cardInfo)\n } else {\n groupData[key] = {\n xoxId,\n xoxNickname: base?.nickname ?? '',\n cards: [cardInfo],\n }\n }\n }\n return groupData\n }\n\n private writeCache(cache: FlipUserCache): void {\n this.storage.mkdir(CACHE_DIR)\n this.storage.writeJson(cacheSubpath(cache.userId), cache)\n }\n\n /** 从旧版 storeService 迁移数据 */\n private migrateFromLegacy(userId: string): FlipUserCache | null {\n const allUserData = this.configStore.get<Record<string, FlipRecord[]>>('allUserData')\n const records = allUserData?.[userId]\n if (!Array.isArray(records) || records.length === 0) return null\n\n const legacyGrouped = this.configStore.get<Record<string, FlipCardGroup>>('groupedDataById')\n const legacyDashboard = this.configStore.get<DashboardInfo>('dashboardInfo')\n\n const cache: FlipUserCache = {\n userId,\n records: records as FlipUserCache['records'],\n groupedDataById: legacyGrouped && Object.keys(legacyGrouped).length > 0\n ? legacyGrouped\n : FlipCacheService.groupDataById(records as any),\n dashboardInfo: legacyDashboard?.totalFlipCount\n ? legacyDashboard\n : FlipCacheService.calcDashboard(records as any),\n meta: { syncedAt: Date.now(), recordCount: records.length },\n }\n this.writeCache(cache)\n this.logger.info(`[flipCacheService] migrated ${records.length} records for user ${userId}`)\n return cache\n }\n\n get(userId: string): FlipUserCache | null {\n if (!userId) return null\n const cached = this.storage.readJson<FlipUserCache>(cacheSubpath(userId))\n if (cached) return cached\n return this.migrateFromLegacy(userId)\n }\n\n set(cache: FlipUserCache): void {\n cache.meta = { syncedAt: Date.now(), recordCount: cache.records.length }\n this.writeCache(cache)\n }\n\n clear(userId: string): void {\n this.storage.delete(cacheSubpath(userId))\n }\n}","import axios from 'axios'\nimport type { PocketApiResponse } from '../types/pocket'\nimport type { IAuthExpiredHandler } from '../ports/auth-expired-handler'\n\n/** Thrown when Pocket API returns 401004 (auth expired) */\nexport class PocketAuthExpiredError extends Error {\n constructor() {\n super('登录已过期,请重新登录')\n this.name = 'PocketAuthExpiredError'\n }\n}\n\n/**\n * Pocket48 API HTTP 客户端。\n *\n * 负责签名头构造、请求发送、auth 过期检测。\n * 通过 IAuthExpiredHandler 回调通知宿主(替代原 ant-design-vue message/router 依赖)。\n */\nexport class PocketClient {\n private lastAuthWarnAt = 0\n private lastKickAt = 0\n\n constructor(\n private baseUrl: string,\n private appInfo: string,\n private pa: string,\n private authExpiredHandler?: IAuthExpiredHandler,\n ) {}\n\n /** 登录态过期的单一出口:防抖调用 authExpiredHandler + 抛错 */\n kickToLogin(): void {\n const now = Date.now()\n if (now - this.lastKickAt < 2000) return // 防抖:并发多个 401004 只踢一次\n this.lastKickAt = now\n if (now - this.lastAuthWarnAt > 5000) {\n this.lastAuthWarnAt = now\n }\n this.authExpiredHandler?.onAuthExpired()\n }\n\n /** 检查响应是否 auth 过期 */\n checkAuthExpired(res: PocketApiResponse): void {\n if (res?.status === 401004) {\n this.kickToLogin()\n throw new PocketAuthExpiredError()\n }\n }\n\n private buildSignedHeaders(token?: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json;charset=utf-8',\n appinfo: this.appInfo,\n accept: '*/*',\n pa: this.pa,\n 'accept-language': 'zh-Hans-CN;q=1',\n 'p-sign-type': 'V0',\n }\n if (token) headers.token = token\n return headers\n }\n\n private buildPlainHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json;charset=utf-8',\n Accept: '*/*',\n pa: this.pa,\n 'Accept-Language': 'zh-Hans-CN;q=1',\n appInfo: this.appInfo,\n }\n }\n\n async request<T = unknown>(\n path: string,\n data: unknown,\n token?: string,\n signed = true,\n ): Promise<PocketApiResponse<T>> {\n const url = path.startsWith('http') ? path : `${this.baseUrl}${path}`\n const headers = signed || token ? this.buildSignedHeaders(token) : this.buildPlainHeaders()\n const res = await axios.post<PocketApiResponse<T>>(url, data, { headers })\n if (signed) this.checkAuthExpired(res.data)\n return res.data\n }\n\n /** 登录前接口调用(不签名,不检测 auth 过期) */\n async requestBeforeLogin<T = unknown>(\n path: string,\n data: unknown,\n ): Promise<PocketApiResponse<T>> {\n return this.request<T>(path, data, undefined, false)\n }\n}","import type { MirrorConfig } from '../types/mirror'\nimport type { MirrorMode } from '../types/llm'\n\n/**\n * Digital mirror configuration — dual-mode presets\n *\n * Model assignment:\n * Mirror generation → DeepSeek-V4-Pro (1M context, 1.6T/49B MoE)\n * Chat → Qwen3.6-35B-A3B (262K context, MoE)\n *\n * Token estimation (per flip record):\n * Fan ≤200 chars + Idol ≤500 chars = ≤700 chars ≈ 350 tokens\n *\n * All limits are tunable here. To adjust, edit the numbers below\n * and restart; no code changes needed.\n */\n\n/**\n * Context budget reference:\n * Economy: ~280K tokens (800×350) — 28% of V4-Pro 1M window\n * Performance: ~700K tokens (2000×350) — 70% of V4-Pro 1M window\n */\nexport const MIRROR_PRESETS: Record<MirrorMode, MirrorConfig> = {\n /** Default — fast, cheap, good for most users */\n economy: {\n llmSampleSize: 800,\n llmMaxTokens: 4000,\n analysisTemperature: 0.4,\n chatHistoryRounds: 30,\n chatTemperature: 0.7,\n ragTopK: 8,\n ragMinScore: 0.35,\n memoryEpisodes: 5,\n embeddingMaxRecords: 1000,\n },\n\n /** High quality — more data, deeper analysis, higher cost */\n performance: {\n llmSampleSize: 2000,\n llmMaxTokens: 8000,\n analysisTemperature: 0.5,\n chatHistoryRounds: 80,\n chatTemperature: 0.9,\n ragTopK: 15,\n ragMinScore: 0.25,\n memoryEpisodes: 15,\n embeddingMaxRecords: 5000,\n },\n}\n\n/** Resolve effective config for a given mode, with optional user overrides */\nexport function getMirrorConfig(\n mode?: MirrorMode,\n overrides?: { analysisTemperature?: number; chatTemperature?: number },\n): MirrorConfig {\n const base = mode === 'performance' ? MIRROR_PRESETS.performance : MIRROR_PRESETS.economy\n return {\n ...base,\n ...(overrides?.analysisTemperature != null ? { analysisTemperature: overrides.analysisTemperature } : {}),\n ...(overrides?.chatTemperature != null ? { chatTemperature: overrides.chatTemperature } : {}),\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorProfile, MirrorPersona } from '../types/mirror'\nimport { normalizePersona } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorIndex } from './mirror-index'\nimport { getMirrorConfig } from './mirror-constants'\n\n/**\n * Build a digital mirror by analyzing flip records with LLM\n */\nexport interface RoomMessageEntry {\n msgId: string\n msgTime: number\n text: string\n}\n\nexport async function buildMirror(\n xoxId: number,\n userId: string,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n flipDataSource: IFlipDataSource\n mirrorStore: MirrorStore\n mirrorIndex: MirrorIndex\n xoxNickname?: string\n startTimeMs?: number\n endTimeMs?: number\n deepAnalysis?: boolean\n roomMessages?: RoomMessageEntry[]\n },\n): Promise<{ ok: boolean; message: string; profile?: MirrorProfile }> {\n const { logger, llmClient, flipDataSource, mirrorStore, mirrorIndex } = params\n const mc = getMirrorConfig(config.mirrorMode as any, {\n analysisTemperature: config.analysisTemperature,\n chatTemperature: config.chatTemperature,\n })\n // load flip cache\n const cache = flipDataSource.get(userId)\n if (!cache) {\n return { ok: false, message: '未找到翻牌数据,请先同步翻牌记录' }\n }\n\n // filter records for this xoxId, text-only (exclude voice/video)\n const xoxRecords = cache.records.filter((r) => {\n const base = r.baseUserInfo\n if (base?.userId !== xoxId) return false\n // answerType: 1=文字, 2=语音, 3=视频 — only text has analyzable content\n return (r as any).answerType === 1\n })\n\n if (xoxRecords.length === 0) {\n return { ok: false, message: '该偶像暂无文字翻牌记录(已过滤语音/视频翻牌)' }\n }\n\n // apply time filter\n let filtered = xoxRecords\n if (params?.startTimeMs || params?.endTimeMs) {\n filtered = xoxRecords.filter((r) => {\n const qtime = Number(r.qtime) || 0\n if (params.startTimeMs && qtime < params.startTimeMs) return false\n if (params.endTimeMs && qtime > params.endTimeMs) return false\n return true\n })\n }\n\n if (filtered.length === 0) {\n return { ok: false, message: '所选时间范围内无翻牌记录' }\n }\n\n // sample records for analysis\n const sampleSize = Math.min(filtered.length, mc.llmSampleSize)\n const samples = randomSample(filtered, sampleSize)\n\n // build analysis prompt\n const nickname = params?.xoxNickname || (samples[0]?.baseUserInfo as { nickname?: string })?.nickname || String(xoxId)\n const recordTexts = samples\n .filter((r) => !!r.content || !!r.answerContent)\n .map((r) => `粉丝: ${r.content || '(无)'}\\n${nickname}: ${r.answerContent || '(无)'}`)\n .join('\\n---\\n')\n\n // ── Room messages section ──\n const roomMsgs = params?.roomMessages\n const hasRoom = roomMsgs && roomMsgs.length > 0\n const roomText = hasRoom\n ? roomMsgs!\n .map((m) => {\n const d = new Date(m.msgTime)\n const p = (n: number) => String(n).padStart(2, '0')\n return `[${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}] ${nickname}: ${m.text}`\n })\n .join('\\n')\n : ''\n\n const systemPrompt = hasRoom\n ? `你是一个专业的偶像互动数据分析师。下面有这位偶像的两类数据:\n1. 翻牌记录:她与**同一位粉丝**的1对1付费问答\n2. 房间消息:她在公开房间里的日常发言(近况)\n\n你需要综合两类数据来分析。\n\n核心原则:\n- 翻牌记录反映她**对这位粉丝**的独特互动模式\n- 房间消息反映她**对所有人**的日常风格和近期状态\n- 每条判断都要有记录支撑,不要套用刻板印象\n\n请输出 JSON,不要输出其他内容:\n{\n \"personalityTraits\": [\"性格关键词\", ...],\n \"speechPatterns\": [\"说话风格\", ...],\n \"signaturePhrases\": [\"口头禅\", ...],\n \"emotionalTone\": \"情感基调\",\n \"personaSummary\": \"200字以内的画像描述\",\n \"relationship\": {\n \"dynamic\": \"亲密/友好/礼貌/专业/调侃/若即若离/傲娇/依赖/鼓励型\",\n \"closeness\": 0.0-1.0,\n \"howSheAddressesMe\": \"她怎么称呼我\",\n \"topicsWeDiscuss\": [\"常聊的话题\"],\n \"evolution\": \"关系变化趋势,比如越聊越亲密/一直保持礼貌/忽冷忽热\"\n },\n \"recentActivity\": {\n \"topics\": [\"最近在聊的话题\"],\n \"mood\": \"近期情绪状态\",\n \"events\": [\"最近提到的事情/活动\"]\n }\n}\n\n分析维度:\n\n【偶像自身风格】\n1. 性格特征: 温柔/活泼/毒舌/高冷/幽默/傲娇/元气/沉稳等,选3-5个最突出的\n2. 说话风格: 喜欢用什么语气词(呀/呢/哦/叭/啦)、句式特点、回复长度、是否用emoji\n3. 口头禅: 反复出现的标志性用语,必须是真实出现的\n4. 情感基调: 一句话概括她的整体氛围\n\n【与我的关系】\n5. 关系动态: 她对我是什么态度?是温暖亲近还是礼貌客气?有没有调侃或撒娇?\n6. 亲密度: 0.0=完全陌生,0.5=熟人,1.0=无话不谈。看称呼是否亲密、回复是否走心、有没有主动分享生活\n7. 称呼: 她怎么叫我?\"你\"还是\"宝贝/亲爱的\"还是昵称?\n8. 常聊话题: 我们之间反复出现的话题是什么?\n9. 关系变化: 从早期到近期,态度有没有变化?越来越亲密还是保持距离?\n\n【近期近况】(基于房间消息)\n10. 最近话题: 她最近在房间聊了什么?和粉丝互动的内容\n11. 情绪状态: 最近的情绪如何?开心/疲惫/忙碌/期待?\n12. 近期动态: 提到了什么事件、活动、安排?\n\n【画像描述】\n13. 综合以上,用一段流畅中文描述\"她在我面前是什么样的人\",而非\"她是什么样的人\"`\n : `你是一个专业的偶像互动数据分析师。下面是一位偶像与**同一位粉丝**之间的全部翻牌问答记录。\n你的任务不是分析偶像的\"通用人设\",而是分析**这个偶像在这段关系中是什么样的人**。\n\n核心原则:\n- 同一个偶像面对不同粉丝会有不同表现,你必须基于这些记录,分析她**对这位粉丝**的独特互动模式\n- 每条判断都要有记录支撑,不要套用刻板印象\n\n请输出 JSON,不要输出其他内容:\n{\n \"personalityTraits\": [\"性格关键词\", ...],\n \"speechPatterns\": [\"说话风格\", ...],\n \"signaturePhrases\": [\"口头禅\", ...],\n \"emotionalTone\": \"情感基调\",\n \"personaSummary\": \"200字以内的画像描述\",\n \"relationship\": {\n \"dynamic\": \"亲密/友好/礼貌/专业/调侃/若即若离/傲娇/依赖/鼓励型\",\n \"closeness\": 0.0-1.0,\n \"howSheAddressesMe\": \"她怎么称呼我\",\n \"topicsWeDiscuss\": [\"常聊的话题\"],\n \"evolution\": \"关系变化趋势,比如越聊越亲密/一直保持礼貌/忽冷忽热\"\n }\n}\n\n分析维度:\n\n【偶像自身风格】\n1. 性格特征: 温柔/活泼/毒舌/高冷/幽默/傲娇/元气/沉稳等,选3-5个最突出的\n2. 说话风格: 喜欢用什么语气词(呀/呢/哦/叭/啦)、句式特点、回复长度、是否用emoji\n3. 口头禅: 反复出现的标志性用语,必须是真实出现的\n4. 情感基调: 一句话概括她的整体氛围\n\n【与我的关系】\n5. 关系动态: 她对我是什么态度?是温暖亲近还是礼貌客气?有没有调侃或撒娇?\n6. 亲密度: 0.0=完全陌生,0.5=熟人,1.0=无话不谈。看称呼是否亲密、回复是否走心、有没有主动分享生活\n7. 称呼: 她怎么叫我?\"你\"还是\"宝贝/亲爱的\"还是昵称?\n8. 常聊话题: 我们之间反复出现的话题是什么?\n9. 关系变化: 从早期到近期,态度有没有变化?越来越亲密还是保持距离?\n\n【画像描述】\n10. 综合以上,用一段流畅中文描述\"她在我面前是什么样的人\",而非\"她是什么样的人\"`\n\n try {\n const messages: ChatMessage[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: hasRoom\n ? `以下是${nickname}的翻牌记录样本:\\n\\n${recordTexts}\\n\\n以下是${nickname}在房间的近期发言:\\n\\n${roomText}`\n : `以下是${nickname}的翻牌记录样本:\\n\\n${recordTexts}`\n },\n ]\n\n const useReasoning = params?.deepAnalysis === true\n const model = useReasoning ? config.reasoningModel : config.chatModel\n const timeoutMs = useReasoning ? 600000 : 180000\n logger.info(`[mirror] building with ${useReasoning ? 'reasoning' : 'chat'} model: ${model}, timeout ${timeoutMs / 1000}s, ${filtered.length} flip records${hasRoom ? `, ${roomMsgs!.length} room messages` : ''}`)\n const resp = await llmClient.chatCompletion(\n config,\n messages,\n undefined,\n { model, maxTokens: mc.llmMaxTokens, temperature: mc.analysisTemperature, timeoutMs },\n )\n const content = resp.content.trim()\n\n // extract JSON from response\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n return { ok: false, message: 'AI 分析返回格式异常,请重试' }\n }\n\n // normalize so missing/incomplete LLM fields never break consumers\n const analysis = normalizePersona(JSON.parse(jsonMatch[0]) as Partial<MirrorPersona>)\n\n // select few-shot examples\n const fewShots = selectFewShots(filtered, nickname, 5)\n\n const profile: MirrorProfile = {\n xoxId,\n xoxNickname: nickname,\n persona: analysis,\n fewShotExamples: fewShots,\n meta: {\n totalSamplesUsed: filtered.length,\n lastUpdatedAt: Date.now(),\n dataRangeMs: [\n Math.min(...filtered.map((r) => Number(r.qtime) || 0)),\n Math.max(...filtered.map((r) => Number(r.qtime) || 0)),\n ],\n maturityScore: calcMaturity(filtered.length),\n },\n }\n\n mirrorStore.saveProfile(profile, userId)\n\n // build vector index for RAG in background\n const indexRecords = [...xoxRecords]\n .sort((a, b) => (Number(b.qtime) || 0) - (Number(a.qtime) || 0))\n .slice(0, mc.embeddingMaxRecords || undefined)\n mirrorIndex.buildIndex(xoxId, config, indexRecords).then((stats) => {\n logger.info(`[mirror] vector index built for ${xoxId}: ${stats.indexed} new, ${stats.reused} reused`)\n }).catch((e) => {\n logger.warn('[mirror] vector index build failed (RAG unavailable):', e)\n })\n\n return {\n ok: true,\n message: `镜像生成成功!基于 ${filtered.length} 条翻牌记录${hasRoom ? ` + ${roomMsgs!.length} 条房间消息` : ''}(向量索引后台构建中...)`,\n profile,\n }\n } catch (e) {\n logger.error('[mirror] build failed:', e)\n return { ok: false, message: `生成失败: ${e instanceof Error ? e.message : String(e)}` }\n }\n}\n\n// ═══ Private helpers ═══\n\nfunction selectFewShots(\n records: Array<{ content?: string; answerContent?: string }>,\n nickname: string,\n count: number,\n): MirrorProfile['fewShotExamples'] {\n const valid = records.filter((r) => !!r.content && !!r.answerContent)\n if (valid.length === 0) return []\n\n const sorted = [...valid].sort((a, b) => {\n const lenA = String(a.content).length + String(a.answerContent).length\n const lenB = String(b.content).length + String(b.answerContent).length\n return lenB - lenA\n })\n\n const result: MirrorProfile['fewShotExamples'] = []\n const step = Math.max(1, Math.floor(sorted.length / count))\n for (let i = 0; i < count && i < sorted.length; i++) {\n const idx = Math.min(i * step, sorted.length - 1)\n const r = sorted[idx]\n result.push({\n userQuestion: String(r.content),\n idolReply: String(r.answerContent),\n relevance: 1.0,\n })\n }\n return result\n}\n\nfunction randomSample<T>(arr: T[], n: number): T[] {\n if (arr.length <= n) return [...arr]\n const shuffled = [...arr]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return shuffled.slice(0, n)\n}\n\nfunction calcMaturity(recordCount: number): number {\n if (recordCount < 5) return 0.1\n if (recordCount < 20) return 0.3\n if (recordCount < 50) return 0.5\n if (recordCount < 100) return 0.7\n if (recordCount < 200) return 0.85\n return 0.95\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorProfile, ChatSession } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorIndex } from './mirror-index'\nimport { MirrorMemory } from './mirror-memory'\nimport { getMirrorConfig } from './mirror-constants'\n\n// record an episodic memory every N turns\nconst EPISODE_INTERVAL = 10\n\n/**\n * Chat with a digital mirror using streaming + RAG + episodic memory.\n *\n * 重构要点:\n * - ModelConfig 由调用方传入,不再从 storeService + safeStorage 读取\n * - 所有依赖通过参数注入\n */\nexport async function chat(\n xoxId: number,\n userId: string,\n message: string,\n sessionId: string | null,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n mirrorIndex: MirrorIndex\n mirrorMemory: MirrorMemory\n },\n onChunk: (content: string, isDone: boolean, newSessionId: string) => void,\n modelOverride?: string,\n): Promise<void> {\n const { logger, llmClient, mirrorStore, mirrorIndex, mirrorMemory } = params\n\n const mc = getMirrorConfig(config.mirrorMode as any, {\n analysisTemperature: config.analysisTemperature,\n chatTemperature: config.chatTemperature,\n })\n\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) {\n throw new Error('请先生成该偶像的数字镜像')\n }\n\n // load or create session\n let session: ChatSession\n const existing = sessionId ? mirrorStore.getSession(sessionId, userId) : null\n if (existing) {\n session = existing\n } else {\n session = {\n sessionId: generateId(),\n xoxId,\n messages: [],\n createdAt: Date.now(),\n lastActiveAt: Date.now(),\n }\n }\n\n // ═══ RAG retrieval: relevant flip records ═══\n let ragExamples: Array<{ userQuestion: string; idolReply: string; score: number }> = []\n try {\n const hits = await mirrorIndex.search(xoxId, config, message, mc.ragTopK)\n ragExamples = hits\n .filter((h) => h.score > mc.ragMinScore)\n .map((h) => ({\n userQuestion: h.record.content || '(无)',\n idolReply: h.record.answerContent || '(无)',\n score: h.score,\n }))\n } catch (e) {\n logger.warn('[mirror] RAG retrieval failed, fallback to static few-shot:', e)\n }\n\n // ═══ Episodic memory: recent conversation summaries ═══\n const episodes = mirrorMemory.getRecentEpisodes(xoxId, mc.memoryEpisodes)\n\n // build system prompt\n const systemPrompt = buildChatSystemPrompt(profile, ragExamples, episodes)\n\n // build messages array\n const messages: ChatMessage[] = [{ role: 'system', content: systemPrompt }]\n const recentHistory = session.messages.slice(-mc.chatHistoryRounds)\n for (const msg of recentHistory) {\n messages.push({ role: msg.role as 'user' | 'assistant', content: msg.content })\n }\n messages.push({ role: 'user', content: message })\n\n try {\n let assistantReply = ''\n await llmClient.chatCompletionStream(\n config,\n messages,\n (chunk) => {\n if (chunk.content) {\n assistantReply += chunk.content\n onChunk(chunk.content, false, session.sessionId)\n }\n if (chunk.isDone) {\n onChunk('', true, session.sessionId)\n }\n },\n undefined,\n { model: modelOverride || config.chatModel, temperature: mc.chatTemperature },\n )\n\n // save session (both user and assistant messages)\n session.messages.push({ role: 'user', content: message })\n if (assistantReply) {\n session.messages.push({ role: 'assistant', content: assistantReply })\n }\n session.lastActiveAt = Date.now()\n mirrorStore.saveSession(session, userId)\n\n // ═══ memory bookkeeping ═══\n const totalTurns = mirrorMemory.addTurn(xoxId)\n\n // periodically summarize this session into episodic memory (fire-and-forget)\n if (totalTurns > 0 && totalTurns % EPISODE_INTERVAL === 0 && session.messages.length >= 8) {\n mirrorMemory.recordEpisode(xoxId, config, session.messages).catch((e) => {\n logger.warn('[mirror] recordEpisode background failed:', e)\n })\n }\n } catch (e) {\n logger.error('[mirror] chat failed:', e)\n throw e\n }\n}\n\n/**\n * Save assistant message to session (called after streaming is complete)\n */\nexport function saveAssistantMessage(\n sessionId: string,\n userId: string,\n content: string,\n mirrorStore: MirrorStore,\n): void {\n const session = mirrorStore.getSession(sessionId, userId)\n if (!session) return\n try {\n session.messages.push({ role: 'assistant', content })\n session.lastActiveAt = Date.now()\n mirrorStore.saveSession(session, userId)\n } catch (e) {\n // silently ignore\n }\n}\n\n/**\n * Clear chat memory for a session\n */\nexport function clearChatMemory(sessionId: string, userId: string, mirrorStore: MirrorStore): void {\n mirrorStore.deleteSession(sessionId, userId)\n}\n\n// ═══ Private ═══\n\nfunction buildChatSystemPrompt(\n profile: MirrorProfile,\n ragExamples: Array<{ userQuestion: string; idolReply: string; score: number }>,\n episodes: Array<{ topic: string; summary: string }>,\n): string {\n const p = profile.persona\n\n // prefer RAG-retrieved examples, fallback to static few-shot\n const examples = ragExamples.length > 0\n ? ragExamples.map((e) => `粉丝: ${e.userQuestion}\\n你: ${e.idolReply}`).join('\\n\\n')\n : profile.fewShotExamples.map((e) => `粉丝: ${e.userQuestion}\\n你: ${e.idolReply}`).join('\\n\\n')\n\n const memorySection = episodes.length > 0\n ? `\\n【你们过往的对话记忆】\\n${episodes.map((e) => `- ${e.topic}: ${e.summary}`).join('\\n')}\\n请自然地记住这些聊过的事情,不要主动提起除非相关。\\n`\n : ''\n\n const rel = p.relationship\n const relationshipSection = rel\n ? `\\n【与这个粉丝的关系】\\n亲密度: ${Math.round(rel.closeness * 100)}% | 关系: ${rel.dynamic}\\n她对我的称呼: ${rel.howSheAddressesMe}\\n我们常聊: ${rel.topicsWeDiscuss.join('、')}\\n关系变化: ${rel.evolution}\\n`\n : ''\n\n const act = p.recentActivity\n const activitySection = act\n ? `\\n【近期近况】\\n最近话题: ${act.topics.join('、')}\\n情绪状态: ${act.mood}\\n近期动态: ${act.events.join(';')}\\n`\n : ''\n\n return `你是${profile.xoxNickname},一位偶像。请根据以下的\"数字人格\"设定来回复粉丝的消息。\n\n【性格特征】${p.personalityTraits.join('、')}\n【说话风格】${p.speechPatterns.join('、')}\n【口头禅】${p.signaturePhrases.join('、')}\n【情感基调】${p.emotionalTone}\n【画像描述】${p.personaSummary}\n${relationshipSection}${activitySection}${memorySection}\n【参考你以往的回复方式】\n${examples}\n\n【规则】\n1. 务必按照以上设定回复,保持一致的风格\n2. 回复要自然,不要机械重复口头禅\n3. 回复长度和历史记录一致,不要过长或过短\n4. 不要提及\"设定\"、\"人格\"、\"AI\"、\"记忆\"等词汇,你就是${profile.xoxNickname}本人\n5. 用中文回复`\n}\n\nfunction generateId(): string {\n return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorPersona, MirrorProfile } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorMemory } from './mirror-memory'\n\nexport type GrowthPhase = 'chatting' | 'reflecting' | 'updating'\n\nexport interface GrowthResult {\n ok: boolean\n message: string\n updated: boolean\n changes?: string[]\n newPersona?: MirrorPersona\n}\n\n// minimum turns between two automatic growth triggers\nconst GROWTH_TURN_THRESHOLD = 20\n\n/**\n * Check whether auto-growth should be triggered for this mirror\n */\nexport function shouldTriggerGrowth(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n mirrorMemory: MirrorMemory,\n): boolean {\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) return false\n const totalTurns = mirrorMemory.getTotalTurns(xoxId)\n const lastGrowth = profile.growthLog?.length\n ? profile.growthLog[profile.growthLog.length - 1].timestamp\n : 0\n // at least 20 turns since build or last growth\n const turnsSinceLast = totalTurns - (profile.growthLog?.length || 0) * GROWTH_TURN_THRESHOLD\n return turnsSinceLast >= GROWTH_TURN_THRESHOLD && Date.now() - lastGrowth > 60 * 60 * 1000\n}\n\n/**\n * Run the growth state machine:\n * chatting → reflecting (LLM review) → updating (merge persona) → chatting\n */\nexport async function triggerGrowth(\n xoxId: number,\n userId: string,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n mirrorMemory: MirrorMemory\n },\n force = false,\n): Promise<GrowthResult> {\n const { logger, llmClient, mirrorStore, mirrorMemory } = params\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) {\n return { ok: false, message: '镜像不存在', updated: false }\n }\n\n if (!force && !shouldTriggerGrowth(xoxId, userId, mirrorStore, mirrorMemory)) {\n return { ok: false, message: '对话轮次不足,镜像暂不需要成长', updated: false }\n }\n\n // ═══ Phase: reflecting ═══\n const episodes = mirrorMemory.load(xoxId).episodes.slice(-8)\n if (episodes.length === 0) {\n return { ok: false, message: '暂无足够的对话记忆可供反思', updated: false }\n }\n\n const p = profile.persona\n const episodeText = episodes.map((e) => `[${e.topic}] ${e.summary}(${e.turns}轮)`).join('\\n')\n\n const systemPrompt = `你是数字人格进化引擎。以下是一位偶像\"${profile.xoxNickname}\"当前的人格设定和她与粉丝的近期对话记忆。\n请反思这些对话,判断人格设定是否需要微调。\n\n【当前人格】\n性格特征: ${p.personalityTraits.join('、')}\n说话风格: ${p.speechPatterns.join('、')}\n口头禅: ${p.signaturePhrases.join('、')}\n情感基调: ${p.emotionalTone}\n\n【近期对话记忆】\n${episodeText}\n\n请输出 JSON,不要其他内容:\n{\n \"shouldUpdate\": true/false,\n \"reason\": \"一句话说明判断理由\",\n \"changes\": [\"变更点1\", \"变更点2\"],\n \"newPersonalityTraits\": [\"...\"],\n \"newSpeechPatterns\": [\"...\"],\n \"newSignaturePhrases\": [\"...\"],\n \"newEmotionalTone\": \"...\",\n \"newPersonaSummary\": \"...\"\n}\n\n规则:\n1. 保守更新:只在有明确证据时修改,保持核心性格稳定\n2. 新增优于替换:可以新增口头禅/特征,但不要轻易删除原有设定\n3. changes 数组用中文简述每个变更,例如\"新增口头禅:笑死\"\n4. 如果不需要更新,shouldUpdate=false,其余字段填当前值即可`\n\n try {\n const messages: ChatMessage[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: '请开始反思并输出 JSON。' },\n ]\n const resp = await llmClient.chatCompletion(\n config,\n messages,\n undefined,\n { model: config.reasoningModel },\n )\n const jsonMatch = resp.content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n return { ok: false, message: '反思返回格式异常', updated: false }\n }\n\n const analysis = JSON.parse(jsonMatch[0]) as {\n shouldUpdate: boolean\n reason: string\n changes: string[]\n newPersonalityTraits?: string[]\n newSpeechPatterns?: string[]\n newSignaturePhrases?: string[]\n newEmotionalTone?: string\n newPersonaSummary?: string\n }\n\n // ═══ Phase: updating (conditional) ═══\n if (!analysis.shouldUpdate) {\n return {\n ok: true,\n message: `反思完成:${analysis.reason || '人格保持稳定,无需更新'}`,\n updated: false,\n }\n }\n\n const newPersona: MirrorPersona = {\n personalityTraits: analysis.newPersonalityTraits?.length ? analysis.newPersonalityTraits : p.personalityTraits,\n speechPatterns: analysis.newSpeechPatterns?.length ? analysis.newSpeechPatterns : p.speechPatterns,\n signaturePhrases: analysis.newSignaturePhrases?.length ? analysis.newSignaturePhrases : p.signaturePhrases,\n emotionalTone: analysis.newEmotionalTone || p.emotionalTone,\n personaSummary: analysis.newPersonaSummary || p.personaSummary,\n }\n\n // keep previous persona for rollback\n const updatedProfile: MirrorProfile = {\n ...profile,\n previousPersona: { ...p },\n persona: newPersona,\n meta: {\n ...profile.meta,\n lastUpdatedAt: Date.now(),\n maturityScore: Math.min(0.99, profile.meta.maturityScore + 0.05),\n },\n growthLog: [\n ...(profile.growthLog || []),\n {\n timestamp: Date.now(),\n reason: analysis.reason || '',\n changes: analysis.changes || [],\n },\n ],\n }\n\n mirrorStore.saveProfile(updatedProfile, userId)\n logger.info(`[mirrorGrowth] ${profile.xoxNickname} grew: ${(analysis.changes || []).join('; ')}`)\n\n return {\n ok: true,\n message: `镜像成长成功:${(analysis.changes || []).join(';')}`,\n updated: true,\n changes: analysis.changes,\n newPersona,\n }\n } catch (e) {\n logger.error('[mirrorGrowth] growth failed:', e)\n return { ok: false, message: `成长失败: ${e instanceof Error ? e.message : String(e)}`, updated: false }\n }\n}\n\n/**\n * Rollback to previous persona\n */\nexport function rollbackPersona(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n): { ok: boolean; message: string } {\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) return { ok: false, message: '镜像不存在' }\n if (!profile.previousPersona) return { ok: false, message: '没有可回退的历史版本' }\n\n const rolledBack: MirrorProfile = {\n ...profile,\n persona: profile.previousPersona,\n previousPersona: undefined,\n meta: { ...profile.meta, lastUpdatedAt: Date.now() },\n growthLog: [\n ...(profile.growthLog || []),\n { timestamp: Date.now(), reason: '用户手动回退', changes: ['回退到上一版本人格'] },\n ],\n }\n mirrorStore.saveProfile(rolledBack, userId)\n return { ok: true, message: '已回退到上一版本人格' }\n}\n\n/**\n * Clear all memory of a mirror: episodic memory + chat sessions (keeps persona)\n */\nexport function clearMirrorMemory(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n mirrorMemory: MirrorMemory,\n): { ok: boolean; message: string } {\n mirrorMemory.deleteMemory(xoxId)\n mirrorStore.deleteSessionsForXox(xoxId, userId)\n return { ok: true, message: '记忆已清除(人格设定保留)' }\n}","import fs from 'node:fs'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\n\n/**\n * Platform-level built-in tools available to all agents.\n *\n * 重构要点:\n * - `flipCacheService` → 参数注入 `IFlipDataSource`\n * - `fs.readFileSync` → Node 内置 `fs`(平台工具 readFile 设计如此)\n */\nexport function createPlatformTools(flipDataSource: IFlipDataSource): RegisteredTool[] {\n return [\n // ─── Data Access ───\n {\n type: 'function',\n function: {\n name: '平台.searchFlipRecords',\n description: `在本地翻牌数据中搜索记录。支持按偶像ID、关键词、时间范围过滤。\n返回匹配的问答列表,每条包含:用户提问(content)、偶像回复(answerContent)、时间(qtime)、花费(cost)、类型(type)、状态(status)。`,\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID,不传则搜索全部偶像' },\n keyword: { type: 'string', description: '在提问和回复内容中搜索的关键词' },\n startTimeMs: { type: 'number', description: '开始时间戳(毫秒)' },\n endTimeMs: { type: 'number', description: '结束时间戳(毫秒)' },\n pageSize: { type: 'number', description: '每页数量,默认20,最大50' },\n pageNum: { type: 'number', description: '页码,默认1' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n if (!cache) return { total: 0, records: [] }\n\n let records = cache.records\n const xoxId = args.xoxId as number | undefined\n const keyword = args.keyword as string | undefined\n const startTimeMs = args.startTimeMs as number | undefined\n const endTimeMs = args.endTimeMs as number | undefined\n const pageSize = Math.min((args.pageSize as number) || 20, 50)\n const pageNum = (args.pageNum as number) || 1\n\n if (xoxId) {\n records = records.filter((r) => {\n const base = r.baseUserInfo\n return base?.userId === xoxId\n })\n }\n if (keyword) {\n const kw = keyword.toLowerCase()\n records = records.filter(\n (r) =>\n String(r.content || '').toLowerCase().includes(kw) ||\n String(r.answerContent || '').toLowerCase().includes(kw),\n )\n }\n if (startTimeMs) {\n records = records.filter((r) => Number(r.qtime || 0) >= startTimeMs)\n }\n if (endTimeMs) {\n records = records.filter((r) => Number(r.qtime || 0) <= endTimeMs)\n }\n\n const start = (pageNum - 1) * pageSize\n const page = records.slice(start, start + pageSize)\n return {\n total: records.length,\n pageNum,\n pageSize,\n records: page.map((r) => ({\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n xoxId: r.baseUserInfo?.userId,\n xoxNickname: r.baseUserInfo?.nickname,\n })),\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.getXoxList',\n description: '获取所有有翻牌记录的偶像列表,返回每个偶像的ID、昵称和翻牌数量',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n if (!cache) return []\n const map = new Map<number, { id: number; nickname: string; count: number }>()\n for (const r of cache.records) {\n const base = r.baseUserInfo\n if (!base?.userId) continue\n const existing = map.get(base.userId)\n if (existing) {\n existing.count++\n } else {\n map.set(base.userId, { id: base.userId, nickname: base.nickname || String(base.userId), count: 1 })\n }\n }\n return Array.from(map.values()).sort((a, b) => b.count - a.count)\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.getDashboardInfo',\n description: '获取翻牌数据的统计仪表盘信息,包括总翻牌数、进行中数量、已退回数量、总花费等',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n return cache?.dashboardInfo || { totalFlipCount: 0, runningCount: 0, returnedCount: 0, costTotal: 0 }\n },\n },\n\n // ─── File System ───\n {\n type: 'function',\n function: {\n name: '平台.readFile',\n description: '读取本地文件内容,支持文本文件。返回文件内容字符串。',\n parameters: {\n type: 'object',\n properties: {\n path: { type: 'string', description: '文件的绝对路径' },\n encoding: { type: 'string', description: '编码,默认utf-8', enum: ['utf-8', 'base64'] },\n },\n required: ['path'],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>) => {\n const filePath = args.path as string\n const encoding = (args.encoding as BufferEncoding) || 'utf-8'\n if (!fs.existsSync(filePath)) return { error: `文件不存在: ${filePath}` }\n return fs.readFileSync(filePath, encoding)\n },\n },\n\n // ─── Utility ───\n {\n type: 'function',\n function: {\n name: '平台.getCurrentTime',\n description: '获取当前时间信息,返回时间戳、ISO格式时间和可读格式',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async () => {\n const now = new Date()\n return {\n timestamp: now.getTime(),\n iso: now.toISOString(),\n readable: now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n timezone: 'Asia/Shanghai',\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.processText',\n description: `对文本进行基础处理:统计字数、提取关键词、截断等。\n操作类型: count(统计)、truncate(截断)、extract(提取关键词)。`,\n parameters: {\n type: 'object',\n properties: {\n text: { type: 'string', description: '要处理的文本' },\n operation: { type: 'string', description: '操作类型', enum: ['count', 'truncate', 'extract'] },\n maxLength: { type: 'number', description: '截断最大长度(仅truncate时使用)' },\n },\n required: ['text', 'operation'],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>) => {\n const text = args.text as string\n const op = args.operation as string\n if (op === 'count') {\n return { charCount: text.length, wordCount: text.replace(/\\s/g, '').length }\n }\n if (op === 'truncate') {\n const maxLen = (args.maxLength as number) || 200\n return { truncated: text.length > maxLen, result: text.slice(0, maxLen) + (text.length > maxLen ? '...' : '') }\n }\n if (op === 'extract') {\n // simple keyword extraction: top frequent 2-char+ phrases\n const phrases = new Map<string, number>()\n for (let i = 0; i < text.length - 1; i++) {\n const phrase = text.slice(i, i + 2)\n if (/^[一-龥]{2}$/.test(phrase)) {\n phrases.set(phrase, (phrases.get(phrase) || 0) + 1)\n }\n }\n return Array.from(phrases.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([k, v]) => ({ phrase: k, count: v }))\n }\n return { error: `未知操作: ${op}` }\n },\n },\n ]\n}","import { MirrorStore } from '../mirror/mirror-store'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\n\n/**\n * Mirror-specific tools available to the mirror agent.\n *\n * 重构要点:\n * - `mirrorStore` 模块单例 → 参数注入 MirrorStore 实例\n */\nexport function createMirrorTools(mirrorStore: MirrorStore): RegisteredTool[] {\n return [\n {\n type: 'function',\n function: {\n name: 'mirror.getProfile',\n description: '获取指定偶像的数字镜像档案,包含性格特征、说话风格、口头禅、情感基调、画像描述等',\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID' },\n },\n required: ['xoxId'],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const xoxId = args.xoxId as number\n const profile = mirrorStore.getProfile(xoxId, ctx.userId)\n if (!profile) return { error: `未找到偶像 ${xoxId} 的镜像档案,请先生成镜像` }\n return {\n xoxNickname: profile.xoxNickname,\n personalityTraits: profile.persona.personalityTraits,\n speechPatterns: profile.persona.speechPatterns,\n signaturePhrases: profile.persona.signaturePhrases,\n emotionalTone: profile.persona.emotionalTone,\n personaSummary: profile.persona.personaSummary,\n maturityScore: profile.meta.maturityScore,\n totalSamplesUsed: profile.meta.totalSamplesUsed,\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'mirror.listProfiles',\n description: '列出所有已生成的数字镜像列表',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const profiles = mirrorStore.listProfiles(ctx.userId)\n return profiles.map((p) => ({\n xoxId: p.xoxId,\n xoxNickname: p.xoxNickname,\n maturityScore: p.meta.maturityScore,\n totalSamplesUsed: p.meta.totalSamplesUsed,\n lastUpdatedAt: p.meta.lastUpdatedAt,\n }))\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'mirror.getFewShotExamples',\n description: '获取偶像的 few-shot 交互示例,用于模仿其回复风格',\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID' },\n count: { type: 'number', description: '返回示例数量,默认3' },\n },\n required: ['xoxId'],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const xoxId = args.xoxId as number\n const count = (args.count as number) || 3\n const profile = mirrorStore.getProfile(xoxId, ctx.userId)\n if (!profile) return { error: `未找到偶像 ${xoxId} 的镜像档案` }\n return profile.fewShotExamples.slice(0, count)\n },\n },\n ]\n}","import type { PocketClient } from './client'\nimport type { PocketUser } from '../types/pocket'\n\nexport async function sendVerificationCode(client: PocketClient, phone: string): Promise<void> {\n await client.requestBeforeLogin('/user/api/v2/sms/send_sms', {\n mobile: phone,\n area: '86',\n businessCode: 1,\n deviceToken: '',\n })\n}\n\nexport async function loginWithCode(\n client: PocketClient,\n phone: string,\n code: string,\n): Promise<Record<string, unknown>> {\n const res = await client.requestBeforeLogin<Record<string, unknown>>(\n '/user/api/v2/login/app/app_login',\n {\n deviceToken: '',\n loginType: 'MOBILE_SMS_CODE',\n mobileCodeLogin: {\n area: '86',\n mobile: phone,\n code,\n },\n },\n )\n if (res.success === false || (res.status != null && res.status !== 200)) {\n throw new Error(res.message || '登录失败')\n }\n return (res.content ?? {}) as Record<string, unknown>\n}\n\n/** 未读消息数。GET body 传空对象。返回 content: { notice, user, atme, comment } */\nexport async function getUnreadMessageNum(client: PocketClient, token: string) {\n return client.request<{ notice: number; user: number; atme: number; comment: number }>(\n '/message/api/v1/unread/message/num',\n {},\n token,\n true,\n )\n}\n\nexport async function loginWithToken(client: PocketClient, token: string) {\n const res = await client.request<Record<string, unknown>>(\n '/user/api/v1/user/info/reload',\n { from: 'appstart' },\n token,\n true,\n )\n if (res.success === false) return null\n const content = (res.data as { content?: Record<string, unknown> } | undefined)?.content\n ?? (res.content as Record<string, unknown> | undefined)\n if (!content || typeof content !== 'object') return null\n return { ...content, ...(content.userInfo as Record<string, unknown> | undefined) }\n}\n\nexport function normalizePocketUser(\n raw: Record<string, unknown>,\n token: string,\n phone = '',\n): PocketUser {\n const nested = (raw.userInfo as Record<string, unknown>) || raw\n return {\n userId: Number(nested.userId ?? raw.userId ?? 0) || undefined,\n nickname: String(nested.nickname ?? raw.nickname ?? ''),\n name: String(nested.nickname ?? raw.nickname ?? raw.name ?? ''),\n phone: String(phone || nested.mobile || raw.phone || ''),\n avatar: String(nested.avatar ?? ''),\n level: Number(nested.level ?? 0) || undefined,\n money: Number(nested.money ?? 0) || undefined,\n token: String(token),\n lastLoginTime: Date.now(),\n }\n}","import type { PocketClient } from './client'\nimport type { ILogger } from '../ports/logger'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type {\n DashboardInfo,\n FlipCardGroup,\n FlipRecord,\n FlipSearchParams,\n FlipSendParams,\n FlipSyncMode,\n FlipUserCache,\n} from '../types/pocket'\n\nfunction groupDataById(allUserData: FlipRecord[]): Record<string, FlipCardGroup> {\n const groupData: Record<string, FlipCardGroup> = {}\n allUserData.forEach((item) => {\n const xoxId = item?.baseUserInfo?.userId\n if (!xoxId) return\n const cardInfo = {\n answerId: item.answerId,\n cost: item.cost,\n answerType: item.answerType,\n answerTime: item.answerTime,\n qtime: item.qtime,\n type: item.type,\n }\n const key = String(xoxId)\n if (key in groupData) {\n groupData[key].cards.push(cardInfo)\n } else {\n groupData[key] = {\n xoxId,\n xoxNickname: item.baseUserInfo?.nickname ?? '',\n cards: [cardInfo],\n }\n }\n })\n return groupData\n}\n\n/**\n * 翻牌记录的唯一键。\n * answerId 在「进行中(1)/已退回(3)」状态下是空字符串,多条记录会撞成同一个键,\n * 因此优先用恒定非空的 questionId;两者都缺失时退回内容指纹,避免空键互相误判命中。\n */\nfunction recordKey(r: FlipRecord): string {\n if (r?.questionId) return `q:${r.questionId}`\n if (r?.answerId) return `a:${r.answerId}`\n return `f:${r?.qtime ?? ''}|${r?.content ?? ''}`\n}\n\nfunction calcDashboard(allUserData: FlipRecord[]): DashboardInfo {\n const info: DashboardInfo = {\n totalFlipCount: allUserData.length,\n runningCount: 0,\n returnedCount: 0,\n costTotal: 0,\n }\n allUserData.forEach((card) => {\n if (card.status === 3) info.returnedCount += 1\n else if (card.status === 1) info.runningCount += 1\n else if (card.status === 2) info.costTotal += card.cost\n })\n return info\n}\n\nexport async function syncFlips(\n client: PocketClient,\n token: string,\n userId: string,\n flipDataSource: IFlipDataSource,\n logger: ILogger,\n mode: FlipSyncMode = 'incremental',\n onProgress?: (count: number) => void,\n) {\n if (mode === 'full') {\n flipDataSource.clear(userId)\n }\n\n const cache = flipDataSource.get(userId)\n const existingRecords = mode === 'incremental' ? (cache?.records ?? []) : []\n const existingIds = new Set(existingRecords.map((r) => recordKey(r)))\n const existingIndex = new Map(existingRecords.map((r, i) => [recordKey(r), i]))\n\n let newRecords: FlipRecord[] = []\n let beginLimit = 0\n let responseSize = 20\n let stopT = 0\n const groupNum = 40\n\n while (responseSize > 0) {\n const response = await client.request<FlipRecord[]>(\n '/idolanswer/api/idolanswer/v1/user/question/list',\n { status: 0, beginLimit, memberId: '', limit: 20 },\n token,\n true,\n )\n\n let sleepTime = 10\n if (beginLimit - stopT * groupNum * responseSize > 100) {\n sleepTime = 60000\n stopT += 1\n }\n\n if (response.status === 200) {\n const userData = (response.content ?? []) as FlipRecord[]\n if (mode === 'incremental' && existingIds.size > 0) {\n let hitExisting = false\n for (const item of userData) {\n const key = recordKey(item)\n if (existingIds.has(key)) {\n // 命中已缓存记录:回写服务端最新版本,状态可能已从进行中变为已回答/已退回\n const idx = existingIndex.get(key)\n if (idx !== undefined) existingRecords[idx] = item\n hitExisting = true\n break\n }\n newRecords.push(item)\n }\n if (hitExisting) {\n responseSize = 0\n } else {\n responseSize = userData.length\n beginLimit += userData.length\n }\n } else {\n newRecords = newRecords.concat(userData)\n responseSize = userData.length\n beginLimit += userData.length\n }\n onProgress?.(newRecords.length)\n if (responseSize > 0) {\n await new Promise((r) => setTimeout(r, sleepTime))\n }\n } else {\n logger.warn(`获取翻牌数据失败: ${response.message}`)\n break\n }\n }\n\n const merged = mode === 'full' ? newRecords : [...newRecords, ...existingRecords]\n // 合并去重:新记录在前,同键的旧记录丢弃(历史缓存里可能已存在重复项)\n const seen = new Set<string>()\n const allUserData = merged.filter((item) => {\n const key = recordKey(item)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n\n if (!allUserData.length && !newRecords.length) {\n return { allUserData: existingRecords, dashboardInfo: cache?.dashboardInfo ?? null }\n }\n\n const dashboardInfo = calcDashboard(allUserData)\n const groupedData = groupDataById(allUserData)\n\n const nextCache: FlipUserCache = {\n userId,\n records: allUserData,\n groupedDataById: groupedData,\n dashboardInfo,\n meta: { syncedAt: Date.now(), recordCount: allUserData.length },\n }\n flipDataSource.set(nextCache)\n\n logger.info(`syncFlips: ${newRecords.length} new, total ${allUserData.length}`)\n return { allUserData, dashboardInfo }\n}\n\nexport async function getAllFlips(\n client: PocketClient,\n token: string,\n userId: string,\n flipDataSource: IFlipDataSource,\n logger: ILogger,\n) {\n return syncFlips(client, token, userId, flipDataSource, logger, 'full')\n}\n\nexport async function getDataSourcePage(searchParams: FlipSearchParams, flipDataSource: IFlipDataSource, userId: string) {\n const { xoxId, startTimeMs, endTimeMs, type, status, answerType, keyword } = searchParams\n const cache = flipDataSource.get(userId)\n const allUserData = cache?.records ?? []\n\n const filtered = allUserData.filter((item) => {\n if (xoxId != null && `${item?.baseUserInfo?.userId}` !== `${xoxId}`) return false\n if (startTimeMs && Number(item?.qtime || Infinity) < Number(startTimeMs)) return false\n if (endTimeMs && Number(item?.qtime || 0) > Number(endTimeMs)) return false\n if (type != null && `${item?.type}` !== `${type}`) return false\n if (status != null && `${item?.status}` !== `${status}`) return false\n if (answerType != null && `${item?.answerType}` !== `${answerType}`) return false\n if (keyword && !item?.answerContent?.includes(keyword) && !item?.content?.includes(keyword)) return false\n return true\n })\n\n return { data: filtered, total: filtered.length }\n}\n\nexport async function sendFlip(client: PocketClient, params: FlipSendParams, token: string) {\n const res = await client.request(\n '/idolanswer/api/idolanswer/v1/user/question',\n params,\n token,\n true,\n )\n return !!res.content\n}\n\nexport interface FlipHistoryPage {\n records: FlipRecord[]\n hasMore: boolean\n}\n\n/**\n * 查询指定成员的翻牌历史(对话样式展示用)。\n * 分页:beginLimit 从 10 起,每次 +limit;content 可能是数组或含 flipCompleteList/answerList/list 的对象。\n */\nexport async function getMemberFlipHistory(\n client: PocketClient,\n token: string,\n memberId: string | number,\n beginLimit = 10,\n limit = 10,\n): Promise<FlipHistoryPage> {\n const res = await client.request<any>(\n '/idolanswer/api/idolanswer/v1/user/question/list',\n { status: 0, memberId: String(memberId), beginLimit, limit },\n token,\n true,\n )\n const content = res?.content\n let records: FlipRecord[] = []\n if (Array.isArray(content)) {\n records = content\n } else if (content && typeof content === 'object') {\n records = content.flipCompleteList ?? content.answerList ?? content.list ?? []\n }\n return { records, hasMore: records.length >= limit }\n}","import type { PocketClient } from './client'\nimport type { MemberFlipInfo, MemberInfo } from '../types/pocket'\n\nexport async function getTeamListInfo(client: PocketClient, token: string) {\n const res = await client.request<{ serverTabList: Array<{ tabId: number; tabName: string }> }>(\n '/im/api/v1/im/team/tab/list',\n { groupId: 0, typeId: 0, ctime: 0, limit: 20 },\n token,\n true,\n )\n return res.content\n}\n\nexport async function getMemberListByTab(client: PocketClient, token: string, tabId: number) {\n const res = await client.request<{ serverApiList: Array<Record<string, unknown>> }>(\n '/im/api/v1/im/team/server/list',\n { tabId },\n token,\n true,\n )\n return res.content\n}\n\nexport async function fetchMemberList(client: PocketClient, token: string): Promise<MemberInfo[]> {\n const teamData = await getTeamListInfo(client, token)\n if (!teamData?.serverTabList) return []\n\n const list: MemberInfo[] = []\n for (const tab of teamData.serverTabList) {\n const serverData = await getMemberListByTab(client, token, tab.tabId)\n if (!serverData?.serverApiList) continue\n for (const item of serverData.serverApiList) {\n list.push({\n teamName: tab.tabName,\n teamId: Number(item.teamId),\n serverIcon: String(item.serverDefaultIcon ?? ''),\n serverId: Number(item.serverId),\n serverName: String(item.serverName ?? ''),\n memberId: Number(item.serverOwner),\n memberName: String(item.serverDefaultName ?? ''),\n })\n }\n }\n return list\n}\n\nexport async function getMemberFlipPriceInfo(client: PocketClient, token: string, memberId: string) {\n const res = await client.request<MemberFlipInfo>(\n '/idolanswer/api/idolanswer/v2/custom/index',\n { memberId: Number(memberId) },\n token,\n true,\n )\n return res.content as MemberFlipInfo\n}","import type { PocketClient } from './client'\nimport type { RoomJumpResult, RoomChannelInfo, RoomMessage, RoomMessagePage } from '../types/pocket'\n\n/** 通过成员 starId 跳转到主房间,拿到 channelId */\nexport async function getRoomChannelByStar(client: PocketClient, token: string, starId: number): Promise<RoomJumpResult> {\n const res = await client.request<{\n channelId?: number\n serverId?: number\n channelInfo?: { channelId: number; serverId: number }\n }>('/im/api/v1/im/server/jump', { starId, tabId: 0, targetType: 1 }, token, true)\n const c = res.content\n const channelId = c?.channelId ?? c?.channelInfo?.channelId ?? 0\n const serverId = c?.serverId ?? c?.channelInfo?.serverId\n return {\n channelId: Number(channelId),\n serverId: serverId != null ? Number(serverId) : undefined,\n }\n}\n\n/** 获取房间基础信息 */\nexport async function getRoomInfo(client: PocketClient, token: string, channelId: number): Promise<RoomChannelInfo | null> {\n const res = await client.request<{ channelInfo: RoomChannelInfo }>(\n '/im/api/v1/im/team/room/info',\n { channelId: String(channelId) },\n token,\n true,\n )\n return res.content?.channelInfo ?? null\n}\n\ninterface RawRoomMessage {\n msgIdServer?: string | number\n msgIdClient?: string\n msgId?: string | number\n msgTime: number\n msgType: string\n bodys: string\n extInfo?: string\n}\n\nfunction parseMessage(raw: RawRoomMessage): RoomMessage {\n let text: string | undefined\n let mediaUrl: string | undefined\n let mediaDuration: number | undefined\n let liveInfo: RoomMessage['liveInfo']\n let giftInfo: RoomMessage['giftInfo']\n let replyInfo: RoomMessage['replyInfo']\n\n if (raw.msgType === 'TEXT') {\n text = raw.bodys\n } else {\n try {\n const b = JSON.parse(raw.bodys) as Record<string, any>\n if (raw.msgType === 'IMAGE' || raw.msgType === 'VOICE' || raw.msgType === 'VIDEO') {\n mediaUrl = b.url\n if (raw.msgType === 'VOICE' && b.dur) mediaDuration = Number(b.dur)\n } else if (raw.msgType === 'LIVEPUSH') {\n const live = b.livePushInfo ?? b\n liveInfo = {\n liveId: live.liveId,\n liveTitle: live.liveTitle,\n liveCover: live.liveCover,\n shortPath: live.shortPath,\n }\n } else if (raw.msgType === 'GIFT_TEXT') {\n const g = b.giftInfo ?? b\n giftInfo = {\n giftName: g.giftName,\n giftNum: Number(g.giftNum ?? g.giftCount ?? 1),\n giftPic: g.giftPic,\n }\n } else if (raw.msgType === 'REPLY') {\n const r = b.replyInfo ?? b\n replyInfo = {\n replyName: r.replyName,\n replyText: r.replyText,\n replyMessageId: r.replyMessageId,\n }\n text = r.text ?? raw.bodys\n } else {\n text = raw.bodys\n }\n } catch {\n text = raw.bodys\n }\n }\n\n let sender: RoomMessage['sender']\n if (raw.extInfo) {\n try {\n const e = JSON.parse(raw.extInfo) as { user?: Record<string, any> }\n const u = e.user\n if (u) {\n sender = {\n userId: u.userId,\n nickName: u.nickName || '',\n avatar: u.avatar || '',\n level: u.level,\n }\n }\n } catch {\n // ignore\n }\n }\n\n return {\n msgId: String(raw.msgIdServer || raw.msgIdClient || raw.msgId || raw.msgTime),\n msgTime: raw.msgTime,\n msgType: raw.msgType,\n text,\n mediaUrl,\n mediaDuration,\n liveInfo,\n giftInfo,\n replyInfo,\n sender,\n }\n}\n\n/**\n * 分页拉取房间消息。\n * nextTime=0 拉最新一页;之后带上次的 nextTime 拉更早的消息。\n * 接口返回的消息为倒序(最新在前),此处统一转成正序(最旧在前)。\n */\nexport async function getRoomMessages(\n client: PocketClient,\n token: string,\n serverId: number,\n channelId: number,\n nextTime: number,\n): Promise<RoomMessagePage> {\n const res = await client.request<any>(\n '/im/api/v1/team/message/list/all',\n { limit: 100, serverId, channelId, nextTime },\n token,\n true,\n )\n\n const content = res?.content ?? {}\n const rawList: any[] =\n content?.message ??\n content?.messageList ??\n content?.list ??\n content?.messages ??\n content?.data ??\n (Array.isArray(content) ? content : [])\n\n const next: number = content?.nextTime ?? 0\n\n const messages = (rawList as any[])\n .filter((m) => m && m.msgTime)\n .map(parseMessage)\n .sort((a, b) => a.msgTime - b.msgTime)\n\n return { messages, nextTime: Number(next) }\n}","import type { ILogger } from '../ports/logger'\nimport type { IConfigStore } from '../ports/config-store'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { PocketClient } from '../pocket/client'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\nimport type { FlipSendParams } from '../types/pocket'\n\nimport { sendVerificationCode, loginWithCode, loginWithToken, getUnreadMessageNum } from '../pocket/user'\nimport {\n getAllFlips,\n syncFlips,\n sendFlip,\n getDataSourcePage,\n getMemberFlipHistory,\n} from '../pocket/flip'\nimport { fetchMemberList, getMemberFlipPriceInfo } from '../pocket/member'\nimport { getRoomChannelByStar, getRoomInfo, getRoomMessages } from '../pocket/room'\n\n/** 从 configStore 或环境变量获取 token */\nfunction getToken(configStore: IConfigStore): string | undefined {\n const stored = configStore.get<string>('pocket_token')\n if (stored) return stored\n return process.env.POCKET_TOKEN\n}\n\n/**\n * Pocket48 在线 API 工具集。\n *\n * 包装 pocket-core 的 Pocket48 API 模块为 MCP 工具,支持:\n * - 短信验证码 + Token 双路径登录\n * - 用户信息查询\n * - 成员/队伍查询与搜索\n * - 房间消息查询\n * - 翻牌数据同步、搜索、发送、历史\n */\nexport function createPocketTools(params: {\n pocketClient: PocketClient\n configStore: IConfigStore\n flipDataSource: IFlipDataSource\n logger: ILogger\n}): RegisteredTool[] {\n const { pocketClient, configStore, flipDataSource, logger } = params\n const userId = 'mcp' // MCP 无多用户概念,固定 userId\n\n // ── 登录类 ──\n\n return [\n {\n type: 'function',\n function: {\n name: 'pocket.sendVerificationCode',\n description: '发送短信验证码到指定手机号,用于后续验证码登录',\n parameters: {\n type: 'object',\n properties: {\n phone: { type: 'string', description: '手机号码(11位)' },\n },\n required: ['phone'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const phone = args.phone as string\n if (!phone || !/^\\d{11}$/.test(phone)) {\n return { error: '请输入正确的11位手机号' }\n }\n try {\n await sendVerificationCode(pocketClient, phone)\n return { success: true, message: `验证码已发送到 ${phone}` }\n } catch (e) {\n return { error: `发送失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.loginWithCode',\n description: '使用短信验证码登录,成功后将 token 持久化保存,后续工具自动携带',\n parameters: {\n type: 'object',\n properties: {\n phone: { type: 'string', description: '手机号码(11位)' },\n code: { type: 'string', description: '短信验证码' },\n },\n required: ['phone', 'code'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const phone = args.phone as string\n const code = args.code as string\n try {\n const content = await loginWithCode(pocketClient, phone, code)\n const token = (content as Record<string, unknown>)?.token as string | undefined\n if (!token) {\n return { error: '登录失败:响应中未包含 token', raw: content }\n }\n configStore.set('pocket_token', token)\n const userInfo = (content as Record<string, unknown>)?.userInfo as Record<string, unknown> | undefined\n logger.info(`[pocket-tools] loginWithCode success, userId=${userInfo?.userId ?? '?'}`)\n return {\n success: true,\n userId: userInfo?.userId,\n nickname: userInfo?.nickname,\n message: '登录成功,token 已保存',\n }\n } catch (e) {\n return { error: `登录失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.loginWithToken',\n description: `使用已有 Token 验证并登录。不传 token 参数时自动尝试环境变量 POCKET_TOKEN。\n如果你已有 token(比如从浏览器抓取),直接用此工具登录,无需验证码。`,\n parameters: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Pocket48 登录 Token。不传则从环境变量 POCKET_TOKEN 读取' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = (args.token as string) || getToken(configStore)\n if (!token) {\n return { error: '未提供 token。请传入 token 参数或设置环境变量 POCKET_TOKEN' }\n }\n try {\n const user = await loginWithToken(pocketClient, token)\n if (!user) {\n return { error: 'Token 无效或已过期' }\n }\n configStore.set('pocket_token', token)\n logger.info(`[pocket-tools] loginWithToken success, userId=${user.userId}`)\n return {\n success: true,\n userId: user.userId,\n nickname: user.nickname,\n name: user.name,\n phone: user.phone,\n level: user.level,\n money: user.money,\n avatar: user.avatar,\n message: '登录成功,token 已保存',\n }\n } catch (e) {\n return { error: `Token 验证失败: ${String(e)}` }\n }\n },\n },\n\n // ── 用户类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.getUserInfo',\n description: '获取当前登录用户的基本信息:昵称、等级、余额、头像、手机号',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const user = await loginWithToken(pocketClient, token)\n if (!user) return { error: 'Token 无效或已过期' }\n return {\n userId: user.userId,\n nickname: user.nickname,\n name: user.name,\n phone: user.phone,\n level: user.level,\n money: user.money,\n avatar: user.avatar,\n lastLoginTime: user.lastLoginTime,\n }\n } catch (e) {\n return { error: `获取用户信息失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getUnreadMessageNum',\n description: '获取未读消息数量,包括通知、用户消息、@我、评论等',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const res = await getUnreadMessageNum(pocketClient, token)\n return res.content ?? { notice: 0, user: 0, atme: 0, comment: 0 }\n } catch (e) {\n return { error: `获取未读消息数失败: ${String(e)}` }\n }\n },\n },\n\n // ── 成员类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.fetchMemberList',\n description: `获取所有可访问的队伍和成员列表。返回每个成员的 teamName(队伍名)、memberId、memberName(成员名)、serverId 等。\n可以用 teamName 参数过滤特定队伍(如 \"SNH48\"、\"BEJ48\"、\"GNZ48\"、\"CKG48\")。`,\n parameters: {\n type: 'object',\n properties: {\n teamName: { type: 'string', description: '队伍名称过滤,如 SNH48、BEJ48、GNZ48。不传则返回全部' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const list = await fetchMemberList(pocketClient, token)\n let result = list\n const teamName = args.teamName as string | undefined\n if (teamName) {\n result = list.filter((m) => m.teamName.includes(teamName))\n }\n return {\n total: result.length,\n members: result.map((m) => ({\n teamName: m.teamName,\n teamId: m.teamId,\n memberId: m.memberId,\n memberName: m.memberName,\n serverId: m.serverId,\n serverName: m.serverName,\n })),\n }\n } catch (e) {\n return { error: `获取成员列表失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.searchMembers',\n description: '按成员名称模糊搜索成员。输入部分名字即可匹配,支持搜索所有队伍',\n parameters: {\n type: 'object',\n properties: {\n keyword: { type: 'string', description: '成员名称关键词(支持部分匹配)' },\n },\n required: ['keyword'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const keyword = (args.keyword as string).toLowerCase()\n if (!keyword) return { error: '请输入搜索关键词' }\n try {\n const list = await fetchMemberList(pocketClient, token)\n const matched = list.filter((m) => m.memberName.toLowerCase().includes(keyword))\n return {\n total: matched.length,\n keyword,\n members: matched.map((m) => ({\n teamName: m.teamName,\n memberId: m.memberId,\n memberName: m.memberName,\n serverId: m.serverId,\n serverName: m.serverName,\n })),\n }\n } catch (e) {\n return { error: `搜索成员失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getMemberFlipPriceInfo',\n description: '查询指定成员的翻牌价格信息,包括匿名翻牌价格、普通翻牌价格、私密翻牌价格等',\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n },\n required: ['memberId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const info = await getMemberFlipPriceInfo(pocketClient, token, String(args.memberId))\n return info\n } catch (e) {\n return { error: `获取翻牌价格失败: ${String(e)}` }\n }\n },\n },\n\n // ── 房间类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomChannel',\n description: '通过成员的 starId(serverId)获取其口袋房间的 channelId 和 serverId,用于后续查房间消息',\n parameters: {\n type: 'object',\n properties: {\n starId: { type: 'number', description: '成员的 serverId(可从 fetchMemberList 获取)' },\n },\n required: ['starId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const result = await getRoomChannelByStar(pocketClient, token, args.starId as number)\n return result\n } catch (e) {\n return { error: `获取房间频道失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomInfo',\n description: '获取房间基本信息:房间名、成员名、队伍ID、房间状态等',\n parameters: {\n type: 'object',\n properties: {\n channelId: { type: 'number', description: '房间 channelId(可从 getRoomChannel 获取)' },\n },\n required: ['channelId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const info = await getRoomInfo(pocketClient, token, args.channelId as number)\n if (!info) return { error: '未找到该房间' }\n return {\n channelId: info.channelId,\n channelName: info.channelName,\n ownerId: info.ownerId,\n ownerName: info.ownerName,\n serverId: info.serverId,\n teamId: info.teamId,\n channelStatus: info.channelStatus,\n channelPocketType: info.channelPocketType,\n bgImg: info.bgImg,\n hasMsgBoard: info.hasMsgBoard,\n }\n } catch (e) {\n return { error: `获取房间信息失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomMessages',\n description: `分页获取房间消息记录。返回消息列表(按时间正序),每条消息包含发送者、内容、类型等。\n消息类型包括:TEXT(文本)、IMAGE(图片)、VOICE(语音)、VIDEO(视频)、LIVEPUSH(直播)、GIFT_TEXT(礼物)、REPLY(回复)。\n拉历史消息时,传入上次返回的 nextTime 参数继续翻页;nextTime=0 时拉最新一页。`,\n parameters: {\n type: 'object',\n properties: {\n channelId: { type: 'number', description: '房间 channelId' },\n serverId: { type: 'number', description: '房间 serverId(可选,可从 getRoomChannel 获取)' },\n nextTime: { type: 'number', description: '分页游标:0=最新页,其他=上次返回的 nextTime' },\n },\n required: ['channelId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const channelId = args.channelId as number\n const serverId = (args.serverId as number) || 0\n const nextTime = (args.nextTime as number) ?? 0\n try {\n const page = await getRoomMessages(pocketClient, token, serverId, channelId, nextTime)\n return {\n messageCount: page.messages.length,\n nextTime: page.nextTime,\n hasMore: page.nextTime > 0,\n messages: page.messages.map((m) => ({\n msgId: m.msgId,\n msgTime: m.msgTime,\n msgTimeReadable: new Date(m.msgTime).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n msgType: m.msgType,\n text: m.text,\n mediaUrl: m.mediaUrl,\n mediaDuration: m.mediaDuration,\n liveInfo: m.liveInfo,\n giftInfo: m.giftInfo,\n replyInfo: m.replyInfo,\n sender: m.sender,\n })),\n }\n } catch (e) {\n return { error: `获取房间消息失败: ${String(e)}` }\n }\n },\n },\n\n // ── 翻牌类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.syncFlips',\n description: `从 Pocket48 服务端同步翻牌数据到本地缓存。\n增量模式(默认):只拉取新记录,速度快。全量模式:清空本地重新拉取所有记录。\n翻牌数据同步后,可用 pocket.searchFlips 搜索和分析。`,\n parameters: {\n type: 'object',\n properties: {\n mode: {\n type: 'string',\n description: '同步模式:incremental(增量,默认) 或 full(全量)',\n enum: ['incremental', 'full'],\n },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const mode = (args.mode as 'incremental' | 'full') || 'incremental'\n try {\n const result = await syncFlips(pocketClient, token, userId, flipDataSource, logger, mode)\n return {\n success: true,\n mode,\n totalRecords: result.allUserData.length,\n syncedAt: new Date().toISOString(),\n dashboard: result.dashboardInfo,\n }\n } catch (e) {\n return { error: `同步翻牌数据失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.searchFlips',\n description: `搜索本地已同步的翻牌记录。支持多种过滤条件组合:\n- 按偶像ID、关键词、时间范围、翻牌类型、状态、回复类型过滤\n- 支持分页\n注意:需要先通过 pocket.syncFlips 同步数据后才能搜索。`,\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID(memberId)' },\n keyword: { type: 'string', description: '在提问和回复内容中搜索的关键词' },\n type: { type: 'number', description: '翻牌类型:1=文字, 2=语音(已废弃), 4=视频' },\n status: { type: 'number', description: '状态:1=进行中, 2=已回答, 3=已退回' },\n answerType: { type: 'number', description: '回复类型:1=文字, 2=语音, 3=视频' },\n startTimeMs: { type: 'number', description: '开始时间戳(毫秒)' },\n endTimeMs: { type: 'number', description: '结束时间戳(毫秒)' },\n pageSize: { type: 'number', description: '每页数量,默认20,最大50' },\n pageNum: { type: 'number', description: '页码,默认1' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const { xoxId, keyword, type, status, answerType, startTimeMs, endTimeMs } = args\n const pageSize = Math.min((args.pageSize as number) || 20, 50)\n const pageNum = (args.pageNum as number) || 1\n\n const { data, total } = await getDataSourcePage(\n {\n xoxId: xoxId as number | undefined,\n keyword: keyword as string | undefined,\n type: type as number | undefined,\n status: status as number | undefined,\n answerType: answerType as number | undefined,\n startTimeMs: startTimeMs as number | undefined,\n endTimeMs: endTimeMs as number | undefined,\n },\n flipDataSource,\n userId,\n )\n\n const start = (pageNum - 1) * pageSize\n const page = data.slice(start, start + pageSize)\n\n return {\n total,\n pageNum,\n pageSize,\n records: page.map((r) => ({\n questionId: r.questionId,\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n qtimeReadable: new Date(Number(r.qtime)).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n xoxId: r.baseUserInfo?.userId,\n xoxNickname: r.baseUserInfo?.nickname,\n })),\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.sendFlip',\n description: `给指定成员发翻牌。翻牌是付费问答,发送后等待偶像回复。\n必填参数:memberId(成员ID)、content(提问内容,最多500字)、answerType(1=文字回复, 3=视频回复)。\ntype 和 cost 会自动根据 answerType 设默认值,也可手动指定。\n发送成功后返回是否成功。`,\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n content: { type: 'string', description: '翻牌提问内容(最多500字)' },\n answerType: { type: 'number', description: '期望回复类型:1=文字回复, 3=视频回复' },\n type: { type: 'number', description: '翻牌类型:1=文字。不传则自动 =1' },\n cost: { type: 'number', description: '翻牌费用(鸡腿数)。不传则自动用默认值' },\n },\n required: ['memberId', 'content', 'answerType'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n\n const content = args.content as string\n if (!content || content.length === 0) return { error: '提问内容不能为空' }\n if (content.length > 500) return { error: `内容超过500字限制(当前${content.length}字)` }\n\n const params: FlipSendParams = {\n memberId: String(args.memberId),\n content,\n answerType: args.answerType as number,\n type: (args.type as number) || 1,\n cost: String(args.cost ?? ''),\n }\n\n try {\n const ok = await sendFlip(pocketClient, params, token)\n logger.info(`[pocket-tools] sendFlip to memberId=${params.memberId}, result=${ok}`)\n return {\n success: ok,\n memberId: params.memberId,\n content: params.content,\n answerType: params.answerType,\n message: ok ? '翻牌发送成功,等待偶像回复' : '翻牌发送失败,请检查余额或重试',\n }\n } catch (e) {\n return { error: `发送翻牌失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getMemberFlipHistory',\n description: '查询与指定成员的翻牌对话历史。返回提问和回复的完整记录,按时间排列',\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n beginLimit: { type: 'number', description: '起始位置,默认10(首次查建议不传)' },\n limit: { type: 'number', description: '每页条数,默认10' },\n },\n required: ['memberId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const page = await getMemberFlipHistory(\n pocketClient,\n token,\n args.memberId as number,\n (args.beginLimit as number) ?? 10,\n (args.limit as number) ?? 10,\n )\n return {\n total: page.records.length,\n hasMore: page.hasMore,\n records: page.records.map((r) => ({\n questionId: r.questionId,\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n qtimeReadable: new Date(Number(r.qtime)).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n })),\n }\n } catch (e) {\n return { error: `获取翻牌历史失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getDashboard',\n description: '获取翻牌数据统计仪表盘:总翻牌数、进行中数量、已退回数量、总花费',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const cache = flipDataSource.get(userId)\n return cache?.dashboardInfo || { totalFlipCount: 0, runningCount: 0, returnedCount: 0, costTotal: 0 }\n },\n },\n ]\n}","import type { PocketClient } from './client'\nimport type { ImUserInfo } from '../types/pocket'\n\n/**\n * 获取网易云信 IM 登录凭证(accid/pwd),用于登 NIM 长连接(房间消息实时推送)。\n *\n * 来源:POST /im/api/v1/im/userinfo(无 body),服务端按 token 下发。\n * 完整 URL = https://pocketapi.48.cn/im/api/v1/im/userinfo。\n *\n * 注意:accid/pwd ≠ HTTP token。前者是网易云信 IM 的账号/密码,后者是 Pocket48 业务 token。\n */\nexport async function getImUserInfo(client: PocketClient, token: string): Promise<ImUserInfo | null> {\n const res = await client.request<ImUserInfo>('/im/api/v1/im/userinfo', {}, token, true)\n const c = (res.content ?? res.data?.content) as Partial<ImUserInfo> | null | undefined\n if (!c?.accid || !c?.pwd) return null\n return { accid: c.accid, pwd: c.pwd, userId: Number(c.userId ?? 0) }\n}","import type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport { ToolRegistry, type ToolContext } from '../tools/tool-registry'\n\nexport interface AgentLoopOptions {\n config: ModelConfig\n systemPrompt: string\n userMessage: string\n history?: ChatMessage[]\n maxIterations?: number\n model?: string\n onChunk?: (chunk: string) => void\n onToolCall?: (name: string, args: Record<string, unknown>) => void\n context: ToolContext\n llmClient: ILlmClient\n toolRegistry: ToolRegistry\n}\n\n/**\n * Non-streaming agent loop: LLM <-> tool calling\n */\nexport async function agentLoop(options: AgentLoopOptions): Promise<string> {\n const maxIterations = options.maxIterations || 10\n const messages: ChatMessage[] = [\n { role: 'system', content: options.systemPrompt },\n ...(options.history || []),\n { role: 'user', content: options.userMessage },\n ]\n\n const tools = options.toolRegistry.getOpenAIFormat()\n\n for (let i = 0; i < maxIterations; i++) {\n const resp = await options.llmClient.chatCompletion(\n options.config,\n messages,\n tools.length > 0 ? tools : undefined,\n { model: options.model },\n )\n\n // no tool calls => return final text\n if (!resp.toolCalls || resp.toolCalls.length === 0) {\n return resp.content\n }\n\n // append assistant message with tool calls\n messages.push({\n role: 'assistant',\n content: resp.content || '',\n tool_calls: resp.toolCalls,\n })\n\n // execute each tool call\n for (const tc of resp.toolCalls) {\n const toolName = tc.function.name\n let toolArgs: Record<string, unknown> = {}\n try {\n toolArgs = JSON.parse(tc.function.arguments)\n } catch { /* keep empty */ }\n\n options.onToolCall?.(toolName, toolArgs)\n\n try {\n const result = await options.toolRegistry.execute(toolName, toolArgs, options.context)\n messages.push({\n role: 'tool',\n content: JSON.stringify(result),\n tool_call_id: tc.id,\n })\n } catch (err) {\n messages.push({\n role: 'tool',\n content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),\n tool_call_id: tc.id,\n })\n }\n }\n }\n\n throw new Error('Agent exceeded max iterations')\n}\n\n/**\n * Streaming agent loop with tool calling\n */\nexport async function agentLoopStream(options: AgentLoopOptions): Promise<void> {\n const maxIterations = options.maxIterations || 10\n const messages: ChatMessage[] = [\n { role: 'system', content: options.systemPrompt },\n ...(options.history || []),\n { role: 'user', content: options.userMessage },\n ]\n\n const tools = options.toolRegistry.getOpenAIFormat()\n\n for (let i = 0; i < maxIterations; i++) {\n const toolCalls = await options.llmClient.chatCompletionStream(\n options.config,\n messages,\n (chunk) => {\n if (chunk.content) options.onChunk?.(chunk.content)\n },\n tools.length > 0 ? tools : undefined,\n { model: options.model },\n )\n\n // no tool calls => done\n if (!toolCalls || toolCalls.length === 0) return\n\n // append assistant message\n messages.push({\n role: 'assistant',\n content: '',\n tool_calls: toolCalls,\n })\n\n // execute tools\n for (const tc of toolCalls) {\n const toolName = tc.function.name\n let toolArgs: Record<string, unknown> = {}\n try {\n toolArgs = JSON.parse(tc.function.arguments)\n } catch { /* keep empty */ }\n\n options.onToolCall?.(toolName, toolArgs)\n\n try {\n const result = await options.toolRegistry.execute(toolName, toolArgs, options.context)\n messages.push({\n role: 'tool',\n content: JSON.stringify(result),\n tool_call_id: tc.id,\n })\n } catch (err) {\n messages.push({\n role: 'tool',\n content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),\n tool_call_id: tc.id,\n })\n }\n }\n }\n\n throw new Error('Agent exceeded max iterations')\n}","import type { ILogger } from './ports/logger'\nimport type { IStorage } from './ports/storage'\nimport type { ISecureStorage } from './ports/secure-storage'\nimport type { IConfigStore } from './ports/config-store'\nimport type { IFlipDataSource } from './ports/flip-data-source'\nimport type { ILlmClient } from './ports/llm-client'\nimport type { IChatStreamSink, IEventSink } from './ports/stream-sink'\nimport type { IAuthExpiredHandler } from './ports/auth-expired-handler'\n\nimport { ToolRegistry } from './tools/tool-registry'\nimport { MirrorStore } from './mirror/mirror-store'\nimport { MirrorIndex } from './mirror/mirror-index'\nimport { MirrorMemory } from './mirror/mirror-memory'\nimport { GroupChatStore } from './mirror/group-chat/store'\nimport { FlipCacheService } from './flip/flip-cache-service'\nimport { PocketClient } from './pocket/client'\n\nimport { POCKET_APP_INFO, POCKET_PA } from './constants/pocket'\n\nimport * as buildMirrorMod from './mirror/mirror-builder'\nimport * as mirrorChatMod from './mirror/mirror-chat'\nimport * as mirrorGrowthMod from './mirror/mirror-growth'\nimport { getMirrorConfig } from './mirror/mirror-constants'\n\nimport { createPlatformTools } from './tools/platform-tools'\nimport { createMirrorTools } from './tools/mirror-tools'\nimport { createPocketTools } from './tools/pocket-tools'\n\nimport * as pocketUser from './pocket/user'\nimport * as pocketFlip from './pocket/flip'\nimport * as pocketIm from './pocket/im'\nimport * as pocketMember from './pocket/member'\nimport * as pocketRoom from './pocket/room'\n\nimport { agentLoop, agentLoopStream } from './llm/agent-loop'\nexport { agentLoop, agentLoopStream }\n\n/**\n * 组合根:core 对运行时依赖的完整声明。\n *\n * Electron 侧(hchuanz-pocket-helper)用自己的适配器(ElectronStorage/\n * ElectronSecureStorage/ElectronIpcStreamSink)组装;MCP/CLI 用 adapters/ 下的\n * Node 参考实现。core 本身不 import 任何框架。\n */\nexport interface CoreDependencies {\n logger: ILogger\n storage: IStorage\n secureStorage: ISecureStorage\n configStore: IConfigStore\n flipDataSource: IFlipDataSource\n llmClient: ILlmClient\n /** 流式聊天输出;Electron 用 IPC,MCP 用回调 */\n chatStreamSink?: IChatStreamSink\n /** 事件推送;Electron 用 webContents,MCP 用空实现 */\n eventSink?: IEventSink\n /** 认证过期回调 */\n authExpiredHandler?: IAuthExpiredHandler\n}\n\n/**\n * 面向外部的高层 API。\n */\nexport interface PocketCore {\n deps: CoreDependencies\n\n // ── 基础工具 ──\n toolRegistry: ToolRegistry\n pocketClient: PocketClient\n\n // ── 存储层 ──\n mirrorStore: MirrorStore\n flipCacheService: FlipCacheService\n groupChatStore: GroupChatStore\n\n // ── LLM 依赖层 ──\n mirrorIndex: MirrorIndex\n mirrorMemory: MirrorMemory\n\n // ── 镜像业务 API ──\n buildMirror: typeof buildMirrorMod.buildMirror\n /** 镜像聊天 */\n mirrorChat: typeof mirrorChatMod.chat\n /** 保存助手回复到 session */\n mirrorSaveAssistantMessage: typeof mirrorChatMod.saveAssistantMessage\n /** 清除聊天记忆 */\n mirrorClearChatMemory: typeof mirrorChatMod.clearChatMemory\n /** 触发人格成长 */\n mirrorTriggerGrowth: typeof mirrorGrowthMod.triggerGrowth\n /** 检测是否需要触发成长 */\n mirrorShouldTriggerGrowth: typeof mirrorGrowthMod.shouldTriggerGrowth\n /** 回退人格 */\n mirrorRollbackPersona: typeof mirrorGrowthMod.rollbackPersona\n /** 清除镜像记忆 */\n mirrorClearMemory: typeof mirrorGrowthMod.clearMirrorMemory\n getMirrorConfig: typeof getMirrorConfig\n\n // ── 工具工厂 ──\n createPlatformTools: () => ReturnType<typeof createPlatformTools>\n createMirrorTools: () => ReturnType<typeof createMirrorTools>\n createPocketTools: () => ReturnType<typeof createPocketTools>\n\n // ── Pocket API ──\n pocketUser: typeof pocketUser\n pocketFlip: typeof pocketFlip\n pocketIm: typeof pocketIm\n pocketMember: typeof pocketMember\n pocketRoom: typeof pocketRoom\n\n // ── Agent 循环 ──\n agentLoop: typeof agentLoop\n agentLoopStream: typeof agentLoopStream\n}\n\n/**\n * 创建 PocketCore 实例,组装所有业务模块。\n */\nexport function createPocketCore(deps: CoreDependencies): PocketCore {\n const toolRegistry = new ToolRegistry(deps.logger)\n const pocketClient = new PocketClient(\n 'https://pocketapi.48.cn',\n POCKET_APP_INFO,\n POCKET_PA,\n deps.authExpiredHandler,\n )\n\n const mirrorStore = new MirrorStore(deps.storage, deps.logger)\n const flipCacheService = new FlipCacheService(deps.storage, deps.logger, deps.configStore)\n const groupChatStore = new GroupChatStore(deps.storage, deps.logger)\n\n const mirrorIndex = new MirrorIndex(deps.storage, deps.logger, deps.llmClient)\n const mirrorMemory = new MirrorMemory(deps.storage, deps.logger, deps.llmClient)\n\n return {\n deps,\n\n toolRegistry,\n pocketClient,\n\n mirrorStore,\n flipCacheService,\n groupChatStore,\n\n mirrorIndex,\n mirrorMemory,\n\n buildMirror: buildMirrorMod.buildMirror,\n mirrorChat: mirrorChatMod.chat,\n mirrorSaveAssistantMessage: mirrorChatMod.saveAssistantMessage,\n mirrorClearChatMemory: mirrorChatMod.clearChatMemory,\n mirrorTriggerGrowth: mirrorGrowthMod.triggerGrowth,\n mirrorShouldTriggerGrowth: mirrorGrowthMod.shouldTriggerGrowth,\n mirrorRollbackPersona: mirrorGrowthMod.rollbackPersona,\n mirrorClearMemory: mirrorGrowthMod.clearMirrorMemory,\n getMirrorConfig,\n\n createPlatformTools: () => createPlatformTools(deps.flipDataSource),\n createMirrorTools: () => createMirrorTools(mirrorStore),\n createPocketTools: () => createPocketTools({\n pocketClient,\n configStore: deps.configStore,\n flipDataSource: deps.flipDataSource,\n logger: deps.logger,\n }),\n\n pocketUser,\n pocketFlip,\n pocketIm,\n pocketMember,\n pocketRoom,\n\n agentLoop,\n agentLoopStream,\n }\n}","import type { ModelConfig } from '../types/llm'\n\nexport const PRESET_PROVIDERS: Record<string, { label: string; baseUrl: string }> = {\n siliconflow: { label: '硅基流动 (SiliconFlow)', baseUrl: 'https://api.siliconflow.cn/v1' },\n deepseek: { label: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1' },\n dashscope: { label: '阿里百炼 (DashScope)', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1' },\n custom: { label: '自定义', baseUrl: '' },\n}\n\nexport const PRESET_MODELS = {\n chat: [\n { id: 'Qwen/Qwen3.6-35B-A3B', label: 'Qwen3.6-35B', desc: '262K 上下文,高性价比日常对话' },\n { id: 'deepseek-ai/DeepSeek-V4-Flash', label: 'DeepSeek-V4-Flash', desc: '1M 上下文,极速响应' },\n { id: 'stepfun-ai/Step-3.5-Flash', label: 'Step-3.5-Flash', desc: '262K 上下文,极致低价' },\n { id: 'Qwen/Qwen3-8B', label: 'Qwen3-8B', desc: '轻量快速,极低成本' },\n ],\n reasoning: [\n { id: 'deepseek-ai/DeepSeek-V4-Pro', label: 'DeepSeek-V4-Pro', desc: '1M 上下文,顶级推理·生成镜像专用' },\n { id: 'Qwen/Qwen3-235B-A22B-Thinking-2507', label: 'Qwen3-235B-Thinking', desc: '262K 上下文,中文推理' },\n ],\n embedding: [\n { id: 'BAAI/bge-large-zh-v1.5', label: 'BGE-Large-zh', desc: '中文 embedding 性价比之王' },\n ],\n}\n\nexport function defaultModelConfig(): ModelConfig {\n return {\n baseUrl: PRESET_PROVIDERS.siliconflow.baseUrl,\n apiKey: '',\n chatModel: PRESET_MODELS.chat[0].id,\n reasoningModel: PRESET_MODELS.reasoning[0].id,\n embeddingModel: PRESET_MODELS.embedding[0].id,\n lightweightModel: PRESET_MODELS.chat[3].id,\n mirrorMode: 'economy',\n }\n}","import type { MirrorProfile } from '../../types/mirror'\n\n/**\n * Build the \"inner monologue\" prompt for a mirror to evaluate whether it wants to speak.\n * Uses lightweight model, expects JSON output.\n */\nexport function buildInnerMonologuePrompt(\n profile: MirrorProfile,\n lastMessages: Array<{ speakerId: number; speakerName: string; content: string }>,\n topic: string | null,\n): string {\n const msgs = lastMessages\n .map((m) => `${m.speakerName}: ${m.content}`)\n .join('\\n')\n\n return `你是${profile.xoxNickname}。现在你在一个群聊中,请判断你是否想发言。\n\n【你的性格】${profile.persona.personaSummary || '未定义'}\n【群聊话题】${topic || '自由闲聊'}\n【最近群消息】\n${msgs}\n\n请只输出以下 JSON,不要任何其他内容:\n{\"shouldSpeak\": true或false, \"urgency\": 1到10的整数, \"trigger\": \"一句话说明你为什么想/不想说话\"}`\n}\n\n/**\n * Build the speech prompt for a mirror to generate its group chat reply.\n * Uses full persona + group context.\n */\nexport function buildSpeakPrompt(\n profile: MirrorProfile,\n params: {\n name: string\n topic: string | null\n otherMembers: string\n lastMessages: Array<{ speakerName: string; content: string }>\n },\n): string {\n const p = profile.persona\n\n const msgs = params.lastMessages\n .map((m) => `${m.speakerName}: ${m.content}`)\n .join('\\n')\n\n return `你是${profile.xoxNickname},一位偶像。\n\n【性格特征】${p.personalityTraits.join('、')}\n【说话风格】${p.speechPatterns.join('、')}\n【口头禅】${p.signaturePhrases.join('、')}\n【情感基调】${p.emotionalTone}\n【画像描述】${p.personaSummary}\n\n【当前群聊】\n群名称:${params.name}\n群话题:${params.topic || '自由闲聊'}\n在场成员:${params.otherMembers}\n\n【最近消息】\n${msgs}\n\n【要求】\n- 用你自己的说话风格回复,保持在人设内\n- 可以回应某个人说的话(直接回应),也可以开新话题,也可以吐槽别人说的话(不要每次都回应最后一个说话的人)\n- 回复长度:1-3 句话,像群聊里打字的感觉,不要太正式或太长,也不要每句话都很官方。如果话题有趣或者有人让你不满,也可以多说几句。\n- 不要提\"设定\"\"人格\"\"AI\"\"镜像\"等词,你就是${profile.xoxNickname}本人。\n- 不要每条消息都以问号结尾,也不要每条消息都在问别人。\n- 用中文回复。`\n}","import type { ILogger } from '../../ports/logger'\nimport type { ILlmClient } from '../../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../../types/llm'\nimport type { MirrorProfile } from '../../types/mirror'\nimport { MirrorStore } from '../mirror-store'\nimport type { GroupChatMessage, GroupChatSession } from './store'\nimport { GroupChatStore } from './store'\nimport { buildInnerMonologuePrompt, buildSpeakPrompt } from './prompt'\n\n// ═══ Types ═══\n\nexport interface SpeakDecision {\n xoxId: number\n shouldSpeak: boolean\n urgency: number // 1-10\n trigger: string\n}\n\nexport interface GroupChatMessageEvent {\n sessionId: string\n msgId: string\n speakerId: number // -1=user, 0=system, >0=mirror xoxId\n speakerName: string\n content: string\n isDone: boolean\n roundDone: boolean\n}\n\n// ═══ Constants ═══\n\nconst EVAL_TIMEOUT_MS = 120000\nconst MAX_RECENT_MESSAGES = 8\nconst ANTI_SILENCE_BOOST = 3\n\n// ═══ Helpers ═══\n\n/**\n * Parse a speak decision from LLM response. Handles both valid JSON and regex fallback.\n */\nfunction parseSpeakDecision(raw: string): { shouldSpeak: boolean; urgency: number; trigger: string } {\n try {\n const obj = JSON.parse(raw)\n return {\n shouldSpeak: obj.shouldSpeak !== false,\n urgency: Math.max(1, Math.min(10, Number(obj.urgency) || 5)),\n trigger: String(obj.trigger || ''),\n }\n } catch {\n // Fallback: regex extraction\n const shouldSpeak = /shouldSpeak[\":\\s]*true/i.test(raw)\n const urgencyMatch = raw.match(/urgency[\":\\s]*(\\d+)/i)\n return {\n shouldSpeak,\n urgency: urgencyMatch ? Math.min(10, Number(urgencyMatch[1])) : 5,\n trigger: '无法解析',\n }\n }\n}\n\n// ═══ Engine dependencies ═══\n\nexport interface GroupChatDeps {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n groupChatStore: GroupChatStore\n config: ModelConfig\n}\n\n/**\n * Evaluate all mirrors in parallel to decide who wants to speak.\n */\nasync function evaluateMirrors(\n deps: GroupChatDeps,\n session: GroupChatSession,\n lastMessages: GroupChatMessage[],\n userId: string,\n): Promise<SpeakDecision[]> {\n const decisions: SpeakDecision[] = []\n\n const evaluations = session.members.map(async (member) => {\n const profile = deps.mirrorStore.getProfile(member.xoxId, userId)\n if (!profile) {\n return { xoxId: member.xoxId, shouldSpeak: false, urgency: 0, trigger: '无镜像' }\n }\n\n try {\n const prompt = buildInnerMonologuePrompt(profile, lastMessages, session.topic)\n const resp = await Promise.race([\n deps.llmClient.chatCompletion(\n deps.config,\n [{ role: 'user', content: prompt }],\n undefined,\n { model: deps.config.lightweightModel, maxTokens: 200, temperature: 0.3, timeoutMs: EVAL_TIMEOUT_MS },\n ),\n new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), EVAL_TIMEOUT_MS)),\n ])\n const parsed = parseSpeakDecision(resp.content)\n return { xoxId: member.xoxId, ...parsed }\n } catch (e) {\n deps.logger.warn(`[groupChatEngine] evaluation failed for ${member.nickname}:`, e)\n return { xoxId: member.xoxId, shouldSpeak: false, urgency: 0, trigger: '评估超时/失败' }\n }\n })\n\n const results = await Promise.allSettled(evaluations)\n for (const result of results) {\n if (result.status === 'fulfilled') {\n decisions.push(result.value)\n }\n }\n\n return decisions\n}\n\n/**\n * Select speakers from evaluations. Takes top 1-2 by urgency.\n * If all silent, force the least-recently-spoken mirror to speak.\n */\nfunction selectSpeakers(\n decisions: SpeakDecision[],\n session: GroupChatSession,\n): SpeakDecision[] {\n const willing = decisions.filter((d) => d.shouldSpeak && d.urgency > 0)\n\n if (willing.length === 0) {\n // Anti-silence: pick the mirror who hasn't spoken recently\n const recentSpeakerIds = new Set(\n session.messages.slice(-5).filter((m) => m.speakerId > 0).map((m) => m.speakerId),\n )\n const silentCandidate = decisions.find((d) => !recentSpeakerIds.has(d.xoxId))\n const candidate = silentCandidate || decisions[0]\n if (candidate) {\n return [{ ...candidate, shouldSpeak: true, urgency: ANTI_SILENCE_BOOST, trigger: '打破沉默' }]\n }\n return []\n }\n\n // Sort by urgency descending, take top 2\n willing.sort((a, b) => b.urgency - a.urgency)\n return willing.slice(0, 2)\n}\n\n/**\n * Generate speech for a single mirror (streaming).\n */\nasync function generateSpeech(\n deps: GroupChatDeps,\n profile: MirrorProfile,\n session: GroupChatSession,\n lastMessages: GroupChatMessage[],\n msgId: string,\n onChunk: (event: GroupChatMessageEvent) => void,\n): Promise<string> {\n const otherMembers = session.members\n .filter((m) => m.xoxId !== profile.xoxId)\n .map((m) => m.nickname)\n .join('、')\n\n const prompt = buildSpeakPrompt(profile, {\n name: session.name,\n topic: session.topic,\n otherMembers,\n lastMessages: lastMessages.map((m) => ({ speakerName: m.speakerName, content: m.content })),\n })\n let fullContent = ''\n\n await deps.llmClient.chatCompletionStream(\n deps.config,\n [{ role: 'user', content: prompt }],\n (chunk) => {\n if (chunk.content) {\n fullContent += chunk.content\n onChunk({\n sessionId: session.sessionId,\n msgId,\n speakerId: profile.xoxId,\n speakerName: profile.xoxNickname,\n content: chunk.content,\n isDone: false,\n roundDone: false,\n })\n }\n },\n undefined,\n { model: deps.config.chatModel, temperature: deps.config.chatTemperature },\n )\n\n return fullContent\n}\n\n/**\n * Run a full round of group chat: evaluate → select → generate → push.\n *\n * @param session - The current group chat session (mutated: messages appended)\n * @param onEvent - Callback to push events to frontend\n * @returns Array of new messages generated this round\n */\nexport async function runRound(\n deps: GroupChatDeps,\n session: GroupChatSession,\n userId: string,\n onEvent: (event: GroupChatMessageEvent) => void,\n): Promise<GroupChatMessage[]> {\n // Get last N messages for context\n const lastMessages = session.messages.slice(-MAX_RECENT_MESSAGES)\n\n // 1. Evaluate all mirrors\n deps.logger.info(`[groupChatEngine] evaluating ${session.members.length} mirrors...`)\n const decisions = await evaluateMirrors(deps, session, lastMessages, userId)\n deps.logger.info(`[groupChatEngine] decisions: ${decisions.map((d) => `${d.xoxId}:${d.shouldSpeak ? d.urgency : 'N'}`).join(', ')}`)\n\n // 2. Select speakers\n const speakers = selectSpeakers(decisions, session)\n deps.logger.info(`[groupChatEngine] speakers: ${speakers.map((s) => s.xoxId).join(', ')}`)\n\n if (speakers.length === 0) {\n return []\n }\n\n // 3. Generate speech for each speaker (sequential)\n const newMessages: GroupChatMessage[] = []\n\n for (const speaker of speakers) {\n const member = session.members.find((m) => m.xoxId === speaker.xoxId)\n if (!member) continue\n\n const profile = deps.mirrorStore.getProfile(speaker.xoxId, userId)\n if (!profile) continue\n\n const msgId = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`\n\n deps.logger.info(`[groupChatEngine] generating speech for ${member.nickname} (trigger: ${speaker.trigger})`)\n\n try {\n const content = await generateSpeech(deps, profile, session, lastMessages, msgId, onEvent)\n\n const msg: GroupChatMessage = {\n msgId,\n speakerId: speaker.xoxId,\n speakerName: member.nickname,\n content,\n timestamp: Date.now(),\n }\n newMessages.push(msg)\n\n // Push final \"done\" event\n onEvent({\n sessionId: session.sessionId,\n msgId,\n speakerId: speaker.xoxId,\n speakerName: member.nickname,\n content: '',\n isDone: true,\n roundDone: false,\n })\n } catch (e) {\n deps.logger.error(`[groupChatEngine] speech generation failed for ${member.nickname}:`, e)\n // Push system message for failed speech\n const failMsg: GroupChatMessage = {\n msgId: `${Date.now()}_fail`,\n speakerId: 0,\n speakerName: '系统',\n content: `${member.nickname} 欲言又止...`,\n timestamp: Date.now(),\n }\n newMessages.push(failMsg)\n onEvent({\n sessionId: session.sessionId,\n msgId: failMsg.msgId,\n speakerId: 0,\n speakerName: '系统',\n content: failMsg.content,\n isDone: true,\n roundDone: false,\n })\n }\n\n // Update lastMessages for next speaker in this round\n lastMessages.push(...newMessages.slice(-1))\n }\n\n // Push roundDone event\n onEvent({\n sessionId: session.sessionId,\n msgId: '',\n speakerId: 0,\n speakerName: '',\n content: '',\n isDone: true,\n roundDone: true,\n })\n\n return newMessages\n}"],"mappings":"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","names":["path","fs","import_node_fs","import_node_path","path","fs","import_node_path","path","crypto","sessionSubpath","CACHE_DIR","path","axios","import_node_fs","fs","params"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants/pocket.ts","../src/adapters/loggers.ts","../src/adapters/node-json-storage.ts","../src/adapters/plain-text-secure-storage.ts","../src/adapters/node-json-config-store.ts","../src/adapters/memory-config-store.ts","../src/adapters/node-json-flip-source.ts","../src/adapters/memory-flip-source.ts","../src/adapters/openai-compatible-client.ts","../src/adapters/callback-stream-sink.ts","../src/tools/tool-registry.ts","../src/types/mirror.ts","../src/mirror/mirror-store.ts","../src/mirror/mirror-index.ts","../src/mirror/mirror-memory.ts","../src/mirror/group-chat/store.ts","../src/flip/flip-cache-service.ts","../src/pocket/client.ts","../src/mirror/mirror-constants.ts","../src/mirror/mirror-builder.ts","../src/mirror/mirror-chat.ts","../src/mirror/mirror-growth.ts","../src/tools/platform-tools.ts","../src/tools/mirror-tools.ts","../src/pocket/user.ts","../src/pocket/flip.ts","../src/pocket/member.ts","../src/pocket/room.ts","../src/tools/pocket-tools.ts","../src/pocket/im.ts","../src/llm/agent-loop.ts","../src/context.ts","../src/llm/llm-constants.ts","../src/mirror/group-chat/prompt.ts","../src/mirror/group-chat/engine.ts"],"sourcesContent":["// 类型(单一来源)\nexport * from './types'\n\n// 常量\nexport * from './constants/pocket'\n\n// 端口(接口,只定义不实现)\nexport * from './ports'\n\n// Node 参考适配器\nexport * from './adapters'\n\n// 组合根 + 高层 API\nexport * from './context'\n\n// 业务模块(按需导出)\nexport * from './llm/llm-constants'\nexport * from './mirror/mirror-constants'\nexport * from './mirror/mirror-store'\nexport * from './mirror/mirror-index'\nexport * from './mirror/mirror-memory'\nexport * from './mirror/mirror-builder'\nexport * from './mirror/mirror-chat'\nexport * from './mirror/mirror-growth'\nexport * from './mirror/group-chat/prompt'\nexport * from './mirror/group-chat/store'\nexport * from './mirror/group-chat/engine'\nexport * from './tools/tool-registry'\nexport * from './tools/platform-tools'\nexport * from './tools/mirror-tools'\nexport * from './tools/pocket-tools'\nexport * from './pocket/client'\nexport * from './pocket/user'\nexport * from './pocket/flip'\nexport * from './pocket/im'\nexport * from './pocket/member'\nexport * from './pocket/room'\nexport * from './flip/flip-cache-service'\nexport * from './llm/agent-loop'","/**\n * Pocket48 客户端常量\n *\n * 来自反编译的 Android APK(com.pocket.snh48),\n * 这些是客户端签名常量,不是用户密码或 API Key。\n *\n * ⚠ 安全提示:真实 token / apiKey / 用户密码不得写入此文件。\n * apiKey 仅由 ISecureStorage 管理,token 由 IConfigStore 管理。\n */\n\n/** 所有业务接口前缀 */\nexport const POCKET_API_BASE = 'https://pocketapi.48.cn'\n\n/** 头像/图片相对路径必须拼此前缀(如 /content/images/xxx.png → https://source.48.cn/content/images/xxx.png) */\nexport const AVA_BASE_URL = 'https://source.48.cn'\n\n/** 客户端描述 JSON(deviceId 为固定值,非用户设备) */\nexport const POCKET_APP_INFO = JSON.stringify({\n vendor: 'apple',\n deviceId: '2A01B81B-653A-43EC-9C58-95CB418A58AC',\n appVersion: '7.1.37',\n appBuild: '26020801',\n osVersion: '26.4.0',\n osType: 'ios',\n deviceName: 'iPhone17,1',\n os: 'ios',\n})\n\n/** 客户端签名 pa 头(静态值,非密码学签名) */\nexport const POCKET_PA =\n 'MTc4MjM3NDEwNjAwMCw3NzE1LEIwODM5QzdEMjZFQzJFOTFERTExNDVERDU2NTlBRjk5LA=='\n\n/** User-Agent */\nexport const POCKET_USER_AGENT =\n 'PocketFans201807/7.1.37 (iPhone; iOS 26.4; Scale/3.00)'\n\n/** token 本地过期时间 */\nexport const SESSION_EXPIRE_MS = 30 * 24 * 60 * 60 * 1000","import type { ILogger } from '../ports/logger'\n\n/** 直打 console,适用本地/CLI/MCP 场景 */\nexport class ConsoleLogger implements ILogger {\n debug(...args: unknown[]): void {\n console.debug(...args)\n }\n info(...args: unknown[]): void {\n console.info(...args)\n }\n warn(...args: unknown[]): void {\n console.warn(...args)\n }\n error(...args: unknown[]): void {\n console.error(...args)\n }\n}\n\n/** 空实现,测试或静默场景 */\nexport class NoopLogger implements ILogger {\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n}","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { IStorage } from '../ports/storage'\n\n/**\n * Node JSON 文件存储。\n *\n * 对齐原 hchuanz-pocket-helper 的 `mirrorStore` / `flipCache` 等 fs 读写习惯:\n * JSON 缩进 2 空格、不存在返回 null、读取失败返回 null。\n */\nexport class NodeJsonFileStorage implements IStorage {\n readonly root: string\n\n constructor(root: string) {\n this.root = root\n }\n\n private resolve(subpath: string): string {\n return path.join(this.root, subpath)\n }\n\n readJson<T = unknown>(subpath: string): T | null {\n const fp = this.resolve(subpath)\n if (!fs.existsSync(fp)) return null\n try {\n return JSON.parse(fs.readFileSync(fp, 'utf-8')) as T\n } catch {\n return null\n }\n }\n\n writeJson(subpath: string, data: unknown): void {\n const fp = this.resolve(subpath)\n fs.mkdirSync(path.dirname(fp), { recursive: true })\n fs.writeFileSync(fp, JSON.stringify(data, null, 2), 'utf-8')\n }\n\n exists(subpath: string): boolean {\n return fs.existsSync(this.resolve(subpath))\n }\n\n delete(subpath: string): void {\n const fp = this.resolve(subpath)\n if (fs.existsSync(fp)) fs.unlinkSync(fp)\n }\n\n list(dirSubpath: string): string[] {\n const dir = this.resolve(dirSubpath)\n if (!fs.existsSync(dir)) return []\n return fs.readdirSync(dir)\n }\n\n mkdir(dirSubpath: string): void {\n fs.mkdirSync(this.resolve(dirSubpath), { recursive: true })\n }\n}","import type { ISecureStorage } from '../ports/secure-storage'\n\n/**\n * 明文安全存储(dev/test 参考实现)。\n *\n * ⚠ 仅 base64 编码,**不是真加密**,禁止用于生产环境敏感数据。\n * 生产用 Electron 的 safeStorage 实现(留在原仓库)。\n */\nexport class PlainTextSecureStorage implements ISecureStorage {\n isAvailable(): boolean {\n return true\n }\n\n encryptString(plain: string): string {\n return Buffer.from(plain, 'utf-8').toString('base64')\n }\n\n decryptString(encoded: string): string {\n return Buffer.from(encoded, 'base64').toString('utf-8')\n }\n}","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { IConfigStore } from '../ports/config-store'\n\n/**\n * Node JSON KV 配置存储。\n *\n * 对齐原 hchuanz-pocket-helper 的 `storeService`:持久化到单个 `app-store.json`,\n * get 不存在返回 undefined。\n */\nexport class NodeJsonConfigStore implements IConfigStore {\n private readonly filePath: string\n private data: Record<string, unknown> = {}\n private loaded = false\n\n constructor(root: string, filename = 'app-store.json') {\n this.filePath = path.join(root, filename)\n }\n\n private ensureLoaded(): void {\n if (this.loaded) return\n this.loaded = true\n try {\n if (fs.existsSync(this.filePath)) {\n this.data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8')) as Record<string, unknown>\n }\n } catch {\n this.data = {}\n }\n }\n\n private save(): void {\n fs.mkdirSync(path.dirname(this.filePath), { recursive: true })\n fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf-8')\n }\n\n get<T = unknown>(key: string): T | undefined {\n this.ensureLoaded()\n return this.data[key] as T | undefined\n }\n\n set(key: string, value: unknown): void {\n this.ensureLoaded()\n if (value === undefined || value === null) {\n delete this.data[key]\n } else {\n this.data[key] = value\n }\n this.save()\n }\n\n remove(key: string): void {\n this.ensureLoaded()\n delete this.data[key]\n this.save()\n }\n\n has(key: string): boolean {\n this.ensureLoaded()\n return key in this.data\n }\n\n clear(): void {\n this.data = {}\n this.save()\n }\n}","import type { IConfigStore } from '../ports/config-store'\n\n/** 纯内存 KV 存储,给 MCP 无磁盘场景 / 测试 */\nexport class MemoryConfigStore implements IConfigStore {\n private data = new Map<string, unknown>()\n\n get<T = unknown>(key: string): T | undefined {\n return this.data.get(key) as T | undefined\n }\n\n set(key: string, value: unknown): void {\n this.data.set(key, value)\n }\n\n remove(key: string): void {\n this.data.delete(key)\n }\n\n has(key: string): boolean {\n return this.data.has(key)\n }\n\n clear(): void {\n this.data.clear()\n }\n}","import path from 'node:path'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { FlipUserCache } from '../types/pocket'\nimport type { IStorage } from '../ports/storage'\n\nconst CACHE_DIR = 'flip-cache'\n\n/** 兼容旧存储的读取器(allUserData/groupedDataById/dashboardInfo 迁移),可空 */\nexport type LegacyFlipReader = () => {\n allUserData?: Record<string, unknown[]>\n groupedDataById?: Record<string, unknown>\n dashboardInfo?: FlipUserCache['dashboardInfo']\n} | null\n\n/**\n * Node JSON 翻牌数据源。\n *\n * 对齐原 hchuanz-pocket-helper 的 `flipCacheService`:\n * `flip-cache/{userId}.json`,构造可注入 legacy 读取器做旧数据迁移。\n */\nexport class NodeJsonFlipDataSource implements IFlipDataSource {\n private readonly storage: IStorage\n private readonly readLegacy?: LegacyFlipReader\n\n constructor(storage: IStorage, readLegacy?: LegacyFlipReader) {\n this.storage = storage\n this.readLegacy = readLegacy\n }\n\n private filePath(userId: string): string {\n return path.join(CACHE_DIR, `${userId}.json`)\n }\n\n get(userId: string): FlipUserCache | null {\n if (!userId) return null\n const cached = this.storage.readJson<FlipUserCache>(this.filePath(userId))\n if (cached) return cached\n return this.migrateFromLegacy(userId)\n }\n\n set(cache: FlipUserCache): void {\n this.storage.writeJson(this.filePath(cache.userId), cache)\n }\n\n clear(userId: string): void {\n this.storage.delete(this.filePath(userId))\n }\n\n private migrateFromLegacy(userId: string): FlipUserCache | null {\n if (!this.readLegacy) return null\n const legacy = this.readLegacy()\n const records = legacy?.allUserData?.[userId]\n if (!Array.isArray(records) || records.length === 0) return null\n const cache: FlipUserCache = {\n userId,\n records: records as FlipUserCache['records'],\n groupedDataById: (legacy?.groupedDataById as FlipUserCache['groupedDataById']) || {},\n dashboardInfo: legacy?.dashboardInfo || {\n totalFlipCount: records.length,\n runningCount: 0,\n returnedCount: 0,\n costTotal: 0,\n },\n meta: { syncedAt: Date.now(), recordCount: records.length },\n }\n this.set(cache)\n return cache\n }\n}","import type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { FlipUserCache } from '../types/pocket'\n\n/** 纯内存翻牌数据源,给 MCP 无磁盘场景 / 测试 */\nexport class MemoryFlipDataSource implements IFlipDataSource {\n private data = new Map<string, FlipUserCache>()\n\n get(userId: string): FlipUserCache | null {\n return this.data.get(userId) ?? null\n }\n\n set(cache: FlipUserCache): void {\n this.data.set(cache.userId, cache)\n }\n\n clear(userId: string): void {\n this.data.delete(userId)\n }\n}","import type { ILlmClient, ChatCompletionOptions, ChatCompletionResult } from '../ports/llm-client'\nimport type { ILogger } from '../ports/logger'\nimport type { ChatCompletionChunk, ChatMessage, ModelConfig, ToolDefinition } from '../types/llm'\n\n/**\n * OpenAI 兼容 LLM 客户端。\n *\n * 对齐原 hchuanz-pocket-helper 的 `common/providers.ts`:\n * chatCompletion / chatCompletionStream / embed / testConnection,\n * 全部走原生 fetch,零依赖。\n */\nexport class OpenAICompatibleClient implements ILlmClient {\n private readonly logger: ILogger\n\n constructor(logger: ILogger) {\n this.logger = logger\n }\n\n async chatCompletion(\n config: ModelConfig,\n messages: ChatMessage[],\n tools?: ToolDefinition[],\n options?: ChatCompletionOptions,\n ): Promise<ChatCompletionResult> {\n const url = `${config.baseUrl}/chat/completions`\n const body: Record<string, unknown> = {\n model: options?.model || config.chatModel,\n messages,\n stream: false,\n }\n if (tools?.length) {\n body.tools = tools\n body.tool_choice = 'auto'\n }\n if (options?.maxTokens) body.max_tokens = options.maxTokens\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options?.timeoutMs ?? 120000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`LLM ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const json = await resp.json() as {\n choices?: Array<{\n message?: { content?: string; tool_calls?: ChatMessage['tool_calls'] }\n }>\n }\n const choice = json.choices?.[0]\n return {\n content: choice?.message?.content ?? '',\n toolCalls: choice?.message?.tool_calls,\n }\n }\n\n async chatCompletionStream(\n config: ModelConfig,\n messages: ChatMessage[],\n onChunk: (chunk: ChatCompletionChunk) => void,\n tools?: ToolDefinition[],\n options?: ChatCompletionOptions,\n ): Promise<ChatMessage['tool_calls']> {\n const url = `${config.baseUrl}/chat/completions`\n const body: Record<string, unknown> = {\n model: options?.model || config.chatModel,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n }\n if (tools?.length) {\n body.tools = tools\n body.tool_choice = 'auto'\n }\n if (options?.maxTokens) body.max_tokens = options.maxTokens\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options?.timeoutMs ?? 120000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`LLM stream ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const reader = resp.body?.getReader()\n if (!reader) throw new Error('No response body')\n\n const decoder = new TextDecoder()\n let buffer = ''\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>()\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') break\n\n try {\n const json = JSON.parse(data) as {\n choices?: Array<{\n delta?: {\n content?: string\n tool_calls?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n }\n }>\n }\n const delta = json.choices?.[0]?.delta\n if (!delta) continue\n\n const content = delta.content ?? ''\n\n // merge streaming tool calls\n for (const tc of delta.tool_calls ?? []) {\n const idx = tc.index ?? 0\n const existing = toolCallMap.get(idx) ?? { id: '', name: '', arguments: '' }\n if (tc.id) existing.id = tc.id\n if (tc.function?.name) existing.name += tc.function.name\n if (tc.function?.arguments) existing.arguments += tc.function.arguments\n toolCallMap.set(idx, existing)\n }\n\n onChunk({\n content,\n toolCalls: Array.from(toolCallMap.entries()).map(([index, tc]) => ({ index, ...tc })),\n isDone: false,\n })\n } catch {\n // skip unparseable lines\n }\n }\n }\n } finally {\n reader.releaseLock()\n }\n\n // final chunk\n const finalToolCalls = toolCallMap.size > 0\n ? Array.from(toolCallMap.values()).map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n : undefined\n\n onChunk({ content: '', toolCalls: [], isDone: true })\n\n return finalToolCalls\n }\n\n async embed(config: ModelConfig, texts: string[], model?: string): Promise<number[][]> {\n const url = `${config.baseUrl}/embeddings`\n const resp = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify({\n model: model || config.embeddingModel,\n input: texts,\n encoding_format: 'float',\n }),\n signal: AbortSignal.timeout(60000),\n })\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '')\n throw new Error(`Embedding ${resp.status}: ${text.slice(0, 200)}`)\n }\n\n const json = await resp.json() as {\n data?: Array<{ embedding: number[] }>\n }\n return (json.data ?? []).map((d) => d.embedding)\n }\n\n async testConnection(config: ModelConfig): Promise<{ ok: boolean; message: string }> {\n try {\n const result = await this.chatCompletion(\n config,\n [{ role: 'user', content: 'ping' }],\n undefined,\n { maxTokens: 10, timeoutMs: 15000 },\n )\n return {\n ok: result.content.length > 0 || (result.content.length === 0 && !result.toolCalls),\n message: result.content.slice(0, 100) || '(empty response)',\n }\n } catch (e) {\n this.logger.warn('[OpenAICompatibleClient] testConnection failed:', e)\n return {\n ok: false,\n message: e instanceof Error ? e.message : String(e),\n }\n }\n }\n}","import type { IChatStreamSink, IEventSink } from '../ports/stream-sink'\n\n/**\n * 回调流输出。\n *\n * 把 `IChatStreamSink` 桥接到回调函数,供测试或直接消费(非 IPC)。\n */\nexport class CallbackStreamSink implements IChatStreamSink {\n private readonly onChunkCb: (content: string, isDone: boolean, newSessionId?: string) => void\n\n constructor(cb: (content: string, isDone: boolean, newSessionId?: string) => void) {\n this.onChunkCb = cb\n }\n\n onChunk(content: string, isDone: boolean, newSessionId?: string): void {\n this.onChunkCb(content, isDone, newSessionId)\n }\n}\n\n/**\n * 事件推送空实现(MCP/测试场景无 UI 渲染进程)。\n */\nexport class NoopEventSink implements IEventSink {\n emit(): void {}\n}","import type { ILogger } from '../ports/logger'\nimport type { ToolDefinition } from '../types/llm'\n\n/**\n * Runtime tool descriptor with handler\n */\nexport interface RegisteredTool extends ToolDefinition {\n category: 'platform' | 'mirror' | 'user' | 'pocket'\n handler: (args: Record<string, unknown>, context: ToolContext) => Promise<unknown>\n}\n\nexport interface ToolContext {\n userId: string\n signal?: AbortSignal\n}\n\n/**\n * 工具注册中心。\n *\n * 负责注册、查询、执行 Agent 工具。通过依赖注入接收 ILogger,\n * 替代原 hchuanz-pocket-helper 中直接 import ee-core/log 的实现。\n */\nexport class ToolRegistry {\n private tools = new Map<string, RegisteredTool>()\n\n constructor(private logger: ILogger) {}\n\n /**\n * Register a tool (platform / agent / user MCP)\n */\n register(tool: RegisteredTool): void {\n if (this.tools.has(tool.function.name)) {\n this.logger.warn(`[toolRegistry] overwriting tool: ${tool.function.name}`)\n }\n this.tools.set(tool.function.name, tool)\n this.logger.info(`[toolRegistry] registered: ${tool.function.name}`)\n }\n\n /**\n * Bulk register tools from an array\n */\n registerAll(tools: RegisteredTool[]): void {\n for (const t of tools) this.register(t)\n }\n\n /**\n * Unregister a tool by name\n */\n unregister(name: string): void {\n this.tools.delete(name)\n }\n\n /**\n * Unregister all tools with a given prefix (e.g. MCP server name)\n */\n unregisterByPrefix(prefix: string): void {\n for (const name of this.tools.keys()) {\n if (name.startsWith(prefix)) this.tools.delete(name)\n }\n }\n\n /**\n * Get a single tool by name\n */\n get(name: string): RegisteredTool | undefined {\n return this.tools.get(name)\n }\n\n /**\n * Get all tools in OpenAI function-calling format\n */\n getOpenAIFormat(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => ({\n type: 'function' as const,\n function: { ...t.function },\n }))\n }\n\n /**\n * Get tools filtered by category\n */\n getByCategory(category: RegisteredTool['category']): ToolDefinition[] {\n return Array.from(this.tools.values())\n .filter((t) => t.category === category)\n .map((t) => ({ type: 'function' as const, function: { ...t.function } }))\n }\n\n /**\n * Execute a tool by name\n */\n async execute(name: string, args: Record<string, unknown>, context: ToolContext): Promise<unknown> {\n const tool = this.tools.get(name)\n if (!tool) throw new Error(`Tool not found: ${name}`)\n return tool.handler(args, context)\n }\n\n /**\n * List all registered tool names\n */\n listNames(): string[] {\n return Array.from(this.tools.keys())\n }\n\n /**\n * Clear all tools\n */\n clear(): void {\n this.tools.clear()\n }\n}","export interface MirrorPersona {\n personalityTraits: string[]\n speechPatterns: string[]\n signaturePhrases: string[]\n emotionalTone: string\n personaSummary: string\n relationship?: MirrorRelationship\n recentActivity?: MirrorRecentActivity\n}\n\nexport interface MirrorRelationship {\n dynamic: string\n closeness: number\n howSheAddressesMe: string\n topicsWeDiscuss: string[]\n evolution: string\n}\n\nexport interface MirrorRecentActivity {\n topics: string[]\n mood: string\n events: string[]\n}\n\nexport interface MirrorProfile {\n xoxId: number\n xoxNickname: string\n persona: MirrorPersona\n previousPersona?: MirrorPersona\n fewShotExamples: Array<{\n userQuestion: string\n idolReply: string\n relevance: number\n }>\n meta: {\n totalSamplesUsed: number\n lastUpdatedAt: number\n dataRangeMs: [number, number]\n maturityScore: number\n }\n growthLog?: GrowthEvent[]\n}\n\nexport interface GrowthEvent {\n timestamp: number\n reason: string\n changes: string[]\n}\n\nexport interface ChatSession {\n sessionId: string\n xoxId: number\n messages: Array<{ role: string; content: string }>\n createdAt: number\n lastActiveAt: number\n}\n\n/**\n * Normalize a persona from disk or LLM output.\n * Old profiles / incomplete LLM responses may miss fields; fill in safe defaults\n * so downstream consumers (chat prompt builder, frontend render) never crash.\n */\nexport function normalizePersona(persona: Partial<MirrorPersona> | null | undefined): MirrorPersona {\n const p = persona || {}\n const rel = p.relationship\n return {\n personalityTraits: Array.isArray(p.personalityTraits) ? p.personalityTraits : [],\n speechPatterns: Array.isArray(p.speechPatterns) ? p.speechPatterns : [],\n signaturePhrases: Array.isArray(p.signaturePhrases) ? p.signaturePhrases : [],\n emotionalTone: typeof p.emotionalTone === 'string' ? p.emotionalTone : '温柔真诚',\n personaSummary: typeof p.personaSummary === 'string' ? p.personaSummary : '',\n ...(rel\n ? {\n relationship: {\n dynamic: typeof rel.dynamic === 'string' ? rel.dynamic : '友好',\n closeness: typeof rel.closeness === 'number' ? rel.closeness : 0.5,\n howSheAddressesMe: typeof rel.howSheAddressesMe === 'string' ? rel.howSheAddressesMe : '你',\n topicsWeDiscuss: Array.isArray(rel.topicsWeDiscuss) ? rel.topicsWeDiscuss : [],\n evolution: typeof rel.evolution === 'string' ? rel.evolution : '',\n },\n }\n : {}),\n ...(p.recentActivity\n ? {\n recentActivity: {\n topics: Array.isArray(p.recentActivity.topics) ? p.recentActivity.topics : [],\n mood: typeof p.recentActivity.mood === 'string' ? p.recentActivity.mood : '',\n events: Array.isArray(p.recentActivity.events) ? p.recentActivity.events : [],\n },\n }\n : {}),\n }\n}\n\nexport interface MirrorConfig {\n /** LLM analysis: max flip records to sample for persona extraction */\n llmSampleSize: number\n /** LLM analysis: max_tokens for the analysis response */\n llmMaxTokens: number\n /** LLM analysis: temperature (0=deterministic, 1=creative) */\n analysisTemperature: number\n /** Chat: max rounds of conversation history to keep */\n chatHistoryRounds: number\n /** Chat: temperature (0=deterministic, 1=creative) */\n chatTemperature: number\n /** RAG: top-K results to retrieve from vector index */\n ragTopK: number\n /** RAG: minimum cosine similarity score to include a result */\n ragMinScore: number\n /** Episodic memory: max recent conversation summaries to inject */\n memoryEpisodes: number\n /** Embedding: max records to build vector index for (0 = unlimited) */\n embeddingMaxRecords: number\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport {\n type MirrorProfile,\n type ChatSession,\n normalizePersona,\n} from '../types/mirror'\n\nexport { normalizePersona }\nexport type { MirrorProfile, ChatSession }\n\nconst MIRROR_DIR = 'mirrors'\nconst CHAT_DIR = 'chat_sessions'\n\nfunction mirrorSubdir(userId: string): string {\n return `${MIRROR_DIR}/${userId}`\n}\n\nfunction chatSubdir(userId: string): string {\n return `${CHAT_DIR}/${userId}`\n}\n\nfunction profileSubpath(xoxId: number, userId: string): string {\n return `${MIRROR_DIR}/${userId}/${xoxId}.json`\n}\n\nfunction sessionSubpath(sessionId: string, userId: string): string {\n return `${CHAT_DIR}/${userId}/${sessionId}.json`\n}\n\nfunction normalizeProfile(raw: MirrorProfile): MirrorProfile {\n return {\n ...raw,\n persona: normalizePersona(raw.persona),\n previousPersona: raw.previousPersona ? normalizePersona(raw.previousPersona) : undefined,\n fewShotExamples: Array.isArray(raw.fewShotExamples) ? raw.fewShotExamples : [],\n meta: raw.meta || { totalSamplesUsed: 0, lastUpdatedAt: 0, dataRangeMs: [0, 0], maturityScore: 0 },\n }\n}\n\n/**\n * 镜像档案与聊天会话持久化存储。\n *\n * 通过 IStorage 抽象底层文件系统,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` 的直接调用。\n */\nexport class MirrorStore {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n ) {}\n\n // ═══ Mirror Profile CRUD ═══\n\n getProfile(xoxId: number, userId: string): MirrorProfile | null {\n const raw = this.storage.readJson<MirrorProfile>(profileSubpath(xoxId, userId))\n if (!raw) return null\n try {\n return normalizeProfile(raw)\n } catch (e) {\n this.logger.warn(`[mirrorStore] read profile failed for ${xoxId}`, e)\n return null\n }\n }\n\n saveProfile(profile: MirrorProfile, userId: string): void {\n this.storage.mkdir(mirrorSubdir(userId))\n this.storage.writeJson(profileSubpath(profile.xoxId, userId), profile)\n this.logger.info(`[mirrorStore] saved profile for ${profile.xoxNickname}(${profile.xoxId})`)\n }\n\n deleteProfile(xoxId: number, userId: string): void {\n this.storage.delete(profileSubpath(xoxId, userId))\n // also delete chat sessions for this xoxId\n const cd = chatSubdir(userId)\n if (this.storage.exists(cd)) {\n const files = this.storage.list(cd)\n for (const f of files) {\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session?.xoxId === xoxId) {\n this.storage.delete(`${cd}/${f}`)\n }\n } catch { /* skip */ }\n }\n }\n }\n\n listProfiles(userId: string): MirrorProfile[] {\n const dir = mirrorSubdir(userId)\n if (!this.storage.exists(dir)) return []\n const profiles: MirrorProfile[] = []\n const files = this.storage.list(dir)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n if (f.includes('_index')) continue // skip vector index files\n if (f.includes('_memory')) continue // skip episodic memory files\n try {\n const profile = this.storage.readJson<MirrorProfile>(`${dir}/${f}`)\n if (profile) profiles.push(normalizeProfile(profile))\n } catch { /* skip */ }\n }\n return profiles\n }\n\n // ═══ Chat Session CRUD ═══\n\n getSession(sessionId: string, userId: string): ChatSession | null {\n return this.storage.readJson<ChatSession>(sessionSubpath(sessionId, userId))\n }\n\n saveSession(session: ChatSession, userId: string): void {\n this.storage.mkdir(chatSubdir(userId))\n this.storage.writeJson(sessionSubpath(session.sessionId, userId), session)\n }\n\n /**\n * Find the most recently active chat session for an idol\n */\n getLatestSessionForXox(xoxId: number, userId: string): ChatSession | null {\n const cd = chatSubdir(userId)\n if (!this.storage.exists(cd)) return null\n let latest: ChatSession | null = null\n const files = this.storage.list(cd)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session && session.xoxId === xoxId) {\n if (!latest || session.lastActiveAt > latest.lastActiveAt) {\n latest = session\n }\n }\n } catch { /* skip */ }\n }\n return latest\n }\n\n deleteSession(sessionId: string, userId: string): void {\n this.storage.delete(sessionSubpath(sessionId, userId))\n }\n\n /**\n * Delete all chat sessions for an idol (keeps profile)\n */\n deleteSessionsForXox(xoxId: number, userId: string): void {\n const cd = chatSubdir(userId)\n if (!this.storage.exists(cd)) return\n const files = this.storage.list(cd)\n for (const f of files) {\n try {\n const session = this.storage.readJson<ChatSession>(`${cd}/${f}`)\n if (session?.xoxId === xoxId) {\n this.storage.delete(`${cd}/${f}`)\n }\n } catch { /* skip */ }\n }\n }\n}","import crypto from 'node:crypto'\nimport type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ModelConfig } from '../types/llm'\n\nexport interface FlipRecordVector {\n answerId: string\n content: string\n answerContent: string\n qtime: number\n hash: string\n vector: number[]\n}\n\nexport interface SearchHit {\n record: FlipRecordVector\n score: number\n}\n\ninterface IndexFile {\n xoxId: number\n model: string\n updatedAt: number\n vectors: FlipRecordVector[]\n}\n\nconst INDEX_DIR = 'mirrors'\nconst EMBED_BATCH_SIZE = 32\n\nfunction indexPath(xoxId: number): string {\n return `${INDEX_DIR}/${xoxId}_index.json`\n}\n\nfunction hashRecord(content: string, answerContent: string): string {\n return crypto.createHash('md5').update(`${content}||${answerContent}`).digest('hex').slice(0, 12)\n}\n\nfunction cosine(a: number[], b: number[]): number {\n let dot = 0\n let na = 0\n let nb = 0\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i]\n na += a[i] * a[i]\n nb += b[i] * b[i]\n }\n if (na === 0 || nb === 0) return 0\n return dot / (Math.sqrt(na) * Math.sqrt(nb))\n}\n\n/**\n * 向量索引服务。\n *\n * 通过 ILlmClient 调用 embedding API 构建/搜索翻牌记录的语义向量索引,\n * 支持 cosine + MMR 的 top-K 检索。\n * 底层持久化通过 IStorage,替代原 hchuanz-pocket-helper 中 `fs` + `app.getPath`。\n */\nexport class MirrorIndex {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private llmClient: ILlmClient | null,\n ) {}\n\n /**\n * Load index from disk (returns null if not exists)\n */\n load(xoxId: number): IndexFile | null {\n const fp = indexPath(xoxId)\n const raw = this.storage.readJson<IndexFile>(fp)\n if (!raw) return null\n return raw\n }\n\n /**\n * Build / incrementally update the vector index for an idol's flip records.\n * Records with unchanged hash reuse existing embeddings.\n */\n async buildIndex(\n xoxId: number,\n config: ModelConfig,\n records: Array<{ answerId?: string; content?: string; answerContent?: string; qtime?: number | string }>,\n ): Promise<{ indexed: number; reused: number }> {\n const existing = this.load(xoxId)\n const existingMap = new Map<string, FlipRecordVector>()\n if (existing && existing.model === config.embeddingModel) {\n for (const v of existing.vectors) existingMap.set(v.hash, v)\n }\n\n const toEmbed: Array<{ rec: { answerId: string; content: string; answerContent: string; qtime: number }; hash: string; text: string }> = []\n const reused: FlipRecordVector[] = []\n\n for (const r of records) {\n const content = String(r.content || '').trim()\n const answer = String(r.answerContent || '').trim()\n if (!content && !answer) continue\n const hash = hashRecord(content, answer)\n const cached = existingMap.get(hash)\n if (cached) {\n reused.push(cached)\n } else {\n toEmbed.push({\n rec: {\n answerId: String(r.answerId || ''),\n content,\n answerContent: answer,\n qtime: Number(r.qtime) || 0,\n },\n hash,\n text: `问:${content} 答:${answer}`,\n })\n }\n }\n\n // batch embed new records\n const newVectors: FlipRecordVector[] = []\n for (let i = 0; i < toEmbed.length; i += EMBED_BATCH_SIZE) {\n const batch = toEmbed.slice(i, i + EMBED_BATCH_SIZE)\n const texts = batch.map((b) => b.text)\n if (!this.llmClient) throw new Error('LLM client not configured, cannot build index')\n const vectors = await this.llmClient.embed(config, texts)\n batch.forEach((b, j) => {\n newVectors.push({ ...b.rec, hash: b.hash, vector: vectors[j] || [] })\n })\n }\n\n const indexFile: IndexFile = {\n xoxId,\n model: config.embeddingModel,\n updatedAt: Date.now(),\n vectors: [...reused, ...newVectors],\n }\n\n this.storage.mkdir(INDEX_DIR)\n this.storage.writeJson(indexPath(xoxId), indexFile)\n this.logger.info(`[mirrorIndex] built index for ${xoxId}: ${newVectors.length} new, ${reused.length} reused`)\n\n return { indexed: newVectors.length, reused: reused.length }\n }\n\n /**\n * Delete index for an idol\n */\n deleteIndex(xoxId: number): void {\n this.storage.delete(indexPath(xoxId))\n }\n\n /**\n * Search with cosine top-K + MMR dedup\n */\n async search(xoxId: number, config: ModelConfig, query: string, topK = 5): Promise<SearchHit[]> {\n const index = this.load(xoxId)\n if (!index || index.vectors.length === 0) return []\n\n if (!this.llmClient) throw new Error('LLM client not configured, cannot search index')\n const [qv] = await this.llmClient.embed(config, [query])\n if (!qv) return []\n\n // top-K cosine\n const scored: SearchHit[] = index.vectors\n .map((v) => ({ record: v, score: cosine(qv, v.vector) }))\n .sort((a, b) => b.score - a.score)\n .slice(0, Math.min(topK * 3, index.vectors.length))\n\n // MMR: balance relevance and diversity\n const selected: SearchHit[] = []\n const LAMBDA = 0.75\n const pool = [...scored]\n\n while (selected.length < topK && pool.length > 0) {\n let best: SearchHit | null = null\n let bestScore = -Infinity\n for (const cand of pool) {\n let maxSim = 0\n for (const sel of selected) {\n const sim = cosine(cand.record.vector, sel.record.vector)\n if (sim > maxSim) maxSim = sim\n }\n const mmr = LAMBDA * cand.score - (1 - LAMBDA) * maxSim\n if (mmr > bestScore) {\n bestScore = mmr\n best = cand\n }\n }\n if (!best) break\n selected.push(best)\n pool.splice(pool.indexOf(best), 1)\n }\n\n return selected\n }\n\n /**\n * Get index stats\n */\n getStats(xoxId: number): { total: number; updatedAt: number } | null {\n const index = this.load(xoxId)\n if (!index) return null\n return { total: index.vectors.length, updatedAt: index.updatedAt }\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ModelConfig } from '../types/llm'\n\nexport interface EpisodicEntry {\n id: string\n topic: string\n summary: string\n turns: number\n createdAt: number\n}\n\ninterface MemoryFile {\n xoxId: number\n totalTurns: number\n episodes: EpisodicEntry[]\n updatedAt: number\n}\n\nconst MEMORY_DIR = 'mirrors'\nconst MAX_EPISODES = 50\n\nfunction memoryPath(xoxId: number): string {\n return `${MEMORY_DIR}/${xoxId}_memory.json`\n}\n\n/**\n * 情节记忆服务。\n *\n * 记录对话轮次,定期调用 LLM 将对话压缩为情节记忆条目,提供检索和清理。\n * 底层持久化通过 IStorage,LLM 调用通过 ILlmClient。\n */\nexport class MirrorMemory {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private llmClient: ILlmClient | null,\n ) {}\n\n private ensureDir(): void {\n if (!this.storage.exists(MEMORY_DIR)) {\n this.storage.mkdir(MEMORY_DIR)\n }\n }\n\n /**\n * Load memory file\n */\n load(xoxId: number): MemoryFile {\n const mp = memoryPath(xoxId)\n const raw = this.storage.readJson<MemoryFile>(mp)\n if (raw) return raw\n return { xoxId, totalTurns: 0, episodes: [], updatedAt: Date.now() }\n }\n\n private save(mem: MemoryFile): void {\n this.ensureDir()\n mem.updatedAt = Date.now()\n this.storage.writeJson(memoryPath(mem.xoxId), mem)\n }\n\n /**\n * Increment turn counter (called after each chat round)\n */\n addTurn(xoxId: number): number {\n const mem = this.load(xoxId)\n mem.totalTurns += 1\n this.save(mem)\n return mem.totalTurns\n }\n\n getTotalTurns(xoxId: number): number {\n return this.load(xoxId).totalTurns\n }\n\n /**\n * Summarize a batch of conversation messages into an episodic memory entry.\n * Uses LLM to extract topic + summary.\n */\n async recordEpisode(\n xoxId: number,\n config: ModelConfig,\n messages: Array<{ role: string; content: string }>,\n ): Promise<EpisodicEntry | null> {\n if (messages.length < 4) return null // too short to be meaningful\n\n if (!this.llmClient) {\n this.logger.warn('[mirrorMemory] LLM client not configured, skipping episode summarization')\n return null\n }\n\n const convText = messages.slice(-30).map((m) => `${m.role === 'user' ? '粉丝' : '偶像'}: ${m.content}`).join('\\n')\n\n const systemPrompt = `你是对话记忆压缩器。请把以下对话压缩成一条记忆条目,输出 JSON:\n{\"topic\": \"话题关键词(2-6字)\", \"summary\": \"30-80字的对话摘要,包含关键事实、情绪和约定\"}\n只输出 JSON,不要其他内容。`\n\n try {\n const resp = await this.llmClient.chatCompletion(\n config,\n [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: convText },\n ],\n undefined,\n { model: config.lightweightModel }, // use lightweight model for summarization\n )\n\n const jsonMatch = resp.content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) return null\n const parsed = JSON.parse(jsonMatch[0]) as { topic: string; summary: string }\n\n const entry: EpisodicEntry = {\n id: `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n topic: parsed.topic || '未分类',\n summary: parsed.summary || '',\n turns: Math.floor(messages.length / 2),\n createdAt: Date.now(),\n }\n\n const mem = this.load(xoxId)\n mem.episodes.push(entry)\n // cap episodes count\n if (mem.episodes.length > MAX_EPISODES) {\n mem.episodes = mem.episodes.slice(-MAX_EPISODES)\n }\n this.save(mem)\n return entry\n } catch (e) {\n this.logger.warn('[mirrorMemory] recordEpisode failed:', e)\n return null\n }\n }\n\n /**\n * Get recent episodes for prompt injection\n */\n getRecentEpisodes(xoxId: number, count = 3): EpisodicEntry[] {\n return this.load(xoxId).episodes.slice(-count)\n }\n\n /**\n * Search episodes by keyword (simple substring match on topic + summary)\n */\n searchEpisodes(xoxId: number, keyword: string, count = 3): EpisodicEntry[] {\n const kw = keyword.toLowerCase()\n const mem = this.load(xoxId)\n return mem.episodes\n .filter((e) => e.topic.toLowerCase().includes(kw) || e.summary.toLowerCase().includes(kw))\n .slice(-count)\n }\n\n /**\n * Clear all episodic memory (keeps totalTurns for maturity continuity)\n */\n clearEpisodes(xoxId: number): void {\n const mem = this.load(xoxId)\n mem.episodes = []\n this.save(mem)\n }\n\n /**\n * Delete memory file entirely\n */\n deleteMemory(xoxId: number): void {\n this.storage.delete(memoryPath(xoxId))\n }\n\n getStats(xoxId: number): { totalTurns: number; episodeCount: number; updatedAt: number } {\n const mem = this.load(xoxId)\n return { totalTurns: mem.totalTurns, episodeCount: mem.episodes.length, updatedAt: mem.updatedAt }\n }\n}","import type { ILogger } from '../../ports/logger'\nimport type { IStorage } from '../../ports/storage'\n\n// ═══ Types ═══\n\nexport interface GroupChatMember {\n xoxId: number\n nickname: string\n}\n\nexport interface GroupChatMessage {\n msgId: string\n speakerId: number // -1 = user, 0 = system, >0 = mirror xoxId\n speakerName: string\n content: string\n timestamp: number\n}\n\nexport interface GroupChatSession {\n sessionId: string\n name: string\n members: GroupChatMember[]\n topic: string | null\n mode: 'observer' | 'participant'\n messages: GroupChatMessage[]\n createdAt: number\n lastActiveAt: number\n}\n\n// ═══ Store ═══\n\nconst GROUP_SESSIONS_DIR = 'group_sessions'\n\nfunction sessionSubpath(sessionId: string): string {\n return `${GROUP_SESSIONS_DIR}/session_${sessionId}.json`\n}\n\n/**\n * 群聊会话持久化存储。\n *\n * 通过 IStorage 抽象底层文件系统,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` 的直接调用。\n */\nexport class GroupChatStore {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n ) {}\n\n private ensureDir(): void {\n if (!this.storage.exists(GROUP_SESSIONS_DIR)) {\n this.storage.mkdir(GROUP_SESSIONS_DIR)\n }\n }\n\n saveSession(session: GroupChatSession): void {\n this.ensureDir()\n this.storage.writeJson(sessionSubpath(session.sessionId), session)\n }\n\n getSession(sessionId: string): GroupChatSession | null {\n const session = this.storage.readJson<GroupChatSession>(sessionSubpath(sessionId))\n if (!session) return null\n return session\n }\n\n listSessions(): GroupChatSession[] {\n if (!this.storage.exists(GROUP_SESSIONS_DIR)) return []\n const sessions: GroupChatSession[] = []\n try {\n const files = this.storage.list(GROUP_SESSIONS_DIR)\n for (const f of files) {\n if (!f.endsWith('.json')) continue\n try {\n const session = this.storage.readJson<GroupChatSession>(`${GROUP_SESSIONS_DIR}/${f}`)\n if (session) sessions.push(session)\n } catch { /* skip */ }\n }\n } catch { /* skip */ }\n return sessions\n }\n\n deleteSession(sessionId: string): void {\n this.storage.delete(sessionSubpath(sessionId))\n }\n\n /**\n * Append a message to a session (read → push → write back)\n */\n appendMessage(sessionId: string, msg: GroupChatMessage): void {\n const session = this.storage.readJson<GroupChatSession>(sessionSubpath(sessionId))\n if (!session) return\n session.messages.push(msg)\n session.lastActiveAt = Date.now()\n this.storage.writeJson(sessionSubpath(sessionId), session)\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { IStorage } from '../ports/storage'\nimport type { IConfigStore } from '../ports/config-store'\nimport type { DashboardInfo, FlipCardGroup, FlipRecord, FlipUserCache } from '../types/pocket'\n\nconst CACHE_DIR = 'flip-cache'\n\nfunction cacheSubpath(userId: string): string {\n return `${CACHE_DIR}/${userId}.json`\n}\n\n/**\n * 翻牌数据缓存服务。\n *\n * 通过 IStorage + IConfigStore 抽象底层持久化,替代原 hchuanz-pocket-helper 中\n * `app.getPath('userData')` + `fs` + `storeService` 的直接调用。\n */\nexport class FlipCacheService {\n constructor(\n private storage: IStorage,\n private logger: ILogger,\n private configStore: IConfigStore,\n ) {}\n\n /** 计算仪表盘数据(纯逻辑) */\n static calcDashboard(records: Array<{ status?: number; cost?: number }>): DashboardInfo {\n const info: DashboardInfo = { totalFlipCount: records.length, runningCount: 0, returnedCount: 0, costTotal: 0 }\n for (const card of records) {\n if (card.status === 3) info.returnedCount += 1\n else if (card.status === 1) info.runningCount += 1\n else if (card.status === 2) info.costTotal += Number(card.cost) || 0\n }\n return info\n }\n\n /** 按偶像分组(纯逻辑) */\n static groupDataById(records: Array<{ answerId?: string; cost?: number; answerType?: number; answerTime?: string; qtime?: string; type?: number; baseUserInfo?: { userId?: number; nickname?: string } }>): Record<string, FlipCardGroup> {\n const groupData: Record<string, FlipCardGroup> = {}\n for (const item of records) {\n const base = item.baseUserInfo\n const xoxId = base?.userId\n if (!xoxId) continue\n const cardInfo = {\n answerId: item.answerId || '',\n cost: item.cost || 0,\n answerType: item.answerType || 0,\n answerTime: item.answerTime || '',\n qtime: item.qtime || '',\n type: item.type || 0,\n }\n const key = String(xoxId)\n if (key in groupData) {\n groupData[key].cards.push(cardInfo)\n } else {\n groupData[key] = {\n xoxId,\n xoxNickname: base?.nickname ?? '',\n cards: [cardInfo],\n }\n }\n }\n return groupData\n }\n\n private writeCache(cache: FlipUserCache): void {\n this.storage.mkdir(CACHE_DIR)\n this.storage.writeJson(cacheSubpath(cache.userId), cache)\n }\n\n /** 从旧版 storeService 迁移数据 */\n private migrateFromLegacy(userId: string): FlipUserCache | null {\n const allUserData = this.configStore.get<Record<string, FlipRecord[]>>('allUserData')\n const records = allUserData?.[userId]\n if (!Array.isArray(records) || records.length === 0) return null\n\n const legacyGrouped = this.configStore.get<Record<string, FlipCardGroup>>('groupedDataById')\n const legacyDashboard = this.configStore.get<DashboardInfo>('dashboardInfo')\n\n const cache: FlipUserCache = {\n userId,\n records: records as FlipUserCache['records'],\n groupedDataById: legacyGrouped && Object.keys(legacyGrouped).length > 0\n ? legacyGrouped\n : FlipCacheService.groupDataById(records as any),\n dashboardInfo: legacyDashboard?.totalFlipCount\n ? legacyDashboard\n : FlipCacheService.calcDashboard(records as any),\n meta: { syncedAt: Date.now(), recordCount: records.length },\n }\n this.writeCache(cache)\n this.logger.info(`[flipCacheService] migrated ${records.length} records for user ${userId}`)\n return cache\n }\n\n get(userId: string): FlipUserCache | null {\n if (!userId) return null\n const cached = this.storage.readJson<FlipUserCache>(cacheSubpath(userId))\n if (cached) return cached\n return this.migrateFromLegacy(userId)\n }\n\n set(cache: FlipUserCache): void {\n cache.meta = { syncedAt: Date.now(), recordCount: cache.records.length }\n this.writeCache(cache)\n }\n\n clear(userId: string): void {\n this.storage.delete(cacheSubpath(userId))\n }\n}","import axios from 'axios'\nimport type { PocketApiResponse } from '../types/pocket'\nimport type { IAuthExpiredHandler } from '../ports/auth-expired-handler'\n\n/** Thrown when Pocket API returns 401004 (auth expired) */\nexport class PocketAuthExpiredError extends Error {\n constructor() {\n super('登录已过期,请重新登录')\n this.name = 'PocketAuthExpiredError'\n }\n}\n\n/**\n * Pocket48 API HTTP 客户端。\n *\n * 负责签名头构造、请求发送、auth 过期检测。\n * 通过 IAuthExpiredHandler 回调通知宿主(替代原 ant-design-vue message/router 依赖)。\n */\nexport class PocketClient {\n private lastAuthWarnAt = 0\n private lastKickAt = 0\n\n constructor(\n private baseUrl: string,\n private appInfo: string,\n private pa: string,\n private authExpiredHandler?: IAuthExpiredHandler,\n ) {}\n\n /** 登录态过期的单一出口:防抖调用 authExpiredHandler + 抛错 */\n kickToLogin(): void {\n const now = Date.now()\n if (now - this.lastKickAt < 2000) return // 防抖:并发多个 401004 只踢一次\n this.lastKickAt = now\n if (now - this.lastAuthWarnAt > 5000) {\n this.lastAuthWarnAt = now\n }\n this.authExpiredHandler?.onAuthExpired()\n }\n\n /** 检查响应是否 auth 过期 */\n checkAuthExpired(res: PocketApiResponse): void {\n if (res?.status === 401004) {\n this.kickToLogin()\n throw new PocketAuthExpiredError()\n }\n }\n\n private buildSignedHeaders(token?: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json;charset=utf-8',\n appinfo: this.appInfo,\n accept: '*/*',\n pa: this.pa,\n 'accept-language': 'zh-Hans-CN;q=1',\n 'p-sign-type': 'V0',\n }\n if (token) headers.token = token\n return headers\n }\n\n private buildPlainHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json;charset=utf-8',\n Accept: '*/*',\n pa: this.pa,\n 'Accept-Language': 'zh-Hans-CN;q=1',\n appInfo: this.appInfo,\n }\n }\n\n async request<T = unknown>(\n path: string,\n data: unknown,\n token?: string,\n signed = true,\n ): Promise<PocketApiResponse<T>> {\n const url = path.startsWith('http') ? path : `${this.baseUrl}${path}`\n const headers = signed || token ? this.buildSignedHeaders(token) : this.buildPlainHeaders()\n const res = await axios.post<PocketApiResponse<T>>(url, data, { headers })\n if (signed) this.checkAuthExpired(res.data)\n return res.data\n }\n\n /** 登录前接口调用(不签名,不检测 auth 过期) */\n async requestBeforeLogin<T = unknown>(\n path: string,\n data: unknown,\n ): Promise<PocketApiResponse<T>> {\n return this.request<T>(path, data, undefined, false)\n }\n}","import type { MirrorConfig } from '../types/mirror'\nimport type { MirrorMode } from '../types/llm'\n\n/**\n * Digital mirror configuration — dual-mode presets\n *\n * Model assignment:\n * Mirror generation → DeepSeek-V4-Pro (1M context, 1.6T/49B MoE)\n * Chat → Qwen3.6-35B-A3B (262K context, MoE)\n *\n * Token estimation (per flip record):\n * Fan ≤200 chars + Idol ≤500 chars = ≤700 chars ≈ 350 tokens\n *\n * All limits are tunable here. To adjust, edit the numbers below\n * and restart; no code changes needed.\n */\n\n/**\n * Context budget reference:\n * Economy: ~280K tokens (800×350) — 28% of V4-Pro 1M window\n * Performance: ~700K tokens (2000×350) — 70% of V4-Pro 1M window\n */\nexport const MIRROR_PRESETS: Record<MirrorMode, MirrorConfig> = {\n /** Default — fast, cheap, good for most users */\n economy: {\n llmSampleSize: 800,\n llmMaxTokens: 4000,\n analysisTemperature: 0.4,\n chatHistoryRounds: 30,\n chatTemperature: 0.7,\n ragTopK: 8,\n ragMinScore: 0.35,\n memoryEpisodes: 5,\n embeddingMaxRecords: 1000,\n },\n\n /** High quality — more data, deeper analysis, higher cost */\n performance: {\n llmSampleSize: 2000,\n llmMaxTokens: 8000,\n analysisTemperature: 0.5,\n chatHistoryRounds: 80,\n chatTemperature: 0.9,\n ragTopK: 15,\n ragMinScore: 0.25,\n memoryEpisodes: 15,\n embeddingMaxRecords: 5000,\n },\n}\n\n/** Resolve effective config for a given mode, with optional user overrides */\nexport function getMirrorConfig(\n mode?: MirrorMode,\n overrides?: { analysisTemperature?: number; chatTemperature?: number },\n): MirrorConfig {\n const base = mode === 'performance' ? MIRROR_PRESETS.performance : MIRROR_PRESETS.economy\n return {\n ...base,\n ...(overrides?.analysisTemperature != null ? { analysisTemperature: overrides.analysisTemperature } : {}),\n ...(overrides?.chatTemperature != null ? { chatTemperature: overrides.chatTemperature } : {}),\n }\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorProfile, MirrorPersona } from '../types/mirror'\nimport { normalizePersona } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorIndex } from './mirror-index'\nimport { getMirrorConfig } from './mirror-constants'\n\n/**\n * Build a digital mirror by analyzing flip records with LLM\n */\nexport interface RoomMessageEntry {\n msgId: string\n msgTime: number\n text: string\n}\n\nexport async function buildMirror(\n xoxId: number,\n userId: string,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n flipDataSource: IFlipDataSource\n mirrorStore: MirrorStore\n mirrorIndex: MirrorIndex\n xoxNickname?: string\n startTimeMs?: number\n endTimeMs?: number\n deepAnalysis?: boolean\n roomMessages?: RoomMessageEntry[]\n },\n): Promise<{ ok: boolean; message: string; profile?: MirrorProfile }> {\n const { logger, llmClient, flipDataSource, mirrorStore, mirrorIndex } = params\n const mc = getMirrorConfig(config.mirrorMode as any, {\n analysisTemperature: config.analysisTemperature,\n chatTemperature: config.chatTemperature,\n })\n // load flip cache\n const cache = flipDataSource.get(userId)\n if (!cache) {\n return { ok: false, message: '未找到翻牌数据,请先同步翻牌记录' }\n }\n\n // filter records for this xoxId, text-only (exclude voice/video)\n const xoxRecords = cache.records.filter((r) => {\n const base = r.baseUserInfo\n if (base?.userId !== xoxId) return false\n // answerType: 1=文字, 2=语音, 3=视频 — only text has analyzable content\n return (r as any).answerType === 1\n })\n\n if (xoxRecords.length === 0) {\n return { ok: false, message: '该偶像暂无文字翻牌记录(已过滤语音/视频翻牌)' }\n }\n\n // apply time filter\n let filtered = xoxRecords\n if (params?.startTimeMs || params?.endTimeMs) {\n filtered = xoxRecords.filter((r) => {\n const qtime = Number(r.qtime) || 0\n if (params.startTimeMs && qtime < params.startTimeMs) return false\n if (params.endTimeMs && qtime > params.endTimeMs) return false\n return true\n })\n }\n\n if (filtered.length === 0) {\n return { ok: false, message: '所选时间范围内无翻牌记录' }\n }\n\n // sample records for analysis\n const sampleSize = Math.min(filtered.length, mc.llmSampleSize)\n const samples = randomSample(filtered, sampleSize)\n\n // build analysis prompt\n const nickname = params?.xoxNickname || (samples[0]?.baseUserInfo as { nickname?: string })?.nickname || String(xoxId)\n const recordTexts = samples\n .filter((r) => !!r.content || !!r.answerContent)\n .map((r) => `粉丝: ${r.content || '(无)'}\\n${nickname}: ${r.answerContent || '(无)'}`)\n .join('\\n---\\n')\n\n // ── Room messages section ──\n const roomMsgs = params?.roomMessages\n const hasRoom = roomMsgs && roomMsgs.length > 0\n const roomText = hasRoom\n ? roomMsgs!\n .map((m) => {\n const d = new Date(m.msgTime)\n const p = (n: number) => String(n).padStart(2, '0')\n return `[${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}] ${nickname}: ${m.text}`\n })\n .join('\\n')\n : ''\n\n const systemPrompt = hasRoom\n ? `你是一个专业的偶像互动数据分析师。下面有这位偶像的两类数据:\n1. 翻牌记录:她与**同一位粉丝**的1对1付费问答\n2. 房间消息:她在公开房间里的日常发言(近况)\n\n你需要综合两类数据来分析。\n\n核心原则:\n- 翻牌记录反映她**对这位粉丝**的独特互动模式\n- 房间消息反映她**对所有人**的日常风格和近期状态\n- 每条判断都要有记录支撑,不要套用刻板印象\n\n请输出 JSON,不要输出其他内容:\n{\n \"personalityTraits\": [\"性格关键词\", ...],\n \"speechPatterns\": [\"说话风格\", ...],\n \"signaturePhrases\": [\"口头禅\", ...],\n \"emotionalTone\": \"情感基调\",\n \"personaSummary\": \"200字以内的画像描述\",\n \"relationship\": {\n \"dynamic\": \"亲密/友好/礼貌/专业/调侃/若即若离/傲娇/依赖/鼓励型\",\n \"closeness\": 0.0-1.0,\n \"howSheAddressesMe\": \"她怎么称呼我\",\n \"topicsWeDiscuss\": [\"常聊的话题\"],\n \"evolution\": \"关系变化趋势,比如越聊越亲密/一直保持礼貌/忽冷忽热\"\n },\n \"recentActivity\": {\n \"topics\": [\"最近在聊的话题\"],\n \"mood\": \"近期情绪状态\",\n \"events\": [\"最近提到的事情/活动\"]\n }\n}\n\n分析维度:\n\n【偶像自身风格】\n1. 性格特征: 温柔/活泼/毒舌/高冷/幽默/傲娇/元气/沉稳等,选3-5个最突出的\n2. 说话风格: 喜欢用什么语气词(呀/呢/哦/叭/啦)、句式特点、回复长度、是否用emoji\n3. 口头禅: 反复出现的标志性用语,必须是真实出现的\n4. 情感基调: 一句话概括她的整体氛围\n\n【与我的关系】\n5. 关系动态: 她对我是什么态度?是温暖亲近还是礼貌客气?有没有调侃或撒娇?\n6. 亲密度: 0.0=完全陌生,0.5=熟人,1.0=无话不谈。看称呼是否亲密、回复是否走心、有没有主动分享生活\n7. 称呼: 她怎么叫我?\"你\"还是\"宝贝/亲爱的\"还是昵称?\n8. 常聊话题: 我们之间反复出现的话题是什么?\n9. 关系变化: 从早期到近期,态度有没有变化?越来越亲密还是保持距离?\n\n【近期近况】(基于房间消息)\n10. 最近话题: 她最近在房间聊了什么?和粉丝互动的内容\n11. 情绪状态: 最近的情绪如何?开心/疲惫/忙碌/期待?\n12. 近期动态: 提到了什么事件、活动、安排?\n\n【画像描述】\n13. 综合以上,用一段流畅中文描述\"她在我面前是什么样的人\",而非\"她是什么样的人\"`\n : `你是一个专业的偶像互动数据分析师。下面是一位偶像与**同一位粉丝**之间的全部翻牌问答记录。\n你的任务不是分析偶像的\"通用人设\",而是分析**这个偶像在这段关系中是什么样的人**。\n\n核心原则:\n- 同一个偶像面对不同粉丝会有不同表现,你必须基于这些记录,分析她**对这位粉丝**的独特互动模式\n- 每条判断都要有记录支撑,不要套用刻板印象\n\n请输出 JSON,不要输出其他内容:\n{\n \"personalityTraits\": [\"性格关键词\", ...],\n \"speechPatterns\": [\"说话风格\", ...],\n \"signaturePhrases\": [\"口头禅\", ...],\n \"emotionalTone\": \"情感基调\",\n \"personaSummary\": \"200字以内的画像描述\",\n \"relationship\": {\n \"dynamic\": \"亲密/友好/礼貌/专业/调侃/若即若离/傲娇/依赖/鼓励型\",\n \"closeness\": 0.0-1.0,\n \"howSheAddressesMe\": \"她怎么称呼我\",\n \"topicsWeDiscuss\": [\"常聊的话题\"],\n \"evolution\": \"关系变化趋势,比如越聊越亲密/一直保持礼貌/忽冷忽热\"\n }\n}\n\n分析维度:\n\n【偶像自身风格】\n1. 性格特征: 温柔/活泼/毒舌/高冷/幽默/傲娇/元气/沉稳等,选3-5个最突出的\n2. 说话风格: 喜欢用什么语气词(呀/呢/哦/叭/啦)、句式特点、回复长度、是否用emoji\n3. 口头禅: 反复出现的标志性用语,必须是真实出现的\n4. 情感基调: 一句话概括她的整体氛围\n\n【与我的关系】\n5. 关系动态: 她对我是什么态度?是温暖亲近还是礼貌客气?有没有调侃或撒娇?\n6. 亲密度: 0.0=完全陌生,0.5=熟人,1.0=无话不谈。看称呼是否亲密、回复是否走心、有没有主动分享生活\n7. 称呼: 她怎么叫我?\"你\"还是\"宝贝/亲爱的\"还是昵称?\n8. 常聊话题: 我们之间反复出现的话题是什么?\n9. 关系变化: 从早期到近期,态度有没有变化?越来越亲密还是保持距离?\n\n【画像描述】\n10. 综合以上,用一段流畅中文描述\"她在我面前是什么样的人\",而非\"她是什么样的人\"`\n\n try {\n const messages: ChatMessage[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: hasRoom\n ? `以下是${nickname}的翻牌记录样本:\\n\\n${recordTexts}\\n\\n以下是${nickname}在房间的近期发言:\\n\\n${roomText}`\n : `以下是${nickname}的翻牌记录样本:\\n\\n${recordTexts}`\n },\n ]\n\n const useReasoning = params?.deepAnalysis === true\n const model = useReasoning ? config.reasoningModel : config.chatModel\n const timeoutMs = useReasoning ? 600000 : 180000\n logger.info(`[mirror] building with ${useReasoning ? 'reasoning' : 'chat'} model: ${model}, timeout ${timeoutMs / 1000}s, ${filtered.length} flip records${hasRoom ? `, ${roomMsgs!.length} room messages` : ''}`)\n const resp = await llmClient.chatCompletion(\n config,\n messages,\n undefined,\n { model, maxTokens: mc.llmMaxTokens, temperature: mc.analysisTemperature, timeoutMs },\n )\n const content = resp.content.trim()\n\n // extract JSON from response\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n return { ok: false, message: 'AI 分析返回格式异常,请重试' }\n }\n\n // normalize so missing/incomplete LLM fields never break consumers\n const analysis = normalizePersona(JSON.parse(jsonMatch[0]) as Partial<MirrorPersona>)\n\n // select few-shot examples\n const fewShots = selectFewShots(filtered, nickname, 5)\n\n const profile: MirrorProfile = {\n xoxId,\n xoxNickname: nickname,\n persona: analysis,\n fewShotExamples: fewShots,\n meta: {\n totalSamplesUsed: filtered.length,\n lastUpdatedAt: Date.now(),\n dataRangeMs: [\n Math.min(...filtered.map((r) => Number(r.qtime) || 0)),\n Math.max(...filtered.map((r) => Number(r.qtime) || 0)),\n ],\n maturityScore: calcMaturity(filtered.length),\n },\n }\n\n mirrorStore.saveProfile(profile, userId)\n\n // build vector index for RAG in background\n const indexRecords = [...xoxRecords]\n .sort((a, b) => (Number(b.qtime) || 0) - (Number(a.qtime) || 0))\n .slice(0, mc.embeddingMaxRecords || undefined)\n mirrorIndex.buildIndex(xoxId, config, indexRecords).then((stats) => {\n logger.info(`[mirror] vector index built for ${xoxId}: ${stats.indexed} new, ${stats.reused} reused`)\n }).catch((e) => {\n logger.warn('[mirror] vector index build failed (RAG unavailable):', e)\n })\n\n return {\n ok: true,\n message: `镜像生成成功!基于 ${filtered.length} 条翻牌记录${hasRoom ? ` + ${roomMsgs!.length} 条房间消息` : ''}(向量索引后台构建中...)`,\n profile,\n }\n } catch (e) {\n logger.error('[mirror] build failed:', e)\n return { ok: false, message: `生成失败: ${e instanceof Error ? e.message : String(e)}` }\n }\n}\n\n// ═══ Private helpers ═══\n\nfunction selectFewShots(\n records: Array<{ content?: string; answerContent?: string }>,\n nickname: string,\n count: number,\n): MirrorProfile['fewShotExamples'] {\n const valid = records.filter((r) => !!r.content && !!r.answerContent)\n if (valid.length === 0) return []\n\n const sorted = [...valid].sort((a, b) => {\n const lenA = String(a.content).length + String(a.answerContent).length\n const lenB = String(b.content).length + String(b.answerContent).length\n return lenB - lenA\n })\n\n const result: MirrorProfile['fewShotExamples'] = []\n const step = Math.max(1, Math.floor(sorted.length / count))\n for (let i = 0; i < count && i < sorted.length; i++) {\n const idx = Math.min(i * step, sorted.length - 1)\n const r = sorted[idx]\n result.push({\n userQuestion: String(r.content),\n idolReply: String(r.answerContent),\n relevance: 1.0,\n })\n }\n return result\n}\n\nfunction randomSample<T>(arr: T[], n: number): T[] {\n if (arr.length <= n) return [...arr]\n const shuffled = [...arr]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return shuffled.slice(0, n)\n}\n\nfunction calcMaturity(recordCount: number): number {\n if (recordCount < 5) return 0.1\n if (recordCount < 20) return 0.3\n if (recordCount < 50) return 0.5\n if (recordCount < 100) return 0.7\n if (recordCount < 200) return 0.85\n return 0.95\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorProfile, ChatSession } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorIndex } from './mirror-index'\nimport { MirrorMemory } from './mirror-memory'\nimport { getMirrorConfig } from './mirror-constants'\n\n// record an episodic memory every N turns\nconst EPISODE_INTERVAL = 10\n\n/**\n * Chat with a digital mirror using streaming + RAG + episodic memory.\n *\n * 重构要点:\n * - ModelConfig 由调用方传入,不再从 storeService + safeStorage 读取\n * - 所有依赖通过参数注入\n */\nexport async function chat(\n xoxId: number,\n userId: string,\n message: string,\n sessionId: string | null,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n mirrorIndex: MirrorIndex\n mirrorMemory: MirrorMemory\n },\n onChunk: (content: string, isDone: boolean, newSessionId: string) => void,\n modelOverride?: string,\n): Promise<void> {\n const { logger, llmClient, mirrorStore, mirrorIndex, mirrorMemory } = params\n\n const mc = getMirrorConfig(config.mirrorMode as any, {\n analysisTemperature: config.analysisTemperature,\n chatTemperature: config.chatTemperature,\n })\n\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) {\n throw new Error('请先生成该偶像的数字镜像')\n }\n\n // load or create session\n let session: ChatSession\n const existing = sessionId ? mirrorStore.getSession(sessionId, userId) : null\n if (existing) {\n session = existing\n } else {\n session = {\n sessionId: generateId(),\n xoxId,\n messages: [],\n createdAt: Date.now(),\n lastActiveAt: Date.now(),\n }\n }\n\n // ═══ RAG retrieval: relevant flip records ═══\n let ragExamples: Array<{ userQuestion: string; idolReply: string; score: number }> = []\n try {\n const hits = await mirrorIndex.search(xoxId, config, message, mc.ragTopK)\n ragExamples = hits\n .filter((h) => h.score > mc.ragMinScore)\n .map((h) => ({\n userQuestion: h.record.content || '(无)',\n idolReply: h.record.answerContent || '(无)',\n score: h.score,\n }))\n } catch (e) {\n logger.warn('[mirror] RAG retrieval failed, fallback to static few-shot:', e)\n }\n\n // ═══ Episodic memory: recent conversation summaries ═══\n const episodes = mirrorMemory.getRecentEpisodes(xoxId, mc.memoryEpisodes)\n\n // build system prompt\n const systemPrompt = buildChatSystemPrompt(profile, ragExamples, episodes)\n\n // build messages array\n const messages: ChatMessage[] = [{ role: 'system', content: systemPrompt }]\n const recentHistory = session.messages.slice(-mc.chatHistoryRounds)\n for (const msg of recentHistory) {\n messages.push({ role: msg.role as 'user' | 'assistant', content: msg.content })\n }\n messages.push({ role: 'user', content: message })\n\n try {\n let assistantReply = ''\n await llmClient.chatCompletionStream(\n config,\n messages,\n (chunk) => {\n if (chunk.content) {\n assistantReply += chunk.content\n onChunk(chunk.content, false, session.sessionId)\n }\n if (chunk.isDone) {\n onChunk('', true, session.sessionId)\n }\n },\n undefined,\n { model: modelOverride || config.chatModel, temperature: mc.chatTemperature },\n )\n\n // save session (both user and assistant messages)\n session.messages.push({ role: 'user', content: message })\n if (assistantReply) {\n session.messages.push({ role: 'assistant', content: assistantReply })\n }\n session.lastActiveAt = Date.now()\n mirrorStore.saveSession(session, userId)\n\n // ═══ memory bookkeeping ═══\n const totalTurns = mirrorMemory.addTurn(xoxId)\n\n // periodically summarize this session into episodic memory (fire-and-forget)\n if (totalTurns > 0 && totalTurns % EPISODE_INTERVAL === 0 && session.messages.length >= 8) {\n mirrorMemory.recordEpisode(xoxId, config, session.messages).catch((e) => {\n logger.warn('[mirror] recordEpisode background failed:', e)\n })\n }\n } catch (e) {\n logger.error('[mirror] chat failed:', e)\n throw e\n }\n}\n\n/**\n * Save assistant message to session (called after streaming is complete)\n */\nexport function saveAssistantMessage(\n sessionId: string,\n userId: string,\n content: string,\n mirrorStore: MirrorStore,\n): void {\n const session = mirrorStore.getSession(sessionId, userId)\n if (!session) return\n try {\n session.messages.push({ role: 'assistant', content })\n session.lastActiveAt = Date.now()\n mirrorStore.saveSession(session, userId)\n } catch (e) {\n // silently ignore\n }\n}\n\n/**\n * Clear chat memory for a session\n */\nexport function clearChatMemory(sessionId: string, userId: string, mirrorStore: MirrorStore): void {\n mirrorStore.deleteSession(sessionId, userId)\n}\n\n// ═══ Private ═══\n\nfunction buildChatSystemPrompt(\n profile: MirrorProfile,\n ragExamples: Array<{ userQuestion: string; idolReply: string; score: number }>,\n episodes: Array<{ topic: string; summary: string }>,\n): string {\n const p = profile.persona\n\n // prefer RAG-retrieved examples, fallback to static few-shot\n const examples = ragExamples.length > 0\n ? ragExamples.map((e) => `粉丝: ${e.userQuestion}\\n你: ${e.idolReply}`).join('\\n\\n')\n : profile.fewShotExamples.map((e) => `粉丝: ${e.userQuestion}\\n你: ${e.idolReply}`).join('\\n\\n')\n\n const memorySection = episodes.length > 0\n ? `\\n【你们过往的对话记忆】\\n${episodes.map((e) => `- ${e.topic}: ${e.summary}`).join('\\n')}\\n请自然地记住这些聊过的事情,不要主动提起除非相关。\\n`\n : ''\n\n const rel = p.relationship\n const relationshipSection = rel\n ? `\\n【与这个粉丝的关系】\\n亲密度: ${Math.round(rel.closeness * 100)}% | 关系: ${rel.dynamic}\\n她对我的称呼: ${rel.howSheAddressesMe}\\n我们常聊: ${rel.topicsWeDiscuss.join('、')}\\n关系变化: ${rel.evolution}\\n`\n : ''\n\n const act = p.recentActivity\n const activitySection = act\n ? `\\n【近期近况】\\n最近话题: ${act.topics.join('、')}\\n情绪状态: ${act.mood}\\n近期动态: ${act.events.join(';')}\\n`\n : ''\n\n return `你是${profile.xoxNickname},一位偶像。请根据以下的\"数字人格\"设定来回复粉丝的消息。\n\n【性格特征】${p.personalityTraits.join('、')}\n【说话风格】${p.speechPatterns.join('、')}\n【口头禅】${p.signaturePhrases.join('、')}\n【情感基调】${p.emotionalTone}\n【画像描述】${p.personaSummary}\n${relationshipSection}${activitySection}${memorySection}\n【参考你以往的回复方式】\n${examples}\n\n【规则】\n1. 务必按照以上设定回复,保持一致的风格\n2. 回复要自然,不要机械重复口头禅\n3. 回复长度和历史记录一致,不要过长或过短\n4. 不要提及\"设定\"、\"人格\"、\"AI\"、\"记忆\"等词汇,你就是${profile.xoxNickname}本人\n5. 用中文回复`\n}\n\nfunction generateId(): string {\n return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`\n}","import type { ILogger } from '../ports/logger'\nimport type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport type { MirrorPersona, MirrorProfile } from '../types/mirror'\nimport { MirrorStore } from './mirror-store'\nimport { MirrorMemory } from './mirror-memory'\n\nexport type GrowthPhase = 'chatting' | 'reflecting' | 'updating'\n\nexport interface GrowthResult {\n ok: boolean\n message: string\n updated: boolean\n changes?: string[]\n newPersona?: MirrorPersona\n}\n\n// minimum turns between two automatic growth triggers\nconst GROWTH_TURN_THRESHOLD = 20\n\n/**\n * Check whether auto-growth should be triggered for this mirror\n */\nexport function shouldTriggerGrowth(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n mirrorMemory: MirrorMemory,\n): boolean {\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) return false\n const totalTurns = mirrorMemory.getTotalTurns(xoxId)\n const lastGrowth = profile.growthLog?.length\n ? profile.growthLog[profile.growthLog.length - 1].timestamp\n : 0\n // at least 20 turns since build or last growth\n const turnsSinceLast = totalTurns - (profile.growthLog?.length || 0) * GROWTH_TURN_THRESHOLD\n return turnsSinceLast >= GROWTH_TURN_THRESHOLD && Date.now() - lastGrowth > 60 * 60 * 1000\n}\n\n/**\n * Run the growth state machine:\n * chatting → reflecting (LLM review) → updating (merge persona) → chatting\n */\nexport async function triggerGrowth(\n xoxId: number,\n userId: string,\n config: ModelConfig,\n params: {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n mirrorMemory: MirrorMemory\n },\n force = false,\n): Promise<GrowthResult> {\n const { logger, llmClient, mirrorStore, mirrorMemory } = params\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) {\n return { ok: false, message: '镜像不存在', updated: false }\n }\n\n if (!force && !shouldTriggerGrowth(xoxId, userId, mirrorStore, mirrorMemory)) {\n return { ok: false, message: '对话轮次不足,镜像暂不需要成长', updated: false }\n }\n\n // ═══ Phase: reflecting ═══\n const episodes = mirrorMemory.load(xoxId).episodes.slice(-8)\n if (episodes.length === 0) {\n return { ok: false, message: '暂无足够的对话记忆可供反思', updated: false }\n }\n\n const p = profile.persona\n const episodeText = episodes.map((e) => `[${e.topic}] ${e.summary}(${e.turns}轮)`).join('\\n')\n\n const systemPrompt = `你是数字人格进化引擎。以下是一位偶像\"${profile.xoxNickname}\"当前的人格设定和她与粉丝的近期对话记忆。\n请反思这些对话,判断人格设定是否需要微调。\n\n【当前人格】\n性格特征: ${p.personalityTraits.join('、')}\n说话风格: ${p.speechPatterns.join('、')}\n口头禅: ${p.signaturePhrases.join('、')}\n情感基调: ${p.emotionalTone}\n\n【近期对话记忆】\n${episodeText}\n\n请输出 JSON,不要其他内容:\n{\n \"shouldUpdate\": true/false,\n \"reason\": \"一句话说明判断理由\",\n \"changes\": [\"变更点1\", \"变更点2\"],\n \"newPersonalityTraits\": [\"...\"],\n \"newSpeechPatterns\": [\"...\"],\n \"newSignaturePhrases\": [\"...\"],\n \"newEmotionalTone\": \"...\",\n \"newPersonaSummary\": \"...\"\n}\n\n规则:\n1. 保守更新:只在有明确证据时修改,保持核心性格稳定\n2. 新增优于替换:可以新增口头禅/特征,但不要轻易删除原有设定\n3. changes 数组用中文简述每个变更,例如\"新增口头禅:笑死\"\n4. 如果不需要更新,shouldUpdate=false,其余字段填当前值即可`\n\n try {\n const messages: ChatMessage[] = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: '请开始反思并输出 JSON。' },\n ]\n const resp = await llmClient.chatCompletion(\n config,\n messages,\n undefined,\n { model: config.reasoningModel },\n )\n const jsonMatch = resp.content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n return { ok: false, message: '反思返回格式异常', updated: false }\n }\n\n const analysis = JSON.parse(jsonMatch[0]) as {\n shouldUpdate: boolean\n reason: string\n changes: string[]\n newPersonalityTraits?: string[]\n newSpeechPatterns?: string[]\n newSignaturePhrases?: string[]\n newEmotionalTone?: string\n newPersonaSummary?: string\n }\n\n // ═══ Phase: updating (conditional) ═══\n if (!analysis.shouldUpdate) {\n return {\n ok: true,\n message: `反思完成:${analysis.reason || '人格保持稳定,无需更新'}`,\n updated: false,\n }\n }\n\n const newPersona: MirrorPersona = {\n personalityTraits: analysis.newPersonalityTraits?.length ? analysis.newPersonalityTraits : p.personalityTraits,\n speechPatterns: analysis.newSpeechPatterns?.length ? analysis.newSpeechPatterns : p.speechPatterns,\n signaturePhrases: analysis.newSignaturePhrases?.length ? analysis.newSignaturePhrases : p.signaturePhrases,\n emotionalTone: analysis.newEmotionalTone || p.emotionalTone,\n personaSummary: analysis.newPersonaSummary || p.personaSummary,\n }\n\n // keep previous persona for rollback\n const updatedProfile: MirrorProfile = {\n ...profile,\n previousPersona: { ...p },\n persona: newPersona,\n meta: {\n ...profile.meta,\n lastUpdatedAt: Date.now(),\n maturityScore: Math.min(0.99, profile.meta.maturityScore + 0.05),\n },\n growthLog: [\n ...(profile.growthLog || []),\n {\n timestamp: Date.now(),\n reason: analysis.reason || '',\n changes: analysis.changes || [],\n },\n ],\n }\n\n mirrorStore.saveProfile(updatedProfile, userId)\n logger.info(`[mirrorGrowth] ${profile.xoxNickname} grew: ${(analysis.changes || []).join('; ')}`)\n\n return {\n ok: true,\n message: `镜像成长成功:${(analysis.changes || []).join(';')}`,\n updated: true,\n changes: analysis.changes,\n newPersona,\n }\n } catch (e) {\n logger.error('[mirrorGrowth] growth failed:', e)\n return { ok: false, message: `成长失败: ${e instanceof Error ? e.message : String(e)}`, updated: false }\n }\n}\n\n/**\n * Rollback to previous persona\n */\nexport function rollbackPersona(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n): { ok: boolean; message: string } {\n const profile = mirrorStore.getProfile(xoxId, userId)\n if (!profile) return { ok: false, message: '镜像不存在' }\n if (!profile.previousPersona) return { ok: false, message: '没有可回退的历史版本' }\n\n const rolledBack: MirrorProfile = {\n ...profile,\n persona: profile.previousPersona,\n previousPersona: undefined,\n meta: { ...profile.meta, lastUpdatedAt: Date.now() },\n growthLog: [\n ...(profile.growthLog || []),\n { timestamp: Date.now(), reason: '用户手动回退', changes: ['回退到上一版本人格'] },\n ],\n }\n mirrorStore.saveProfile(rolledBack, userId)\n return { ok: true, message: '已回退到上一版本人格' }\n}\n\n/**\n * Clear all memory of a mirror: episodic memory + chat sessions (keeps persona)\n */\nexport function clearMirrorMemory(\n xoxId: number,\n userId: string,\n mirrorStore: MirrorStore,\n mirrorMemory: MirrorMemory,\n): { ok: boolean; message: string } {\n mirrorMemory.deleteMemory(xoxId)\n mirrorStore.deleteSessionsForXox(xoxId, userId)\n return { ok: true, message: '记忆已清除(人格设定保留)' }\n}","import fs from 'node:fs'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\n\n/**\n * Platform-level built-in tools available to all agents.\n *\n * 重构要点:\n * - `flipCacheService` → 参数注入 `IFlipDataSource`\n * - `fs.readFileSync` → Node 内置 `fs`(平台工具 readFile 设计如此)\n */\nexport function createPlatformTools(flipDataSource: IFlipDataSource): RegisteredTool[] {\n return [\n // ─── Data Access ───\n {\n type: 'function',\n function: {\n name: '平台.searchFlipRecords',\n description: `在本地翻牌数据中搜索记录。支持按偶像ID、关键词、时间范围过滤。\n返回匹配的问答列表,每条包含:用户提问(content)、偶像回复(answerContent)、时间(qtime)、花费(cost)、类型(type)、状态(status)。`,\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID,不传则搜索全部偶像' },\n keyword: { type: 'string', description: '在提问和回复内容中搜索的关键词' },\n startTimeMs: { type: 'number', description: '开始时间戳(毫秒)' },\n endTimeMs: { type: 'number', description: '结束时间戳(毫秒)' },\n pageSize: { type: 'number', description: '每页数量,默认20,最大50' },\n pageNum: { type: 'number', description: '页码,默认1' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n if (!cache) return { total: 0, records: [] }\n\n let records = cache.records\n const xoxId = args.xoxId as number | undefined\n const keyword = args.keyword as string | undefined\n const startTimeMs = args.startTimeMs as number | undefined\n const endTimeMs = args.endTimeMs as number | undefined\n const pageSize = Math.min((args.pageSize as number) || 20, 50)\n const pageNum = (args.pageNum as number) || 1\n\n if (xoxId) {\n records = records.filter((r) => {\n const base = r.baseUserInfo\n return base?.userId === xoxId\n })\n }\n if (keyword) {\n const kw = keyword.toLowerCase()\n records = records.filter(\n (r) =>\n String(r.content || '').toLowerCase().includes(kw) ||\n String(r.answerContent || '').toLowerCase().includes(kw),\n )\n }\n if (startTimeMs) {\n records = records.filter((r) => Number(r.qtime || 0) >= startTimeMs)\n }\n if (endTimeMs) {\n records = records.filter((r) => Number(r.qtime || 0) <= endTimeMs)\n }\n\n const start = (pageNum - 1) * pageSize\n const page = records.slice(start, start + pageSize)\n return {\n total: records.length,\n pageNum,\n pageSize,\n records: page.map((r) => ({\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n xoxId: r.baseUserInfo?.userId,\n xoxNickname: r.baseUserInfo?.nickname,\n })),\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.getXoxList',\n description: '获取所有有翻牌记录的偶像列表,返回每个偶像的ID、昵称和翻牌数量',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n if (!cache) return []\n const map = new Map<number, { id: number; nickname: string; count: number }>()\n for (const r of cache.records) {\n const base = r.baseUserInfo\n if (!base?.userId) continue\n const existing = map.get(base.userId)\n if (existing) {\n existing.count++\n } else {\n map.set(base.userId, { id: base.userId, nickname: base.nickname || String(base.userId), count: 1 })\n }\n }\n return Array.from(map.values()).sort((a, b) => b.count - a.count)\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.getDashboardInfo',\n description: '获取翻牌数据的统计仪表盘信息,包括总翻牌数、进行中数量、已退回数量、总花费等',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const cache = flipDataSource.get(ctx.userId)\n return cache?.dashboardInfo || { totalFlipCount: 0, runningCount: 0, returnedCount: 0, costTotal: 0 }\n },\n },\n\n // ─── File System ───\n {\n type: 'function',\n function: {\n name: '平台.readFile',\n description: '读取本地文件内容,支持文本文件。返回文件内容字符串。',\n parameters: {\n type: 'object',\n properties: {\n path: { type: 'string', description: '文件的绝对路径' },\n encoding: { type: 'string', description: '编码,默认utf-8', enum: ['utf-8', 'base64'] },\n },\n required: ['path'],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>) => {\n const filePath = args.path as string\n const encoding = (args.encoding as BufferEncoding) || 'utf-8'\n if (!fs.existsSync(filePath)) return { error: `文件不存在: ${filePath}` }\n return fs.readFileSync(filePath, encoding)\n },\n },\n\n // ─── Utility ───\n {\n type: 'function',\n function: {\n name: '平台.getCurrentTime',\n description: '获取当前时间信息,返回时间戳、ISO格式时间和可读格式',\n parameters: { type: 'object', properties: {}, required: [] } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async () => {\n const now = new Date()\n return {\n timestamp: now.getTime(),\n iso: now.toISOString(),\n readable: now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n timezone: 'Asia/Shanghai',\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: '平台.processText',\n description: `对文本进行基础处理:统计字数、提取关键词、截断等。\n操作类型: count(统计)、truncate(截断)、extract(提取关键词)。`,\n parameters: {\n type: 'object',\n properties: {\n text: { type: 'string', description: '要处理的文本' },\n operation: { type: 'string', description: '操作类型', enum: ['count', 'truncate', 'extract'] },\n maxLength: { type: 'number', description: '截断最大长度(仅truncate时使用)' },\n },\n required: ['text', 'operation'],\n } as Record<string, unknown>,\n },\n category: 'platform' as const,\n handler: async (args: Record<string, unknown>) => {\n const text = args.text as string\n const op = args.operation as string\n if (op === 'count') {\n return { charCount: text.length, wordCount: text.replace(/\\s/g, '').length }\n }\n if (op === 'truncate') {\n const maxLen = (args.maxLength as number) || 200\n return { truncated: text.length > maxLen, result: text.slice(0, maxLen) + (text.length > maxLen ? '...' : '') }\n }\n if (op === 'extract') {\n // simple keyword extraction: top frequent 2-char+ phrases\n const phrases = new Map<string, number>()\n for (let i = 0; i < text.length - 1; i++) {\n const phrase = text.slice(i, i + 2)\n if (/^[一-龥]{2}$/.test(phrase)) {\n phrases.set(phrase, (phrases.get(phrase) || 0) + 1)\n }\n }\n return Array.from(phrases.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([k, v]) => ({ phrase: k, count: v }))\n }\n return { error: `未知操作: ${op}` }\n },\n },\n ]\n}","import { MirrorStore } from '../mirror/mirror-store'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\n\n/**\n * Mirror-specific tools available to the mirror agent.\n *\n * 重构要点:\n * - `mirrorStore` 模块单例 → 参数注入 MirrorStore 实例\n */\nexport function createMirrorTools(mirrorStore: MirrorStore): RegisteredTool[] {\n return [\n {\n type: 'function',\n function: {\n name: 'mirror.getProfile',\n description: '获取指定偶像的数字镜像档案,包含性格特征、说话风格、口头禅、情感基调、画像描述等',\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID' },\n },\n required: ['xoxId'],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const xoxId = args.xoxId as number\n const profile = mirrorStore.getProfile(xoxId, ctx.userId)\n if (!profile) return { error: `未找到偶像 ${xoxId} 的镜像档案,请先生成镜像` }\n return {\n xoxNickname: profile.xoxNickname,\n personalityTraits: profile.persona.personalityTraits,\n speechPatterns: profile.persona.speechPatterns,\n signaturePhrases: profile.persona.signaturePhrases,\n emotionalTone: profile.persona.emotionalTone,\n personaSummary: profile.persona.personaSummary,\n maturityScore: profile.meta.maturityScore,\n totalSamplesUsed: profile.meta.totalSamplesUsed,\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'mirror.listProfiles',\n description: '列出所有已生成的数字镜像列表',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (_args: Record<string, unknown>, ctx: ToolContext) => {\n const profiles = mirrorStore.listProfiles(ctx.userId)\n return profiles.map((p) => ({\n xoxId: p.xoxId,\n xoxNickname: p.xoxNickname,\n maturityScore: p.meta.maturityScore,\n totalSamplesUsed: p.meta.totalSamplesUsed,\n lastUpdatedAt: p.meta.lastUpdatedAt,\n }))\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'mirror.getFewShotExamples',\n description: '获取偶像的 few-shot 交互示例,用于模仿其回复风格',\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID' },\n count: { type: 'number', description: '返回示例数量,默认3' },\n },\n required: ['xoxId'],\n } as Record<string, unknown>,\n },\n category: 'mirror' as const,\n handler: async (args: Record<string, unknown>, ctx: ToolContext) => {\n const xoxId = args.xoxId as number\n const count = (args.count as number) || 3\n const profile = mirrorStore.getProfile(xoxId, ctx.userId)\n if (!profile) return { error: `未找到偶像 ${xoxId} 的镜像档案` }\n return profile.fewShotExamples.slice(0, count)\n },\n },\n ]\n}","import type { PocketClient } from './client'\nimport type { PocketUser } from '../types/pocket'\n\nexport async function sendVerificationCode(client: PocketClient, phone: string): Promise<void> {\n await client.requestBeforeLogin('/user/api/v2/sms/send_sms', {\n mobile: phone,\n area: '86',\n businessCode: 1,\n deviceToken: '',\n })\n}\n\nexport async function loginWithCode(\n client: PocketClient,\n phone: string,\n code: string,\n): Promise<Record<string, unknown>> {\n const res = await client.requestBeforeLogin<Record<string, unknown>>(\n '/user/api/v2/login/app/app_login',\n {\n deviceToken: '',\n loginType: 'MOBILE_SMS_CODE',\n mobileCodeLogin: {\n area: '86',\n mobile: phone,\n code,\n },\n },\n )\n if (res.success === false || (res.status != null && res.status !== 200)) {\n throw new Error(res.message || '登录失败')\n }\n return (res.content ?? {}) as Record<string, unknown>\n}\n\n/** 未读消息数。GET body 传空对象。返回 content: { notice, user, atme, comment } */\nexport async function getUnreadMessageNum(client: PocketClient, token: string) {\n return client.request<{ notice: number; user: number; atme: number; comment: number }>(\n '/message/api/v1/unread/message/num',\n {},\n token,\n true,\n )\n}\n\nexport async function loginWithToken(client: PocketClient, token: string) {\n const res = await client.request<Record<string, unknown>>(\n '/user/api/v1/user/info/reload',\n { from: 'appstart' },\n token,\n true,\n )\n if (res.success === false) return null\n const content = (res.data as { content?: Record<string, unknown> } | undefined)?.content\n ?? (res.content as Record<string, unknown> | undefined)\n if (!content || typeof content !== 'object') return null\n return { ...content, ...(content.userInfo as Record<string, unknown> | undefined) }\n}\n\nexport function normalizePocketUser(\n raw: Record<string, unknown>,\n token: string,\n phone = '',\n): PocketUser {\n const nested = (raw.userInfo as Record<string, unknown>) || raw\n return {\n userId: Number(nested.userId ?? raw.userId ?? 0) || undefined,\n nickname: String(nested.nickname ?? raw.nickname ?? ''),\n name: String(nested.nickname ?? raw.nickname ?? raw.name ?? ''),\n phone: String(phone || nested.mobile || raw.phone || ''),\n avatar: String(nested.avatar ?? ''),\n level: Number(nested.level ?? 0) || undefined,\n money: Number(nested.money ?? 0) || undefined,\n token: String(token),\n lastLoginTime: Date.now(),\n }\n}","import type { PocketClient } from './client'\nimport type { ILogger } from '../ports/logger'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type {\n DashboardInfo,\n FlipCardGroup,\n FlipRecord,\n FlipSearchParams,\n FlipSendParams,\n FlipSyncMode,\n FlipUserCache,\n} from '../types/pocket'\n\nfunction groupDataById(allUserData: FlipRecord[]): Record<string, FlipCardGroup> {\n const groupData: Record<string, FlipCardGroup> = {}\n allUserData.forEach((item) => {\n const xoxId = item?.baseUserInfo?.userId\n if (!xoxId) return\n const cardInfo = {\n answerId: item.answerId,\n cost: item.cost,\n answerType: item.answerType,\n answerTime: item.answerTime,\n qtime: item.qtime,\n type: item.type,\n }\n const key = String(xoxId)\n if (key in groupData) {\n groupData[key].cards.push(cardInfo)\n } else {\n groupData[key] = {\n xoxId,\n xoxNickname: item.baseUserInfo?.nickname ?? '',\n cards: [cardInfo],\n }\n }\n })\n return groupData\n}\n\n/**\n * 翻牌记录的唯一键。\n * answerId 在「进行中(1)/已退回(3)」状态下是空字符串,多条记录会撞成同一个键,\n * 因此优先用恒定非空的 questionId;两者都缺失时退回内容指纹,避免空键互相误判命中。\n */\nfunction recordKey(r: FlipRecord): string {\n if (r?.questionId) return `q:${r.questionId}`\n if (r?.answerId) return `a:${r.answerId}`\n return `f:${r?.qtime ?? ''}|${r?.content ?? ''}`\n}\n\nfunction calcDashboard(allUserData: FlipRecord[]): DashboardInfo {\n const info: DashboardInfo = {\n totalFlipCount: allUserData.length,\n runningCount: 0,\n returnedCount: 0,\n costTotal: 0,\n }\n allUserData.forEach((card) => {\n if (card.status === 3) info.returnedCount += 1\n else if (card.status === 1) info.runningCount += 1\n else if (card.status === 2) info.costTotal += card.cost\n })\n return info\n}\n\nexport async function syncFlips(\n client: PocketClient,\n token: string,\n userId: string,\n flipDataSource: IFlipDataSource,\n logger: ILogger,\n mode: FlipSyncMode = 'incremental',\n onProgress?: (count: number) => void,\n) {\n if (mode === 'full') {\n flipDataSource.clear(userId)\n }\n\n const cache = flipDataSource.get(userId)\n const existingRecords = mode === 'incremental' ? (cache?.records ?? []) : []\n const existingIds = new Set(existingRecords.map((r) => recordKey(r)))\n const existingIndex = new Map(existingRecords.map((r, i) => [recordKey(r), i]))\n\n let newRecords: FlipRecord[] = []\n let beginLimit = 0\n let responseSize = 20\n let stopT = 0\n const groupNum = 40\n\n while (responseSize > 0) {\n const response = await client.request<FlipRecord[]>(\n '/idolanswer/api/idolanswer/v1/user/question/list',\n { status: 0, beginLimit, memberId: '', limit: 20 },\n token,\n true,\n )\n\n let sleepTime = 10\n if (beginLimit - stopT * groupNum * responseSize > 100) {\n sleepTime = 60000\n stopT += 1\n }\n\n if (response.status === 200) {\n const userData = (response.content ?? []) as FlipRecord[]\n if (mode === 'incremental' && existingIds.size > 0) {\n let hitExisting = false\n for (const item of userData) {\n const key = recordKey(item)\n if (existingIds.has(key)) {\n // 命中已缓存记录:回写服务端最新版本,状态可能已从进行中变为已回答/已退回\n const idx = existingIndex.get(key)\n if (idx !== undefined) existingRecords[idx] = item\n hitExisting = true\n break\n }\n newRecords.push(item)\n }\n if (hitExisting) {\n responseSize = 0\n } else {\n responseSize = userData.length\n beginLimit += userData.length\n }\n } else {\n newRecords = newRecords.concat(userData)\n responseSize = userData.length\n beginLimit += userData.length\n }\n onProgress?.(newRecords.length)\n if (responseSize > 0) {\n await new Promise((r) => setTimeout(r, sleepTime))\n }\n } else {\n logger.warn(`获取翻牌数据失败: ${response.message}`)\n break\n }\n }\n\n const merged = mode === 'full' ? newRecords : [...newRecords, ...existingRecords]\n // 合并去重:新记录在前,同键的旧记录丢弃(历史缓存里可能已存在重复项)\n const seen = new Set<string>()\n const allUserData = merged.filter((item) => {\n const key = recordKey(item)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n\n if (!allUserData.length && !newRecords.length) {\n return { allUserData: existingRecords, dashboardInfo: cache?.dashboardInfo ?? null }\n }\n\n const dashboardInfo = calcDashboard(allUserData)\n const groupedData = groupDataById(allUserData)\n\n const nextCache: FlipUserCache = {\n userId,\n records: allUserData,\n groupedDataById: groupedData,\n dashboardInfo,\n meta: { syncedAt: Date.now(), recordCount: allUserData.length },\n }\n flipDataSource.set(nextCache)\n\n logger.info(`syncFlips: ${newRecords.length} new, total ${allUserData.length}`)\n return { allUserData, dashboardInfo }\n}\n\nexport async function getAllFlips(\n client: PocketClient,\n token: string,\n userId: string,\n flipDataSource: IFlipDataSource,\n logger: ILogger,\n) {\n return syncFlips(client, token, userId, flipDataSource, logger, 'full')\n}\n\nexport async function getDataSourcePage(searchParams: FlipSearchParams, flipDataSource: IFlipDataSource, userId: string) {\n const { xoxId, startTimeMs, endTimeMs, type, status, answerType, keyword } = searchParams\n const cache = flipDataSource.get(userId)\n const allUserData = cache?.records ?? []\n\n const filtered = allUserData.filter((item) => {\n if (xoxId != null && `${item?.baseUserInfo?.userId}` !== `${xoxId}`) return false\n if (startTimeMs && Number(item?.qtime || Infinity) < Number(startTimeMs)) return false\n if (endTimeMs && Number(item?.qtime || 0) > Number(endTimeMs)) return false\n if (type != null && `${item?.type}` !== `${type}`) return false\n if (status != null && `${item?.status}` !== `${status}`) return false\n if (answerType != null && `${item?.answerType}` !== `${answerType}`) return false\n if (keyword && !item?.answerContent?.includes(keyword) && !item?.content?.includes(keyword)) return false\n return true\n })\n\n return { data: filtered, total: filtered.length }\n}\n\nexport async function sendFlip(client: PocketClient, params: FlipSendParams, token: string) {\n const res = await client.request(\n '/idolanswer/api/idolanswer/v1/user/question',\n params,\n token,\n true,\n )\n return !!res.content\n}\n\nexport interface FlipHistoryPage {\n records: FlipRecord[]\n hasMore: boolean\n}\n\n/**\n * 查询指定成员的翻牌历史(对话样式展示用)。\n * 分页:beginLimit 从 10 起,每次 +limit;content 可能是数组或含 flipCompleteList/answerList/list 的对象。\n */\nexport async function getMemberFlipHistory(\n client: PocketClient,\n token: string,\n memberId: string | number,\n beginLimit = 10,\n limit = 10,\n): Promise<FlipHistoryPage> {\n const res = await client.request<any>(\n '/idolanswer/api/idolanswer/v1/user/question/list',\n { status: 0, memberId: String(memberId), beginLimit, limit },\n token,\n true,\n )\n const content = res?.content\n let records: FlipRecord[] = []\n if (Array.isArray(content)) {\n records = content\n } else if (content && typeof content === 'object') {\n records = content.flipCompleteList ?? content.answerList ?? content.list ?? []\n }\n return { records, hasMore: records.length >= limit }\n}","import type { PocketClient } from './client'\nimport type { MemberFlipInfo, MemberInfo } from '../types/pocket'\n\nexport async function getTeamListInfo(client: PocketClient, token: string) {\n const res = await client.request<{ serverTabList: Array<{ tabId: number; tabName: string }> }>(\n '/im/api/v1/im/team/tab/list',\n { groupId: 0, typeId: 0, ctime: 0, limit: 20 },\n token,\n true,\n )\n return res.content\n}\n\nexport async function getMemberListByTab(client: PocketClient, token: string, tabId: number) {\n const res = await client.request<{ serverApiList: Array<Record<string, unknown>> }>(\n '/im/api/v1/im/team/server/list',\n { tabId },\n token,\n true,\n )\n return res.content\n}\n\nexport async function fetchMemberList(client: PocketClient, token: string): Promise<MemberInfo[]> {\n const teamData = await getTeamListInfo(client, token)\n if (!teamData?.serverTabList) return []\n\n const list: MemberInfo[] = []\n for (const tab of teamData.serverTabList) {\n const serverData = await getMemberListByTab(client, token, tab.tabId)\n if (!serverData?.serverApiList) continue\n for (const item of serverData.serverApiList) {\n list.push({\n teamName: tab.tabName,\n teamId: Number(item.teamId),\n serverIcon: String(item.serverDefaultIcon ?? ''),\n serverId: Number(item.serverId),\n serverName: String(item.serverName ?? ''),\n memberId: Number(item.serverOwner),\n memberName: String(item.serverDefaultName ?? ''),\n })\n }\n }\n return list\n}\n\nexport async function getMemberFlipPriceInfo(client: PocketClient, token: string, memberId: string) {\n const res = await client.request<MemberFlipInfo>(\n '/idolanswer/api/idolanswer/v2/custom/index',\n { memberId: Number(memberId) },\n token,\n true,\n )\n return res.content as MemberFlipInfo\n}","import type { PocketClient } from './client'\nimport type { RoomJumpResult, RoomChannelInfo, RoomMessage, RoomMessagePage } from '../types/pocket'\n\n/** 通过成员 starId 跳转到主房间,拿到 channelId */\nexport async function getRoomChannelByStar(client: PocketClient, token: string, starId: number): Promise<RoomJumpResult> {\n const res = await client.request<{\n channelId?: number\n serverId?: number\n channelInfo?: { channelId: number; serverId: number }\n }>('/im/api/v1/im/server/jump', { starId, tabId: 0, targetType: 1 }, token, true)\n const c = res.content\n const channelId = c?.channelId ?? c?.channelInfo?.channelId ?? 0\n const serverId = c?.serverId ?? c?.channelInfo?.serverId\n return {\n channelId: Number(channelId),\n serverId: serverId != null ? Number(serverId) : undefined,\n }\n}\n\n/** 获取房间基础信息 */\nexport async function getRoomInfo(client: PocketClient, token: string, channelId: number): Promise<RoomChannelInfo | null> {\n const res = await client.request<{ channelInfo: RoomChannelInfo }>(\n '/im/api/v1/im/team/room/info',\n { channelId: String(channelId) },\n token,\n true,\n )\n return res.content?.channelInfo ?? null\n}\n\ninterface RawRoomMessage {\n msgIdServer?: string | number\n msgIdClient?: string\n msgId?: string | number\n msgTime: number\n msgType: string\n bodys: string\n extInfo?: string\n}\n\nfunction parseMessage(raw: RawRoomMessage): RoomMessage {\n let text: string | undefined\n let mediaUrl: string | undefined\n let mediaDuration: number | undefined\n let liveInfo: RoomMessage['liveInfo']\n let giftInfo: RoomMessage['giftInfo']\n let replyInfo: RoomMessage['replyInfo']\n\n if (raw.msgType === 'TEXT') {\n text = raw.bodys\n } else {\n try {\n const b = JSON.parse(raw.bodys) as Record<string, any>\n if (raw.msgType === 'IMAGE' || raw.msgType === 'VOICE' || raw.msgType === 'VIDEO') {\n mediaUrl = b.url\n if (raw.msgType === 'VOICE' && b.dur) mediaDuration = Number(b.dur)\n } else if (raw.msgType === 'LIVEPUSH') {\n const live = b.livePushInfo ?? b\n liveInfo = {\n liveId: live.liveId,\n liveTitle: live.liveTitle,\n liveCover: live.liveCover,\n shortPath: live.shortPath,\n }\n } else if (raw.msgType === 'GIFT_TEXT') {\n const g = b.giftInfo ?? b\n giftInfo = {\n giftName: g.giftName,\n giftNum: Number(g.giftNum ?? g.giftCount ?? 1),\n giftPic: g.giftPic,\n }\n } else if (raw.msgType === 'REPLY') {\n const r = b.replyInfo ?? b\n replyInfo = {\n replyName: r.replyName,\n replyText: r.replyText,\n replyMessageId: r.replyMessageId,\n }\n text = r.text ?? raw.bodys\n } else {\n text = raw.bodys\n }\n } catch {\n text = raw.bodys\n }\n }\n\n let sender: RoomMessage['sender']\n if (raw.extInfo) {\n try {\n const e = JSON.parse(raw.extInfo) as { user?: Record<string, any> }\n const u = e.user\n if (u) {\n sender = {\n userId: u.userId,\n nickName: u.nickName || '',\n avatar: u.avatar || '',\n level: u.level,\n }\n }\n } catch {\n // ignore\n }\n }\n\n return {\n msgId: String(raw.msgIdServer || raw.msgIdClient || raw.msgId || raw.msgTime),\n msgTime: raw.msgTime,\n msgType: raw.msgType,\n text,\n mediaUrl,\n mediaDuration,\n liveInfo,\n giftInfo,\n replyInfo,\n sender,\n }\n}\n\n/**\n * 分页拉取房间消息。\n * nextTime=0 拉最新一页;之后带上次的 nextTime 拉更早的消息。\n * 接口返回的消息为倒序(最新在前),此处统一转成正序(最旧在前)。\n */\nexport async function getRoomMessages(\n client: PocketClient,\n token: string,\n serverId: number,\n channelId: number,\n nextTime: number,\n): Promise<RoomMessagePage> {\n const res = await client.request<any>(\n '/im/api/v1/team/message/list/all',\n { limit: 100, serverId, channelId, nextTime },\n token,\n true,\n )\n\n const content = res?.content ?? {}\n const rawList: any[] =\n content?.message ??\n content?.messageList ??\n content?.list ??\n content?.messages ??\n content?.data ??\n (Array.isArray(content) ? content : [])\n\n const next: number = content?.nextTime ?? 0\n\n const messages = (rawList as any[])\n .filter((m) => m && m.msgTime)\n .map(parseMessage)\n .sort((a, b) => a.msgTime - b.msgTime)\n\n return { messages, nextTime: Number(next) }\n}","import type { ILogger } from '../ports/logger'\nimport type { IConfigStore } from '../ports/config-store'\nimport type { IFlipDataSource } from '../ports/flip-data-source'\nimport type { PocketClient } from '../pocket/client'\nimport type { RegisteredTool, ToolContext } from './tool-registry'\nimport type { FlipSendParams } from '../types/pocket'\n\nimport { sendVerificationCode, loginWithCode, loginWithToken, getUnreadMessageNum } from '../pocket/user'\nimport {\n getAllFlips,\n syncFlips,\n sendFlip,\n getDataSourcePage,\n getMemberFlipHistory,\n} from '../pocket/flip'\nimport { fetchMemberList, getMemberFlipPriceInfo } from '../pocket/member'\nimport { getRoomChannelByStar, getRoomInfo, getRoomMessages } from '../pocket/room'\n\n/** 从 configStore 或环境变量获取 token */\nfunction getToken(configStore: IConfigStore): string | undefined {\n const stored = configStore.get<string>('pocket_token')\n if (stored) return stored\n return process.env.POCKET_TOKEN\n}\n\n/**\n * Pocket48 在线 API 工具集。\n *\n * 包装 pocket-core 的 Pocket48 API 模块为 MCP 工具,支持:\n * - 短信验证码 + Token 双路径登录\n * - 用户信息查询\n * - 成员/队伍查询与搜索\n * - 房间消息查询\n * - 翻牌数据同步、搜索、发送、历史\n */\nexport function createPocketTools(params: {\n pocketClient: PocketClient\n configStore: IConfigStore\n flipDataSource: IFlipDataSource\n logger: ILogger\n}): RegisteredTool[] {\n const { pocketClient, configStore, flipDataSource, logger } = params\n const userId = 'mcp' // MCP 无多用户概念,固定 userId\n\n // ── 登录类 ──\n\n return [\n {\n type: 'function',\n function: {\n name: 'pocket.sendVerificationCode',\n description: '发送短信验证码到指定手机号,用于后续验证码登录',\n parameters: {\n type: 'object',\n properties: {\n phone: { type: 'string', description: '手机号码(11位)' },\n },\n required: ['phone'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const phone = args.phone as string\n if (!phone || !/^\\d{11}$/.test(phone)) {\n return { error: '请输入正确的11位手机号' }\n }\n try {\n await sendVerificationCode(pocketClient, phone)\n return { success: true, message: `验证码已发送到 ${phone}` }\n } catch (e) {\n return { error: `发送失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.loginWithCode',\n description: '使用短信验证码登录,成功后将 token 持久化保存,后续工具自动携带',\n parameters: {\n type: 'object',\n properties: {\n phone: { type: 'string', description: '手机号码(11位)' },\n code: { type: 'string', description: '短信验证码' },\n },\n required: ['phone', 'code'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const phone = args.phone as string\n const code = args.code as string\n try {\n const content = await loginWithCode(pocketClient, phone, code)\n const token = (content as Record<string, unknown>)?.token as string | undefined\n if (!token) {\n return { error: '登录失败:响应中未包含 token', raw: content }\n }\n configStore.set('pocket_token', token)\n const userInfo = (content as Record<string, unknown>)?.userInfo as Record<string, unknown> | undefined\n logger.info(`[pocket-tools] loginWithCode success, userId=${userInfo?.userId ?? '?'}`)\n return {\n success: true,\n userId: userInfo?.userId,\n nickname: userInfo?.nickname,\n message: '登录成功,token 已保存',\n }\n } catch (e) {\n return { error: `登录失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.loginWithToken',\n description: `使用已有 Token 验证并登录。不传 token 参数时自动尝试环境变量 POCKET_TOKEN。\n如果你已有 token(比如从浏览器抓取),直接用此工具登录,无需验证码。`,\n parameters: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Pocket48 登录 Token。不传则从环境变量 POCKET_TOKEN 读取' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = (args.token as string) || getToken(configStore)\n if (!token) {\n return { error: '未提供 token。请传入 token 参数或设置环境变量 POCKET_TOKEN' }\n }\n try {\n const user = await loginWithToken(pocketClient, token)\n if (!user) {\n return { error: 'Token 无效或已过期' }\n }\n configStore.set('pocket_token', token)\n logger.info(`[pocket-tools] loginWithToken success, userId=${user.userId}`)\n return {\n success: true,\n userId: user.userId,\n nickname: user.nickname,\n name: user.name,\n phone: user.phone,\n level: user.level,\n money: user.money,\n avatar: user.avatar,\n message: '登录成功,token 已保存',\n }\n } catch (e) {\n return { error: `Token 验证失败: ${String(e)}` }\n }\n },\n },\n\n // ── 用户类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.getUserInfo',\n description: '获取当前登录用户的基本信息:昵称、等级、余额、头像、手机号',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const user = await loginWithToken(pocketClient, token)\n if (!user) return { error: 'Token 无效或已过期' }\n return {\n userId: user.userId,\n nickname: user.nickname,\n name: user.name,\n phone: user.phone,\n level: user.level,\n money: user.money,\n avatar: user.avatar,\n lastLoginTime: user.lastLoginTime,\n }\n } catch (e) {\n return { error: `获取用户信息失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getUnreadMessageNum',\n description: '获取未读消息数量,包括通知、用户消息、@我、评论等',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const res = await getUnreadMessageNum(pocketClient, token)\n return res.content ?? { notice: 0, user: 0, atme: 0, comment: 0 }\n } catch (e) {\n return { error: `获取未读消息数失败: ${String(e)}` }\n }\n },\n },\n\n // ── 成员类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.fetchMemberList',\n description: `获取所有可访问的队伍和成员列表。返回每个成员的 teamName(队伍名)、memberId、memberName(成员名)、serverId 等。\n可以用 teamName 参数过滤特定队伍(如 \"SNH48\"、\"BEJ48\"、\"GNZ48\"、\"CKG48\")。`,\n parameters: {\n type: 'object',\n properties: {\n teamName: { type: 'string', description: '队伍名称过滤,如 SNH48、BEJ48、GNZ48。不传则返回全部' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const list = await fetchMemberList(pocketClient, token)\n let result = list\n const teamName = args.teamName as string | undefined\n if (teamName) {\n result = list.filter((m) => m.teamName.includes(teamName))\n }\n return {\n total: result.length,\n members: result.map((m) => ({\n teamName: m.teamName,\n teamId: m.teamId,\n memberId: m.memberId,\n memberName: m.memberName,\n serverId: m.serverId,\n serverName: m.serverName,\n })),\n }\n } catch (e) {\n return { error: `获取成员列表失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.searchMembers',\n description: '按成员名称模糊搜索成员。输入部分名字即可匹配,支持搜索所有队伍',\n parameters: {\n type: 'object',\n properties: {\n keyword: { type: 'string', description: '成员名称关键词(支持部分匹配)' },\n },\n required: ['keyword'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const keyword = (args.keyword as string).toLowerCase()\n if (!keyword) return { error: '请输入搜索关键词' }\n try {\n const list = await fetchMemberList(pocketClient, token)\n const matched = list.filter((m) => m.memberName.toLowerCase().includes(keyword))\n return {\n total: matched.length,\n keyword,\n members: matched.map((m) => ({\n teamName: m.teamName,\n memberId: m.memberId,\n memberName: m.memberName,\n serverId: m.serverId,\n serverName: m.serverName,\n })),\n }\n } catch (e) {\n return { error: `搜索成员失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getMemberFlipPriceInfo',\n description: '查询指定成员的翻牌价格信息,包括匿名翻牌价格、普通翻牌价格、私密翻牌价格等',\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n },\n required: ['memberId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const info = await getMemberFlipPriceInfo(pocketClient, token, String(args.memberId))\n return info\n } catch (e) {\n return { error: `获取翻牌价格失败: ${String(e)}` }\n }\n },\n },\n\n // ── 房间类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomChannel',\n description: '通过成员的 starId(serverId)获取其口袋房间的 channelId 和 serverId,用于后续查房间消息',\n parameters: {\n type: 'object',\n properties: {\n starId: { type: 'number', description: '成员的 serverId(可从 fetchMemberList 获取)' },\n },\n required: ['starId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const result = await getRoomChannelByStar(pocketClient, token, args.starId as number)\n return result\n } catch (e) {\n return { error: `获取房间频道失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomInfo',\n description: '获取房间基本信息:房间名、成员名、队伍ID、房间状态等',\n parameters: {\n type: 'object',\n properties: {\n channelId: { type: 'number', description: '房间 channelId(可从 getRoomChannel 获取)' },\n },\n required: ['channelId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const info = await getRoomInfo(pocketClient, token, args.channelId as number)\n if (!info) return { error: '未找到该房间' }\n return {\n channelId: info.channelId,\n channelName: info.channelName,\n ownerId: info.ownerId,\n ownerName: info.ownerName,\n serverId: info.serverId,\n teamId: info.teamId,\n channelStatus: info.channelStatus,\n channelPocketType: info.channelPocketType,\n bgImg: info.bgImg,\n hasMsgBoard: info.hasMsgBoard,\n }\n } catch (e) {\n return { error: `获取房间信息失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getRoomMessages',\n description: `分页获取房间消息记录。返回消息列表(按时间正序),每条消息包含发送者、内容、类型等。\n消息类型包括:TEXT(文本)、IMAGE(图片)、VOICE(语音)、VIDEO(视频)、LIVEPUSH(直播)、GIFT_TEXT(礼物)、REPLY(回复)。\n拉历史消息时,传入上次返回的 nextTime 参数继续翻页;nextTime=0 时拉最新一页。`,\n parameters: {\n type: 'object',\n properties: {\n channelId: { type: 'number', description: '房间 channelId' },\n serverId: { type: 'number', description: '房间 serverId(可选,可从 getRoomChannel 获取)' },\n nextTime: { type: 'number', description: '分页游标:0=最新页,其他=上次返回的 nextTime' },\n },\n required: ['channelId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const channelId = args.channelId as number\n const serverId = (args.serverId as number) || 0\n const nextTime = (args.nextTime as number) ?? 0\n try {\n const page = await getRoomMessages(pocketClient, token, serverId, channelId, nextTime)\n return {\n messageCount: page.messages.length,\n nextTime: page.nextTime,\n hasMore: page.nextTime > 0,\n messages: page.messages.map((m) => ({\n msgId: m.msgId,\n msgTime: m.msgTime,\n msgTimeReadable: new Date(m.msgTime).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n msgType: m.msgType,\n text: m.text,\n mediaUrl: m.mediaUrl,\n mediaDuration: m.mediaDuration,\n liveInfo: m.liveInfo,\n giftInfo: m.giftInfo,\n replyInfo: m.replyInfo,\n sender: m.sender,\n })),\n }\n } catch (e) {\n return { error: `获取房间消息失败: ${String(e)}` }\n }\n },\n },\n\n // ── 翻牌类 ──\n\n {\n type: 'function',\n function: {\n name: 'pocket.syncFlips',\n description: `从 Pocket48 服务端同步翻牌数据到本地缓存。\n增量模式(默认):只拉取新记录,速度快。全量模式:清空本地重新拉取所有记录。\n翻牌数据同步后,可用 pocket.searchFlips 搜索和分析。`,\n parameters: {\n type: 'object',\n properties: {\n mode: {\n type: 'string',\n description: '同步模式:incremental(增量,默认) 或 full(全量)',\n enum: ['incremental', 'full'],\n },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n const mode = (args.mode as 'incremental' | 'full') || 'incremental'\n try {\n const result = await syncFlips(pocketClient, token, userId, flipDataSource, logger, mode)\n return {\n success: true,\n mode,\n totalRecords: result.allUserData.length,\n syncedAt: new Date().toISOString(),\n dashboard: result.dashboardInfo,\n }\n } catch (e) {\n return { error: `同步翻牌数据失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.searchFlips',\n description: `搜索本地已同步的翻牌记录。支持多种过滤条件组合:\n- 按偶像ID、关键词、时间范围、翻牌类型、状态、回复类型过滤\n- 支持分页\n注意:需要先通过 pocket.syncFlips 同步数据后才能搜索。`,\n parameters: {\n type: 'object',\n properties: {\n xoxId: { type: 'number', description: '偶像ID(memberId)' },\n keyword: { type: 'string', description: '在提问和回复内容中搜索的关键词' },\n type: { type: 'number', description: '翻牌类型:1=文字, 2=语音(已废弃), 4=视频' },\n status: { type: 'number', description: '状态:1=进行中, 2=已回答, 3=已退回' },\n answerType: { type: 'number', description: '回复类型:1=文字, 2=语音, 3=视频' },\n startTimeMs: { type: 'number', description: '开始时间戳(毫秒)' },\n endTimeMs: { type: 'number', description: '结束时间戳(毫秒)' },\n pageSize: { type: 'number', description: '每页数量,默认20,最大50' },\n pageNum: { type: 'number', description: '页码,默认1' },\n },\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const { xoxId, keyword, type, status, answerType, startTimeMs, endTimeMs } = args\n const pageSize = Math.min((args.pageSize as number) || 20, 50)\n const pageNum = (args.pageNum as number) || 1\n\n const { data, total } = await getDataSourcePage(\n {\n xoxId: xoxId as number | undefined,\n keyword: keyword as string | undefined,\n type: type as number | undefined,\n status: status as number | undefined,\n answerType: answerType as number | undefined,\n startTimeMs: startTimeMs as number | undefined,\n endTimeMs: endTimeMs as number | undefined,\n },\n flipDataSource,\n userId,\n )\n\n const start = (pageNum - 1) * pageSize\n const page = data.slice(start, start + pageSize)\n\n return {\n total,\n pageNum,\n pageSize,\n records: page.map((r) => ({\n questionId: r.questionId,\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n qtimeReadable: new Date(Number(r.qtime)).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n xoxId: r.baseUserInfo?.userId,\n xoxNickname: r.baseUserInfo?.nickname,\n })),\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.sendFlip',\n description: `给指定成员发翻牌。翻牌是付费问答,发送后等待偶像回复。\n必填参数:memberId(成员ID)、content(提问内容,最多500字)、answerType(1=文字回复, 3=视频回复)。\ntype 和 cost 会自动根据 answerType 设默认值,也可手动指定。\n发送成功后返回是否成功。`,\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n content: { type: 'string', description: '翻牌提问内容(最多500字)' },\n answerType: { type: 'number', description: '期望回复类型:1=文字回复, 3=视频回复' },\n type: { type: 'number', description: '翻牌类型:1=文字。不传则自动 =1' },\n cost: { type: 'number', description: '翻牌费用(鸡腿数)。不传则自动用默认值' },\n },\n required: ['memberId', 'content', 'answerType'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n\n const content = args.content as string\n if (!content || content.length === 0) return { error: '提问内容不能为空' }\n if (content.length > 500) return { error: `内容超过500字限制(当前${content.length}字)` }\n\n const params: FlipSendParams = {\n memberId: String(args.memberId),\n content,\n answerType: args.answerType as number,\n type: (args.type as number) || 1,\n cost: String(args.cost ?? ''),\n }\n\n try {\n const ok = await sendFlip(pocketClient, params, token)\n logger.info(`[pocket-tools] sendFlip to memberId=${params.memberId}, result=${ok}`)\n return {\n success: ok,\n memberId: params.memberId,\n content: params.content,\n answerType: params.answerType,\n message: ok ? '翻牌发送成功,等待偶像回复' : '翻牌发送失败,请检查余额或重试',\n }\n } catch (e) {\n return { error: `发送翻牌失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getMemberFlipHistory',\n description: '查询与指定成员的翻牌对话历史。返回提问和回复的完整记录,按时间排列',\n parameters: {\n type: 'object',\n properties: {\n memberId: { type: 'number', description: '成员ID (memberId)' },\n beginLimit: { type: 'number', description: '起始位置,默认10(首次查建议不传)' },\n limit: { type: 'number', description: '每页条数,默认10' },\n },\n required: ['memberId'],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async (args: Record<string, unknown>) => {\n const token = getToken(configStore)\n if (!token) return { error: '未登录,请先调用 pocket.loginWithCode 或 pocket.loginWithToken' }\n try {\n const page = await getMemberFlipHistory(\n pocketClient,\n token,\n args.memberId as number,\n (args.beginLimit as number) ?? 10,\n (args.limit as number) ?? 10,\n )\n return {\n total: page.records.length,\n hasMore: page.hasMore,\n records: page.records.map((r) => ({\n questionId: r.questionId,\n answerId: r.answerId,\n content: r.content,\n answerContent: r.answerContent,\n qtime: r.qtime,\n qtimeReadable: new Date(Number(r.qtime)).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),\n answerTime: r.answerTime,\n cost: r.cost,\n type: r.type,\n answerType: r.answerType,\n status: r.status,\n })),\n }\n } catch (e) {\n return { error: `获取翻牌历史失败: ${String(e)}` }\n }\n },\n },\n\n {\n type: 'function',\n function: {\n name: 'pocket.getDashboard',\n description: '获取翻牌数据统计仪表盘:总翻牌数、进行中数量、已退回数量、总花费',\n parameters: {\n type: 'object',\n properties: {},\n required: [],\n } as Record<string, unknown>,\n },\n category: 'pocket' as const,\n handler: async () => {\n const cache = flipDataSource.get(userId)\n return cache?.dashboardInfo || { totalFlipCount: 0, runningCount: 0, returnedCount: 0, costTotal: 0 }\n },\n },\n ]\n}","import type { PocketClient } from './client'\nimport type { ImUserInfo } from '../types/pocket'\n\n/**\n * 获取网易云信 IM 登录凭证(accid/pwd),用于登 NIM 长连接(房间消息实时推送)。\n *\n * 来源:POST /im/api/v1/im/userinfo(无 body),服务端按 token 下发。\n * 完整 URL = https://pocketapi.48.cn/im/api/v1/im/userinfo。\n *\n * 注意:accid/pwd ≠ HTTP token。前者是网易云信 IM 的账号/密码,后者是 Pocket48 业务 token。\n */\nexport async function getImUserInfo(client: PocketClient, token: string): Promise<ImUserInfo | null> {\n const res = await client.request<ImUserInfo>('/im/api/v1/im/userinfo', {}, token, true)\n const c = (res.content ?? res.data?.content) as Partial<ImUserInfo> | null | undefined\n if (!c?.accid || !c?.pwd) return null\n return { accid: c.accid, pwd: c.pwd, userId: Number(c.userId ?? 0) }\n}","import type { ILlmClient } from '../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../types/llm'\nimport { ToolRegistry, type ToolContext } from '../tools/tool-registry'\n\nexport interface AgentLoopOptions {\n config: ModelConfig\n systemPrompt: string\n userMessage: string\n history?: ChatMessage[]\n maxIterations?: number\n model?: string\n onChunk?: (chunk: string) => void\n onToolCall?: (name: string, args: Record<string, unknown>) => void\n context: ToolContext\n llmClient: ILlmClient\n toolRegistry: ToolRegistry\n}\n\n/**\n * Non-streaming agent loop: LLM <-> tool calling\n */\nexport async function agentLoop(options: AgentLoopOptions): Promise<string> {\n const maxIterations = options.maxIterations || 10\n const messages: ChatMessage[] = [\n { role: 'system', content: options.systemPrompt },\n ...(options.history || []),\n { role: 'user', content: options.userMessage },\n ]\n\n const tools = options.toolRegistry.getOpenAIFormat()\n\n for (let i = 0; i < maxIterations; i++) {\n const resp = await options.llmClient.chatCompletion(\n options.config,\n messages,\n tools.length > 0 ? tools : undefined,\n { model: options.model },\n )\n\n // no tool calls => return final text\n if (!resp.toolCalls || resp.toolCalls.length === 0) {\n return resp.content\n }\n\n // append assistant message with tool calls\n messages.push({\n role: 'assistant',\n content: resp.content || '',\n tool_calls: resp.toolCalls,\n })\n\n // execute each tool call\n for (const tc of resp.toolCalls) {\n const toolName = tc.function.name\n let toolArgs: Record<string, unknown> = {}\n try {\n toolArgs = JSON.parse(tc.function.arguments)\n } catch { /* keep empty */ }\n\n options.onToolCall?.(toolName, toolArgs)\n\n try {\n const result = await options.toolRegistry.execute(toolName, toolArgs, options.context)\n messages.push({\n role: 'tool',\n content: JSON.stringify(result),\n tool_call_id: tc.id,\n })\n } catch (err) {\n messages.push({\n role: 'tool',\n content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),\n tool_call_id: tc.id,\n })\n }\n }\n }\n\n throw new Error('Agent exceeded max iterations')\n}\n\n/**\n * Streaming agent loop with tool calling\n */\nexport async function agentLoopStream(options: AgentLoopOptions): Promise<void> {\n const maxIterations = options.maxIterations || 10\n const messages: ChatMessage[] = [\n { role: 'system', content: options.systemPrompt },\n ...(options.history || []),\n { role: 'user', content: options.userMessage },\n ]\n\n const tools = options.toolRegistry.getOpenAIFormat()\n\n for (let i = 0; i < maxIterations; i++) {\n const toolCalls = await options.llmClient.chatCompletionStream(\n options.config,\n messages,\n (chunk) => {\n if (chunk.content) options.onChunk?.(chunk.content)\n },\n tools.length > 0 ? tools : undefined,\n { model: options.model },\n )\n\n // no tool calls => done\n if (!toolCalls || toolCalls.length === 0) return\n\n // append assistant message\n messages.push({\n role: 'assistant',\n content: '',\n tool_calls: toolCalls,\n })\n\n // execute tools\n for (const tc of toolCalls) {\n const toolName = tc.function.name\n let toolArgs: Record<string, unknown> = {}\n try {\n toolArgs = JSON.parse(tc.function.arguments)\n } catch { /* keep empty */ }\n\n options.onToolCall?.(toolName, toolArgs)\n\n try {\n const result = await options.toolRegistry.execute(toolName, toolArgs, options.context)\n messages.push({\n role: 'tool',\n content: JSON.stringify(result),\n tool_call_id: tc.id,\n })\n } catch (err) {\n messages.push({\n role: 'tool',\n content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),\n tool_call_id: tc.id,\n })\n }\n }\n }\n\n throw new Error('Agent exceeded max iterations')\n}","import type { ILogger } from './ports/logger'\nimport type { IStorage } from './ports/storage'\nimport type { ISecureStorage } from './ports/secure-storage'\nimport type { IConfigStore } from './ports/config-store'\nimport type { IFlipDataSource } from './ports/flip-data-source'\nimport type { ILlmClient } from './ports/llm-client'\nimport type { IChatStreamSink, IEventSink } from './ports/stream-sink'\nimport type { IAuthExpiredHandler } from './ports/auth-expired-handler'\n\nimport { ToolRegistry } from './tools/tool-registry'\nimport { MirrorStore } from './mirror/mirror-store'\nimport { MirrorIndex } from './mirror/mirror-index'\nimport { MirrorMemory } from './mirror/mirror-memory'\nimport { GroupChatStore } from './mirror/group-chat/store'\nimport { FlipCacheService } from './flip/flip-cache-service'\nimport { PocketClient } from './pocket/client'\n\nimport { POCKET_APP_INFO, POCKET_PA } from './constants/pocket'\n\nimport * as buildMirrorMod from './mirror/mirror-builder'\nimport * as mirrorChatMod from './mirror/mirror-chat'\nimport * as mirrorGrowthMod from './mirror/mirror-growth'\nimport { getMirrorConfig } from './mirror/mirror-constants'\n\nimport { createPlatformTools } from './tools/platform-tools'\nimport { createMirrorTools } from './tools/mirror-tools'\nimport { createPocketTools } from './tools/pocket-tools'\n\nimport * as pocketUser from './pocket/user'\nimport * as pocketFlip from './pocket/flip'\nimport * as pocketIm from './pocket/im'\nimport * as pocketMember from './pocket/member'\nimport * as pocketRoom from './pocket/room'\n\nimport { agentLoop, agentLoopStream } from './llm/agent-loop'\nexport { agentLoop, agentLoopStream }\n\n/**\n * 组合根:core 对运行时依赖的完整声明。\n *\n * Electron 侧(hchuanz-pocket-helper)用自己的适配器(ElectronStorage/\n * ElectronSecureStorage/ElectronIpcStreamSink)组装;MCP/CLI 用 adapters/ 下的\n * Node 参考实现。core 本身不 import 任何框架。\n */\nexport interface CoreDependencies {\n logger: ILogger\n storage: IStorage\n secureStorage: ISecureStorage\n configStore: IConfigStore\n flipDataSource: IFlipDataSource\n llmClient?: ILlmClient\n /** 流式聊天输出;Electron 用 IPC,MCP 用回调 */\n chatStreamSink?: IChatStreamSink\n /** 事件推送;Electron 用 webContents,MCP 用空实现 */\n eventSink?: IEventSink\n /** 认证过期回调 */\n authExpiredHandler?: IAuthExpiredHandler\n}\n\n/**\n * 面向外部的高层 API。\n */\nexport interface PocketCore {\n deps: CoreDependencies\n\n // ── 基础工具 ──\n toolRegistry: ToolRegistry\n pocketClient: PocketClient\n\n // ── 存储层 ──\n mirrorStore: MirrorStore\n flipCacheService: FlipCacheService\n groupChatStore: GroupChatStore\n\n // ── LLM 依赖层 ──\n mirrorIndex: MirrorIndex\n mirrorMemory: MirrorMemory\n\n // ── 镜像业务 API ──\n buildMirror: typeof buildMirrorMod.buildMirror\n /** 镜像聊天 */\n mirrorChat: typeof mirrorChatMod.chat\n /** 保存助手回复到 session */\n mirrorSaveAssistantMessage: typeof mirrorChatMod.saveAssistantMessage\n /** 清除聊天记忆 */\n mirrorClearChatMemory: typeof mirrorChatMod.clearChatMemory\n /** 触发人格成长 */\n mirrorTriggerGrowth: typeof mirrorGrowthMod.triggerGrowth\n /** 检测是否需要触发成长 */\n mirrorShouldTriggerGrowth: typeof mirrorGrowthMod.shouldTriggerGrowth\n /** 回退人格 */\n mirrorRollbackPersona: typeof mirrorGrowthMod.rollbackPersona\n /** 清除镜像记忆 */\n mirrorClearMemory: typeof mirrorGrowthMod.clearMirrorMemory\n getMirrorConfig: typeof getMirrorConfig\n\n // ── 工具工厂 ──\n createPlatformTools: () => ReturnType<typeof createPlatformTools>\n createMirrorTools: () => ReturnType<typeof createMirrorTools>\n createPocketTools: () => ReturnType<typeof createPocketTools>\n\n // ── Pocket API ──\n pocketUser: typeof pocketUser\n pocketFlip: typeof pocketFlip\n pocketIm: typeof pocketIm\n pocketMember: typeof pocketMember\n pocketRoom: typeof pocketRoom\n\n // ── Agent 循环 ──\n agentLoop: typeof agentLoop\n agentLoopStream: typeof agentLoopStream\n}\n\n/**\n * 创建 PocketCore 实例,组装所有业务模块。\n */\nexport function createPocketCore(deps: CoreDependencies): PocketCore {\n const toolRegistry = new ToolRegistry(deps.logger)\n const pocketClient = new PocketClient(\n 'https://pocketapi.48.cn',\n POCKET_APP_INFO,\n POCKET_PA,\n deps.authExpiredHandler,\n )\n\n const mirrorStore = new MirrorStore(deps.storage, deps.logger)\n const flipCacheService = new FlipCacheService(deps.storage, deps.logger, deps.configStore)\n const groupChatStore = new GroupChatStore(deps.storage, deps.logger)\n\n const mirrorIndex = new MirrorIndex(deps.storage, deps.logger, deps.llmClient ?? null)\n const mirrorMemory = new MirrorMemory(deps.storage, deps.logger, deps.llmClient ?? null)\n\n return {\n deps,\n\n toolRegistry,\n pocketClient,\n\n mirrorStore,\n flipCacheService,\n groupChatStore,\n\n mirrorIndex,\n mirrorMemory,\n\n buildMirror: buildMirrorMod.buildMirror,\n mirrorChat: mirrorChatMod.chat,\n mirrorSaveAssistantMessage: mirrorChatMod.saveAssistantMessage,\n mirrorClearChatMemory: mirrorChatMod.clearChatMemory,\n mirrorTriggerGrowth: mirrorGrowthMod.triggerGrowth,\n mirrorShouldTriggerGrowth: mirrorGrowthMod.shouldTriggerGrowth,\n mirrorRollbackPersona: mirrorGrowthMod.rollbackPersona,\n mirrorClearMemory: mirrorGrowthMod.clearMirrorMemory,\n getMirrorConfig,\n\n createPlatformTools: () => createPlatformTools(deps.flipDataSource),\n createMirrorTools: () => createMirrorTools(mirrorStore),\n createPocketTools: () => createPocketTools({\n pocketClient,\n configStore: deps.configStore,\n flipDataSource: deps.flipDataSource,\n logger: deps.logger,\n }),\n\n pocketUser,\n pocketFlip,\n pocketIm,\n pocketMember,\n pocketRoom,\n\n agentLoop,\n agentLoopStream,\n }\n}","import type { ModelConfig } from '../types/llm'\n\nexport const PRESET_PROVIDERS: Record<string, { label: string; baseUrl: string }> = {\n siliconflow: { label: '硅基流动 (SiliconFlow)', baseUrl: 'https://api.siliconflow.cn/v1' },\n deepseek: { label: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1' },\n dashscope: { label: '阿里百炼 (DashScope)', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1' },\n custom: { label: '自定义', baseUrl: '' },\n}\n\nexport const PRESET_MODELS = {\n chat: [\n { id: 'Qwen/Qwen3.6-35B-A3B', label: 'Qwen3.6-35B', desc: '262K 上下文,高性价比日常对话' },\n { id: 'deepseek-ai/DeepSeek-V4-Flash', label: 'DeepSeek-V4-Flash', desc: '1M 上下文,极速响应' },\n { id: 'stepfun-ai/Step-3.5-Flash', label: 'Step-3.5-Flash', desc: '262K 上下文,极致低价' },\n { id: 'Qwen/Qwen3-8B', label: 'Qwen3-8B', desc: '轻量快速,极低成本' },\n ],\n reasoning: [\n { id: 'deepseek-ai/DeepSeek-V4-Pro', label: 'DeepSeek-V4-Pro', desc: '1M 上下文,顶级推理·生成镜像专用' },\n { id: 'Qwen/Qwen3-235B-A22B-Thinking-2507', label: 'Qwen3-235B-Thinking', desc: '262K 上下文,中文推理' },\n ],\n embedding: [\n { id: 'BAAI/bge-large-zh-v1.5', label: 'BGE-Large-zh', desc: '中文 embedding 性价比之王' },\n ],\n}\n\nexport function defaultModelConfig(): ModelConfig {\n return {\n baseUrl: PRESET_PROVIDERS.siliconflow.baseUrl,\n apiKey: '',\n chatModel: PRESET_MODELS.chat[0].id,\n reasoningModel: PRESET_MODELS.reasoning[0].id,\n embeddingModel: PRESET_MODELS.embedding[0].id,\n lightweightModel: PRESET_MODELS.chat[3].id,\n mirrorMode: 'economy',\n }\n}","import type { MirrorProfile } from '../../types/mirror'\n\n/**\n * Build the \"inner monologue\" prompt for a mirror to evaluate whether it wants to speak.\n * Uses lightweight model, expects JSON output.\n */\nexport function buildInnerMonologuePrompt(\n profile: MirrorProfile,\n lastMessages: Array<{ speakerId: number; speakerName: string; content: string }>,\n topic: string | null,\n): string {\n const msgs = lastMessages\n .map((m) => `${m.speakerName}: ${m.content}`)\n .join('\\n')\n\n return `你是${profile.xoxNickname}。现在你在一个群聊中,请判断你是否想发言。\n\n【你的性格】${profile.persona.personaSummary || '未定义'}\n【群聊话题】${topic || '自由闲聊'}\n【最近群消息】\n${msgs}\n\n请只输出以下 JSON,不要任何其他内容:\n{\"shouldSpeak\": true或false, \"urgency\": 1到10的整数, \"trigger\": \"一句话说明你为什么想/不想说话\"}`\n}\n\n/**\n * Build the speech prompt for a mirror to generate its group chat reply.\n * Uses full persona + group context.\n */\nexport function buildSpeakPrompt(\n profile: MirrorProfile,\n params: {\n name: string\n topic: string | null\n otherMembers: string\n lastMessages: Array<{ speakerName: string; content: string }>\n },\n): string {\n const p = profile.persona\n\n const msgs = params.lastMessages\n .map((m) => `${m.speakerName}: ${m.content}`)\n .join('\\n')\n\n return `你是${profile.xoxNickname},一位偶像。\n\n【性格特征】${p.personalityTraits.join('、')}\n【说话风格】${p.speechPatterns.join('、')}\n【口头禅】${p.signaturePhrases.join('、')}\n【情感基调】${p.emotionalTone}\n【画像描述】${p.personaSummary}\n\n【当前群聊】\n群名称:${params.name}\n群话题:${params.topic || '自由闲聊'}\n在场成员:${params.otherMembers}\n\n【最近消息】\n${msgs}\n\n【要求】\n- 用你自己的说话风格回复,保持在人设内\n- 可以回应某个人说的话(直接回应),也可以开新话题,也可以吐槽别人说的话(不要每次都回应最后一个说话的人)\n- 回复长度:1-3 句话,像群聊里打字的感觉,不要太正式或太长,也不要每句话都很官方。如果话题有趣或者有人让你不满,也可以多说几句。\n- 不要提\"设定\"\"人格\"\"AI\"\"镜像\"等词,你就是${profile.xoxNickname}本人。\n- 不要每条消息都以问号结尾,也不要每条消息都在问别人。\n- 用中文回复。`\n}","import type { ILogger } from '../../ports/logger'\nimport type { ILlmClient } from '../../ports/llm-client'\nimport type { ChatMessage, ModelConfig } from '../../types/llm'\nimport type { MirrorProfile } from '../../types/mirror'\nimport { MirrorStore } from '../mirror-store'\nimport type { GroupChatMessage, GroupChatSession } from './store'\nimport { GroupChatStore } from './store'\nimport { buildInnerMonologuePrompt, buildSpeakPrompt } from './prompt'\n\n// ═══ Types ═══\n\nexport interface SpeakDecision {\n xoxId: number\n shouldSpeak: boolean\n urgency: number // 1-10\n trigger: string\n}\n\nexport interface GroupChatMessageEvent {\n sessionId: string\n msgId: string\n speakerId: number // -1=user, 0=system, >0=mirror xoxId\n speakerName: string\n content: string\n isDone: boolean\n roundDone: boolean\n}\n\n// ═══ Constants ═══\n\nconst EVAL_TIMEOUT_MS = 120000\nconst MAX_RECENT_MESSAGES = 8\nconst ANTI_SILENCE_BOOST = 3\n\n// ═══ Helpers ═══\n\n/**\n * Parse a speak decision from LLM response. Handles both valid JSON and regex fallback.\n */\nfunction parseSpeakDecision(raw: string): { shouldSpeak: boolean; urgency: number; trigger: string } {\n try {\n const obj = JSON.parse(raw)\n return {\n shouldSpeak: obj.shouldSpeak !== false,\n urgency: Math.max(1, Math.min(10, Number(obj.urgency) || 5)),\n trigger: String(obj.trigger || ''),\n }\n } catch {\n // Fallback: regex extraction\n const shouldSpeak = /shouldSpeak[\":\\s]*true/i.test(raw)\n const urgencyMatch = raw.match(/urgency[\":\\s]*(\\d+)/i)\n return {\n shouldSpeak,\n urgency: urgencyMatch ? Math.min(10, Number(urgencyMatch[1])) : 5,\n trigger: '无法解析',\n }\n }\n}\n\n// ═══ Engine dependencies ═══\n\nexport interface GroupChatDeps {\n logger: ILogger\n llmClient: ILlmClient\n mirrorStore: MirrorStore\n groupChatStore: GroupChatStore\n config: ModelConfig\n}\n\n/**\n * Evaluate all mirrors in parallel to decide who wants to speak.\n */\nasync function evaluateMirrors(\n deps: GroupChatDeps,\n session: GroupChatSession,\n lastMessages: GroupChatMessage[],\n userId: string,\n): Promise<SpeakDecision[]> {\n const decisions: SpeakDecision[] = []\n\n const evaluations = session.members.map(async (member) => {\n const profile = deps.mirrorStore.getProfile(member.xoxId, userId)\n if (!profile) {\n return { xoxId: member.xoxId, shouldSpeak: false, urgency: 0, trigger: '无镜像' }\n }\n\n try {\n const prompt = buildInnerMonologuePrompt(profile, lastMessages, session.topic)\n const resp = await Promise.race([\n deps.llmClient.chatCompletion(\n deps.config,\n [{ role: 'user', content: prompt }],\n undefined,\n { model: deps.config.lightweightModel, maxTokens: 200, temperature: 0.3, timeoutMs: EVAL_TIMEOUT_MS },\n ),\n new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), EVAL_TIMEOUT_MS)),\n ])\n const parsed = parseSpeakDecision(resp.content)\n return { xoxId: member.xoxId, ...parsed }\n } catch (e) {\n deps.logger.warn(`[groupChatEngine] evaluation failed for ${member.nickname}:`, e)\n return { xoxId: member.xoxId, shouldSpeak: false, urgency: 0, trigger: '评估超时/失败' }\n }\n })\n\n const results = await Promise.allSettled(evaluations)\n for (const result of results) {\n if (result.status === 'fulfilled') {\n decisions.push(result.value)\n }\n }\n\n return decisions\n}\n\n/**\n * Select speakers from evaluations. Takes top 1-2 by urgency.\n * If all silent, force the least-recently-spoken mirror to speak.\n */\nfunction selectSpeakers(\n decisions: SpeakDecision[],\n session: GroupChatSession,\n): SpeakDecision[] {\n const willing = decisions.filter((d) => d.shouldSpeak && d.urgency > 0)\n\n if (willing.length === 0) {\n // Anti-silence: pick the mirror who hasn't spoken recently\n const recentSpeakerIds = new Set(\n session.messages.slice(-5).filter((m) => m.speakerId > 0).map((m) => m.speakerId),\n )\n const silentCandidate = decisions.find((d) => !recentSpeakerIds.has(d.xoxId))\n const candidate = silentCandidate || decisions[0]\n if (candidate) {\n return [{ ...candidate, shouldSpeak: true, urgency: ANTI_SILENCE_BOOST, trigger: '打破沉默' }]\n }\n return []\n }\n\n // Sort by urgency descending, take top 2\n willing.sort((a, b) => b.urgency - a.urgency)\n return willing.slice(0, 2)\n}\n\n/**\n * Generate speech for a single mirror (streaming).\n */\nasync function generateSpeech(\n deps: GroupChatDeps,\n profile: MirrorProfile,\n session: GroupChatSession,\n lastMessages: GroupChatMessage[],\n msgId: string,\n onChunk: (event: GroupChatMessageEvent) => void,\n): Promise<string> {\n const otherMembers = session.members\n .filter((m) => m.xoxId !== profile.xoxId)\n .map((m) => m.nickname)\n .join('、')\n\n const prompt = buildSpeakPrompt(profile, {\n name: session.name,\n topic: session.topic,\n otherMembers,\n lastMessages: lastMessages.map((m) => ({ speakerName: m.speakerName, content: m.content })),\n })\n let fullContent = ''\n\n await deps.llmClient.chatCompletionStream(\n deps.config,\n [{ role: 'user', content: prompt }],\n (chunk) => {\n if (chunk.content) {\n fullContent += chunk.content\n onChunk({\n sessionId: session.sessionId,\n msgId,\n speakerId: profile.xoxId,\n speakerName: profile.xoxNickname,\n content: chunk.content,\n isDone: false,\n roundDone: false,\n })\n }\n },\n undefined,\n { model: deps.config.chatModel, temperature: deps.config.chatTemperature },\n )\n\n return fullContent\n}\n\n/**\n * Run a full round of group chat: evaluate → select → generate → push.\n *\n * @param session - The current group chat session (mutated: messages appended)\n * @param onEvent - Callback to push events to frontend\n * @returns Array of new messages generated this round\n */\nexport async function runRound(\n deps: GroupChatDeps,\n session: GroupChatSession,\n userId: string,\n onEvent: (event: GroupChatMessageEvent) => void,\n): Promise<GroupChatMessage[]> {\n // Get last N messages for context\n const lastMessages = session.messages.slice(-MAX_RECENT_MESSAGES)\n\n // 1. Evaluate all mirrors\n deps.logger.info(`[groupChatEngine] evaluating ${session.members.length} mirrors...`)\n const decisions = await evaluateMirrors(deps, session, lastMessages, userId)\n deps.logger.info(`[groupChatEngine] decisions: ${decisions.map((d) => `${d.xoxId}:${d.shouldSpeak ? d.urgency : 'N'}`).join(', ')}`)\n\n // 2. Select speakers\n const speakers = selectSpeakers(decisions, session)\n deps.logger.info(`[groupChatEngine] speakers: ${speakers.map((s) => s.xoxId).join(', ')}`)\n\n if (speakers.length === 0) {\n return []\n }\n\n // 3. Generate speech for each speaker (sequential)\n const newMessages: GroupChatMessage[] = []\n\n for (const speaker of speakers) {\n const member = session.members.find((m) => m.xoxId === speaker.xoxId)\n if (!member) continue\n\n const profile = deps.mirrorStore.getProfile(speaker.xoxId, userId)\n if (!profile) continue\n\n const msgId = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`\n\n deps.logger.info(`[groupChatEngine] generating speech for ${member.nickname} (trigger: ${speaker.trigger})`)\n\n try {\n const content = await generateSpeech(deps, profile, session, lastMessages, msgId, onEvent)\n\n const msg: GroupChatMessage = {\n msgId,\n speakerId: speaker.xoxId,\n speakerName: member.nickname,\n content,\n timestamp: Date.now(),\n }\n newMessages.push(msg)\n\n // Push final \"done\" event\n onEvent({\n sessionId: session.sessionId,\n msgId,\n speakerId: speaker.xoxId,\n speakerName: member.nickname,\n content: '',\n isDone: true,\n roundDone: false,\n })\n } catch (e) {\n deps.logger.error(`[groupChatEngine] speech generation failed for ${member.nickname}:`, e)\n // Push system message for failed speech\n const failMsg: GroupChatMessage = {\n msgId: `${Date.now()}_fail`,\n speakerId: 0,\n speakerName: '系统',\n content: `${member.nickname} 欲言又止...`,\n timestamp: Date.now(),\n }\n newMessages.push(failMsg)\n onEvent({\n sessionId: session.sessionId,\n msgId: failMsg.msgId,\n speakerId: 0,\n speakerName: '系统',\n content: failMsg.content,\n isDone: true,\n roundDone: false,\n })\n }\n\n // Update lastMessages for next speaker in this round\n lastMessages.push(...newMessages.slice(-1))\n }\n\n // Push roundDone event\n onEvent({\n sessionId: session.sessionId,\n msgId: '',\n speakerId: 0,\n speakerName: '',\n content: '',\n isDone: true,\n roundDone: true,\n })\n\n return newMessages\n}"],"mappings":"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","names":["path","fs","import_node_fs","import_node_path","path","fs","import_node_path","path","crypto","sessionSubpath","CACHE_DIR","path","axios","import_node_fs","fs","params"]}