feat(agent): implement tool-controlled display protocol (Steps 2-3)

This commit is contained in:
Michael Bleigh
2026-04-10 10:51:21 -07:00
parent 31069329bb
commit 7f7e69e6c6
13 changed files with 229 additions and 73 deletions

View File

@@ -470,7 +470,10 @@ export async function runNonInteractive({
case 'tool_response': {
textOutput.ensureTrailingNewline();
if (streamFormatter) {
const displayText = getTextContent(event.displayContent);
const displayText =
event.display?.result?.type === 'text'
? event.display.result.text
: undefined;
const errorMsg = getTextContent(event.content) ?? 'Tool error';
streamFormatter.emitEvent({
type: JsonStreamEventType.TOOL_RESULT,
@@ -490,7 +493,10 @@ export async function runNonInteractive({
});
}
if (event.isError) {
const displayText = getTextContent(event.displayContent);
const displayText =
event.display?.result?.type === 'text'
? event.display.result.text
: undefined;
const errorMsg = getTextContent(event.content) ?? 'Tool error';
if (event.data?.['errorType'] === ToolErrorType.STOP_EXECUTION) {

View File

@@ -197,6 +197,7 @@ export const useAgentStream = ({
name: displayName,
originalRequestName: event.name,
description: desc,
display: event.display,
status: CoreToolCallStatus.Scheduled,
isClientInitiated: false,
renderOutputAsMarkdown: isOutputMarkdown,
@@ -223,8 +224,8 @@ export const useAgentStream = ({
status = CoreToolCallStatus.Success;
const liveOutput =
event.displayContent?.[0]?.type === 'text'
? event.displayContent[0].text
event.display?.result?.type === 'text'
? event.display.result.text
: tc.resultDisplay;
const progressMessage =
legacyState?.progressMessage ?? tc.progressMessage;
@@ -237,6 +238,7 @@ export const useAgentStream = ({
return {
...tc,
status,
display: event.display ?? tc.display,
resultDisplay: liveOutput,
progressMessage,
progress,
@@ -256,8 +258,8 @@ export const useAgentStream = ({
const legacyState = event._meta?.legacyState;
const outputFile = legacyState?.outputFile;
const resultDisplay =
event.displayContent?.[0]?.type === 'text'
? event.displayContent[0].text
event.display?.result?.type === 'text'
? event.display.result.text
: tc.resultDisplay;
return {
@@ -265,6 +267,7 @@ export const useAgentStream = ({
status: event.isError
? CoreToolCallStatus.Error
: CoreToolCallStatus.Success,
display: event.display ?? tc.display,
resultDisplay,
outputFile,
};

View File

@@ -11,6 +11,7 @@ import {
type ThoughtSummary,
type SerializableConfirmationDetails,
type ToolResultDisplay,
type ToolDisplay,
type RetrieveUserQuotaResponse,
type SkillDefinition,
type AgentDefinition,
@@ -121,6 +122,7 @@ export interface IndividualToolCallDisplay {
name: string;
args?: Record<string, unknown>;
description: string;
display?: ToolDisplay;
resultDisplay: ToolResultDisplay | undefined;
status: CoreToolCallStatus;
// True when the tool was initiated directly by the user (slash/@/shell flows).