fix(desktop): stop painting memory writes as errors

A memory tool call rendered destructive-red with an alert glyph and, when
expanded, a raw dump of the whole args + result payload.

Two causes. The red came from toolStatus treating any error text as a
failure: the memory store rejects an over-budget batch so the agent can
retry a smaller one, which is routine bookkeeping, not something the user
acts on. Those now land on the existing amber warning tier.

The payload dump came from memory having no case in toolSubtitle or
toolDetailText, so both fell through to the generic stringify-everything
fallback. Both now surface just the human-readable line.
This commit is contained in:
Brooklyn Nicholson 2026-07-26 01:59:54 -05:00
parent dacd8d5416
commit 6192d3bb57

View file

@ -180,6 +180,10 @@ const TOOL_META: Record<ToolTitleKey, ToolMetaSpec> = {
icon: 'files',
tone: 'file'
},
memory: {
icon: 'brain',
tone: 'agent'
},
patch: { icon: 'edit', tone: 'file' },
read_file: { icon: 'file', tone: 'file' },
search_files: {
@ -688,7 +692,15 @@ function toolStatus(part: ToolPart, resultRecord: Record<string, unknown>): Tool
return 'running'
}
return toolErrorText(part, resultRecord) ? 'error' : 'success'
if (!toolErrorText(part, resultRecord)) {
return 'success'
}
// A rejected memory write is a budget negotiation, not a failure: the store
// refuses an over-limit batch and the agent immediately retries a smaller
// one. Painting the row destructive-red puts an alarm next to routine
// bookkeeping the user never has to act on. Amber says "noted" instead.
return part.toolName === 'memory' ? 'warning' : 'error'
}
function durationLabel(resultRecord: Record<string, unknown>): string | undefined {
@ -1009,6 +1021,13 @@ function toolSubtitle(
return url ? hostnameOf(url) : 'Fetched webpage'
}
if (toolName === 'memory') {
// The raw payload is bookkeeping the user never needs: usage counters, a
// note telling the model not to retry, and the full operations array. The
// human-readable line is the only part worth showing.
return firstStringField(resultRecord, ['message', 'error'])
}
if (toolName === 'cronjob') {
return cronjobSubtitle(argsRecord, resultRecord)
}
@ -1091,6 +1110,12 @@ function toolDetailText(
}
}
if (part.toolName === 'memory') {
// Same reasoning as toolSubtitle: without this the generic fallback dumps
// the whole args + result payload into the expanded row.
return firstStringField(resultRecord, ['message', 'error'])
}
if (isFileEditTool(part.toolName)) {
if (inlineDiffFromResult(part.result)) {
return ''