You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#5629 added opt-in next-prompt suggestions, which fill an empty composer after a reply. The other half of what a user types is a draft they have started but not finished: "帮我把这个函数" → "改成异步的", "Let's add" → "tests first". Today that draft gets no help, even though its most likely ending is usually implied by the last reply.
The pieces #5629 landed already cover most of what a continuation needs:
the opt-in preference, synchronised across renderers;
the Host-owned, tool-free, metered prompt_suggestion call path, including the off-reasoning gate and maxRetries: 0;
the shared generated-text cleanup;
the overlay that sits beside ChatComposerInput without measuring anything;
Tab to accept through native insertText (undoable), and Esc to dismiss.
What is missing is a trigger for a non-empty draft, a way to place the ghost text after that draft, and a Host operation that accepts a prefix.
Desired outcome
With suggestions enabled, when the user pauses on a draft of at least 4 characters, the composer shows a short continuation in ghost text directly after the caret. Tab appends it, Esc dismisses it, and anything else the user types discards it. Nothing is sent and nothing reaches the transcript.
The proposed design keeps the #4117 invariant of no layout-driven state and stays inside the constraints #5629 established:
1. Placing the ghost without measuring anything.
The existing .maka-composer-next-prompt overlay already shares the editor's grid cell, typography, padding and pre-wrap wrapping.
For a continuation, the overlay renders the current draft as an invisible span, followed by the visible continuation. The browser's own line breaking puts the ghost exactly where the caret is.
No getBoundingClientRect, no scroll sync, no state written from layout.
This only holds when the overlay's text matches what the editor renders. So a continuation is offered only when:
the draft is plain text: no skill, mention or paste tokens (checked with a DOM query at request time, never stored in state);
the caret is collapsed at the end of the draft;
the draft does not overflow maxRows.
Everything else simply gets no offer.
2. Trigger (renderer).
The feature is enabled and the Session is not streaming.
There is no IME composition and no open trigger menu (aria-expanded).
The draft is at least 4 characters and does not end in whitespace.
A 350 ms debounce; any edit bumps the existing epoch and drops the pending result.
Accepting re-checks that the draft still equals the value the request was made for.
Streaming is deliberately excluded in v1. While a turn is running, Esc stops the model, and an offer on a queued message would compete with that.
3. Host operation.
Either a new session.prompt-continuation.generate { sessionId, prefix } (prefix capped, e.g. 2,000 code points), or an optional prefix on the existing operation. Either way this needs a compatibility-epoch bump.
It reuses readSource eligibility, the prompt_suggestion call path and the off gate, with its own continuation prompt: append-only, no answering, no invented facts, empty when the draft is already complete.
Cancellation without a protocol abort frame. The Host keeps one request in flight per Session and aborts the previous one when a newer prefix arrives. This bounds both the paid calls and the shared connection's in-flight slots to one per Session, and debouncing keeps the rate low.
A simple per-client rate cap as a backstop, e.g. 30 per minute.
4. Accounting. Either a distinct callKind (prompt_continuation) so the two features can be measured apart, or reuse prompt_suggestion. I lean towards a distinct kind; #5691 would then let the Session views separate both from main.
5. Tests.
Unit tests: the trigger predicate; stale-prefix discard; cleanup, including prefix echo.
A linkedom test: Tab with a menu open, and Tab during composition.
A Chromium Storybook play asserting that the ghost's first glyph rectangle starts where the draft's last glyph ends, in both light and dark themes and at a narrow width, plus the wrap-at-line-end case.
Alternatives or workarounds
Measuring the caret (Range.getBoundingClientRect) and positioning an absolute ghost. This is more flexible (tokens, mid-text caret), but it reintroduces the layout-to-state path that Bug:alert error dialog #4117 warns about. The invisible-mirror approach avoids that at the cost of skipping drafts that contain tokens.
Inserting a non-editable ghost node inside the contenteditable. Rejected: serialize would read its text, controlled writes would wipe it, slash-query detection would break, and it cannot be undone cleanly.
Allowing continuation while streaming. Possible later, once Esc priority between "dismiss offer" and "stop turn" is decided.
Note: with the off gate from #5629, continuation is unavailable on models that cannot disable reasoning (GPT-5, o-series, GPT-6, Gemini 3.x), the same as next-prompt suggestions. #5690 tracks lifting that.
Happy to implement this as a single PR if maintainers agree with the direction, especially the mirror placement and the one-in-flight-per-Session cancellation.
AI disclosure: Claude Code (Claude Opus) studied the merged #5629 code and drafted this proposal; liugddx reviewed it and is the human contributor of record.
Problem
#5629 added opt-in next-prompt suggestions, which fill an empty composer after a reply. The other half of what a user types is a draft they have started but not finished: "帮我把这个函数" → "改成异步的", "Let's add" → "tests first". Today that draft gets no help, even though its most likely ending is usually implied by the last reply.
The pieces #5629 landed already cover most of what a continuation needs:
prompt_suggestioncall path, including theoff-reasoning gate andmaxRetries: 0;ChatComposerInputwithout measuring anything;insertText(undoable), and Esc to dismiss.What is missing is a trigger for a non-empty draft, a way to place the ghost text after that draft, and a Host operation that accepts a prefix.
Desired outcome
With suggestions enabled, when the user pauses on a draft of at least 4 characters, the composer shows a short continuation in ghost text directly after the caret. Tab appends it, Esc dismisses it, and anything else the user types discards it. Nothing is sent and nothing reaches the transcript.
The proposed design keeps the #4117 invariant of no layout-driven state and stays inside the constraints #5629 established:
1. Placing the ghost without measuring anything.
.maka-composer-next-promptoverlay already shares the editor's grid cell, typography, padding andpre-wrapwrapping.getBoundingClientRect, no scroll sync, no state written from layout.maxRows.2. Trigger (renderer).
aria-expanded).blockedconditions feat(desktop): add opt-in next prompt suggestions #5629 already uses.Streaming is deliberately excluded in v1. While a turn is running, Esc stops the model, and an offer on a queued message would compete with that.
3. Host operation.
session.prompt-continuation.generate { sessionId, prefix }(prefix capped, e.g. 2,000 code points), or an optionalprefixon the existing operation. Either way this needs a compatibility-epoch bump.readSourceeligibility, theprompt_suggestioncall path and theoffgate, with its own continuation prompt: append-only, no answering, no invented facts, empty when the draft is already complete.4. Accounting. Either a distinct
callKind(prompt_continuation) so the two features can be measured apart, or reuseprompt_suggestion. I lean towards a distinct kind; #5691 would then let the Session views separate both frommain.5. Tests.
Alternatives or workarounds
Range.getBoundingClientRect) and positioning an absolute ghost. This is more flexible (tokens, mid-text caret), but it reintroduces the layout-to-state path that Bug:alert error dialog #4117 warns about. The invisible-mirror approach avoids that at the cost of skipping drafts that contain tokens.serializewould read its text, controlled writes would wipe it, slash-query detection would break, and it cannot be undone cleanly.Note: with the
offgate from #5629, continuation is unavailable on models that cannot disable reasoning (GPT-5, o-series, GPT-6, Gemini 3.x), the same as next-prompt suggestions. #5690 tracks lifting that.Happy to implement this as a single PR if maintainers agree with the direction, especially the mirror placement and the one-in-flight-per-Session cancellation.
AI disclosure: Claude Code (Claude Opus) studied the merged #5629 code and drafted this proposal; liugddx reviewed it and is the human contributor of record.