feat(schedule): add durable after reminders

This commit is contained in:
pku-xht
2026-08-06 03:47:37 +08:00
committed by Tianyi Cui
parent f7e7851e3f
commit 8b69252664
41 changed files with 354 additions and 146 deletions

View File

@@ -24,7 +24,7 @@ The chat view keeps Tool placement but delegates Tool presentation. It passes ea
The chat flow projects consecutive model-retry nodes across retry turns into one stable, muted status row updated to the latest attempt; every retry event remains in the runtime snapshot and session log. Its frontend countdown anchors the scheduled delay to client receipt, avoiding host/browser clock skew, rounds remaining time up to seconds, and has a one-second floor. The latest unresolved retry uses a left-to-right text shimmer. Subsequent turn facts distinguish an attempt that started from one cancelled during backoff, while the Host running bit only controls the live animation; the row then shows a static completed or cancelled label. Normal policy rows show the finite retry maximum; always policy rows show `∞`. Activating the row reveals the latest exact retry delay and failure message. The client runtime removes each failed step's streaming tail before its retry node arrives, while the status remains visible after a later attempt succeeds. An unretried terminal failure renders as a persistent inline status at its turn boundary, showing the display-safe durable message and optional error code without offering an action the Host cannot fulfill; AUTH copy never echoes provider-supplied credential fragments.
Host-presented durable events use the keyed `'conversation.chat.eventview'` seat alongside whole-Tool presentation. The React-free runtime turns a generic `{ presentationKey, view }` sidecar into a `PresentedEventNode`; Chat dispatches on that open key, and a domain UI plugin may register its own row without adding domain vocabulary here. When no registrant is loaded, `GenericEventCard` keeps the presentation key and JSON payload visible in an expandable disclosure rather than dropping the durable event.
Host-presented durable events use the keyed `'conversation.chat.eventview'` seat alongside whole-Tool presentation. The React-free runtime turns a generic event sidecar into a `PresentedEventNode` carrying the durable event type and view; Chat dispatches on that open type, and a domain UI plugin may register its own row without adding domain vocabulary here. When no registrant is loaded, `GenericEventCard` keeps the event type and JSON payload visible in an expandable disclosure rather than dropping the durable event.
`TodoDock` takes the `'conversation.input.dock'` list slot at `order: 0` — before Goal and Queue — and is the plan strip: it reads the host-computed `todos` projection via `useProjection` (standing plan: latest `todo/write` with no later `turn/start`) and renders `TodoPanel`, which takes the plain list, hides itself while the list is empty, and starts collapsed as a header of title plus its own `·`-joined per-status counts (localized, `1 completed · 2 in progress · 1 pending`, zero-count segments omitted). The dock adapter owns selection so the panel stays a pure function of its props. Anything the input-zone composer chain hides (a `conversation.composer` takeover such as ui-question's) hides the whole dock, this strip included. The `todo_write` Tool row belongs to [`ui-tool`](../ui-tool/README.md).