mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
c573e9c524
## Why Telemetry cannot tell a physical iPhone from a simulator: `platform` is `ios` for both, and the tool-server's `kind` never reaches the events. Physical-iOS support shipped in 0.24.0 with no way to count its adoption except through `ios_device_*` failure stages on `tool:fail`. Physical Android phones have the same gap. ## What `tool:invoke` / `tool:complete` / `tool:fail` gain `device_kind: simulator | emulator | vvd | device | app`, derived from the same device id as `platform` (single classifier in `telemetry-platform.ts`, which `debugger:tool_outcome` and `lens:*` also go through for their platform). `platform: ios` + `device_kind: device` is a physical iPhone. The kind is emitted only for positively recognised id shapes, so the metric is not polluted by ids that are not devices: | id shape | platform | device_kind | |---|---|---| | simulator UUID / `remote:` | ios / ios-remote (tvos when the cache is warm) | simulator | | physical iPhone UDID | ios | device | | `emulator-NNNN` | android (android-tv when warm) | emulator | | USB serial, 6–20 alnum with a digit and a letter | android | device | | non-loopback `ip:port` | android | device (best effort) | | loopback `127.0.0.1:port` / `localhost:port` / `::1:port` | android | emulator | | anything else non-iOS-shaped (Metro 40-hex handle, `booted`, `Pixel_7`, IPv6, mDNS) | android | omitted | | `chromium-cdp-<port>` / `amazon-…` | chromium / vega | app / vvd | | `avdName`-only boot | android | omitted | A child sub-tool that names its own device replaces the parent's platform, kind and provider label wholesale, so an `avdName`-only child never reports the parent's `simulator` next to its own `android`. A logical-keyed Metro session strips the kind. `unknown` (in the registry `DeviceKind`) is rejected by the sanitizer like `platform: "unknown"`; no registry change. ## Querying - Physical iPhone adoption: `tool:invoke` where `platform = 'ios' and device_kind = 'device'`, distinct users over it. `isIosPhysicalUdid` is a positive match, so invoke is clean. - Physical Android: count on `tool:complete` (the device answered). `tool:invoke` / `tool:fail` still carry residual mistakes that happen to look like a serial (`Pixel7`), and a non-loopback `ip:port` may be a networked virtual device. ## Docs - `Telemetry.md` → 1.04, effective date 7 September 2026 (adjust to the release date on merge). Discloses the new category and, while touching that section, the external-provider label that #735 added without a notice line — happy to drop that second bullet if it should ship separately. - No docs-site change: `docs/reference/telemetry.mdx` links to `Telemetry.md` and has no per-property list. ## Notes - Pre-existing, unchanged: HTTP-edge failures carry no `tool_invocation_id`; `stop-all-simulator-servers` declares no capability, so its invoke row has neither field while its fail row has both. `device_kind` follows `platform` in each site. - Legacy 40-hex iPhone UDIDs already classify as `android` today; they now get no kind rather than a wrong one. - Design went through three adversarial review rounds before implementation and one on the diff; the fallback-bucket and child-attribution rules above came out of those. ## Verification - `packages/telemetry`: 319 tests, `packages/tool-server`: full suite 5670 passed / 1 skipped; `typecheck:tests` both packages; `eslint --max-warnings 0`; `knip`; prettier. - New: `test/telemetry-device-kind.test.ts` (the shape table above), plus cases in `http-tools-meta`, `http-platform-alias`, `sanitize`, `registry-listener`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BSGpeAwvoCv2JAhbLsuto2 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Telemetry now records a device category—such as simulator, emulator, physical device, virtual device, or desktop app—alongside the platform. * Device categories are inferred from device information and may include a short label for external device providers. * Device category information is included in tool invocation, completion, and failure telemetry when available. * Device identifiers themselves continue to be excluded from telemetry. * **Documentation** * Updated the Argent Privacy Notice (Telemetry) to version 1.04, effective 7 September 2026. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
69 lines
3.3 KiB
TypeScript
69 lines
3.3 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
attributeDeviceForTelemetry,
|
|
classifyDeviceForTelemetry,
|
|
} from "../src/utils/telemetry-platform";
|
|
|
|
// Cache-only readers: cold here, so every platform stays coarse and the kind can
|
|
// be pinned independently of the TV refinement.
|
|
vi.mock("../src/utils/ios-devices", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../src/utils/ios-devices")>();
|
|
return { ...actual, getCachedSimulatorRuntimeKind: () => undefined };
|
|
});
|
|
vi.mock("../src/utils/adb", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../src/utils/adb")>();
|
|
return { ...actual, getCachedAndroidRuntimeKind: () => undefined };
|
|
});
|
|
|
|
const SIM_UDID = "11111111-1111-1111-1111-111111111111";
|
|
const PHYSICAL_UDID = "00008030-000A1B2C3D4E5F60";
|
|
const METRO_HANDLE = "8b9223b1392be193fa9058e0cef5cefb2bddeb68";
|
|
|
|
describe("attributeDeviceForTelemetry", () => {
|
|
it.each([
|
|
// Positive shapes `resolveDevice` already recognises.
|
|
[SIM_UDID, { platform: "ios", device_kind: "simulator" }],
|
|
[PHYSICAL_UDID, { platform: "ios", device_kind: "device" }],
|
|
[`remote:${SIM_UDID}`, { platform: "ios-remote", device_kind: "simulator" }],
|
|
["emulator-5554", { platform: "android", device_kind: "emulator" }],
|
|
["chromium-cdp-9222", { platform: "chromium", device_kind: "app" }],
|
|
["amazon-1a2b3c", { platform: "vega", device_kind: "vvd" }],
|
|
// Android hardware: USB serials with a digit and a letter, non-loopback ip:port.
|
|
["R5CT12345678", { platform: "android", device_kind: "device" }],
|
|
["HT82A0203045", { platform: "android", device_kind: "device" }],
|
|
["192.168.1.5:5555", { platform: "android", device_kind: "device" }],
|
|
// Loopback ip:port is an emulator console slot (adb.ts consolePortFromAdbSerial).
|
|
["127.0.0.1:5555", { platform: "android", device_kind: "emulator" }],
|
|
["localhost:5555", { platform: "android", device_kind: "emulator" }],
|
|
["::1:5555", { platform: "android", device_kind: "emulator" }],
|
|
// `ext:` ids attribute by their native id.
|
|
[`ext:acme-3f2a9c:${PHYSICAL_UDID}`, { platform: "ios", device_kind: "device" }],
|
|
["ext:acme-3f2a9c:emulator-5554", { platform: "android", device_kind: "emulator" }],
|
|
["ext:acme-3f2a9c:R5CT12345678", { platform: "android", device_kind: "device" }],
|
|
])("%s → %o", (id, expected) => {
|
|
expect(attributeDeviceForTelemetry(id)).toEqual(expected);
|
|
});
|
|
|
|
it.each([
|
|
// `resolveDevice` sends every one of these to android/device; none is hardware.
|
|
METRO_HANDLE, // 40-hex Metro logicalDeviceId, also a legacy iPhone UDID shape
|
|
"oops",
|
|
"booted",
|
|
"Pixel_7",
|
|
"abcdefgh",
|
|
"1234567890",
|
|
"[fe80::1]:5555",
|
|
"pixel.local:5555",
|
|
"adb-R5CT12345678-AbCdEf._adb-tls-connect._tcp.",
|
|
"0123456789ABCDEF0123456789ABCDEF", // 32-hex, over the 20-char cap
|
|
])("%s carries the fallback platform but no kind", (id) => {
|
|
expect(attributeDeviceForTelemetry(id)).toEqual({ platform: "android" });
|
|
});
|
|
|
|
it("classifyDeviceForTelemetry is the platform half of the same classifier", () => {
|
|
for (const id of [SIM_UDID, PHYSICAL_UDID, "emulator-5554", "R5CT12345678", METRO_HANDLE]) {
|
|
expect(classifyDeviceForTelemetry(id)).toBe(attributeDeviceForTelemetry(id).platform);
|
|
}
|
|
});
|
|
});
|