fix: surface missing-history-acked messages as Not delivered with explicit resend - #74
Merged
Merged
Conversation
e8d8748 introduced an unused rest parameter in session-file-content-view.test.tsx and a valueless return in KeyboardShortcutsSetting.stories.tsx, leaving 'pnpm lint' (and therefore 'pnpm check') red at HEAD. Remove the dead parameter and return undefined explicitly so the repository-wide check can pass again. Model: kimi-code/k3
The missing-history negative acknowledgement (SessionMeta lastMissingHistoryUserMsgId) was a one-way door: once the bounded history-sync wait expired, the exact turn was excluded from every dispatch path forever, even when its history entry arrived late and sat visibly pending in the conversation (issue #47, defect 2). Adopt the option C semantics: the marker stays a one-shot negative ack for the exact turn and nothing ever re-dispatches it automatically. The renderer now derives a terminal "Not delivered" state for the matching non-terminal entry (display-only derivation from meta + entry state — no CLI repair write, no schema change) and offers an explicit "Deliver now" action. That action is a dispatch-producer write: it re-aims latestUserMsgId at the exact entry and clears the marker only when it names that entry, landing before the RPC fast path fires, so ordinary turn selection dispatches the entry exactly once. The RPC stash honors the same exact-id suppression, so a duplicate offer alone cannot resurrect the turn while the marker stands. Also log the pending turn id when the history-sync wait starts and when the missing-history recovery is recorded (issue #47, defect 3). Model: kimi-code/k3
…viving Rework the recovery UX for the missing-history negative acknowledgement (issue #47, defect 2) to option A semantics: lastMissingHistoryUserMsgId is a permanent one-way door for the exact turn — no path re-dispatches it. - The user row keeps the derived "Not delivered" label (now shared via src/lib/undelivered-user-turn.ts) and loses its row-level action. - Recovery moves to a conversation-bottom resend bar above the composer: it resends the undelivered turn's exact content (canonical inputConfig blocks, attachments included) as a brand-new message through the ordinary composer send path with a fresh turn id. The ordinary producer write clears the marker, hiding both the bar and the label. - The abandoned entry is superseded to 'canceled' on resend, and the RPC stash now drops turns whose history entry is already terminal — otherwise the stale pending payload could duplicate-dispatch once the marker is gone. - Removed the previous old-turn redelivery write path (redeliverSessionUserTurnWithRuntime / hook method) and its tests. The missing-history wait/recovery logs keep the pending turn id (defect 3). Model: kimi-code/k3
…dialog Simplify the missing-history recovery UX (issue #47, defect 2): the conversation-bottom resend bar is gone. The "Not delivered" label on the user row is now itself the recovery entry point — clicking it opens a confirmation dialog that resends the turn's exact content as a NEW message through the ordinary composer send path (fresh turn id), then supersedes the abandoned entry to 'canceled' so the stale pending copy can never duplicate-dispatch once the ordinary send clears the marker. Model: kimi-code/k3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes defect 2 (and the one-line part of defect 3) of #47. The missing-history negative acknowledgement (
SessionMeta.lastMissingHistoryUserMsgId) was a one-way door: once the bounded history-sync wait expired, the exact user turn was excluded from every dispatch path forever, even when its history entry arrived late and sat visibly pending in the conversation.Final semantics (option A): the marker is a permanent one-shot negative ack for the exact turn — no path ever re-dispatches the old turn. Recovery is an explicit, user-driven resend-as-new-message.
Behavior
packages/components/src/lib/undelivered-user-turn.ts) — no CLI repair write, no schema change.inputConfig.inputBlocks, attachments included) as a brand-new message through the ordinary composer send path with a fresh turn id. The ordinary producer write clears the marker, hiding both the dialog trigger and the label.canceled, and the daemon's RPC stash now drops turns whose history entry is already terminal — otherwise the stale pending payload could execute again once the marker is gone.peekStashedRpcTurnalso honors the marker itself, so a duplicate RPC offer can never resurrect the turn while the marker stands.Test plan
apps/cli: marker-matched turn is not dispatchable from history or the RPC stash; a stashed turn superseded tocanceledis dropped (never duplicate-dispatched); existing marker-suppression suites stay green (50 tests).packages/components: derivation predicate (exact id, terminal states, legacyread); resend block extraction (canonical blocks with attachments, history-items and prompt fallbacks); dialog flow (label opens dialog, confirm resends exact content once, cancel is a no-op, label non-interactive without a handler); ordinary send path mints a fresh turn id for identical content (36 tests).Sessions/UserMessageNotDelivered(row label + click-to-open dialog, light/dark, en/zh).pnpm check: typecheck, type-aware lint (0 errors), i18n and boundary checks pass. The only failing tests are 7 pre-existing failures inlocal-terminal-panelandcreate-workspace-runtime-meta-recovery, verified to fail identically on cleanmain.Model: kimi-code/k3