mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): hide raw internals in Agent tool card (#481)
* fix(web): hide raw internals in Agent tool card (#480) The Agent tool card was exposing raw JSON input (including full prompts) and internal system messages (agentId, output_file paths, system instructions) in the details dialog. Register dedicated views: - knownTools: show description as title, subagent_type as subtitle - AgentFullView: show description, type, background status (not prompt) - AgentResultView: detect internal launch messages and show "Agent launched" instead; render actual results as markdown for completed agents via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix: widen Agent result redaction to catch more internal metadata variants Use || instead of && so any single internal marker (agentId:, output_file:, internal ID) triggers redaction, not just the combination of all markers. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix: use structural checks for Agent metadata redaction Replace loose substring matching (which could false-positive on legitimate agent output) with: 1. Structural check: result object has agentId/output_file keys 2. Strict text pattern: starts with the exact launch message prefix Also make the label state-aware: "Done" for completed, "Agent launched" for running. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> --------- Co-authored-by: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -285,6 +285,18 @@ export const knownTools: Record<string, {
|
||||
},
|
||||
minimal: true
|
||||
},
|
||||
Agent: {
|
||||
icon: () => <RocketIcon className={DEFAULT_ICON_CLASS} />,
|
||||
title: (opts) => {
|
||||
const description = getInputStringAny(opts.input, ['description'])
|
||||
return description ?? 'Agent'
|
||||
},
|
||||
subtitle: (opts) => {
|
||||
const model = getInputStringAny(opts.input, ['subagent_type'])
|
||||
return model ?? null
|
||||
},
|
||||
minimal: true
|
||||
},
|
||||
CodexReasoning: {
|
||||
icon: () => <BulbIcon className={DEFAULT_ICON_CLASS} />,
|
||||
title: (opts) => getInputStringAny(opts.input, ['title']) ?? 'Reasoning',
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MultiEditFullView, MultiEditView } from '@/components/ToolCard/views/Mu
|
||||
import { TodoWriteView } from '@/components/ToolCard/views/TodoWriteView'
|
||||
import { UpdatePlanView } from '@/components/ToolCard/views/UpdatePlanView'
|
||||
import { WriteView } from '@/components/ToolCard/views/WriteView'
|
||||
import { isObject } from '@hapi/protocol'
|
||||
import { getInputStringAny } from '@/lib/toolInputUtils'
|
||||
|
||||
export type ToolViewProps = {
|
||||
@@ -29,6 +30,25 @@ const SkillFullView: ToolViewComponent = ({ block }: ToolViewProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
const AgentFullView: ToolViewComponent = ({ block }: ToolViewProps) => {
|
||||
const input = block.tool.input
|
||||
const description = getInputStringAny(input, ['description'])
|
||||
const subagentType = getInputStringAny(input, ['subagent_type'])
|
||||
const runInBackground = isObject(input) && input.run_in_background === true
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-sm">
|
||||
{description && (
|
||||
<div className="text-[var(--app-fg)]">{description}</div>
|
||||
)}
|
||||
<div className="flex gap-3 text-[var(--app-hint)]">
|
||||
{subagentType && <span>Type: {subagentType}</span>}
|
||||
{runInBackground && <span>Background</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const toolViewRegistry: Record<string, ToolViewComponent> = {
|
||||
Edit: EditView,
|
||||
MultiEdit: MultiEditView,
|
||||
@@ -50,6 +70,7 @@ export const toolFullViewRegistry: Record<string, ToolViewComponent> = {
|
||||
CodexDiff: CodexDiffFullView,
|
||||
CodexPatch: CodexPatchView,
|
||||
Skill: SkillFullView,
|
||||
Agent: AgentFullView,
|
||||
AskUserQuestion: AskUserQuestionView,
|
||||
ExitPlanMode: ExitPlanModeView,
|
||||
ask_user_question: AskUserQuestionView,
|
||||
|
||||
@@ -507,6 +507,47 @@ const TodoWriteResultView: ToolViewComponent = (props: ToolViewProps) => {
|
||||
return <ChecklistList items={todos} />
|
||||
}
|
||||
|
||||
const AgentResultView: ToolViewComponent = (props: ToolViewProps) => {
|
||||
const { state, result } = props.block.tool
|
||||
|
||||
if (result === undefined || result === null) {
|
||||
return <div className="text-sm text-[var(--app-hint)]">{placeholderForState(state)}</div>
|
||||
}
|
||||
|
||||
// For errors, show the error text
|
||||
if (state === 'error') {
|
||||
const text = extractTextFromResult(result)
|
||||
return (
|
||||
<div className="text-sm text-red-600">
|
||||
{text?.trim() ? text : 'Agent failed'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const text = extractTextFromResult(result)
|
||||
if (!text) {
|
||||
return <div className="text-sm text-[var(--app-hint)]">{state === 'completed' ? 'Done' : placeholderForState(state)}</div>
|
||||
}
|
||||
|
||||
// Detect internal launch metadata. Check structurally first (result object
|
||||
// may carry agentId/output_file keys), then fall back to a strict text
|
||||
// pattern that is unlikely to appear in normal agent prose.
|
||||
const isInternalMeta = isObject(result) && ('agentId' in result || 'output_file' in result)
|
||||
|| (text.startsWith('Async agent launched successfully.') && text.includes('agentId:'))
|
||||
|
||||
if (isInternalMeta) {
|
||||
const label = state === 'completed' ? 'Done' : 'Agent launched'
|
||||
return <div className="text-sm text-[var(--app-hint)]">{label}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MarkdownRenderer content={text} />
|
||||
<RawJsonDevOnly value={result} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const SkillResultView: ToolViewComponent = (props: ToolViewProps) => {
|
||||
const { state, result, input } = props.block.tool
|
||||
|
||||
@@ -597,6 +638,7 @@ export const toolResultViewRegistry: Record<string, ToolViewComponent> = {
|
||||
CodexPatch: CodexPatchResultView,
|
||||
CodexDiff: CodexDiffResultView,
|
||||
Skill: SkillResultView,
|
||||
Agent: AgentResultView,
|
||||
AskUserQuestion: AskUserQuestionResultView,
|
||||
ExitPlanMode: MarkdownResultView,
|
||||
ask_user_question: AskUserQuestionResultView,
|
||||
|
||||
Reference in New Issue
Block a user