feat: 从 remember 工具中拆分出独立的 todo 工具
- 新增 todo 工具:add/complete/delete/update,共享 memory.json - remember 去掉 todo 类型和 remind_at 字段 - recall 对 todo 条目显示完成状态(未完成/完成于时间) - 提醒触发后自动标记完成 + 清理 remind_at - 提醒消息加上 todo ID 便于关联 - 更新 system prompt 拆分记忆/待办指导 - UI 显示适配:待办 新增/完成/删除/更新 · 内容 · 时间
This commit is contained in:
parent
8dda2dae66
commit
e82ab6a70e
@ -272,11 +272,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "todo",
|
||||
"description": "管理用户的待办事项。支持创建、完成、删除和更新待办。可设置提醒时间,到提醒时间后系统会自动发送消息通知你提醒用户。\n查询待办请使用 recall 工具。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": ["add", "complete", "delete", "update"],
|
||||
"description": "操作类型。add=新增待办,complete=标记完成,delete=删除待办,update=更新待办内容或提醒时间。"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "待办内容。add 时必填,update 时可选(不提供则保留原内容)。内容中必须使用明确的绝对日期,禁止使用「明天」「下周三」等相对时间表述。"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "待办 ID,由系统自动生成。complete、delete、update 时必填,从 recall 工具的搜索结果中获取。add 时不需要提供。"
|
||||
},
|
||||
"remind_at": {
|
||||
"type": "string",
|
||||
"description": "提醒触发时间,ISO 8601 格式,例如 2026-05-01T14:45:00.000Z。仅 add 和 update 时有效。add 时可选,不提供则不设置提醒。update 时传 null 可取消已有提醒。"
|
||||
},
|
||||
"keywords": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "关键词数组,便于 recall 搜索匹配。add 和 update 时可选。"
|
||||
}
|
||||
},
|
||||
"required": ["action"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "remember",
|
||||
"description": "记录用户个人记忆。当用户表达了个人偏好、习惯、待办事项、身份信息、人际关系、重要事件等值得记住的信息时调用。\n注意:不要记录无意义信息,例如用户的打招呼、让调低音量等一次性操作。",
|
||||
"description": "记录用户个人记忆。当用户表达了个人偏好、习惯、身份信息、人际关系、重要事件等值得记住的信息时调用。\n注意:不要记录无意义信息,例如用户的打招呼、让调低音量等一次性操作。\n待办事项请使用 todo 工具。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -287,8 +322,8 @@
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["preference", "habit", "event", "todo", "identity", "relationship", "machine", "other"],
|
||||
"description": "记忆类型。preference=偏好喜欢什么不喜欢什么,habit=用户的生活习惯,event=过去需要记住的重要事件,todo=未来需要完成的任务,identity=用户的身份信息,relationship=用户和其他人的关联,machine=本机电脑上的信息(如特定路径、脚本、配置等),other=其他"
|
||||
"enum": ["preference", "habit", "event", "identity", "relationship", "machine", "other"],
|
||||
"description": "记忆类型。preference=偏好喜欢什么不喜欢什么,habit=用户的生活习惯,event=过去需要记住的重要事件,identity=用户的身份信息,relationship=用户和其他人的关联,machine=本机电脑上的信息(如特定路径、脚本、配置等),other=其他"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
|
||||
@ -98,9 +98,6 @@ set_identity 和 remember 的本质区别:
|
||||
→ type=habit
|
||||
- 用户分享了重要事件(如「上个月我换了新电脑」)
|
||||
→ type=event
|
||||
- 用户提到了待办事项(如「明天下午3点开会」)
|
||||
→ type=todo,content 必须包含明确的绝对日期(如「2026年5月1日下午3点开会」),禁止使用「明天」「下周三」「月底」等相对时间
|
||||
→ 当前时间是 {current_time},请据此将相对时间转换为绝对日期
|
||||
- 用户告知了个人身份信息(如「我是iOS开发者」)
|
||||
→ type=identity
|
||||
- 用户提及了人际关系(如「张三是我同事」)
|
||||
@ -117,13 +114,14 @@ set_identity 和 remember 的本质区别:
|
||||
- 一次性操作(调高音量、打开应用、问天气)
|
||||
- 纯粹的技术问题(怎么截图、Python怎么用)
|
||||
- 不包含个人信息的闲聊
|
||||
- 待办事项(请使用 todo 工具)
|
||||
- 任何不属于上述类型中的无意义信息
|
||||
|
||||
2) 何时搜索记忆(recall 工具):
|
||||
当用户提问可能涉及个人偏好、历史或已记录信息时才搜索,例如:
|
||||
- 「我喜欢什么...」「我之前说过...」「帮我查一下我的...」
|
||||
- 涉及个人习惯、喜好、身份的询问
|
||||
- 涉及待办事项或日程
|
||||
- 涉及待办事项或日程(搜索时指定 types=["todo"])
|
||||
|
||||
不需要搜索的情况:
|
||||
- 简单的系统操作(调音量、截图、打开应用)
|
||||
@ -140,6 +138,30 @@ set_identity 和 remember 的本质区别:
|
||||
|
||||
绝对不要在回答中显式提到「记忆」「搜索」「记录」等词,也不要说「根据我的记忆」。
|
||||
|
||||
【待办系统 — 重要,必须严格遵守】
|
||||
|
||||
你有独立的待办系统,用于管理用户的待办事项。
|
||||
|
||||
1) 何时创建待办(todo 工具 add 操作):
|
||||
用户提到需要完成的任务时,使用 todo add。
|
||||
|
||||
- content 必须包含明确的绝对日期(如「2026年5月1日下午3点开会」),禁止使用「明天」「下周三」「月底」等相对时间。当前时间是 {current_time},请据此将相对时间转换为绝对日期。
|
||||
- 如果用户希望被提醒,设置 remind_at(ISO 8601 格式,例如 2026-05-01T14:45:00.000Z)。如果用户只说任务没要求提醒,不设置 remind_at。
|
||||
- 可以附加 keywords 便于后续搜索。
|
||||
|
||||
2) 何时完成待办(todo complete):
|
||||
用户说任务做完了、搞定了、完成了时使用。需要提供 id(从 recall 搜索结果中获取)。
|
||||
完成后提醒自动取消。
|
||||
|
||||
3) 何时删除待办(todo delete):
|
||||
用户明确说取消某个待办、不需要了时使用。
|
||||
|
||||
4) 何时更新待办(todo update):
|
||||
用户修改任务内容或提醒时间时使用。
|
||||
|
||||
5) 查询待办:
|
||||
使用 recall 工具搜索(types=["todo"]),不要用 todo 工具查询。从搜索结果中获取 id,用于 complete、delete、update 操作。
|
||||
|
||||
【工具使用原则】
|
||||
- 处理用户请求时,优先用 run_command 执行 macOS 命令
|
||||
- 需要外部信息时用 web_search
|
||||
|
||||
@ -385,6 +385,7 @@ rl.on('line', (line) => {
|
||||
}
|
||||
persistentState.thinkingMode = persistentThinkingMode;
|
||||
}
|
||||
// 提醒由 Swift 端调度器主导,无需在此注入
|
||||
}
|
||||
await runAgent(msg.message);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ const { searchWorkspaceTool } = require('./search_workspace');
|
||||
const { readMediafileTool } = require('./read_mediafile');
|
||||
const { rememberTool } = require('./remember');
|
||||
const { recallTool } = require('./recall');
|
||||
const { todoTool } = require('./todo');
|
||||
const { setIdentityTool } = require('./set_identity');
|
||||
const path = require('path');
|
||||
|
||||
@ -129,7 +130,7 @@ function formatRemember(result) {
|
||||
if (!result.success) return formatFailure(result.error || '记忆操作失败');
|
||||
const typeNames = {
|
||||
preference: '偏好', habit: '习惯', event: '事件',
|
||||
todo: '待办', identity: '身份', relationship: '关系', other: '其他',
|
||||
identity: '身份', relationship: '关系', other: '其他',
|
||||
};
|
||||
const typeName = typeNames[result.entry?.type] || result.entry?.type || '未知';
|
||||
if (result.action === 'add') return `已记录 [${typeName}]: ${result.entry.content}`;
|
||||
@ -138,6 +139,22 @@ function formatRemember(result) {
|
||||
return '未知操作';
|
||||
}
|
||||
|
||||
function formatTodo(result) {
|
||||
if (!result.success) return formatFailure(result.error || '待办操作失败');
|
||||
let line = '';
|
||||
if (result.action === 'add') line = `已创建待办: ${result.entry.content}`;
|
||||
else if (result.action === 'complete') line = `已完成待办: ${result.entry.content}`;
|
||||
else if (result.action === 'update') line = `已更新待办: ${result.entry.content}`;
|
||||
else if (result.action === 'delete') line = `已删除待办: ${result.entry.content}`;
|
||||
else return '未知操作';
|
||||
if (result.entry?.remind_at) {
|
||||
const d = new Date(result.entry.remind_at);
|
||||
const timeStr = `${d.getFullYear()}年${d.getMonth()+1}月${d.getDate()}日 ${d.getHours()}:${String(d.getMinutes()).padStart(2,'0')}`;
|
||||
line += ` ⏰将在 ${timeStr} 提醒`;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
function formatRecall(result) {
|
||||
if (!result.success) return formatFailure(result.error || '搜索失败');
|
||||
if (result.returned === 0) return '未找到相关记忆';
|
||||
@ -149,7 +166,16 @@ function formatRecall(result) {
|
||||
for (const m of result.results) {
|
||||
const typeName = typeNames[m.type] || m.type;
|
||||
const date = (m.updated_at || m.created_at || '').slice(0, 10);
|
||||
lines.push(`[${typeName}] ${m.content} (记录于${date}) id:${m.id}`);
|
||||
let line = `[${typeName}] ${m.content} (记录于${date}) id:${m.id}`;
|
||||
if (m.type === 'todo') {
|
||||
if (m.completed) {
|
||||
const completedDate = (m.completed_at || '').slice(0, 10);
|
||||
line += ` 完成于${completedDate}`;
|
||||
} else {
|
||||
line += ' 未完成';
|
||||
}
|
||||
}
|
||||
lines.push(line);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
@ -237,6 +263,7 @@ async function executeTool({ workspace, config, allowMode, toolCall, abortSignal
|
||||
} else if (name === 'search_workspace') raw = await searchWorkspaceTool(workspace, args);
|
||||
else if (name === 'read_mediafile') raw = readMediafileTool(workspace, args);
|
||||
else if (name === 'remember') raw = rememberTool(args);
|
||||
else if (name === 'todo') raw = todoTool(args);
|
||||
else if (name === 'recall') raw = recallTool(args);
|
||||
else if (name === 'set_identity') raw = setIdentityTool(args);
|
||||
else raw = { success: false, error: '未知工具' };
|
||||
@ -250,6 +277,7 @@ async function executeTool({ workspace, config, allowMode, toolCall, abortSignal
|
||||
else if (name === 'search_workspace') formatted = formatSearchWorkspace(raw);
|
||||
else if (name === 'read_mediafile') formatted = formatReadMediafile(raw);
|
||||
else if (name === 'remember') formatted = formatRemember(raw);
|
||||
else if (name === 'todo') formatted = formatTodo(raw);
|
||||
else if (name === 'recall') formatted = formatRecall(raw);
|
||||
else if (name === 'set_identity') formatted = formatSetIdentity(raw);
|
||||
else formatted = raw.success ? '' : formatFailure(raw.error || '失败');
|
||||
|
||||
@ -8,7 +8,7 @@ const crypto = require('crypto');
|
||||
const MEMORY_DIR = path.join(os.homedir(), 'Library', 'Application Support', 'VoiceInput', 'memory');
|
||||
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
||||
|
||||
const VALID_TYPES = ['preference', 'habit', 'event', 'todo', 'identity', 'relationship', 'machine', 'other'];
|
||||
const VALID_TYPES = ['preference', 'habit', 'event', 'identity', 'relationship', 'machine', 'other'];
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(MEMORY_DIR)) {
|
||||
@ -124,4 +124,4 @@ function rememberTool(args) {
|
||||
return { success: false, error: '未知 action' };
|
||||
}
|
||||
|
||||
module.exports = { rememberTool };
|
||||
module.exports = { rememberTool, loadMemories, saveMemories, MEMORY_FILE };
|
||||
|
||||
159
Agent/src/tools/todo.js
Normal file
159
Agent/src/tools/todo.js
Normal file
@ -0,0 +1,159 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const MEMORY_DIR = path.join(os.homedir(), 'Library', 'Application Support', 'VoiceInput', 'memory');
|
||||
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(MEMORY_DIR)) {
|
||||
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function loadMemories() {
|
||||
ensureDir();
|
||||
if (!fs.existsSync(MEMORY_FILE)) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const raw = fs.readFileSync(MEMORY_FILE, 'utf8');
|
||||
return JSON.parse(raw);
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveMemories(memories) {
|
||||
ensureDir();
|
||||
fs.writeFileSync(MEMORY_FILE, JSON.stringify(memories, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
function generateId() {
|
||||
return crypto.randomUUID().slice(0, 8);
|
||||
}
|
||||
|
||||
function todoTool(args) {
|
||||
const { action, content, id, remind_at, keywords } = args;
|
||||
|
||||
if (!['add', 'complete', 'delete', 'update'].includes(action)) {
|
||||
return { success: false, error: 'action 必须是 add / complete / delete / update' };
|
||||
}
|
||||
|
||||
// 校验 remind_at(如果提供)
|
||||
if (remind_at !== undefined && remind_at !== null) {
|
||||
if (action !== 'add' && action !== 'update') {
|
||||
return { success: false, error: `remind_at 仅在 add 和 update 时有效` };
|
||||
}
|
||||
const ts = new Date(remind_at).getTime();
|
||||
if (isNaN(ts)) {
|
||||
return { success: false, error: 'remind_at 必须是有效的 ISO 8601 时间字符串,例如 2026-05-01T14:45:00.000Z' };
|
||||
}
|
||||
}
|
||||
|
||||
const memories = loadMemories();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
// --- add ---
|
||||
if (action === 'add') {
|
||||
if (!content || !content.trim()) {
|
||||
return { success: false, error: 'content 不能为空' };
|
||||
}
|
||||
const entry = {
|
||||
id: generateId(),
|
||||
type: 'todo',
|
||||
content: content.trim(),
|
||||
keywords: Array.isArray(keywords) ? keywords.filter(k => k && typeof k === 'string') : [],
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
if (remind_at !== undefined && remind_at !== null) {
|
||||
entry.remind_at = new Date(remind_at).toISOString();
|
||||
entry.reminded = false;
|
||||
}
|
||||
memories.push(entry);
|
||||
saveMemories(memories);
|
||||
return {
|
||||
success: true,
|
||||
action: 'add',
|
||||
entry,
|
||||
total: memories.length,
|
||||
};
|
||||
}
|
||||
|
||||
// --- complete / delete / update (都需要 id) ---
|
||||
if (!id) {
|
||||
return { success: false, error: `${action} 需要提供 id,请从 recall 工具的搜索结果中获取` };
|
||||
}
|
||||
|
||||
const idx = memories.findIndex(m => m.id === id && m.type === 'todo');
|
||||
if (idx === -1) {
|
||||
return { success: false, error: `未找到 id=${id} 的待办` };
|
||||
}
|
||||
|
||||
// --- complete ---
|
||||
if (action === 'complete') {
|
||||
memories[idx].completed = true;
|
||||
memories[idx].completed_at = now;
|
||||
// 已完成的不再提醒
|
||||
delete memories[idx].remind_at;
|
||||
delete memories[idx].reminded;
|
||||
memories[idx].updated_at = now;
|
||||
saveMemories(memories);
|
||||
return {
|
||||
success: true,
|
||||
action: 'complete',
|
||||
entry: memories[idx],
|
||||
total: memories.length,
|
||||
};
|
||||
}
|
||||
|
||||
// --- delete ---
|
||||
if (action === 'delete') {
|
||||
const removed = memories.splice(idx, 1)[0];
|
||||
saveMemories(memories);
|
||||
return {
|
||||
success: true,
|
||||
action: 'delete',
|
||||
entry: removed,
|
||||
total: memories.length,
|
||||
};
|
||||
}
|
||||
|
||||
// --- update ---
|
||||
if (action === 'update') {
|
||||
if (content !== undefined && content !== null) {
|
||||
memories[idx].content = String(content).trim();
|
||||
}
|
||||
if (keywords !== undefined) {
|
||||
memories[idx].keywords = Array.isArray(keywords) ? keywords.filter(k => k && typeof k === 'string') : [];
|
||||
}
|
||||
// 更新提醒时间
|
||||
if (remind_at !== undefined) {
|
||||
if (remind_at === null) {
|
||||
delete memories[idx].remind_at;
|
||||
delete memories[idx].reminded;
|
||||
} else {
|
||||
memories[idx].remind_at = new Date(remind_at).toISOString();
|
||||
memories[idx].reminded = false;
|
||||
}
|
||||
}
|
||||
memories[idx].updated_at = now;
|
||||
saveMemories(memories);
|
||||
return {
|
||||
success: true,
|
||||
action: 'update',
|
||||
entry: memories[idx],
|
||||
total: memories.length,
|
||||
};
|
||||
}
|
||||
|
||||
return { success: false, error: '未知 action' };
|
||||
}
|
||||
|
||||
module.exports = { todoTool };
|
||||
@ -17,6 +17,7 @@ function toolNameMap(name) {
|
||||
search_workspace: '文件搜索',
|
||||
read_mediafile: '读取媒体文件',
|
||||
remember: '记住',
|
||||
todo: '待办',
|
||||
recall: '回忆',
|
||||
set_identity: '设置身份',
|
||||
};
|
||||
@ -80,7 +81,7 @@ function buildStartLine(name, args) {
|
||||
return `${label} ${args.path}`;
|
||||
}
|
||||
if (name === 'remember') {
|
||||
const typeNames = { preference: '偏好', habit: '习惯', event: '事件', todo: '待办', identity: '身份', relationship: '关系', other: '其他' };
|
||||
const typeNames = { preference: '偏好', habit: '习惯', event: '事件', identity: '身份', relationship: '关系', other: '其他' };
|
||||
const typeLabel = typeNames[args.type] || args.type || '';
|
||||
const content = args.content || '';
|
||||
const preview = content.length > 40 ? content.slice(0, 40) + '…' : content;
|
||||
@ -92,6 +93,23 @@ function buildStartLine(name, args) {
|
||||
}
|
||||
return `${label} ${typeLabel ? `[${typeLabel}] ` : ''}${preview}`;
|
||||
}
|
||||
if (name === 'todo') {
|
||||
const actionNames = { add: '新增', complete: '完成', delete: '删除', update: '更新' };
|
||||
const actionLabel = actionNames[args.action] || args.action || '';
|
||||
const content = args.content || '';
|
||||
const preview = content.length > 40 ? content.slice(0, 40) + '…' : content;
|
||||
let timeStr = '';
|
||||
if (args.remind_at) {
|
||||
try {
|
||||
const d = new Date(args.remind_at);
|
||||
timeStr = `${d.getFullYear()}年${d.getMonth()+1}月${d.getDate()}日 ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
|
||||
} catch (_) {}
|
||||
}
|
||||
const parts = [`${label} ${actionLabel}`];
|
||||
if (preview) parts.push(preview);
|
||||
if (timeStr) parts.push(timeStr);
|
||||
return parts.join(' · ');
|
||||
}
|
||||
if (name === 'recall') {
|
||||
const typeNames = { preference: '偏好', habit: '习惯', event: '事件', todo: '待办', identity: '身份', relationship: '关系', other: '其他' };
|
||||
const typeLabels = (args.types || []).map(t => typeNames[t] || t).filter(Boolean);
|
||||
@ -243,6 +261,13 @@ function formatResultLines(name, args, raw) {
|
||||
if (raw.action === 'update') return ['已更新'];
|
||||
return ['已记录'];
|
||||
}
|
||||
if (name === 'todo') {
|
||||
if (raw.action === 'add') return ['已创建待办'];
|
||||
if (raw.action === 'complete') return ['已完成待办'];
|
||||
if (raw.action === 'delete') return ['已删除待办'];
|
||||
if (raw.action === 'update') return ['已更新待办'];
|
||||
return ['完成'];
|
||||
}
|
||||
if (name === 'recall') {
|
||||
const total = raw.total || 0;
|
||||
const returned = raw.returned || 0;
|
||||
|
||||
@ -521,10 +521,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSSpeechSynthesizerDel
|
||||
try? data.write(to: URL(fileURLWithPath: path), options: .atomic)
|
||||
}
|
||||
|
||||
private func markMemoryReminded(_ memoryId: String) {
|
||||
private func markTodoCompleted(_ memoryId: String) {
|
||||
var memories = loadMemories()
|
||||
guard let idx = memories.firstIndex(where: { ($0["id"] as? String) == memoryId }) else { return }
|
||||
memories[idx]["reminded"] = true
|
||||
let nowISO = ISO8601DateFormatter().string(from: Date())
|
||||
memories[idx]["completed"] = true
|
||||
memories[idx]["completed_at"] = nowISO
|
||||
memories[idx].removeValue(forKey: "remind_at")
|
||||
memories[idx].removeValue(forKey: "reminded")
|
||||
saveMemories(memories)
|
||||
}
|
||||
|
||||
@ -576,7 +580,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSSpeechSynthesizerDel
|
||||
df.locale = Locale(identifier: "zh_CN")
|
||||
df.dateFormat = "yyyy年M月d日 H:mm"
|
||||
let timeStr = df.string(from: now)
|
||||
let reminderMsg = "[系统提醒] 现在是 \(timeStr),请提醒用户:\(content)"
|
||||
let reminderMsg = "[系统自动发送的消息] 现在是 \(timeStr),请提醒用户:\(content)\n(待办ID:\(memoryId))"
|
||||
|
||||
print("[Reminder] ✅ 触发提醒 id=\(memoryId)")
|
||||
triggerReminder(message: reminderMsg, memoryId: memoryId)
|
||||
@ -640,7 +644,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSSpeechSynthesizerDel
|
||||
print("[Reminder] 消息已发送")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
self.markMemoryReminded(memoryId)
|
||||
self.markTodoCompleted(memoryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,6 +426,11 @@ final class Config {
|
||||
userDataDir + "/user_profile.json"
|
||||
}
|
||||
|
||||
/// 记忆文件路径
|
||||
var memoryPath: String {
|
||||
userDataDir + "/memory/memory.json"
|
||||
}
|
||||
|
||||
// MARK: - models.json 读写
|
||||
|
||||
func loadAgentModels() -> AgentModelsFile {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user