From 32188bfe528b78c5c9c6ff71b0ae16ffa7edc6dc Mon Sep 17 00:00:00 2001 From: Mruthunjai Govindaraju Date: Sat, 17 Oct 2026 23:07:49 +0530 Subject: [PATCH 1/3] fix(desktop): no past-tense line or tool name on a waiting approval card --- changes/+approval-card-small-print.fixed.md | 3 +++ desktop/src/features/chat/ApprovalCard.tsx | 11 +++-------- desktop/src/features/chat/approvalRisk.ts | 10 ++++++++++ desktop/src/features/chat/toolMeta.ts | 20 +++++++++++++------- desktop/tests/approvalRisk.test.ts | 17 ++++++++++++++++- 5 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 changes/+approval-card-small-print.fixed.md diff --git a/changes/+approval-card-small-print.fixed.md b/changes/+approval-card-small-print.fixed.md new file mode 100644 index 00000000..d500468a --- /dev/null +++ b/changes/+approval-card-small-print.fixed.md @@ -0,0 +1,3 @@ +**A request waiting for your approval no longer reads as if it already happened.** A click waiting for your +answer showed a line like "Clicked using browser_click" under it. Now the card says only what Sentient would do, +in plain words. diff --git a/desktop/src/features/chat/ApprovalCard.tsx b/desktop/src/features/chat/ApprovalCard.tsx index 738e9b39..29a48b34 100644 --- a/desktop/src/features/chat/ApprovalCard.tsx +++ b/desktop/src/features/chat/ApprovalCard.tsx @@ -29,8 +29,8 @@ import type { ApprovalDecision } from '@/lib/types' import { cn, humanize, truncate } from '@/lib/utils' import { ActionPreview } from './ActionPreview' import { hostOf, refLabel } from './cards/bits' -import { toolMeta } from './toolMeta' -import { approvalCardReason, resolveWording, type RiskLabel } from './approvalRisk' +import { askingLabel } from './toolMeta' +import { approvalCardReason, resolveWording, waitingSmallPrint, type RiskLabel } from './approvalRisk' const STATUS_TEXT: Partial> = { allow: 'You allowed this once', @@ -152,7 +152,6 @@ export function ApprovalCard({ onRespond?: (approvalId: string, decision: ApprovalDecision) => void }) { const risk = effectiveRisk(approval) - const meta = toolMeta(approval.name) if (approval.status !== 'pending') { const allowed = approval.status !== 'deny' && approval.status !== 'expired' @@ -193,11 +192,7 @@ export function ApprovalCard({ (command && 'It runs on this computer with your permissions, in this folder:') || purpose || reason || - (meta.done ? ( - <> - {meta.done} using {approval.name} - - ) : null) + waitingSmallPrint(action, askingLabel(approval.name)) return ( = { terminal_run: { running: 'Running a command', done: 'Ran a command', icon: IconTerminal2, would: 'Would run a command' }, delegate_task: { running: 'Asking a helper', done: 'Asked a helper', icon: IconUserBolt, would: 'Would ask a helper' }, delegate_tasks: { running: 'Asking helpers', done: 'Asked helpers', icon: IconUsersGroup, would: 'Would ask helpers' }, - browser_open: { running: 'Opening a page', done: 'Opened a page', icon: IconWorldWww }, + browser_open: { running: 'Opening a page', done: 'Opened a page', icon: IconWorldWww, would: 'Would open a page' }, browser_snapshot: { running: 'Reading the page', done: 'Read the page', icon: IconListDetails }, browser_click: { running: 'Clicking', done: 'Clicked', icon: IconPointer, would: 'Would click' }, browser_type: { running: 'Typing', done: 'Typed', icon: IconKeyboard, would: 'Would type' }, @@ -88,12 +88,12 @@ const KNOWN: Record = { browser_screenshot: { running: 'Taking a screenshot', done: 'Took a screenshot', icon: IconScreenshot }, browser_close: { running: 'Closing the browser', done: 'Closed the browser', icon: IconWorld }, device_list: { running: 'Checking your devices', done: 'Checked your devices', icon: IconDevices }, - device_take_photo: { running: 'Taking a photo', done: 'Took a photo', icon: IconCamera }, - device_capture_screen: { running: 'Looking at the screen', done: 'Looked at the screen', icon: IconScreenshot }, - device_get_location: { running: 'Finding your location', done: 'Found your location', icon: IconMapPin }, - device_notify: { running: 'Sending a notification', done: 'Sent a notification', icon: IconBellRinging }, - device_display: { running: 'Showing it on your device', done: 'Showed it on your device', icon: IconDevices }, - device_speak: { running: 'Speaking on your device', done: 'Spoke on your device', icon: IconSpeakerphone } + device_take_photo: { running: 'Taking a photo', done: 'Took a photo', icon: IconCamera, would: 'Would take a photo' }, + device_capture_screen: { running: 'Looking at the screen', done: 'Looked at the screen', icon: IconScreenshot, would: 'Would look at the screen' }, + device_get_location: { running: 'Finding your location', done: 'Found your location', icon: IconMapPin, would: 'Would find your location' }, + device_notify: { running: 'Sending a notification', done: 'Sent a notification', icon: IconBellRinging, would: 'Would send a notification' }, + device_display: { running: 'Showing it on your device', done: 'Showed it on your device', icon: IconDevices, would: 'Would show it on your device' }, + device_speak: { running: 'Speaking on your device', done: 'Spoke on your device', icon: IconSpeakerphone, would: 'Would speak on your device' } } const PREFIX: Array<[RegExp, Icon]> = [ @@ -136,6 +136,12 @@ export function wouldLabel(name: string): string { return `Would use ${meta.done}` } +/** What a waiting approval says the call would do, in plain words; null for a known tool without such wording + * (its past-tense label would read "Would use Checked the date and time"). */ +export function askingLabel(name: string): string | null { + return KNOWN[name] && !KNOWN[name].would ? null : wouldLabel(name) +} + export function toolMeta(name: string): ToolMeta { const known = KNOWN[name] if (known) return known diff --git a/desktop/tests/approvalRisk.test.ts b/desktop/tests/approvalRisk.test.ts index 87bca6f4..56d96e82 100644 --- a/desktop/tests/approvalRisk.test.ts +++ b/desktop/tests/approvalRisk.test.ts @@ -3,7 +3,7 @@ // Run with `npm test` (no Electron needed). import assert from 'node:assert/strict' import { test } from 'node:test' -import { approvalCardReason, approvalRisk, engineWording, resolveWording } from '../src/features/chat/approvalRisk.ts' +import { approvalCardReason, approvalRisk, engineWording, resolveWording, waitingSmallPrint } from '../src/features/chat/approvalRisk.ts' const email = (name: string, body: string, reason = 'mcp_mailer: Send an email to one address') => ({ risk: 'send' as const, @@ -351,3 +351,18 @@ test('a free Subscribe click shows "Signs up" once, a delete click keeps its own assert.equal(resolveWording(remove).label, 'Deletes') assert.equal(approvalCardReason(remove.reason, resolveWording(remove).label), 'Click "Delete account"') }) + +// A waiting card with no reason of its own (every browser click since #404 and #412) used to read +// "Clicked using browser_click": past tense before anything happened, and a developer name. +test('a waiting approval card never says what already happened or names the tool', () => { + // the action line already says what will happen: no small print under it + assert.equal(waitingSmallPrint('Click “Subscribe”', 'Would click'), null) + assert.equal(waitingSmallPrint('Type “hello” and submit', 'Would type'), null) + // no action line: the tool's plain wording, about what would happen + assert.equal(waitingSmallPrint(null, 'Would write file'), 'Would write file') + assert.equal(waitingSmallPrint(undefined, 'Would send a notification'), 'Would send a notification') + assert.equal(waitingSmallPrint('', 'Would use Gmail: send email'), 'Would use Gmail: send email') + // nothing to say: no line at all + assert.equal(waitingSmallPrint(null, null), null) + assert.equal(waitingSmallPrint(' ', ' '), null) +}) From 274668abc1c75419a87297f9c876664be4002fcd Mon Sep 17 00:00:00 2001 From: itsskofficial Date: Sun, 18 Oct 2026 06:38:01 +0530 Subject: [PATCH 2/3] Rename the changelog entry to its issue number (#435) --- changes/{+approval-card-small-print.fixed.md => 435.fixed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changes/{+approval-card-small-print.fixed.md => 435.fixed.md} (100%) diff --git a/changes/+approval-card-small-print.fixed.md b/changes/435.fixed.md similarity index 100% rename from changes/+approval-card-small-print.fixed.md rename to changes/435.fixed.md From 56c8c73a2cbf061eaac6bcc0f8c0eba81494cd98 Mon Sep 17 00:00:00 2001 From: itsskofficial Date: Sun, 18 Oct 2026 07:02:56 +0530 Subject: [PATCH 3/3] fix(desktop): every known tool says what it would do on a waiting card --- desktop/src/features/chat/toolMeta.ts | 55 ++++++++++++++------------- desktop/tests/approvalRisk.test.ts | 15 ++++++++ 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/desktop/src/features/chat/toolMeta.ts b/desktop/src/features/chat/toolMeta.ts index 17ca7105..c53ef6bf 100644 --- a/desktop/src/features/chat/toolMeta.ts +++ b/desktop/src/features/chat/toolMeta.ts @@ -47,49 +47,50 @@ interface ToolMeta { would?: string } -const KNOWN: Record = { - memory_recall: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain }, - memory_search: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain }, +/** Every tool the window names; each has a `would` line for waiting approvals and try runs (tests/approvalRisk.test.ts). */ +export const KNOWN: Record = { + memory_recall: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain, would: 'Would search memory' }, + memory_search: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain, would: 'Would search memory' }, memory_save: { running: 'Saving to memory', done: 'Saved to memory', icon: IconBrain, would: 'Would save to memory' }, memory_remember: { running: 'Saving to memory', done: 'Saved to memory', icon: IconBrain, would: 'Would save to memory' }, memory_add: { running: 'Saving to memory', done: 'Saved to memory', icon: IconBrain, would: 'Would save to memory' }, memory_forget: { running: 'Forgetting', done: 'Forgot', icon: IconBrain, would: 'Would forget' }, - memory_search_history: { running: 'Searching past chats', done: 'Searched past chats', icon: IconHistory }, - memory_search_by_source: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain }, - history_semantic_search: { running: 'Searching past chats', done: 'Searched past chats', icon: IconHistory }, - history_time_search: { running: 'Looking back in time', done: 'Looked back in time', icon: IconHistory }, - current_datetime: { running: 'Checking the date and time', done: 'Checked the date and time', icon: IconClock }, - file_list: { running: 'Listing files', done: 'Listed files', icon: IconFolder }, - file_read: { running: 'Reading file', done: 'Read file', icon: IconFile }, + memory_search_history: { running: 'Searching past chats', done: 'Searched past chats', icon: IconHistory, would: 'Would search past chats' }, + memory_search_by_source: { running: 'Searching memory', done: 'Searched memory', icon: IconBrain, would: 'Would search memory' }, + history_semantic_search: { running: 'Searching past chats', done: 'Searched past chats', icon: IconHistory, would: 'Would search past chats' }, + history_time_search: { running: 'Looking back in time', done: 'Looked back in time', icon: IconHistory, would: 'Would look back in time' }, + current_datetime: { running: 'Checking the date and time', done: 'Checked the date and time', icon: IconClock, would: 'Would check the date and time' }, + file_list: { running: 'Listing files', done: 'Listed files', icon: IconFolder, would: 'Would list files' }, + file_read: { running: 'Reading file', done: 'Read file', icon: IconFile, would: 'Would read a file' }, file_write: { running: 'Writing file', done: 'Wrote file', icon: IconFilePencil, would: 'Would write file' }, - folder_search: { running: 'Searching your notes', done: 'Searched your notes', icon: IconNotebook }, - folder_read: { running: 'Reading a note', done: 'Read a note', icon: IconNotebook }, - time_now: { running: 'Checking the time', done: 'Checked the time', icon: IconClock }, - current_time: { running: 'Checking the time', done: 'Checked the time', icon: IconClock }, - skill_view: { running: 'Opening skill', done: 'Opened skill', icon: IconSparkles }, + folder_search: { running: 'Searching your notes', done: 'Searched your notes', icon: IconNotebook, would: 'Would search your notes' }, + folder_read: { running: 'Reading a note', done: 'Read a note', icon: IconNotebook, would: 'Would read a note' }, + time_now: { running: 'Checking the time', done: 'Checked the time', icon: IconClock, would: 'Would check the time' }, + current_time: { running: 'Checking the time', done: 'Checked the time', icon: IconClock, would: 'Would check the time' }, + skill_view: { running: 'Opening skill', done: 'Opened skill', icon: IconSparkles, would: 'Would open a skill' }, skill_save: { running: 'Saving a skill', done: 'Saved a skill', icon: IconSparkles, would: 'Would save a skill' }, - skill_list: { running: 'Listing skills', done: 'Listed skills', icon: IconSparkles }, - web_search: { running: 'Searching the web', done: 'Searched the web', icon: IconWorld }, + skill_list: { running: 'Listing skills', done: 'Listed skills', icon: IconSparkles, would: 'Would list skills' }, + web_search: { running: 'Searching the web', done: 'Searched the web', icon: IconWorld, would: 'Would search the web' }, // §10-13 code, helpers, browser and devices execute_code: { running: 'Running code', done: 'Ran code', icon: IconTerminal2, would: 'Would run code' }, terminal_run: { running: 'Running a command', done: 'Ran a command', icon: IconTerminal2, would: 'Would run a command' }, delegate_task: { running: 'Asking a helper', done: 'Asked a helper', icon: IconUserBolt, would: 'Would ask a helper' }, delegate_tasks: { running: 'Asking helpers', done: 'Asked helpers', icon: IconUsersGroup, would: 'Would ask helpers' }, - read_with_reader: { running: 'Reading it with a safe reader', done: 'Read it with a safe reader', icon: IconShieldSearch }, + read_with_reader: { running: 'Reading it with a safe reader', done: 'Read it with a safe reader', icon: IconShieldSearch, would: 'Would read it with a safe reader' }, browser_open: { running: 'Opening a page', done: 'Opened a page', icon: IconWorldWww, would: 'Would open a page' }, - browser_snapshot: { running: 'Reading the page', done: 'Read the page', icon: IconListDetails }, + browser_snapshot: { running: 'Reading the page', done: 'Read the page', icon: IconListDetails, would: 'Would read the page' }, browser_click: { running: 'Clicking', done: 'Clicked', icon: IconPointer, would: 'Would click' }, browser_type: { running: 'Typing', done: 'Typed', icon: IconKeyboard, would: 'Would type' }, browser_select: { running: 'Choosing an option', done: 'Chose an option', icon: IconListDetails, would: 'Would choose an option' }, browser_press: { running: 'Pressing a key', done: 'Pressed a key', icon: IconKeyboard, would: 'Would press a key' }, - browser_scroll: { running: 'Scrolling', done: 'Scrolled', icon: IconArrowsVertical }, - browser_back: { running: 'Going back', done: 'Went back', icon: IconArrowBackUp }, - browser_tabs: { running: 'Checking tabs', done: 'Checked tabs', icon: IconAppWindow }, - browser_switch_tab: { running: 'Switching tabs', done: 'Switched tabs', icon: IconAppWindow }, - browser_extract: { running: 'Reading the page', done: 'Read the page', icon: IconListDetails }, - browser_screenshot: { running: 'Taking a screenshot', done: 'Took a screenshot', icon: IconScreenshot }, - browser_close: { running: 'Closing the browser', done: 'Closed the browser', icon: IconWorld }, - device_list: { running: 'Checking your devices', done: 'Checked your devices', icon: IconDevices }, + browser_scroll: { running: 'Scrolling', done: 'Scrolled', icon: IconArrowsVertical, would: 'Would scroll' }, + browser_back: { running: 'Going back', done: 'Went back', icon: IconArrowBackUp, would: 'Would go back' }, + browser_tabs: { running: 'Checking tabs', done: 'Checked tabs', icon: IconAppWindow, would: 'Would check tabs' }, + browser_switch_tab: { running: 'Switching tabs', done: 'Switched tabs', icon: IconAppWindow, would: 'Would switch tabs' }, + browser_extract: { running: 'Reading the page', done: 'Read the page', icon: IconListDetails, would: 'Would read the page' }, + browser_screenshot: { running: 'Taking a screenshot', done: 'Took a screenshot', icon: IconScreenshot, would: 'Would take a screenshot' }, + browser_close: { running: 'Closing the browser', done: 'Closed the browser', icon: IconWorld, would: 'Would close the browser' }, + device_list: { running: 'Checking your devices', done: 'Checked your devices', icon: IconDevices, would: 'Would check your devices' }, device_take_photo: { running: 'Taking a photo', done: 'Took a photo', icon: IconCamera, would: 'Would take a photo' }, device_capture_screen: { running: 'Looking at the screen', done: 'Looked at the screen', icon: IconScreenshot, would: 'Would look at the screen' }, device_get_location: { running: 'Finding your location', done: 'Found your location', icon: IconMapPin, would: 'Would find your location' }, diff --git a/desktop/tests/approvalRisk.test.ts b/desktop/tests/approvalRisk.test.ts index 56d96e82..2d137ed3 100644 --- a/desktop/tests/approvalRisk.test.ts +++ b/desktop/tests/approvalRisk.test.ts @@ -366,3 +366,18 @@ test('a waiting approval card never says what already happened or names the tool assert.equal(waitingSmallPrint(null, null), null) assert.equal(waitingSmallPrint(' ', ' '), null) }) + +// #450 review: a known tool without `would` wording left a waiting card with only its heading +// (a browser_screenshot under an Ask rule). Every known tool now says what it would do. toolMeta.ts imports +// through the `@/` alias, which node's test runner can't load, so this reads its table as text. +test('every known tool says what it would do while it waits', async () => { + const { readFileSync } = await import('node:fs') + const src = readFileSync(new URL('../src/features/chat/toolMeta.ts', import.meta.url), 'utf8') + const lines = src.slice(src.indexOf('export const KNOWN')).split(/\r?\n/) + const table = lines.slice(1, lines.findIndex((line) => line === '}')) + const entries = table.filter((line) => /^ {2}[a-z_]+: \{/.test(line)) + assert.ok(entries.length > 40, 'found the tool table') + const missing = entries.filter((line) => !/would: '/.test(line)).map((line) => line.trim().split(':')[0]) + assert.deepEqual(missing, []) + assert.ok(entries.some((line) => line.includes("browser_screenshot") && line.includes("would: 'Would take a screenshot'"))) +})