feat: find/get digest response-views + batch-step elision — Phase 4#955
Conversation
Add opt-in leveled response views for the find and get selector reads and elide intermediate batch steps to digest, completing the two remaining Phase 4 agent-cost grafts. All additions activate only when a non-default responseLevel (digest/full) is requested; the default wire shape is byte-identical to today (Maestro .ad recompare safe). - response-views: register a shared selectorReadView under find and get. A text read keeps ref/selector + text and drops the redundant verbose node; an attrs read keeps a compacted node (semantic attributes only, geometry/index/process plumbing dropped); exists/wait/click keep their cheap actionable signals. default/full return today's shape unchanged. - batch: when a non-default level is requested, intermediate steps are forced to digest while the final step keeps the requested level. With no responseLevel the per-step meta is passed through unchanged. - tests mirror the existing response-views / response-level suites.
Size Report
Startup median (7 runs, lower is better):
Top changed chunks:
|
|
I don’t think this is ready yet. The new |
Review feedback on #955: `find` is registered command-wide, but `find fill/focus/type` return the underlying INTERACTION response, which can carry cheap, agent-critical signals (notably `warning` from Android blocking-dialog recovery, plus `message`). The previous allowlist-based digest silently dropped those under --level digest. The only token sink in a find/get result is the verbose matched snapshot `node`, which appears solely on a selector READ (text/attrs). The view is now conservative: it acts ONLY on a result carrying such a node and otherwise returns the data UNCHANGED, so node-less shapes (exists/wait/click and the fill/focus/type interaction responses) are never narrowed. For a text read the redundant node is dropped; for an attrs read the node is compacted; in both cases every other cheap field (e.g. `warning`) is preserved verbatim. Adds a regression test asserting a `find fill` response carrying a `warning` is returned unchanged under digest.
|
Good catch — fixed in dcedd0e. The bug was that Fix: the view is now conservative. The only token sink in a
I confirmed against Added a regression test: a Local gates re-run: tsc, oxlint --deny-warnings, oxfmt --check, rslib build, fallow audit (no issues in changed files), and vitest (response-views + router-response-level + batch) all pass — 32 tests. |
|
Re-reviewed dcedd0e after the warning-loss fix. The response view now only acts on selector-read payloads carrying a snapshot node; node-less interaction shapes pass through unchanged, and text/attrs keep non-node cheap fields. Batch response-level routing still preserves default meta and elides only intermediate non-default steps. Checks are green. No remaining blockers; marking ready-for-human. |
|
What
Completes the two remaining Phase 4 agent-cost grafts (plans/perfect-shape.md §5.4):
find/getdigest response-views — a token-cheapdigestview registered for both selector reads.digestwhile the final step returns at the requested level.Both are strictly opt-in: they only activate when a non-default
responseLevel(digest/full) is requested. Thedefaultwire shape is byte-identical to today.Why
North-star goal #2 (fast + cheap for AI agents): leveled/digest payloads cut tokens, and a multi-step batch collapses its intermediate noise.
find/getare the last high-token selector reads without a digest (they carry a full matchednodeon the wire); batch is the canonical N-round-trips-into-1 path where intermediate snapshot/find/get steps dominate the response.How
find/getdigest (src/daemon/response-views.ts)findandgetshare a wire shape (ref/selector+textfor a text read,+ nodefor an attrs read, plusfound/waitedMs/coordinate signals), so a singleselectorReadViewis registered under both command names (mirrors the existingsnapshotView/screenshotViewpattern):ref/selector+text, drops the redundant verbosenode(thetextis the answer).role/type/subrole/label/value/identifier/enabled/selected/focused/hittable); the token sink — geometry (rect), tree indices (index/parentIndex/depth), and process/app plumbing (pid/bundleId/appName/windowTitle/surface/…) — is dropped.found/waitedMs/locator/query/x/y).defaultandfullreturn today's shape unchanged (same reference).(Note: on the success path
findmatches at most one element — ambiguity is anAMBIGUOUS_MATCHerror, not success data — so there is no count/unique field to surface; the digest keeps the single matched ref + label/text.)Batch-step elision (
src/core/batch.ts)When
req.meta.responseLevelis a non-default level,runBatchStepoverrides the per-stepresponseLeveltodigestfor every step except the last, which keeps the requested level. The existing per-command views (snapshot/screenshot/find/get) then collapse those intermediate steps automatically.Byte-identical-default safety argument
applyResponseLevelView(request-router) only invokes a registered view whenresponseLevelisdigest/full; fordefault/absent it returns the response untouched.selectorReadViewitself early-returns the originaldatareference for any non-digestlevel. Sofind/getdefaultoutput is unchanged.batchStepMetareturns the samereq.metareference unless a non-default level was requested (gated onisNonDefaultResponseLevel). With noresponseLevel(ordefault) every step is invoked with identical meta — byte-for-byte today's behavior. A unit test asserts the default path passes[undefined, undefined, undefined]through.Verification (local)
tsc -p tsconfig.json: passoxlint . --deny-warnings: passoxfmt --check: passrslib build: passaudit --base origin/main: pass (No issues in changed files)