Draw the treemap into the conversation an agent is in - #2970
Open
pyricau wants to merge 1 commit into
Open
Conversation
Every other tool answers a model in words, which is the right shape for a model and the wrong one for a treemap: "the biggest thing is a 42 MB bitmap cache" is a sentence, and the picture is a hundred rectangles with the cache filling a quarter of them. `draw_treemap` puts that picture where the person is — the same layout, colours and leak shading the window draws, over the same dominator tree. It is a Remote Compose document rather than an image, so the rectangles carry click areas: pressing one is the client asking this server for the treemap under that address, and the title strip leads back up. Nobody's model is in that loop, so walking into the heap costs one round trip and no tokens. The agent finds out where somebody got to through `ui/update-model-context`, which a host defers until the next message. And the drawing is an MCP resource, never a tool result. Measured, one whole-heap-dump drawing of `large-dump.hprof` at 900x560 is 456 KB, 608 KB as base64 — a tool result would put that in the context of a model that cannot see it. The URI says which heap dump, which object and what size, so the page fetches at its own canvas size and lays out again rather than scaling when it is dragged, and the session stays stateless. The player is vendored because it is not published, built from a pinned commit with a command that reproduces it byte for byte; the licence file beside it carries the provenance. `shark/shark-dive/notes/remote-compose.md` has what the writer cannot do, the player's traps and the measurements. `HeapDominatorTreemap.parentOf` is new and is what "up" needed: the root's children are gathered into piles by class, so an object nothing in particular holds is drawn inside a pile while its dominator is the whole heap dump, and a pile has no dominator at all. Going up by the dominator was a drawing of a pile with no way out of it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
draw_treemap: the treemap itself, drawn where the person is talking to their agent, and pressablefrom there.
Every other tool on this surface answers a model in words, which is the right shape for a model and the
wrong one for a treemap — "the biggest thing is a 42 MB bitmap cache" is a sentence, and the picture is a
hundred rectangles with the cache filling a quarter of them. This one draws the same layout, colours and
leak shading the window draws, over the same dominator tree.
It is a document, not an image
The drawing is Remote Compose,
played on a canvas by a page this server hands to an MCP Apps host. Which
buys the part worth having: the rectangles carry click areas, so pressing one is the client asking Shark
Dive for the treemap under that address, and the title strip leads back up. No model in that loop, no image
round trip, no token per step — one
resources/read, tens of milliseconds. The agent finds out where somebodygot to through
ui/update-model-context, which a host defers until the next user message, i.e. exactly whenit needs to know.
The agent cannot see the drawing, so this replaces nothing it would have read:
dominator_treeis thesame tree in words and is still the one to call. The tool description says so.
A resource, never a tool result
McpSession.toolResultpretty-prints an answer into the text a model reads. Measured on this branch:leak_asynctask_o.hprof, 7.8 MBlarge-dump.hprof, 39 MBSo a tool result would be a quarter to two thirds of a megabyte of base64 in the context of a model that
cannot see it. As a resource it goes from the server to the page, and the model is told a URI.
And the URI says everything —
shark-dive://treemap/{heapDump}/{object}?width&height— which is what keepsthe session stateless: a model never knows the size of the client's canvas, so
draw_treemapdraws nothingitself. It checks the object is a node of the tree and hands back a URI; the page fetches at the size it
measured, refetches rooted elsewhere when somebody presses, and lays out again rather than scaling when the
panel is dragged.
Where up leads
A player has no history and no chrome of ours to put a button in, so the title carries a click area and reads
↑ <where you are>. That neededHeapDominatorTreemap.parentOf: the root's children are gathered into pilesby class, so an object nothing in particular holds is drawn inside a pile while its dominator is the whole
heap dump, and a pile has no dominator at all. Going up by the dominator was a drawing of a pile with no way
out of it — found by pressing one in a browser, not by reading the code.
The player is vendored, reproducibly
remote-compose-player.js, 422,192 bytes, built fromcamaelon/remotecompose-experiments at
2168eb72a5f2de29029e9e3d88daf9c3abcf5a2a, Apache 2.0, byte identical on a rebuild. Vendored because theupstream package is
private: true— there is nothing to depend on — and because a page that fetched aplayer over the network would need a CSP allowing a domain, which is the one thing this app doesn't ask a
host for. Provenance, the exact build command and the licence are in
remote-compose-player.LICENSE.txt.Testing
TreemapDocumentTestwrites a real document from a synthetic dump and inflates it back, asserting the twothings the navigation is built on: every named rectangle carries the address a press leads to, and the
title carries the way back out (and doesn't, for the whole heap dump).
HeapDiveTestcoversparentOfwhere it differs fromdominatorOf.McpSessionTestcovers the page being served with the player in it, the drawings being a template, adrawing read as bytes at the size asked for, and an unknown URI being a resource error rather than an
internal one.
--no-ui --mcp-stdioserver: ahost page around the served HTML,
resources/readforwarded back to Shark Dive, rectangles pressed overCDP. What that ends with is a second
resources/readat a different address and aui/update-model-contextnaming it — a drawing that renders and doesn't navigate looks perfect in a screenshot.
./gradlew buildis green.shark/shark-dive/notes/remote-compose.mdhas the rest: what the writer can't do (it cannot measure text,there are no brushes), the player's traps (the canvas is 256×256 until told otherwise; click areas arrive on
a different channel from named actions), and how to read a document back in a test.
🤖 Generated with Claude Code