mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-27 17:58:07 +00:00
Merge pull request #72220 from NousResearch/bb/tool-row-dedupe
fix(tools): stop the inline tool row stuttering its verb and repeating the command
This commit is contained in:
commit
79b4a40568
4 changed files with 61 additions and 5 deletions
|
|
@ -340,6 +340,28 @@ describe('buildToolView title actions', () => {
|
|||
expect(view.titleAction).toEqual({ prefix: '', text: 'Running', suffix: ' pnpm run lint' })
|
||||
})
|
||||
|
||||
it('never stutters the verb or echoes the command when the backend context is a phrased label', () => {
|
||||
// Older backends stamped tool.start with a *phrased* label
|
||||
// ("Running sleep 70 + 2 commands") rather than a raw arg preview, and the
|
||||
// desktop merges that into args.context. The row must still prepend its own
|
||||
// verb exactly once, show the real command in the `$` transcript, and not
|
||||
// repeat either string as detail.
|
||||
const command = 'sleep 70; echo "a"; echo "b"'
|
||||
|
||||
const view = buildToolView(
|
||||
part({
|
||||
args: { command, context: 'Running sleep 70 + 2 commands' },
|
||||
result: { exit_code: 0 },
|
||||
toolName: 'terminal'
|
||||
}),
|
||||
''
|
||||
)
|
||||
|
||||
expect(view.title).toBe('Ran sleep 70 + 2 commands')
|
||||
expect(view.terminalCommand).toBe(command)
|
||||
expect(view.detail).toBe('')
|
||||
})
|
||||
|
||||
it('uses the runtime locale for title text and action placement', () => {
|
||||
setRuntimeI18nLocale('ja')
|
||||
|
||||
|
|
|
|||
|
|
@ -128,9 +128,14 @@ function readFileDisplayTarget(args: Record<string, unknown>, result: Record<str
|
|||
return [fileEditBasename(path), lineLabel].filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
// The real command, preferring the actual argument over the backend's
|
||||
// display preview. `context` is a *summarized* preview ("sleep 70 + 2
|
||||
// commands") the gateway sends on tool.start before real args arrive — fine
|
||||
// as a placeholder for the live title, wrong for the `$` transcript, which
|
||||
// must show what actually ran.
|
||||
function shellCommand(args: Record<string, unknown>): string {
|
||||
return (
|
||||
firstStringField(args, ['context', 'preview']) || firstStringField(args, ['command', 'code']) || contextValue(args)
|
||||
firstStringField(args, ['command', 'code']) || firstStringField(args, ['context', 'preview']) || contextValue(args)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1079,6 +1084,13 @@ function toolDetailText(
|
|||
if (output || lines) {
|
||||
return [output, lines].filter(Boolean).join('\n')
|
||||
}
|
||||
|
||||
// A terminal row with no output already shows its command in the `$`
|
||||
// transcript above; the generic fallback would print the same string a
|
||||
// second time. `execute_code` has no transcript, so it keeps the fallback.
|
||||
if (part.toolName === 'terminal') {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
if (part.toolName === 'web_extract') {
|
||||
|
|
|
|||
|
|
@ -1551,12 +1551,24 @@ def test_history_to_messages_preserves_tool_calls_for_resume_display():
|
|||
|
||||
assert server._history_to_messages(history) == [
|
||||
{"role": "user", "text": "first prompt"},
|
||||
{"context": "Searching files for resume", "name": "search_files", "role": "tool"},
|
||||
{"context": "resume", "name": "search_files", "role": "tool"},
|
||||
{"role": "assistant", "text": "first answer"},
|
||||
{"role": "user", "text": "second prompt"},
|
||||
]
|
||||
|
||||
|
||||
def test_tool_ctx_sends_an_arg_preview_not_a_phrased_label():
|
||||
# Clients phrase their own verb around this string: the TUI renders
|
||||
# `Terminal("<ctx>")` and the desktop prepends "Running"/"Ran". Sending a
|
||||
# pre-phrased label made both stutter ("Ran Running sleep 70 + 2 commands")
|
||||
# and stood in for the real command in the desktop's `$` transcript.
|
||||
assert server._tool_ctx("terminal", {"command": 'sleep 70; echo "a"; echo "b"'}) == (
|
||||
"sleep 70 + 2 commands"
|
||||
)
|
||||
assert server._tool_ctx("read_file", {"path": "/tmp/demo/package.json"}) == "package.json"
|
||||
assert server._tool_ctx("web_search", {"query": "weather in NYC"}) == "weather in NYC"
|
||||
|
||||
|
||||
def test_history_to_messages_keeps_reasoning_only_assistant_turn():
|
||||
# A thinking-only assistant turn (reasoning present, no visible text) is
|
||||
# persisted and recallable, but was dropped from the resumed session view
|
||||
|
|
|
|||
|
|
@ -4505,10 +4505,20 @@ def _session_info(agent, session: dict | None = None) -> dict:
|
|||
|
||||
|
||||
def _tool_ctx(name: str, args: dict) -> str:
|
||||
try:
|
||||
from agent.display import build_tool_label
|
||||
"""Argument preview for a tool row — never a phrased label.
|
||||
|
||||
return build_tool_label(name, args, max_len=80) or ""
|
||||
Clients own their own phrasing: the TUI wraps this as ``Terminal("...")``
|
||||
and the desktop prepends its own localized verb ("Running"/"Ran"). Sending
|
||||
``build_tool_label`` here instead of the raw preview stutters the verb on
|
||||
both surfaces ("Running Running sleep 70 + 2 commands") and leaks a display
|
||||
label into the desktop's ``args.context``, where it stands in for the real
|
||||
command. The friendly labels belong on the CLI spinner, which builds them
|
||||
from ``build_tool_label`` at its own call sites.
|
||||
"""
|
||||
try:
|
||||
from agent.display import build_tool_preview
|
||||
|
||||
return build_tool_preview(name, args, max_len=80) or ""
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue