From 4ef392e96442130c7c74a038ab2aee2abdb33c4c Mon Sep 17 00:00:00 2001 From: john Date: Mon, 24 Aug 2026 16:29:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(schedule):=20parse=E3=80=8C=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=B8=80=E4=B8=AA=E6=8F=90=E9=86=92=E3=80=8Das=20time?= =?UTF-8?q?d=20reminder=20for=20ITL=20Confirm=20Gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Colloquial phrasing with optional 一个/个 was falling through to agent_schedule, so schedule-guard blocked false confirmations. Also strip orphaned 分 after HH:MM times. Co-authored-by: Cursor --- schedule-intent.mjs | 7 +++++-- schedule-intent.test.mjs | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/schedule-intent.mjs b/schedule-intent.mjs index cc9c93d..512d867 100644 --- a/schedule-intent.mjs +++ b/schedule-intent.mjs @@ -73,7 +73,7 @@ function countTimeExpressions(text) { } function wantsSimpleTimedReminder(compact, text) { - if (!/(?:提醒我|设置提醒|设个?提醒|到点提醒|提醒一下|闹钟|叫我)/u.test(compact)) return false; + if (!/(?:提醒我|设置(?:一个|个)?提醒|设(?:一个|个)?提醒|到点提醒|提醒一下|闹钟|叫我)/u.test(compact)) return false; if (countTimeExpressions(text) !== 1) return false; if (/[0-9零一二两三四五六七八九十]{1,3}(?:点|:|:).{1,24}[0-9零一二两三四五六七八九十]{1,3}(?:点|:|:)/u.test(compact)) { return false; @@ -100,7 +100,10 @@ function cleanupTimedReminderTitleFragment(text) { title = title .replace(/(?:今天|今日|今晚|明天|后天|明早|今早)/gu, ' ') .replace(/(?:早上|上午|清晨|中午|下午|傍晚|晚上|凌晨)/gu, ' ') - .replace(/[0-9零一二两三四五六七八九十]{1,3}(?:点|:|:)(?:半|[0-9]{1,2}(?:分(?!开)|(?![0-9]))?)?/gu, ' ') + .replace( + /[0-9零一二两三四五六七八九十]{1,3}(?:点|:|:)(?:半|[0-9]{1,2}(?:\s*分(?!开)|(?![0-9]))?)?/gu, + ' ', + ) .replace(/(?:提醒我|提醒|闹钟|叫我|设置提醒|设提醒)/gu, ' ') .replace(/[,,、::]/g, ' ') .replace(/\s+/g, ' ') diff --git a/schedule-intent.test.mjs b/schedule-intent.test.mjs index a95a5e9..9862506 100644 --- a/schedule-intent.test.mjs +++ b/schedule-intent.test.mjs @@ -1,6 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; import { parseScheduleIntent, shouldUseScheduleAssistant } from './schedule-intent.mjs'; +import { classifyUserIntent } from './intent-classifier.mjs'; import { nextDailyRunAt } from './schedule-time.mjs'; test('daily news automation does not route to schedule assistant', () => { @@ -135,6 +136,25 @@ test('parses direct reminder setup phrase from wechat', () => { assert.equal(intent.remindLocal, '2026-08-24 14:30'); }); +test('parses set-a-reminder colloquial phrase with numeric time', () => { + const now = Date.UTC(2026, 7, 24, 6, 0, 0); // 2026-08-24 14:00 Asia/Shanghai + const intent = parseScheduleIntent('设置一个提醒,今天下午 16:25 分要出门', { + timezone: 'Asia/Shanghai', + now, + }); + assert.equal(intent.action, 'create_timed_reminder'); + assert.equal(intent.title, '要出门'); + assert.equal(intent.hour, 16); + assert.equal(intent.minute, 25); + assert.equal(intent.remindLocal, '2026-08-24 16:25'); +}); + +test('set-a-reminder colloquial phrase routes to ITL timed reminder not agent', () => { + const result = classifyUserIntent('设置一个提醒,今天下午 16:25 分要出门'); + assert.equal(result.kind, 'timed_reminder'); + assert.equal(result.layer, 'L1'); +}); + test('simple reminder today rolls to tomorrow when time already passed', () => { const now = Date.UTC(2026, 7, 24, 7, 0, 0); // 2026-08-24 15:00 Asia/Shanghai const intent = parseScheduleIntent('今天10点提醒我吃药', {