feat: inject and steer new guidance into the running turn - #356
Closed
mulyawansentosa wants to merge 2 commits into
Closed
mulyawansentosa wants to merge 2 commits into
mulyawansentosa wants to merge 2 commits into
Conversation
While a turn was running, Enter was rejected with "wait for the current response or press esc to interrupt", so sending a follow-up instruction meant waiting for the whole turn or interrupting the agent. A message queued until the end of the turn would still arrive too late to override instructions the agent had already executed. SessionManager now keeps a small per-session queue of supplemental prompts. Before every LLM call of the running turn the queue is drained: each entry becomes a user message (including images and selected skills) appended to the conversation, so the model reads it together with the work it already did and can revise or supersede the earlier instructions. When a turn would end while guidance is still queued, the loop stays alive for one more iteration instead of deferring the guidance to a later turn. - session: addSupplementaryPrompt / cancelSupplementaryPrompt / listPendingSupplementaryPrompts / countPendingSupplementaryPrompts, the onSupplementaryQueueChanged and onSupplementaryPromptInjected callbacks, and MessageMeta.isSupplementary - session: drain the queue before each request, keep the loop alive while guidance is pending, and drop the queue when a session is cleaned up - cli: prompts typed while busy are handed to the session, the prompt area lists the guidance that is still waiting, Backspace on an empty prompt removes the last entry, and injected guidance is rendered as a labelled block (also in raw mode) - tests: 7 core tests covering the queue semantics and the request payloads of a turn that receives guidance mid-flight, plus 4 Ink tests for the input wiring - docs: README and quickstart key tables Refs lessweb#113, lessweb#117
Guidance injected before the next LLM call still had to wait for the answer that
was already streaming, so a long text answer could not be redirected until it
finished on its own.
The session now tracks the streaming request separately from the turn. When the
user sends guidance while an answer is streaming, that request is aborted,
whatever the model already wrote is kept as an assistant message marked
meta.interrupted, and the loop immediately issues a new request that carries the
guidance. Tools are never interrupted: steering only targets the model stream, so
a running command finishes first and the guidance is injected at the next request
boundary.
- session: steerActiveSession(), a per-request steer signal released once the
request settles, and LlmSteeredError carrying the partial answer
- session: a steered answer is not an interruption - the turn continues, the
status stays completed, and the request is not retried
- settings: steerMode ("interrupt" by default, "queue" keeps the old behaviour)
- cli: submitting while busy calls steerActiveSession() when steerMode is
"interrupt"; the cut-short answer is labelled in the transcript
- tests: core covers the aborted stream, the follow-up request payload, the kept
partial answer, and the no-op case; settings cover steerMode resolution
- docs: configuration table and section, plus the README notes
Refs lessweb#113, lessweb#117
Author
|
Opened from the wrong fork by mistake. The implementation is now opened from the org fork instead: #357 |
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
Closes #113, #117.
While a turn was running,
Enterwas rejected with "wait for the current response or press escto interrupt", so sending a follow-up instruction meant waiting for the whole turn or
interrupting the agent. This PR makes new instructions flow into the work that is already
running:
before the model's next step of that turn.
short so the prompt is read immediately; what it already wrote stays in the conversation.
How it works
SessionManagerkeeps a per-session FIFO queue of supplemental prompts(
addSupplementaryPrompt, max 10). Before every LLM call of the running turn the queue isdrained and each entry becomes a user message with its images and selected skills:
When the model is streaming an answer,
steerActiveSession()aborts that single request:Details:
meta.interrupted, so the model (andthe transcript) still know what it had said when the user changed direction.
npm installfinishes and the guidance is injected at the next request boundary.completed, andthe aborted request is not retried.
iteration instead of deferring the guidance to a later, unrelated turn.
Escstill interrupts the whole turn.ask_user_questionanswer ora permission decision), so it can never be consumed as that answer.
role: "user"andmeta.isSupplementary.userwas chosenover a
systemreminder from the original sketch because it is the strongest signal forsuperseding earlier instructions and it carries images natively.
Configuration
steerModecontrols what happens when a prompt arrives while the model is writing:interrupt(default)queueRunning tools are never interrupted in either mode.
UI
Injected guidance is labelled in the transcript (
✧ Guidance sent while the turn was running),and so is a cut-short answer (
— superseded by your guidance).Backspaceon an empty promptremoves the last entry that has not been injected yet.
Changes
packages/core/src/session.tsMessageMeta.isSupplementary/.interruptedpackages/core/src/common/llm-retry.tsLlmSteeredErrorcarrying the partial answerpackages/core/src/settings.ts,index.tssteerModesetting and exportspackages/cli/src/ui/views/App.tsxpackages/cli/src/ui/views/PromptInput.tsxEnterusable while busy,Backspaceremoves the last entrypackages/cli/src/ui/components/MessageView/*steerModeconfiguration.md/_en.mdtable and section, README key tablesTesting
npm run check— type check, ESLint and Prettier all passnpm test— 333 CLI + 392 core + 59 VS Code tests passflight shows up in the next request of the same turn, and that a steered answer appears in the
follow-up request together with the guidance.
中文说明
AI 正在回复时,
Enter不再被拒绝:消息会作为补充指引,在本轮下一次 LLM 调用前作为 user 消息(含图片与 skills)注入,模型因此能结合已完成的工作修改甚至推翻之前的指令。如果模型正在流式输出
回答,该回答会被截断,已生成内容保留在对话中(标记
meta.interrupted),新指令立即被读取;正在执行的命令不会被中断,steering 只作用于模型输出流。可用
steerMode选择"interrupt"(默认,截断并立即读取)或
"queue"(等到下次请求边界再注入)。等待ask_user_question或权限确认时不会注入,Esc仍会中断整个轮次。