From babbff151ac943f486187d837b0ba6d42d00b3a6 Mon Sep 17 00:00:00 2001 From: huangruiteng <14976749+huangruiteng@users.noreply.github.com> Date: Mon, 14 Sep 2026 00:20:51 +0800 Subject: [PATCH 1/5] feat(todo): add typed date resume trigger Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com> --- .../smoke/personal-workspace-router-smoke.ts | 3 + .../presentation/dashboard/src/data/status.ts | 2 + .../personal-workspace/context-drawer.tsx | 6 +- .../src/features/personal-workspace/i18n.tsx | 26 ++-- .../personal-workspace-model.ts | 2 + .../personal-workspace-router.ts | 2 +- .../dashboard/src/views/dashboard-page.tsx | 11 ++ docs/project-agent-todo-contract.md | 13 ++ docs/quota-allocation.md | 8 ++ docs/reference/protocols/README.md | 2 + .../protocols/typed-date-resume-trigger-v0.md | 80 +++++++++++ .../typed-date-resume-trigger-v0.zh-CN.md | 71 ++++++++++ docs/status-data-contract.md | 6 + .../personal-workspace-browser/fixture.mjs | 26 +++- .../navigation-sorting.mjs | 19 +++ .../cli_commands/todo_argument_validation.py | 5 +- .../todos/active_state_todo_parser.py | 5 + loopx/control_plane/todos/contract.py | 1 + loopx/control_plane/todos/resume_condition.py | 51 ++++++- loopx/control_plane/todos/resume_condition.ts | 124 +++++++++++++++++- loopx/control_plane/todos/todo_summary.py | 5 + .../work_items/delivery_history.py | 2 + loopx/web/chat/assets/index-B-l9MJT-.js | 19 ++- .../{index-CF2GUu4X.js => index-CXAKvcG-.js} | 16 +-- tests/control_plane/test_delivery_response.py | 6 +- .../test_sqlite_authority_cli.py | 4 +- .../test_todo_resume_condition_adapter.py | 124 ++++++++++++++++++ .../delivery_response.test.ts | 9 +- .../todo_resume_condition.test.ts | 59 ++++++++- 29 files changed, 666 insertions(+), 41 deletions(-) create mode 100644 docs/reference/protocols/typed-date-resume-trigger-v0.md create mode 100644 docs/reference/protocols/typed-date-resume-trigger-v0.zh-CN.md rename loopx/web/chat/assets/{index-CF2GUu4X.js => index-CXAKvcG-.js} (71%) diff --git a/apps/presentation/dashboard/smoke/personal-workspace-router-smoke.ts b/apps/presentation/dashboard/smoke/personal-workspace-router-smoke.ts index 6d2e1e2de9..50688c84da 100644 --- a/apps/presentation/dashboard/smoke/personal-workspace-router-smoke.ts +++ b/apps/presentation/dashboard/smoke/personal-workspace-router-smoke.ts @@ -36,6 +36,9 @@ equal(deferMissingCondition.missingFields.join(","), "resume_when", "todo defer const deferUntilPr = routeWorkspaceInput("把 todo-1 暂缓到pr_merged:huangruiteng/loopx#3399", goalContext); equal(deferUntilPr.route, "typed_action", "todo defer with condition routes to typed action"); equal(deferUntilPr.normalizedParameters.resume_when, "pr_merged:huangruiteng/loopx#3399", "todo defer preserves supported condition"); +const deferUntilDate = routeWorkspaceInput("把 todo-1 暂缓到 resume_at:2026-09-14T09:00:00+08:00", goalContext); +equal(deferUntilDate.route, "typed_action", "todo defer accepts a timezone-aware resume_at condition"); +equal(deferUntilDate.normalizedParameters.resume_when, "resume_at:2026-09-14t09:00:00+08:00", "todo defer preserves an explicit timezone"); equal(routeWorkspaceInput("帮我修复 MR 冲突,跑测试,然后 push", goalContext).actionKind, "todo.create", "execution task"); equal(routeWorkspaceInput("创建任务并设置 Heartbeat", goalContext).route, "clarify", "compound intent"); equal(routeWorkspaceInput("创建任务并设置 Heartbeat", goalContext).missingFields.join(","), "single_intent", "compound missing field"); diff --git a/apps/presentation/dashboard/src/data/status.ts b/apps/presentation/dashboard/src/data/status.ts index 07802eaecf..ea50e3255d 100644 --- a/apps/presentation/dashboard/src/data/status.ts +++ b/apps/presentation/dashboard/src/data/status.ts @@ -70,6 +70,8 @@ export const todoItemSchema = z.object({ role: z.string().optional().nullable(), status: z.string().optional().nullable(), resume_when: z.string().optional().nullable(), + resume_ready: z.boolean().optional().nullable(), + resume_condition: z.record(z.string(), z.unknown()).optional().nullable(), priority: z.string().optional().nullable(), title: z.string().optional().nullable(), archive_state: z.string().optional().nullable(), diff --git a/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx b/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx index 9c4767e9a7..3f469759a3 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx @@ -558,8 +558,10 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention