Skip to content

Send compact tool payloads to the UI; fetch full output on demand - #222

Merged
juacker merged 26 commits into
mainfrom
feat/lightweight-tool-display
Sep 27, 2026
Merged

juacker merged 26 commits into
mainfrom
feat/lightweight-tool-display

Conversation

@juacker

@juacker juacker commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Every tool event sent the whole tool output twice: once in ToolCallCompleted.result and again in the payload of the tool-role message, which nothing reads. It also sent the full input twice, in params and in tool_use.arguments, and resent it on every message update. Cold page loads carried the same duplicates, even though tool cards start collapsed.

Change

  • Backend projection (src-tauri/src/assistant/display.rs): UI events (emit_event) and the load and list commands now send compact tool data: name, compact input, a resultSummary, and the flags hasFullResult and hasFullInput. Tool-role message payloads are sent as null. Chart and workspace-task cards keep the metadata they need, so they still render right away.
  • Stored and model data stay full. Only what is sent to the UI is compacted.
  • On-demand reads: new commands assistant_get_tool_call_result and assistant_get_tool_call_input look up a call within its own session only.
  • Frontend: the new src/assistant/toolCallResult.ts hook fetches output and input when a row is expanded and keeps a small cache (about 30 entries). A row shows loading, a retry on error, or "no longer available". The old frontend summarizeToolResult is removed; summaries now come from the backend.

Testing

  • Rust: cargo fmt --check and cargo clippy --all-targets -D warnings pass. The focused display, tool, repository and run_lifecycle tests pass.
  • Frontend: vitest for toolCallResult and AssistantChat (80 tests), eslint and tsc pass.
  • Two independent reviews (frontend and backend) both returned production_quality, and their minor findings are fixed on this branch.

Known limitations

  • A tool_use with no matching tool-call row, such as during a brief stream race, shows only the compact input.
  • Payload and CPU savings have not been measured yet.

Chart display (added)

  • Charts showed twice in chat: once as the tool's own card and again from the markdown embed the model writes. Models set display almost every time, so the option didn't prevent the duplicate. The embed is now the only way a chart shows in chat.
  • create_vega_chart no longer has a display parameter. The tool description and the system prompt now tell the model to embed the returned markdown once, where the chart belongs.
  • A chart call is shown as a normal tool row, like any other tool. The chart itself only appears where the model embeds it.
  • The compact chart result is now {ok, path}.

Rows render the backend's compact projection of each call. Expanding a
row fetches the full result for the Output tab and, when the call's input
was projected, the full input for the Input tab and for Output views that
draw from it (bash command, ask_user question, fs_write content).
The chart is the result, so like a task card it takes the call's place
in the message flow: no tool row, no Output/Input tabs, no result fetch.
Running and failed chart calls still render as a normal tool row, and a
failed one stays expandable to show its error.
The reply's markdown embed is now the only place a chart is drawn in chat,
so the create_vega_chart call no longer renders a chart card. A successful
call is a compact row (Chart <title> ... <path>) whose click hands the
saved path to the page's onOpenArtifact; no Output/Input tabs, no result
fetch. Running and failed calls stay normal rows, and without an
onOpenArtifact (read-only views) the row expands as usual.

The path comes from the result, falling back to params; a legacy
`display` field in old results is ignored.
Workspace passes onOpenArtifact to the chat: it opens the artifacts
drawer with the file previewed, reusing the synthesized entry that
in-preview link navigation already builds.
…_chart

Drop the display parameter and result field; the tool description and
system prompt now tell the model to embed the returned markdown to show
the chart.
The backend compacts create_vega_chart params to {title} and the result
always carries the saved path, so the params fallback and slash strip
were unreachable.
A chart row opened every path as an artifact, so a chart under
.clai/memory/ got the artifact-only delete button.
A chart under .clai/memory/ opens in the memories drawer, so the row no
longer claims it opens in artifacts.
onOpenArtifact/openArtifactPath now open memories too; rename them to
onOpenFile/openWorkspaceFile, fix the comments that still said
"artifacts panel", and cover a preview link to an unlisted memory file.
The backend rejects on a missing file rather than resolving null, so
the default mock now rejects and fixtures return a WorkspaceFileContent.
Fixes the CI typecheck of Workspace.test.tsx.
A create_vega_chart call is now a plain expandable tool row again; the
special saved-chart row, its "Open chart" action and chartArtifactPath
are gone. The file opener moves from the task-card context to
WorkspaceFileContext, where an embedded .vl.json chart reads it: every
file chart gets a slim header with its title (alt text, else the file
name) and an "Open ↗" button that opens the resolved workspace path in
the memories or artifacts drawer. Views without an opener (task
transcripts, the file preview drawer) show the title only.

The chat passes the opener through a ref so a page handler that changes
identity on a memory refresh does not re-read every embedded chart.
Embedded .vl.json charts render as they did before 593f5ec: no title
header and no "Open ↗" button. The create_vega_chart call stays a plain
expandable tool row.

With nothing left to call it, the chat's file opener goes too: the
onOpenFile field on WorkspaceFileContext, the ChatMessageList prop and
ref, and openWorkspaceFile in the Workspace page. previewEntryForPath
stays for link navigation inside a previewed file; its unlisted-memory
test now opens the preview from the memories panel.
@juacker
juacker marked this pull request as ready for review September 27, 2026 21:03
@juacker
juacker merged commit 3dbd30e into main Sep 27, 2026
2 checks passed
@juacker
juacker deleted the feat/lightweight-tool-display branch September 27, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant