feat (chat): add queued mid-turn steer handling#1501
Conversation
|
Thanks for taking this on, @donle — the core idea is a real improvement and I want to land it. The loop-side FIFO is clean, the six exit-path queue-clears are right, and the A few things to address before I merge: 1. Comments — code-as-documentation is the project rule. A handful of new comments restate what the code already says, which the
2. The wrapper string is mine to author. This text:
is a prompt-engineering choice that affects model behavior across every steer, and I'd like to land that line myself in a follow-up after this PR merges. For this PR, please either:
I lean (a). Either is fine. 3.
4. Open a tracking issue for "mid-turn steer queue", and link it from the PR. The interaction primitive deserves a public design pointer for the next person who touches this surface — busy-input behavior is something I get asked about. One paragraph is enough; the PR description already has most of the material. Once 1-4 are addressed I'll merge. CI side is already in great shape — just substance and policy. |
|
Addressed the local review items:
Opened follow-up tracking issue ^ |
f2fa4e6 to
df05310
Compare
df05310 to
1574836
Compare
…se (#1565) * chore(release): 0.49.0 — static-history TUI, queued steers, Bing default, lifecycle plans Headline themes: - TUI: Static-history renderer is the only path; virtual-viewport layers removed (#1529 stages 1-4) - Chat: queued mid-turn steer handling so input mid-render doesn't drop or fight the live frame (#1501) - Web search: default switches to Bing; dashboard engine switcher; Mojeek dropped (#1558) - Plans: lifecycle evidence summaries surface why a plan is ready to accept (#1500) - Desktop: native OS notifications for approvals + completion (#1519) - i18n: CLI command output (/mcp /sessions /prune /theme) + approval-prompt labels translated (#1524, #1560) - Security: SSRF block in web_fetch (#1544), edit-snapshot path containment (#1454), shell redirect sandbox (#1457), Task integrity guardrail (#1516) - Tools: per-turn dispatch-rate limit (#1356); run_command discourages shell-based edits (#1514) - Client: DeepSeek 429 → concurrency-limit hint (#1526); timeoutMs honored with AbortSignal (#1535); --no-proxy opt-out for direct route (#1507) - Files: read/edit/restore preserves source encoding (GB18030 / UTF-8 BOM) (#1518) - Context: pinned constraints survive folds + full tail capture (#1515, #1552) - Refactor: lifecycle risk policy extracted into its own module (#1557) See CHANGELOG for the full list. * fix(context): align fold summary prefix with main agent for cache reuse The summarizer call was sending a bespoke "You compress conversation history" system prompt and no tools, guaranteeing a 0% cache hit against the main agent's just-cached prefix. Reshape the request so system + tools + head bytes mirror the live agent's last call — the only novel bytes are the trailing summarize instruction. Skill-pin handling now collects bodies read-only instead of stubbing mid-head, so the cache prefix stays unbroken. The summarize instruction names pinned skills so the model knows not to paraphrase their bodies (which we append verbatim regardless). Measured on a real session at 48.7K prompt tokens: OLD shape: 0.0% cache hit → $0.145 per fold NEW shape: 99.6% cache hit → $0.015 per fold saving: 89.6% per fold * tools: add fold-cache shape + live benchmarks bench-fold-cache-shape.mjs replays real session jsonls, simulates OLD vs NEW summary-call shapes at the fold point, and reports byte-level shared-prefix with the main agent's preceding request. Pure local — no API required. bench-fold-cache-live.mjs sends one priming + two summary calls to DeepSeek and reports prompt_cache_hit_tokens / cost for each shape. Used to confirm the shape change actually translates to API-side cache hits. --------- Co-authored-by: reasonix <reasonix@deepseek.com>
Summary
This PR adds queued mid-turn steering for busy turns.
When a turn is already running, users can now type ordinary prompt text to steer the current task instead of waiting for the turn to finish. The steer text is not executed immediately and does not interrupt the in-flight model request. It is queued and applied at the next loop iteration boundary.
Motivation
Previously, busy turns blocked prompt input. The first local change opened the input while busy, but it had two problems:
/newor/clearThis PR makes the behavior explicit:
Execution Flow
During a normal idle submit, prompt handling is unchanged.
During a busy turn:
/new,/clear,/model!ls#noteThis guarantees that steer input affects only future request payloads, never the request that is already in flight.
Queue Semantics
The loop now stores pending steer messages in a FIFO queue instead of a single pending value.
This changes the behavior from “last steer wins” to “all accepted steers are applied in order”.
Example:
focus on the frontend issue.also check mobile layout.No steer message is silently overwritten.
UI Behavior
The TUI and Dashboard prompt areas now communicate the busy-steer mode directly:
The Changes panel is also aligned with the main Chat panel: busy input can now actually submit steer text instead of appearing editable while the send path silently returns.
Tests
The loop tests now cover: