Commit 2604f5ce40077e2e1affb2f0a35f31332def9b8d
1 parent
2e6f62a5
fix(agent-chat): sanitize task description to prevent breaking markdown tables in todo.md/plan.md
Showing
1 changed file
with
8 additions
and
4 deletions
server/routes/agent-chat.js
| @@ -921,12 +921,15 @@ router.post('/task/dispatch', async (req, res) => { | @@ -921,12 +921,15 @@ router.post('/task/dispatch', async (req, res) => { | ||
| 921 | const taskId = `T-${Date.now().toString(36)}`; | 921 | const taskId = `T-${Date.now().toString(36)}`; |
| 922 | const now = new Date().toISOString().slice(0, 16).replace('T', ' '); | 922 | const now = new Date().toISOString().slice(0, 16).replace('T', ' '); |
| 923 | 923 | ||
| 924 | + // 净化多行文本和竖线,防止破坏 Markdown 表格格式 | ||
| 925 | + const safeTask = task.replace(/\n/g, '<br>').replace(/\|/g, '|'); | ||
| 926 | + | ||
| 924 | // 1. 写入目标 Agent 的 todo.md | 927 | // 1. 写入目标 Agent 的 todo.md |
| 925 | const todoPath = path.join(targetDir, 'todo.md'); | 928 | const todoPath = path.join(targetDir, 'todo.md'); |
| 926 | let todoContent = fs.existsSync(todoPath) ? fs.readFileSync(todoPath, 'utf-8') : ''; | 929 | let todoContent = fs.existsSync(todoPath) ? fs.readFileSync(todoPath, 'utf-8') : ''; |
| 927 | // 移除空占位行 | 930 | // 移除空占位行 |
| 928 | todoContent = todoContent.replace(/\| *\| *(暂无待办).*\n?/g, ''); | 931 | todoContent = todoContent.replace(/\| *\| *(暂无待办).*\n?/g, ''); |
| 929 | - const newTodoRow = `| ${taskId} | ${task} | ⬚ 未开始 | ${from} | ${priority} | ${now} | - |\n`; | 932 | + const newTodoRow = `| ${taskId} | ${safeTask} | ⬚ 未开始 | ${from} | ${priority} | ${now} | - |\n`; |
| 930 | todoContent = todoContent.trimEnd() + '\n' + newTodoRow; | 933 | todoContent = todoContent.trimEnd() + '\n' + newTodoRow; |
| 931 | fs.writeFileSync(todoPath, todoContent, 'utf-8'); | 934 | fs.writeFileSync(todoPath, todoContent, 'utf-8'); |
| 932 | 935 | ||
| @@ -935,7 +938,7 @@ router.post('/task/dispatch', async (req, res) => { | @@ -935,7 +938,7 @@ router.post('/task/dispatch', async (req, res) => { | ||
| 935 | const planPath = path.join(senderDir, 'plan.md'); | 938 | const planPath = path.join(senderDir, 'plan.md'); |
| 936 | let planContent = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf-8') : ''; | 939 | let planContent = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf-8') : ''; |
| 937 | planContent = planContent.replace(/\| *\| *(暂无计划).*\n?/g, ''); | 940 | planContent = planContent.replace(/\| *\| *(暂无计划).*\n?/g, ''); |
| 938 | - const newPlanRow = `| ${planId} | ${task} | ${to} | 🔄 已分发 | ${now} | taskId: ${taskId} |\n`; | 941 | + const newPlanRow = `| ${planId} | ${safeTask} | ${to} | 🔄 已分发 | ${now} | taskId: ${taskId} |\n`; |
| 939 | planContent = planContent.trimEnd() + '\n' + newPlanRow; | 942 | planContent = planContent.trimEnd() + '\n' + newPlanRow; |
| 940 | fs.writeFileSync(planPath, planContent, 'utf-8'); | 943 | fs.writeFileSync(planPath, planContent, 'utf-8'); |
| 941 | } | 944 | } |
| @@ -1028,6 +1031,7 @@ router.post('/task/report', (req, res) => { | @@ -1028,6 +1031,7 @@ router.post('/task/report', (req, res) => { | ||
| 1028 | 1031 | ||
| 1029 | const now = new Date().toISOString().slice(0, 16).replace('T', ' '); | 1032 | const now = new Date().toISOString().slice(0, 16).replace('T', ' '); |
| 1030 | const statusEmoji = status === 'done' ? '✅ 完成' : status === 'failed' ? '❌ 失败' : '⏸ 阻塞'; | 1033 | const statusEmoji = status === 'done' ? '✅ 完成' : status === 'failed' ? '❌ 失败' : '⏸ 阻塞'; |
| 1034 | + const safeSummary = (summary || '').replace(/\n/g, '<br>').replace(/\|/g, '|'); | ||
| 1031 | 1035 | ||
| 1032 | // 1. 更新执行者的 todo.md (标记完成) | 1036 | // 1. 更新执行者的 todo.md (标记完成) |
| 1033 | const executorDir = path.join(DATA_DIR, from); | 1037 | const executorDir = path.join(DATA_DIR, from); |
| @@ -1063,8 +1067,8 @@ router.post('/task/report', (req, res) => { | @@ -1063,8 +1067,8 @@ router.post('/task/report', (req, res) => { | ||
| 1063 | // cells: ['', '#', '子任务', '责任人', '状态', '上次检查', '备注', ''] | 1067 | // cells: ['', '#', '子任务', '责任人', '状态', '上次检查', '备注', ''] |
| 1064 | cells[4] = ` ${statusEmoji} `; | 1068 | cells[4] = ` ${statusEmoji} `; |
| 1065 | cells[5] = ` ${now} `; | 1069 | cells[5] = ` ${now} `; |
| 1066 | - if (summary && cells.length >= 7) { | ||
| 1067 | - cells[6] = ` ${summary} `; | 1070 | + if (safeSummary && cells.length >= 7) { |
| 1071 | + cells[6] = ` ${safeSummary} `; | ||
| 1068 | } | 1072 | } |
| 1069 | lines[i] = cells.join(' | ').replace(/\*\*/g, ''); // 去掉粗体 | 1073 | lines[i] = cells.join(' | ').replace(/\*\*/g, ''); // 去掉粗体 |
| 1070 | updated = true; | 1074 | updated = true; |