#!/usr/bin/env node /** * Verify WeChat reminder utterances route to ITL timed_reminder (L1), not agent_schedule. */ import assert from 'node:assert/strict'; import { classifyUserIntent } from '../intent-classifier.mjs'; import { parseScheduleIntent, tryResolveTimedReminderIntent } from '../schedule-intent.mjs'; import { hasReminderSetupCue } from '../schedule-utterance-normalize.mjs'; const timezone = 'Asia/Shanghai'; const now = Date.UTC(2026, 7, 24, 9, 0, 0); // 2026-08-24 17:00 Asia/Shanghai const matrix = [ { text: '设置一个提醒,今天下午 17 :08要开会', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '要开会', hour: 17, minute: 8 }, }, { text: '设置每天18:00提醒打卡', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '打卡', recurrence: 'daily', hour: 18 }, }, { text: '加个提醒明天9点开会', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '开会', hour: 9 }, }, { text: '添加提醒后天10点体检', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '体检', hour: 10 }, }, { text: '创建提醒明早6点跑步', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '跑步', hour: 6 }, }, { text: '备忘提醒下午3点交报告', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '交报告', hour: 15 }, }, { text: '安排一个提醒今晚8点', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', clarify: ['reminder_title'], hour: 20 }, }, { text: '定个闹钟明天7点起床', expect: { kind: 'timed_reminder', action: 'create_timed_reminder', title: '起床', hour: 7 }, }, ]; let passed = 0; let failed = 0; function pass(label) { passed += 1; console.log(`✔ ${label}`); } function fail(label, detail) { failed += 1; console.error(`✘ ${label}: ${detail}`); } for (const row of matrix) { const compact = String(row.text).replace(/\s+/g, ''); if (!hasReminderSetupCue(compact)) { fail(row.text, 'missing reminder setup cue'); continue; } const parsed = parseScheduleIntent(row.text, { timezone, now }) ?? tryResolveTimedReminderIntent(row.text, { timezone, now }); if (parsed?.action !== row.expect.action) { fail(row.text, `parse action ${parsed?.action} !== ${row.expect.action}`); continue; } if (row.expect.title && parsed.title !== row.expect.title) { fail(row.text, `title ${parsed.title} !== ${row.expect.title}`); continue; } if (row.expect.hour != null && parsed.hour !== row.expect.hour) { fail(row.text, `hour ${parsed.hour} !== ${row.expect.hour}`); continue; } if (row.expect.minute != null && parsed.minute !== row.expect.minute) { fail(row.text, `minute ${parsed.minute} !== ${row.expect.minute}`); continue; } if (row.expect.recurrence && parsed.recurrence !== row.expect.recurrence) { fail(row.text, `recurrence ${parsed.recurrence} !== ${row.expect.recurrence}`); continue; } if (row.expect.clarify) { assert.deepEqual(parsed.needsClarification, row.expect.clarify); } const classified = classifyUserIntent(row.text, { timezone, now }); if (classified.kind !== row.expect.kind) { fail(row.text, `classified kind ${classified.kind} !== ${row.expect.kind}`); continue; } if (classified.action === 'agent_schedule') { fail(row.text, 'must not route to agent_schedule'); continue; } pass(row.text); } console.log(`\nschedule reminder routing: ${passed} passed, ${failed} failed`); process.exit(failed > 0 ? 1 : 0);