feat(coding-agent): added toggleable prompt cache miss markers
- Added `display.cacheMissMarker` setting to enable visual indicators for prompt cache invalidation in assistant messages. - Included updated theme symbols to represent cache misses across supported icon sets. - Configured the selector controller to rebuild the chat display when the marker visibility setting is toggled.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
## [Unreleased]
|
||||
### Added
|
||||
|
||||
- Added visual markers to assistant messages when prompt caching is invalidated
|
||||
- Added a prompt-cache miss marker: a slim `⊘ cache miss · <n> tokens` divider above an assistant turn whose request lost the provider prompt cache. Detected from per-turn usage — the previous turn cached a meaningful prefix but this request read none of it back and reprocessed the prompt (e.g. after a thinking-level / service-tier / model change, tool or system-prompt change, or a history rewrite). Reports the reprocessed token count. Toggle with the `display.cacheMissMarker` setting (Appearance → Display, on by default).
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -849,6 +849,17 @@ export const SETTINGS_SCHEMA = {
|
||||
},
|
||||
},
|
||||
|
||||
"display.cacheMissMarker": {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
ui: {
|
||||
tab: "appearance",
|
||||
group: "Display",
|
||||
label: "Cache Miss Marker",
|
||||
description: "Show a divider above an assistant turn whose request lost (missed) the prompt cache",
|
||||
},
|
||||
},
|
||||
|
||||
showHardwareCursor: {
|
||||
type: "boolean",
|
||||
default: true, // will be computed based on platform if undefined
|
||||
|
||||
@@ -252,8 +252,10 @@ export class ChatTranscriptBuilder {
|
||||
);
|
||||
this.container.addChild(assistantComponent);
|
||||
|
||||
const invalidation = detectCacheInvalidation(this.#lastAssistantUsage, message.usage);
|
||||
if (invalidation) assistantComponent.setCacheInvalidation(invalidation);
|
||||
if (settings.get("display.cacheMissMarker")) {
|
||||
const invalidation = detectCacheInvalidation(this.#lastAssistantUsage, message.usage);
|
||||
if (invalidation) assistantComponent.setCacheInvalidation(invalidation);
|
||||
}
|
||||
if (message.usage.cacheRead + message.usage.cacheWrite + message.usage.input > 0) {
|
||||
this.#lastAssistantUsage = message.usage;
|
||||
}
|
||||
|
||||
@@ -664,8 +664,10 @@ export class EventController {
|
||||
// meaningful prefix and this request read none of it back, flag the turn.
|
||||
const usage = event.message.usage;
|
||||
if (usage.cacheRead + usage.cacheWrite + usage.input > 0) {
|
||||
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
||||
if (invalidation) this.ctx.streamingComponent.setCacheInvalidation(invalidation);
|
||||
if (settings.get("display.cacheMissMarker")) {
|
||||
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
||||
if (invalidation) this.ctx.streamingComponent.setCacheInvalidation(invalidation);
|
||||
}
|
||||
this.ctx.lastAssistantUsage = usage;
|
||||
}
|
||||
this.#lastAssistantComponent = this.ctx.streamingComponent;
|
||||
|
||||
@@ -327,6 +327,13 @@ export class SelectorController {
|
||||
// InputController.toggleThinkingBlockVisibility).
|
||||
this.ctx.ui.resetDisplay();
|
||||
break;
|
||||
case "display.cacheMissMarker":
|
||||
// Rebuild re-runs the usage-based detection under the new setting so
|
||||
// markers appear/disappear; full reset retires any already committed
|
||||
// to native scrollback (mirrors hideThinking).
|
||||
this.ctx.rebuildChatFromMessages();
|
||||
this.ctx.ui.resetDisplay();
|
||||
break;
|
||||
case "tui.tight":
|
||||
setTuiTight(value as boolean);
|
||||
this.ctx.ui.invalidate();
|
||||
|
||||
@@ -111,6 +111,7 @@ export type SymbolKey =
|
||||
| "icon.agents"
|
||||
| "icon.job"
|
||||
| "icon.cache"
|
||||
| "icon.cacheMiss"
|
||||
| "icon.input"
|
||||
| "icon.output"
|
||||
| "icon.host"
|
||||
@@ -310,6 +311,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
||||
"icon.agents": "👥",
|
||||
"icon.job": "⚙",
|
||||
"icon.cache": "💾",
|
||||
"icon.cacheMiss": "⊘",
|
||||
"icon.input": "⤵",
|
||||
"icon.output": "⤴",
|
||||
"icon.host": "🖥",
|
||||
@@ -579,6 +581,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
||||
"icon.job": "\uf013",
|
||||
// pick: | alt:
|
||||
"icon.cache": "\uf1c0",
|
||||
// pick: (fa-ban) | alt: ⊘
|
||||
"icon.cacheMiss": "\uf05e",
|
||||
// pick: | alt: →
|
||||
"icon.input": "\uf090",
|
||||
// pick: | alt: →
|
||||
@@ -810,6 +814,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
||||
"icon.agents": "AG",
|
||||
"icon.job": "bg",
|
||||
"icon.cache": "cache",
|
||||
"icon.cacheMiss": "!",
|
||||
"icon.input": "in:",
|
||||
"icon.output": "out:",
|
||||
"icon.host": "host",
|
||||
@@ -1770,6 +1775,7 @@ export class Theme {
|
||||
agents: this.#symbols["icon.agents"],
|
||||
job: this.#symbols["icon.job"],
|
||||
cache: this.#symbols["icon.cache"],
|
||||
cacheMiss: this.#symbols["icon.cacheMiss"],
|
||||
input: this.#symbols["icon.input"],
|
||||
output: this.#symbols["icon.output"],
|
||||
host: this.#symbols["icon.host"],
|
||||
|
||||
@@ -412,8 +412,10 @@ export class UiHelpers {
|
||||
const assistantComponent = lastChild instanceof AssistantMessageComponent ? lastChild : undefined;
|
||||
if (assistantComponent) {
|
||||
const usage = message.usage;
|
||||
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
||||
if (invalidation) assistantComponent.setCacheInvalidation(invalidation);
|
||||
if (this.ctx.settings.get("display.cacheMissMarker")) {
|
||||
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
||||
if (invalidation) assistantComponent.setCacheInvalidation(invalidation);
|
||||
}
|
||||
if (usage.cacheRead + usage.cacheWrite + usage.input > 0) {
|
||||
this.ctx.lastAssistantUsage = usage;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user