mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
v0.20.6
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
58a9d4bf71 |
refactor(daemon): consolidate surface-evidence helpers, delete the wording heuristic (#1615)
Re-derived onto #1633's split (`post-gesture-stabilization.ts` became `post-gesture-stability.ts` + `deferred-interaction-outcome.ts` + `gesture-no-effect.ts`). None of this had been subsumed by that refactor — it relocated the code and carried every one of these forward untouched. - Three copies of the four-field rect comparison in `interaction-outcome-policy.ts` become one `rectsWithinTolerance`. - `identifiedContent` returns the entry instead of `{ entry }`, dropping the `.entry` indirection at every call site. - `haveIdenticalDiscriminatingSurfaces` records why it keys on `key` while `classifyBaselineSurfaceEvidence` keys on `identity` — opposite choices, once, at the function that makes the stricter one. - `SnapshotCaptureBackend` names the capture-strategy union in kernel/snapshot beside `SnapshotBackend`, and `PostGestureStabilization.baselineBackend` uses it instead of `string`, closing the silent-typo gap on the comparison the field exists for. Deliberately NOT applied to `post-gesture-stability.ts`: #1633 made that module generic over the surface type, and `backend: string` is right for an interface that must not know about iOS capture strategies. - `formatGestureNoEffectWarning` echoes positionals verbatim. The `/^[\d.-]+$/` filter it replaces ate all four coordinates of `swipe <x1> <y1> <x2> <y2>` and emitted a contentless bare "swipe"; the warning names the gesture the agent issued, and `scroll down 1` is what they issued. - `PostGestureStabilization.positionals` is required — its only writer always sets it, so the `?? []` at the read site guarded an impossible state. Tightening it caught six test fixtures building the state directly. Red evidence: restoring the numeric filter fails the wording test ("scroll down 1 produced no visible change"); 95 files / 757 tests green with it deleted. |
||
|
|
a67c72c211 |
fix(ios): pin tap-outcome corroboration probes to the baseline's backend (#1634)
* fix(ios): pin tap-outcome corroboration probes to the baseline's backend The recorded-failure screens are exactly where the capture plan flips between XCTest and private-AX (the penalty boundary), so #1605's same-backend requirement failed closed right where XCTest tap false negatives actually happen: the baseline was captured via private-AX under penalty, the probe came back via tree, and a landed tap surfaced as XCTEST_RECORDED_FAILURE. In the AppControlBench bsky-16 run this fired four times, each sending the model into a re-observe/retry spiral. The comparison stays same-backend by design (backends are not comparable views of a screen); instead the probe is now CAPTURED the way its baseline was: a new internal preferredBackend option (never CLI-exposed) threads daemon -> runner, and a private-AX-preferred capture takes the exact penalized route — privateAX-first plan, 'deferred' verdict, no degradation warning, no settle budget reset. Live-verified on the deterministic repro (Bluesky drawer-menu press under penalty, seeded bench feed): errored with the backend-mismatch diagnostic before, corroborates as landed after, with no mismatch phase in the request diagnostics. Daemon tests cover pinned and unpinned baselines end to end through the dispatch context; the Swift plan gate is a pure function with an executed in-bundle test (added to the ios.yml regression list). * style: oxfmt * fix: exclude raw baselines from corroboration and prove the pin end to end (review) Raw baselines could not be pinned: the raw diagnostic plan keeps tree-first error propagation by contract and is never rerouted by the penalty or the preferred backend, so preserving 'raw: true' on the probe recreated exactly the backend-mismatch false failure this PR removes. Corroboration now declines raw baselines up front (they are diagnostics, not evidence baselines) with a regression pinning that no probe capture is dispatched at all. The wire is now regression-proven at every hop: a dispatch-level test drives dispatchCommand with the context flag and asserts the emitted RunnerCommand carries preferredBackend (red if handleSnapshotCommand or the interactor stops forwarding); the injected-transport test asserts the interactor's snapshot payload both ways; and a runner unit test decodes the wire JSON, projects it through the extracted snapshotOptions(from:), and composes it with the plan rule — pinned regular plan defers to privateAX-first, RAW plan stays untouched. Executed on-simulator; added to the ios.yml regression list. |
||
|
|
8d526f2402 |
perf(ios): halve hostile-screen capture cost under the XCTest-channel penalty (#1587)
* perf(ios): stop re-paying known-failing work on penalized private-AX captures Live-measured on the Bluesky bench feed (139-171 nodes), each private-AX capture wasted ~1.35s of its ~1.65s runner-side cost re-doing work a prior capture already proved futile: - ~1000ms: the viewport read is XCTest main-thread work; under the channel penalty it reliably burns its full timeout and falls back to the root frame anyway. Honor the penalty in privateAXSnapshotViewport the same way capture plans do. - ~310ms: the depth ladder re-paid the kAXErrorIllegalArgument rejection of the default depth on every capture. Remember the accepted rung per bundle (penalty-duration TTL, cleared on target process change); expiry re-probes the full depth so screens that recover are not capped forever. Explicit --depth requests bypass the memory in both directions. Steady-state hostile-screen captures drop 2.65s -> ~0.55s CLI wall, and press --settle round-trips drop ~5.4s -> ~2s (settle needs two captures). * fix(snapshot): distinguish penalty-deferred captures from genuine recoveries A capture whose backend was PRE-selected by the XCTest-channel penalty was stamped with the same recovered verdict as one that ground through a live failure. Two costs followed on hostile screens (Bluesky bench: 130 repeats per 30-task run): - the daemon repeated the full fell-back warning on every capture, long after the arming capture had already said it once, and - settle's one-shot private-AX budget reset fired on every loop even though the capture paid no grind to give the budget back for. The deferred plan now stamps reasonCode 'deferred' (a new code; older daemons drop unknown codes and keep today's behavior). The daemon keeps the verdict recovered but suppresses the repeated warning, keeps the depth-cap line, and skips the settle budget reset for deferred captures. * style: wrap reasonCode union to satisfy oxfmt * fix(ios): bind accepted-depth memory to the process, pin deferred through the wire parser Review follow-up (#1587): - The depth memory was cleared only in refreshCachedTargetIfProcessChanged; resetTargetAfterExternalRelaunch -> invalidateCachedTarget drops the cached PID without clearing it, so the next activation had no old PID to compare and could reuse a stale shallow rung for up to 120s (also A->B->A when A restarted while inactive). The memory now stores the PID it was learned under and only matches the same live process; recording without a PID is refused. Every invalidation path is covered automatically because they all drop currentAppProcessIdentifier. - The deferred settle/warning tests constructed typed verdicts directly, so removing 'deferred' from the accepted reason-code set would silently restore the repeated warning and budget reset while tests stayed green. They now parse a raw runner-wire object through readSnapshotQualityVerdict (red on base: parser strips the code -> warning re-appears, reset fires). - Extracted shouldReadPrivateAXViewportViaXCTest() and pinned the penalized viewport skip with an in-bundle regression test. |
||
|
|
bcaa106845 |
refactor: extract snapshot and replay identity semantics (#1582)
* refactor: extract snapshot and replay identity semantics * refactor: move pure rect primitives from contracts to kernel/rect containsPoint, pickLargestRect, and isRectVisibleInViewport are raw rectangle arithmetic with no snapshot awareness, so they belong beside rectContains/rectArea in @agent-device/kernel/rect rather than in the snapshot-semantics vocabulary. The node-aware resolveViewportRect folds into contracts/snapshot-visibility.ts, retiring snapshot-geometry.ts; after this split, everything behavioral in @agent-device/contracts/snapshot is policy that interprets the snapshot model. * refactor: restore ADR-0012 rationale docs and dedupe replay identity shapes The #1478/#1581 extraction moved the identity/structural helpers but compressed their invariant documentation to one-liners; the deliberate no-ancestry-exclusion rule on idMatchCountInTree, the fail-closed guard comparison, and the who-throws/who-detects contracts on the two divergence reason markers now travel with their definitions again. LocalIdentity and NodeStructuralDenotation move to contracts/target-annotation.ts (beside TargetAncestryEntry, which WaitLandmarkMismatchEvidence now references directly), so the guard shapes in contracts/replay.ts are nominal instead of hand-rolled structural twins; ad-script re-exports the vocabulary beside the readers that produce it. Also inlines the demoteNonUniqueId pass-through wrapper in session-target-evidence.ts. * style: fix oxfmt formatting in snapshot-visibility * refactor: move findSnapshotAncestor into contracts/snapshot-tree The last root value import from src/selectors: predicates.ts reached src/snapshot/snapshot-processing.ts for the ancestor walker. The walker is index-based tree traversal with no presentation policy, so it joins buildSnapshotNodeMap in contracts/snapshot-tree.ts; both consumers repoint to the façade and the non-contiguous-index/cycle coverage moves to the package test. src/selectors now has zero value imports from root src in production files. |
||
|
|
47540f1f07 | refactor: consolidate daemon errors (#1496) | ||
|
|
76453add71 |
refactor: pnpm workspace + @agent-device/kernel pilot (#1490 W0) (#1494)
* refactor: pnpm workspace + @agent-device/kernel pilot (#1490 W0) Extend the workspace with packages/* and move the kernel behind an enforced public API: packages/kernel with nine consumer-earned subpath exports (errors, device, snapshot, contracts, collections, rect, redaction, daemon-error, bounds — the last absorbed from utils as Rect vocabulary). Every kernel import repo-wide becomes the @agent-device/kernel/<sub> specifier; kernel tests move to src/__tests__/kernel/ and exercise the package surface. The root declares the package in devDependencies (workspace:*), tsdown bundles it (noExternal) so the published artifact and its runtime dependency manifest are unchanged. Gate rewiring in the same change, per the W0 brief: - R1 kernel-sink retires (physically subsumed); new R11 package-boundaries guards no-root-back-imports, relative tunnelling past exports maps, undeclared workspace deps, and non-exported subpaths, with runtime resolution pins via import.meta.resolve. - resolveImportEdges and mutation ownership follow workspace specifiers through exports maps, keeping R4 cycle checks, depgraph, and derived test ownership connected across the seam (kernel-errors still owns 495 tests). listSourceFiles includes packages/*/src. - kernel becomes an unranked zone; mutation registry, stryker mutate globs, and the mutation-affected workflow path filter move to packages/kernel/src/errors.ts. - check:affected gains packages/ ownership (manifests fail open); vitest and coverage include packages/*/src; fallow ignores packages/** (its resolver cannot follow workspace specifiers). - The affected-selector CI job installs dependencies: its closure now crosses workspace specifiers, and the R8 relative exception is unsafe for production src files (Node ESM does not realpath, so dual specifier/relative loads would instantiate modules twice). The R8 zero-dep set is pinned empty with that rationale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FUv7bvbWNryuXgSBuqTtep * fix: address W0 review — mutation sandbox, exports-map resolution, tsc -b Review findings on #1494, all five: 1. contracts-schema-public.test.ts reads the kernel source at its packages/ path (fs access invisible to the codemod and typecheck). 2. Mutation lane: Stryker sandboxes the tree but pnpm's node_modules symlink resolves @agent-device/* back to the real repo, so mutants in the sandbox never load and vitest.related finds no tests. vitest.mutation.config.ts now aliases each EXPORTED specifier to its source (derived from exports maps, never a wildcard), keeping resolution inside the mutated tree. Validated: kernel-errors module runs end to end (dry run 3,984 tests, mutants killed, exit 0). 3. Layering/depgraph resolve workspace specifiers through the exports-derived map (workspaceSpecifierTargets) instead of reconstructing paths, so '.'-facade packages resolve; the positional fallback remains only for map-less fixtures (P0 pin). 4. Per-package project references implemented: packages/kernel is composite (emitDeclarationOnly -> dist-types, gitignored), the root references it, and typecheck becomes tsc -b — probed to catch type errors on both sides under TypeScript 7 native. 5. R11's relative-route exception now requires membership in an actual R8 zero-dep job closure (zeroDepClosureFiles walks entries), not mere scripts/ placement — closing the dual-instantiation bypass. Also from review discussion: daemon-error moves out of the kernel package to src/client/ — its consumers (cli, client facade) rehydrate wire DaemonErrors client-side; the daemon only produces them. Kernel drops to 8 exported subpaths before any of them ship. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FUv7bvbWNryuXgSBuqTtep * refactor: one exports-map reader for mutation alias and ownership Fallow flagged workspaceExportAliases (cognitive 15, CRAP 90). The manifest-reading logic already exists as workspaceSpecifierTargets in scripts/layering/package-boundaries.ts, so both the Stryker sandbox alias table and the mutation ownership walker now consume it instead of carrying near-clones. Behavior unchanged; mutation suite 45/45 and changed-code fallow green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FUv7bvbWNryuXgSBuqTtep * fix: composite kernel without a root references edge FreeRange runs plain `tsc -p tsconfig.json`, and a root `references` entry makes non-build-mode TypeScript demand the referenced project's built declarations (TS6305) — a standing "build first" tax on every plain -p consumer (fr, editors). Keep the per-package composite project and build it in typecheck (`tsc -b packages/kernel` before the root and examples/sdk passes), but drop the root references edge: root consumption resolves through exports to source, identical to runtime and to the bundler. Probed: plain -p green with no prebuilt output; kernel-side type errors still caught by its own build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FUv7bvbWNryuXgSBuqTtep * fix: R11 uses the layering parser; mutation config is a fallow entry Review blockers on #1494: - R11's private single-quote regex could miss a double-quoted or re-export route into packages/*/src. specifierSites now delegates to the layering model's parseImports (both quote styles, side-effect imports, re-exports, dynamic imports), with direct regressions for each formerly-invisible form. - vitest.mutation.config.ts becomes a declared fallow entry instead of a tolerated unused-file finding: the full-repo audit now reports it reachable (unused files 2 -> 1; the remainder predates this PR). FreeRange clean-checkout evidence: with packages/kernel/dist-types and every *.tsbuildinfo deleted, `pnpm check:freerange` reports 0 findings on this head — the TS6305 topology died with the root references edge in the previous commit; check:freerange has no build precondition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FUv7bvbWNryuXgSBuqTtep --------- Co-authored-by: Claude <noreply@anthropic.com> |