5de58af9ed
Use a lightweight LLM over active task lists to pick taskId for cancel, exclude page.generate from scheduled-task inbound, and remove single-task auto-cancel so Cursor page generation stays isolated. Co-authored-by: Cursor <cursoragent@cursor.com>
223 lines
6.5 KiB
JavaScript
223 lines
6.5 KiB
JavaScript
import assert from 'node:assert/strict';
|
||
import test from 'node:test';
|
||
import { handleWechatScheduledTaskIntent } from './scheduled-task.mjs';
|
||
|
||
test('handleWechatScheduledTaskIntent passes create requests to goose', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '每天6点帮我做今日新闻页面',
|
||
msgId: 'msg-1',
|
||
},
|
||
user: { userId: 'user-1' },
|
||
scheduledTaskService: {
|
||
async createTask() {
|
||
throw new Error('should not create from inbound regex');
|
||
},
|
||
},
|
||
});
|
||
assert.equal(reply, null);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent passes incomplete create requests to goose', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '帮我设一个定时自动任务',
|
||
msgId: 'msg-1',
|
||
},
|
||
user: { userId: 'user-1' },
|
||
scheduledTaskService: {
|
||
async createTask() {
|
||
throw new Error('should not create from inbound regex');
|
||
},
|
||
},
|
||
});
|
||
assert.equal(reply, null);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent yields page.generate requests to cursor/goose path', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '帮我做一个苏州天气展示页面',
|
||
msgId: 'msg-page-1',
|
||
},
|
||
user: { userId: 'user-tang' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
throw new Error('should not list for page.generate');
|
||
},
|
||
},
|
||
});
|
||
assert.equal(reply, null);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent cancels via manage llm when task is identified', async () => {
|
||
const calls = [];
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '取消定时推送苏州天气任务',
|
||
msgId: 'msg-123-1',
|
||
},
|
||
user: { userId: 'user-123' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
return [{
|
||
id: 'task-suzhou',
|
||
title: '每天早上6:30推送苏州市天气预报',
|
||
recurrence: 'daily',
|
||
hour: 6,
|
||
minute: 30,
|
||
timezone: 'Asia/Shanghai',
|
||
}];
|
||
},
|
||
async cancelTask(payload) {
|
||
calls.push(payload);
|
||
return {
|
||
id: 'task-suzhou',
|
||
title: '每天早上6:30推送苏州市天气预报',
|
||
status: 'cancelled',
|
||
};
|
||
},
|
||
},
|
||
wechatScheduledTaskManageLlmConfigService: {
|
||
async isManageLlmEnabled() {
|
||
return true;
|
||
},
|
||
async getConfig() {
|
||
return { modelProviderKeyId: 'key-1', model: 'deepseek-chat' };
|
||
},
|
||
},
|
||
llmProviderService: {
|
||
async createChatCompletion() {
|
||
return {
|
||
ok: true,
|
||
reply: JSON.stringify({ action: 'cancel', taskId: 'task-suzhou' }),
|
||
};
|
||
},
|
||
},
|
||
});
|
||
assert.match(reply, /已取消定时任务/u);
|
||
assert.deepEqual(calls, [{ userId: 'user-123', taskId: 'task-suzhou' }]);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent passes cancel to goose when manage llm is disabled', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '取消定时推送苏州天气任务',
|
||
msgId: 'msg-123-2',
|
||
},
|
||
user: { userId: 'user-123' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
return [
|
||
{ id: 'task-1', title: '苏州天气', recurrence: 'daily', hour: 6, minute: 30, timezone: 'Asia/Shanghai' },
|
||
{ id: 'task-2', title: '新闻早报', recurrence: 'daily', hour: 5, minute: 30, timezone: 'Asia/Shanghai' },
|
||
];
|
||
},
|
||
async cancelTask() {
|
||
throw new Error('should not cancel from inbound when llm disabled');
|
||
},
|
||
},
|
||
wechatScheduledTaskManageLlmConfigService: {
|
||
async isManageLlmEnabled() {
|
||
return false;
|
||
},
|
||
},
|
||
});
|
||
assert.equal(reply, null);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent returns clarify message when manage llm is ambiguous', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '帮我取消定时任务',
|
||
msgId: 'msg-123-3',
|
||
},
|
||
user: { userId: 'user-tang' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
return [
|
||
{ id: 'task-news', title: '每日新闻早报页面(5:30)', recurrence: 'daily', hour: 5, minute: 30 },
|
||
{ id: 'task-checkin', title: '每日打卡提醒', recurrence: 'daily', hour: 18, minute: 0 },
|
||
];
|
||
},
|
||
},
|
||
wechatScheduledTaskManageLlmConfigService: {
|
||
async isManageLlmEnabled() {
|
||
return true;
|
||
},
|
||
async getConfig() {
|
||
return { modelProviderKeyId: 'key-1', model: 'deepseek-chat' };
|
||
},
|
||
},
|
||
llmProviderService: {
|
||
async createChatCompletion() {
|
||
return {
|
||
ok: true,
|
||
reply: JSON.stringify({
|
||
action: 'clarify',
|
||
message: '你是想取消 5:30 新闻早报,还是 18:00 打卡提醒?',
|
||
}),
|
||
};
|
||
},
|
||
},
|
||
});
|
||
assert.match(reply, /新闻早报/u);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent ignores non automation text', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '明天下午三点提醒我开会',
|
||
msgId: 'msg-1',
|
||
},
|
||
user: { userId: 'user-1' },
|
||
scheduledTaskService: {
|
||
async createTask() {
|
||
throw new Error('should not create');
|
||
},
|
||
},
|
||
});
|
||
assert.equal(reply, null);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent still lists tasks', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '看看我的定时自动任务',
|
||
msgId: 'msg-1',
|
||
},
|
||
user: { userId: 'user-1' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
return [{ title: '新闻页面', recurrence: 'daily', hour: 6, minute: 0, timezone: 'Asia/Shanghai' }];
|
||
},
|
||
},
|
||
});
|
||
assert.match(reply, /新闻页面/u);
|
||
});
|
||
|
||
test('handleWechatScheduledTaskIntent lists tasks for 有哪些 phrasing', async () => {
|
||
const reply = await handleWechatScheduledTaskIntent({
|
||
intent: {
|
||
msgType: 'text',
|
||
agentText: '我有哪些定时任务',
|
||
msgId: 'msg-2',
|
||
},
|
||
user: { userId: 'user-1' },
|
||
scheduledTaskService: {
|
||
async listTasks() {
|
||
return [{ title: '苏州天气', recurrence: 'daily', hour: 6, minute: 30, timezone: 'Asia/Shanghai' }];
|
||
},
|
||
},
|
||
});
|
||
assert.match(reply, /苏州天气/u);
|
||
});
|