mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(gemini): surface tool_call input on Gemini ACP cards (#562)
* fix(acp): derive tool_call input from kind+title fallback
Gemini 2.5 Flash and 3 Flash Preview omit rawInput entirely on
tool_call events while emitting prose (non-JSON) thoughts. Neither
the existing rawInput path nor JSON-thought hoisting fires, so the
UI shows "Input: null" alongside a perfectly readable title like
"README.md" or "ls -la /tmp".
Add a conservative fallback that maps known kinds to a minimal
input shape:
read -> { file_path: title }
execute -> { command: title }
search -> { pattern: title }
think -> null (topic-update prose has no clean arg mapping)
unknown -> null (no guessing on shapes we have not verified)
Priority: rawInput > hoisted JSON thought > kind+title derive.
Lock the new behaviour with synthetic unit tests (8 cases) and a
real-Gemini fixture suite captured from gemini-3-flash-preview
and gemini-2.5-flash via ACP stdio (4 fixtures, 33/27/13/4 raw
sessionUpdate events). The fixtures double as regression guards
against future ACP handler changes.
* fix(web): suppress duplicate subtitle when equal to tool title
Gemini ACP emits a tool_call whose title field is a human-readable
summary (often the verbatim shell command or file path). Combined with
the kind+title input fallback, an unknown-tool card ends up with the
same string in both the title and subtitle slots — e.g. title
"cat /tmp/hello.txt" over subtitle "cat /tmp/hello.txt".
Add a guard in getToolPresentation's unknown-tool branch: emit
subtitle only when it differs from toolName. The known-tool and
mcp__* branches are unaffected.
* test(acp): align Gemini fixtures to current model set
- Drop gemini-2.5-flash fixtures: the captures came from a model that
is not part of the PR's evidence model set, and re-running the
capture is gated on quota that is not currently available.
- Refresh gemini-3-flash-preview read_file / run_shell fixtures with
a fresh live capture so they reflect the latest ACP shape (e.g.
a `kind: think` tool_call expressing reasoning when the model emits
no agent_thought_chunk).
- Update fixture-replay expectations: read_file no longer requires
reasoning chunks (zero are emitted on this path) and now requires
>= 2 tool_calls (think + read).
* feat(web): promote semantic title for Gemini ACP tool cards
When the unknown-tool ToolCard would render the same string as both
the title and the subtitle, promote a semantic label to the title
slot so the card reads like a sentence:
cat /tmp/hello.txt → Run shell / cat /tmp/hello.txt
README.md → Read file / README.md
*.ts → Search / *.ts
This is a web-only ergonomic change; the underlying ACP message
shape (tool_name = title, input = derived from kind+title) is
unchanged. Builds on the dedup guard so the title-equals-subtitle
case is now handled by promotion rather than by hiding the subtitle.
* fix(acp): derive tool_call.input for kind=edit from locations[0].path
Gemini's write_file and replace tools both surface as ACP tool_call
with kind="edit" and rawInput omitted. The path lives on locations[0]
from the very first event; the title is prose like "Writing to foo.txt"
or "foo.txt: old => new", which is not safely usable as a file_path.
Extend the kind+title fallback to read locations[0].path when kind is
"edit", and synthesize { file_path } from it. Title fallback is
intentionally not used here so we never feed prose into file_path.
Lock the behaviour in with two new fixtures captured live from
gemini-3-flash-preview (write_file and replace) plus two synthetic
unit tests covering the locations-present and locations-empty paths.
* test(acp): add gemini-3.1-pro-preview fixtures for regression coverage
Captured 4 raw ACP `sessionUpdate` sequences from a live
`gemini-3.1-pro-preview` session via the same isolated hub +
runner + spawn pattern used for the existing flash captures
(read_file 31 events / run_shell 83 events / write_file 4 events /
edit_file 11 events).
The pro tier reuses the same kind/title shape as flash:
`rawInput` is omitted on every tool_call across read / execute /
edit kinds, so the kind+title (and locations[0].path for edit)
fallback is exactly what derives the modal Input. Locking these
fixtures in guards against future regressions on a second model.
The fixture-based regression test gains 4 entries (read / shell /
write / edit) mirroring the flash matrix; assertions are unchanged.
ACP handler suite: 53 -> 57 pass.
This commit is contained in:
@@ -201,6 +201,11 @@ export default {
|
||||
'tool.trace': 'Trace',
|
||||
'tool.trace.callsSuffix': 'calls',
|
||||
'tool.result': 'Result',
|
||||
'tool.semanticTitle.readFile': 'Read file',
|
||||
'tool.semanticTitle.runShell': 'Run shell',
|
||||
'tool.semanticTitle.search': 'Search',
|
||||
'tool.semanticTitle.openUrl': 'Open URL',
|
||||
'tool.semanticTitle.query': 'Query',
|
||||
'tool.questionsAnswers': 'Questions & Answers',
|
||||
'tool.submit': 'Submit',
|
||||
'tool.submitting': 'Submitting…',
|
||||
|
||||
@@ -203,6 +203,11 @@ export default {
|
||||
'tool.trace': '追踪',
|
||||
'tool.trace.callsSuffix': '次调用',
|
||||
'tool.result': '结果',
|
||||
'tool.semanticTitle.readFile': '读取文件',
|
||||
'tool.semanticTitle.runShell': '运行命令',
|
||||
'tool.semanticTitle.search': '搜索',
|
||||
'tool.semanticTitle.openUrl': '打开链接',
|
||||
'tool.semanticTitle.query': '查询',
|
||||
'tool.questionsAnswers': '问答',
|
||||
'tool.submit': '提交',
|
||||
'tool.submitting': '提交中…',
|
||||
|
||||
Reference in New Issue
Block a user