diff --git a/.fallowrc.json b/.fallowrc.json index 121bc33f18..66eb67b5b7 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -65,6 +65,11 @@ "file": "src/platforms/android/perf.ts", "exports": ["sampleAndroidMemoryPerf"] }, + { + "comment": "Apple perf sampling metadata is selected through the same lazy platform host; Fallow cannot follow the dynamic property read in src/platform-runtime-perf-host.ts.", + "file": "src/platforms/apple/core/perf.ts", + "exports": ["buildAppleFrameSamplingMetadata", "buildAppleMemorySamplingMetadata"] + }, { "comment": "Daemon route handlers are reached only through the dynamic `import()` table in request-handler-chain.ts, which --production analysis cannot follow to a consumer.", "file": "src/daemon/handlers/{lease,session,snapshot,react-native,record-trace,find,interaction}.ts", @@ -212,6 +217,13 @@ "rules": { "unused-types": "off" } + }, + { + "comment": "Host-kit seam façades: same false-positive shape as the contracts façades above — each seam names every type its re-exported functions put in a public signature, and callers use those types structurally without importing the name. unused-exports (the VALUE check) stays on.", + "files": ["packages/host-kit/src/*.ts"], + "rules": { + "unused-types": "off" + } } ], "production": { diff --git a/.oxlintrc.json b/.oxlintrc.json index ba1f3d3a0a..c88ec9613b 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -37,7 +37,7 @@ }, "overrides": [ { - "files": ["src/**/*.ts"], + "files": ["src/**/*.ts", "packages/capture-kit/src/**/*.ts"], "rules": { "eslint/no-restricted-imports": [ "error", @@ -45,7 +45,7 @@ "paths": [ { "name": "node:child_process", - "message": "Use process helpers from src/utils/exec.ts instead of importing node:child_process directly." + "message": "Use process helpers from @agent-device/capture-kit/exec instead of importing node:child_process directly." } ] } @@ -53,7 +53,12 @@ } }, { - "files": ["src/utils/exec.ts", "src/**/*.test.ts", "src/**/__tests__/**/*.ts"], + "files": [ + "packages/capture-kit/src/exec.ts", + "packages/capture-kit/src/*.test.ts", + "src/**/*.test.ts", + "src/**/__tests__/**/*.ts" + ], "rules": { "eslint/no-restricted-imports": ["error", { "paths": [] }] } diff --git a/AGENTS.md b/AGENTS.md index 5ec92172d5..45daae9698 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,9 +61,10 @@ Read the declaration rather than maintaining a prose copy: - common command input fields, and which surface may write an input key (model, operator, retired): `src/commands/common-input-fields.ts` and `src/commands/input-audience.ts` -Shared selector parsing and matching belongs in `@agent-device/selectors`; request cancellation and -progress in `src/request`; cross-layer contracts in `src/contracts`; CLI flags in -`src/commands/cli-grammar`; cross-surface schema composition in `src/cli-schema`. +Shared selector parsing and matching belongs in `@agent-device/selectors`; request cancellation +and progress in `@agent-device/capture-kit` (`request-cancel`, `request-progress`); cross-layer +contracts in `src/contracts`; CLI flags in `src/commands/cli-grammar`; cross-surface schema +composition in `src/cli-schema`. The enforced registries are self-declaring. A failing completeness, parity, coverage, timeout, layering, or construction gate means the new cell or path is unclassified; do not suppress or @@ -123,7 +124,7 @@ The OS-neutral Apple runner lives under `packages/platform-apple/src/runner/`. F retry policy, or command typing, start at `runner-contract.ts`; transport stays below session/client behavior, and xctestrun build/cache logic stays outside request execution. -Diagnostics use `src/utils/diagnostics.ts`. Request diagnostics belong in the session request log; +Diagnostics use `@agent-device/capture-kit/diagnostics`. Request diagnostics belong in the session request log; session artifact paths come from `src/daemon/session-store.ts`. App/device logs remain in `app.log`; Apple runner and xcodebuild output remains in `runner.log`. diff --git a/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift b/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift index aeec438aa9..379f78b734 100644 --- a/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +++ b/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift @@ -11,8 +11,8 @@ import XCTest // This is pure geometry on purpose — no XCUIElement — so the exact decision // can be proven against the golden fixture table shared with the TS twin: // table: contracts/fixtures/tap-point-policy.json -// TS twin: src/snapshot/mobile-snapshot-semantics.ts#isTapPointInsideViewport -// TS test: src/snapshot/__tests__/tap-point-policy-parity.test.ts +// TS twin: packages/contracts/src/snapshot-visibility.ts#isTapPointInsideViewport +// TS test: packages/contracts/src/snapshot-tap-point-policy.test.ts // Drift on either side turns CI red without needing a simulator. enum TapPointPolicy { static func isAllowed(elementFrame: CGRect, windowFrame: CGRect) -> Bool { diff --git a/docs/adr/0019-request-bound-platform-runtime.md b/docs/adr/0019-request-bound-platform-runtime.md index ce74f03674..5f128c7bd8 100644 --- a/docs/adr/0019-request-bound-platform-runtime.md +++ b/docs/adr/0019-request-bound-platform-runtime.md @@ -132,6 +132,41 @@ platform-common package, and it preserves the package façades' implementation-l Its introduction carries the normal workspace-package compliance surface: `check:affected` selection, R11/R13 package enumeration, and the composite typecheck project list. +> **Amendment (#2082): the substrate below the platform families.** Retiring the shared +> `src/utils` and `src/platforms` root surfaces (so the family trees can move behind their +> exports maps) forces every shared file onto a declared domain owner, and the paragraph above +> is amended to name that layout rather than let capture-kit absorb it: +> +> - `@agent-device/host-kit` owns mechanics that act on the host machine, and nothing else. Each +> export is one narrow capability port, not a category barrel: `command` (running host +> commands), `process` (observing and owning host processes), `diagnostics`, `retry` +> (deadline/backoff/sleep), `archive` (bounded extraction and byte limits), `file` (atomic +> publishes, locks, path resolution), `request` (request-scoped cancellation and progress), +> and `version` (the installed version off disk). Modules under `src/internal/` are reachable +> only through a port, and a port may only hold mechanics a consumer of that capability +> needs — the eager-closure row per port is what keeps that honest. +> - A helper that touches no process, file, or environment is not host mechanics and does not +> belong here: pure record readers, config-source values, result text, memoization, async +> scoping, coordinate validation, and device-scope parsing live in `@agent-device/kernel` +> beside its other primitives. +> - `@agent-device/capture-kit` owns capture, snapshot, and recording behavior — PNG tooling, +> screenshot density and pixel diffing, snapshot occlusion, mobile snapshot semantics, +> quality verdicts and backend capability tables. Snapshot *behavior* is capture domain, not +> contracts vocabulary, and host mechanics are host-kit's, not capture-kit's. +> - `@agent-device/provision-kit` owns provisioning mechanics — install-artifact acquisition +> (local paths, archives, guarded network downloads) and host toolchain probing. +> - The enforced direction is `kernel < contracts < host-kit < capture-kit < provision-kit < +> platform/provider/daemon`. +> - Contracts stays vocabulary, plan models, and pure classification with no process, +> filesystem, or timer mechanics (the existing planted-red gate); platform-specific parsing +> stays with its family package and reaches legacy callers through composition, never by a +> family importing another owner's internals. +> +> Enforcement: each substrate package's exported subpaths are pinned in +> `package-boundaries.test.ts` (widening fails the gate), the contracts mechanics gate stays +> planted red, and the `platforms-root-shape` rule rejects any new shared file or directory +> appearing directly under `src/platforms`. + The Apple XCUITest runner client is a durable platform-owned implementation facet colocated inside `packages/platform-apple` as the `src/runner/` subtree (#2040) — Apple mechanics belong to the Apple package. R13 models the facet by enumeration rather than by exception sprawl: the family diff --git a/docs/agents/testing.md b/docs/agents/testing.md index b32c346d24..2d5c266759 100644 --- a/docs/agents/testing.md +++ b/docs/agents/testing.md @@ -129,7 +129,7 @@ whether tests distinguish changed decision logic. Do not infer redundancy from l Run `pnpm depgraph affected` before touching a high-fan-in module: ```sh -pnpm depgraph affected src/utils/exec.ts +pnpm depgraph affected packages/capture-kit/src/exec.ts pnpm depgraph affected src/daemon/ref-frame.ts --json --limit 25 ``` diff --git a/package.json b/package.json index 3bfa2f704b..ebaa46113b 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "check:unit": "pnpm test:unit && pnpm check:tmpdir-leaks && pnpm test:smoke", "check": "pnpm check:tooling && pnpm check:fallow && pnpm check:unit", "prepack": "pnpm check:mcp-metadata && pnpm package:npm", - "typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/capture-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/ad-replay packages/maestro packages/replay-test packages/provider-webdriver packages/provider-limrun && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json", + "typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/host-kit packages/capture-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/ad-replay packages/maestro packages/replay-test packages/provider-webdriver packages/provider-limrun && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json", "test-app:install": "pnpm install --dir examples/test-app", "test-app:start": "pnpm --dir examples/test-app start", "test-app:ios": "pnpm --dir examples/test-app ios", @@ -278,6 +278,7 @@ "@agent-device/ad-script": "workspace:*", "@agent-device/capture-kit": "workspace:*", "@agent-device/contracts": "workspace:*", + "@agent-device/host-kit": "workspace:*", "@agent-device/kernel": "workspace:*", "@agent-device/maestro": "workspace:*", "@agent-device/platform-android": "workspace:*", diff --git a/packages/ad-replay/src/internal/canonical-json.ts b/packages/ad-replay/src/internal/canonical-json.ts deleted file mode 100644 index 14a0f40397..0000000000 --- a/packages/ad-replay/src/internal/canonical-json.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function canonicalJson(value: unknown): string { - return JSON.stringify(sortKeysDeep(value)); -} - -function sortKeysDeep(value: unknown): unknown { - if (Array.isArray(value)) return value.map(sortKeysDeep); - if (!value || typeof value !== 'object') return value; - const record = value as Record; - return Object.fromEntries( - Object.keys(record) - .sort() - .map((key) => [key, sortKeysDeep(record[key])]), - ); -} diff --git a/packages/ad-replay/src/internal/plan-digest.ts b/packages/ad-replay/src/internal/plan-digest.ts index 1eafd390b3..58b0ff31d0 100644 --- a/packages/ad-replay/src/internal/plan-digest.ts +++ b/packages/ad-replay/src/internal/plan-digest.ts @@ -1,6 +1,6 @@ import { createHash } from 'node:crypto'; import type { SessionAction } from '@agent-device/contracts/session'; -import { canonicalJson } from './canonical-json.ts'; +import { canonicalJson } from '@agent-device/kernel/collections'; /** * ADR 0012 decision 4 / migration step 5: `planDigest` is SHA-256 over the diff --git a/packages/capture-kit/package.json b/packages/capture-kit/package.json index 002b5a0b1d..0b2ba0e64e 100644 --- a/packages/capture-kit/package.json +++ b/packages/capture-kit/package.json @@ -3,15 +3,64 @@ "version": "0.0.0", "private": true, "type": "module", - "description": "Private durable-capture mechanics shared by platform runtimes, providers, and daemon orchestration.", + "description": "Private host and durable-capture mechanics shared by platform runtimes, providers, and daemon orchestration: process execution, diagnostics, archives, PNG tooling, and request-scoped plumbing.", "dependencies": { "@agent-device/contracts": "workspace:*", + "@agent-device/host-kit": "workspace:*", "@agent-device/kernel": "workspace:*" }, "exports": { ".": { "types": "./src/index.ts", "default": "./src/index.ts" + }, + "./mobile-snapshot-semantics": { + "types": "./src/mobile-snapshot-semantics.ts", + "default": "./src/mobile-snapshot-semantics.ts" + }, + "./png": { + "types": "./src/png.ts", + "default": "./src/png.ts" + }, + "./png-resize": { + "types": "./src/png-resize.ts", + "default": "./src/png-resize.ts" + }, + "./png-rgb-difference": { + "types": "./src/png-rgb-difference.ts", + "default": "./src/png-rgb-difference.ts" + }, + "./png-size": { + "types": "./src/png-size.ts", + "default": "./src/png-size.ts" + }, + "./png-worker-client": { + "types": "./src/png-worker-client.ts", + "default": "./src/png-worker-client.ts" + }, + "./screenshot-density": { + "types": "./src/screenshot-density.ts", + "default": "./src/screenshot-density.ts" + }, + "./screenshot-diff-pixels": { + "types": "./src/screenshot-diff-pixels.ts", + "default": "./src/screenshot-diff-pixels.ts" + }, + "./snapshot-occlusion": { + "types": "./src/snapshot-occlusion.ts", + "default": "./src/snapshot-occlusion.ts" + }, + "./snapshot-quality-backend-capabilities": { + "types": "./src/snapshot-quality-backend-capabilities.ts", + "default": "./src/snapshot-quality-backend-capabilities.ts" + }, + "./snapshot-quality-verdict": { + "types": "./src/snapshot-quality-verdict.ts", + "default": "./src/snapshot-quality-verdict.ts" } + }, + "devDependencies": { + "@types/pngjs": "^6.0.5", + "pngjs": "^7.0.0" } } diff --git a/src/utils/internal-entry.ts b/packages/capture-kit/src/internal-entry.ts similarity index 100% rename from src/utils/internal-entry.ts rename to packages/capture-kit/src/internal-entry.ts diff --git a/src/utils/__tests__/mobile-snapshot-semantics.test.ts b/packages/capture-kit/src/mobile-snapshot-semantics.test.ts similarity index 99% rename from src/utils/__tests__/mobile-snapshot-semantics.test.ts rename to packages/capture-kit/src/mobile-snapshot-semantics.test.ts index fd452be447..bd87f817fe 100644 --- a/src/utils/__tests__/mobile-snapshot-semantics.test.ts +++ b/packages/capture-kit/src/mobile-snapshot-semantics.test.ts @@ -4,7 +4,7 @@ import { buildMobileSnapshotPresentation, classifyOffscreenScrollDirection, isConfirmedOnScreenProbe, -} from '../../snapshot/mobile-snapshot-semantics.ts'; +} from './mobile-snapshot-semantics.ts'; import { isNodeVisibleInEffectiveViewport } from '@agent-device/contracts/snapshot'; import type { Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; diff --git a/src/snapshot/mobile-snapshot-semantics.ts b/packages/capture-kit/src/mobile-snapshot-semantics.ts similarity index 98% rename from src/snapshot/mobile-snapshot-semantics.ts rename to packages/capture-kit/src/mobile-snapshot-semantics.ts index 8e0c80bde9..bf6d0dc0ec 100644 --- a/src/snapshot/mobile-snapshot-semantics.ts +++ b/packages/capture-kit/src/mobile-snapshot-semantics.ts @@ -8,7 +8,7 @@ import { resolveEffectiveViewportRect, resolveViewportRect, } from '@agent-device/contracts/snapshot'; -import { inferVerticalScrollIndicatorDirections } from '../utils/scroll-indicator.ts'; +import { inferVerticalScrollIndicatorDirections } from '@agent-device/kernel/scroll-indicator'; import type { HiddenContentHint, Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; type Direction = 'above' | 'below'; @@ -320,6 +320,7 @@ function classifyVerticalDirection(targetRect: Rect, viewportRect: Rect): Direct return null; } +// fallow-ignore-next-line complexity function isDiscoverableOffscreenNode(node: SnapshotNode): boolean { if (node.hittable === true) { return true; @@ -378,6 +379,7 @@ function findNearestVisibleScrollableAncestor( ); } +// fallow-ignore-next-line complexity function mergeScrollIndicatorDirections( nodes: SnapshotNode[], visibleNodeIndexes: Set, diff --git a/src/utils/__tests__/png-resize.test.ts b/packages/capture-kit/src/png-resize.test.ts similarity index 86% rename from src/utils/__tests__/png-resize.test.ts rename to packages/capture-kit/src/png-resize.test.ts index af8bc248c1..f9bc207e44 100644 --- a/src/utils/__tests__/png-resize.test.ts +++ b/packages/capture-kit/src/png-resize.test.ts @@ -2,10 +2,10 @@ import { afterAll, test } from 'vitest'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import { PNG } from '../png.ts'; -import { resizePngFileToScale } from '../png-resize.ts'; -import { terminatePngWorker } from '../png-worker-client.ts'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +import { PNG } from './png.ts'; +import { resizePngFileToScale } from './png-resize.ts'; +import { terminatePngWorker } from './png-worker-client.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; afterAll(async () => { await terminatePngWorker(); diff --git a/src/utils/png-resize.ts b/packages/capture-kit/src/png-resize.ts similarity index 100% rename from src/utils/png-resize.ts rename to packages/capture-kit/src/png-resize.ts diff --git a/src/utils/png-rgb-difference.ts b/packages/capture-kit/src/png-rgb-difference.ts similarity index 100% rename from src/utils/png-rgb-difference.ts rename to packages/capture-kit/src/png-rgb-difference.ts diff --git a/src/utils/__tests__/png-size.test.ts b/packages/capture-kit/src/png-size.test.ts similarity index 85% rename from src/utils/__tests__/png-size.test.ts rename to packages/capture-kit/src/png-size.test.ts index e8ab097bad..a0bb63c35d 100644 --- a/src/utils/__tests__/png-size.test.ts +++ b/packages/capture-kit/src/png-size.test.ts @@ -3,9 +3,9 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { PNG } from '../png.ts'; -import { readPngSize } from '../png-size.ts'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +import { PNG } from './png.ts'; +import { readPngSize } from './png-size.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; test('readPngSize reads PNG dimensions from the file header', async () => { const filePath = tmpPngPath('size'); diff --git a/src/utils/png-size.ts b/packages/capture-kit/src/png-size.ts similarity index 100% rename from src/utils/png-size.ts rename to packages/capture-kit/src/png-size.ts diff --git a/src/utils/__tests__/png-worker-client.test.ts b/packages/capture-kit/src/png-worker-client.test.ts similarity index 96% rename from src/utils/__tests__/png-worker-client.test.ts rename to packages/capture-kit/src/png-worker-client.test.ts index 4244e304f3..52a00cccb5 100644 --- a/src/utils/__tests__/png-worker-client.test.ts +++ b/packages/capture-kit/src/png-worker-client.test.ts @@ -1,15 +1,15 @@ import { afterAll, test } from 'vitest'; import assert from 'node:assert/strict'; import { AppError } from '@agent-device/kernel/errors'; -import { PNG } from '../png.ts'; +import { PNG } from './png.ts'; import { computePngRgbDifferenceAsync, computeScreenshotDiffPixelsAsync, decodePngAsync, encodePngAsync, terminatePngWorker, -} from '../png-worker-client.ts'; -import { computeScreenshotDiffPixels } from '../screenshot-diff-pixels.ts'; +} from './png-worker-client.ts'; +import { computeScreenshotDiffPixels } from './screenshot-diff-pixels.ts'; afterAll(async () => { await terminatePngWorker(); diff --git a/src/utils/png-worker-client.ts b/packages/capture-kit/src/png-worker-client.ts similarity index 99% rename from src/utils/png-worker-client.ts rename to packages/capture-kit/src/png-worker-client.ts index 932d4b6bb4..9208ba1d38 100644 --- a/src/utils/png-worker-client.ts +++ b/packages/capture-kit/src/png-worker-client.ts @@ -1,5 +1,5 @@ import { Worker } from 'node:worker_threads'; -import { emitDiagnostic } from './diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { AppError, toAppErrorCode } from '@agent-device/kernel/errors'; import { resolveInternalEntryModulePath } from './internal-entry.ts'; import { decodePng, PNG } from './png.ts'; diff --git a/src/utils/png-worker-contract.ts b/packages/capture-kit/src/png-worker-contract.ts similarity index 100% rename from src/utils/png-worker-contract.ts rename to packages/capture-kit/src/png-worker-contract.ts diff --git a/src/utils/__tests__/png-worker.test.ts b/packages/capture-kit/src/png-worker.test.ts similarity index 96% rename from src/utils/__tests__/png-worker.test.ts rename to packages/capture-kit/src/png-worker.test.ts index eb154f60e8..e4a2d9b140 100644 --- a/src/utils/__tests__/png-worker.test.ts +++ b/packages/capture-kit/src/png-worker.test.ts @@ -1,6 +1,6 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; -import { resultTransferList } from '../png-worker.ts'; +import { resultTransferList } from './png-worker.ts'; test('resultTransferList transfers a buffer that fully owns its ArrayBuffer', () => { const owned = new Uint8Array(8).fill(1); diff --git a/src/utils/png-worker.ts b/packages/capture-kit/src/png-worker.ts similarity index 100% rename from src/utils/png-worker.ts rename to packages/capture-kit/src/png-worker.ts diff --git a/src/utils/png.ts b/packages/capture-kit/src/png.ts similarity index 100% rename from src/utils/png.ts rename to packages/capture-kit/src/png.ts diff --git a/src/utils/screenshot-density.ts b/packages/capture-kit/src/screenshot-density.ts similarity index 100% rename from src/utils/screenshot-density.ts rename to packages/capture-kit/src/screenshot-density.ts diff --git a/src/utils/screenshot-diff-pixels.ts b/packages/capture-kit/src/screenshot-diff-pixels.ts similarity index 100% rename from src/utils/screenshot-diff-pixels.ts rename to packages/capture-kit/src/screenshot-diff-pixels.ts diff --git a/src/snapshot/__tests__/snapshot-occlusion.test.ts b/packages/capture-kit/src/snapshot-occlusion.test.ts similarity index 99% rename from src/snapshot/__tests__/snapshot-occlusion.test.ts rename to packages/capture-kit/src/snapshot-occlusion.test.ts index f2548df449..1137058544 100644 --- a/src/snapshot/__tests__/snapshot-occlusion.test.ts +++ b/packages/capture-kit/src/snapshot-occlusion.test.ts @@ -1,7 +1,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { annotateCoveredSnapshotNodes } from '../snapshot-occlusion.ts'; +import { annotateCoveredSnapshotNodes } from './snapshot-occlusion.ts'; // #1478 P5 codec-extraction regression: `annotateCoveredSnapshotNodes` marks // a touch candidate `interactionBlocked: 'covered'` when a later, floating diff --git a/src/snapshot/snapshot-occlusion.ts b/packages/capture-kit/src/snapshot-occlusion.ts similarity index 99% rename from src/snapshot/snapshot-occlusion.ts rename to packages/capture-kit/src/snapshot-occlusion.ts index 8a1c074211..ae26367e4e 100644 --- a/src/snapshot/snapshot-occlusion.ts +++ b/packages/capture-kit/src/snapshot-occlusion.ts @@ -1,6 +1,6 @@ import type { RawSnapshotNode, Rect } from '@agent-device/kernel/snapshot'; import { centerOfRect } from '@agent-device/kernel/snapshot'; -import { areRectsApproximatelyEqual, normalizeRect } from '../utils/rect-center.ts'; +import { areRectsApproximatelyEqual, normalizeRect } from '@agent-device/kernel/rect-center'; import { containsPoint } from '@agent-device/kernel/rect'; import { normalizeType, isViewportRootNode } from '@agent-device/contracts/snapshot'; diff --git a/src/snapshot-quality/backend-capabilities.test.ts b/packages/capture-kit/src/snapshot-quality-backend-capabilities.test.ts similarity index 99% rename from src/snapshot-quality/backend-capabilities.test.ts rename to packages/capture-kit/src/snapshot-quality-backend-capabilities.test.ts index 6ae1523df9..4cf2b012e6 100644 --- a/src/snapshot-quality/backend-capabilities.test.ts +++ b/packages/capture-kit/src/snapshot-quality-backend-capabilities.test.ts @@ -6,7 +6,7 @@ import { expect, test } from 'vitest'; import { ANDROID_SNAPSHOT_BACKEND_CAPABILITIES, SNAPSHOT_BACKEND_CAPABILITIES, -} from './backend-capabilities.ts'; +} from './snapshot-quality-backend-capabilities.ts'; type SnapshotBackendParityFixture = { backends: Array<{ @@ -33,6 +33,7 @@ const SNAPSHOT_BACKEND_PARITY_FIXTURE_PATH = path.resolve( import.meta.dirname, '..', '..', + '..', 'contracts', 'fixtures', 'ios-snapshot-backends.json', diff --git a/src/snapshot-quality/backend-capabilities.ts b/packages/capture-kit/src/snapshot-quality-backend-capabilities.ts similarity index 100% rename from src/snapshot-quality/backend-capabilities.ts rename to packages/capture-kit/src/snapshot-quality-backend-capabilities.ts diff --git a/src/snapshot-quality/__tests__/verdict.test.ts b/packages/capture-kit/src/snapshot-quality-verdict.test.ts similarity index 98% rename from src/snapshot-quality/__tests__/verdict.test.ts rename to packages/capture-kit/src/snapshot-quality-verdict.test.ts index d3c3796e43..ea8afbc157 100644 --- a/src/snapshot-quality/__tests__/verdict.test.ts +++ b/packages/capture-kit/src/snapshot-quality-verdict.test.ts @@ -5,7 +5,7 @@ import { isSparseSnapshotQualityVerdict, preferredSnapshotBackendForVerdict, readSnapshotQualityVerdict, -} from '../verdict.ts'; +} from './snapshot-quality-verdict.ts'; test('readSnapshotQualityVerdict accepts a well-formed verdict', () => { const verdict = readSnapshotQualityVerdict({ diff --git a/src/snapshot-quality/verdict.ts b/packages/capture-kit/src/snapshot-quality-verdict.ts similarity index 97% rename from src/snapshot-quality/verdict.ts rename to packages/capture-kit/src/snapshot-quality-verdict.ts index 623bd95949..2913abe32f 100644 --- a/src/snapshot-quality/verdict.ts +++ b/packages/capture-kit/src/snapshot-quality-verdict.ts @@ -1,5 +1,5 @@ import type { SnapshotQualityVerdict } from '@agent-device/kernel/snapshot'; -import { SNAPSHOT_QUALITY_BACKEND_CAPABILITIES } from './backend-capabilities.ts'; +import { SNAPSHOT_QUALITY_BACKEND_CAPABILITIES } from './snapshot-quality-backend-capabilities.ts'; const SNAPSHOT_QUALITY_STATES = new Set([ 'healthy', diff --git a/packages/capture-kit/src/tmp-dir.fixtures.ts b/packages/capture-kit/src/tmp-dir.fixtures.ts new file mode 100644 index 0000000000..6df3c9f35c --- /dev/null +++ b/packages/capture-kit/src/tmp-dir.fixtures.ts @@ -0,0 +1,12 @@ +import fs from 'node:fs'; +import fsPromises from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +export async function mkdtempForTest(prefix: string): Promise { + return fsPromises.mkdtemp(path.join(os.tmpdir(), prefix)); +} + +export function mkdtempForTestSync(prefix: string): string { + return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); +} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0f93cfc8bd..5df12899ff 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -23,6 +23,10 @@ "types": "./src/android-clipboard-support.ts", "default": "./src/android-clipboard-support.ts" }, + "./android-helper-artifacts": { + "types": "./src/android-helper-artifacts.ts", + "default": "./src/android-helper-artifacts.ts" + }, "./android-input-ownership": { "types": "./src/android-input-ownership.ts", "default": "./src/android-input-ownership.ts" @@ -39,6 +43,10 @@ "types": "./src/android-system-chrome.ts", "default": "./src/android-system-chrome.ts" }, + "./android-touch-plan": { + "types": "./src/android-touch-plan.ts", + "default": "./src/android-touch-plan.ts" + }, "./app-deployment-runtime": { "types": "./src/app-deployment-runtime.ts", "default": "./src/app-deployment-runtime.ts" @@ -151,6 +159,10 @@ "types": "./src/command-platform-execution.ts", "default": "./src/command-platform-execution.ts" }, + "./daemon-owner-cleanup": { + "types": "./src/daemon-owner-cleanup.ts", + "default": "./src/daemon-owner-cleanup.ts" + }, "./device": { "types": "./src/facades/device.ts", "default": "./src/facades/device.ts" @@ -199,18 +211,6 @@ "types": "./src/gesture-plan.ts", "default": "./src/gesture-plan.ts" }, - "./host-diagnostics": { - "types": "./src/host-diagnostics.ts", - "default": "./src/host-diagnostics.ts" - }, - "./daemon-owner-cleanup": { - "types": "./src/daemon-owner-cleanup.ts", - "default": "./src/daemon-owner-cleanup.ts" - }, - "./managed-web-backend": { - "types": "./src/managed-web-backend.ts", - "default": "./src/managed-web-backend.ts" - }, "./gesture-plan-types": { "types": "./src/gesture-plan-types.ts", "default": "./src/gesture-plan-types.ts" @@ -223,6 +223,10 @@ "types": "./src/home-runtime.ts", "default": "./src/home-runtime.ts" }, + "./host-diagnostics": { + "types": "./src/host-diagnostics.ts", + "default": "./src/host-diagnostics.ts" + }, "./interaction": { "types": "./src/interaction.ts", "default": "./src/interaction.ts" @@ -259,6 +263,10 @@ "types": "./src/logs-runtime-plan.ts", "default": "./src/logs-runtime-plan.ts" }, + "./managed-web-backend": { + "types": "./src/managed-web-backend.ts", + "default": "./src/managed-web-backend.ts" + }, "./navigation": { "types": "./src/navigation.ts", "default": "./src/navigation.ts" @@ -283,6 +291,22 @@ "types": "./src/orientation-runtime.ts", "default": "./src/orientation-runtime.ts" }, + "./perf-runtime": { + "types": "./src/perf-runtime.ts", + "default": "./src/perf-runtime.ts" + }, + "./perf-runtime-host": { + "types": "./src/perf-runtime-host.ts", + "default": "./src/perf-runtime-host.ts" + }, + "./perf-runtime-operation-builder": { + "types": "./src/perf-runtime-operation-builder.ts", + "default": "./src/perf-runtime-operation-builder.ts" + }, + "./perf-runtime-plan": { + "types": "./src/perf-runtime-plan.ts", + "default": "./src/perf-runtime-plan.ts" + }, "./platform-module": { "types": "./src/platform-module.ts", "default": "./src/platform-module.ts" @@ -295,6 +319,10 @@ "types": "./src/platform-providers.ts", "default": "./src/platform-providers.ts" }, + "./platform-resource-cleanup": { + "types": "./src/platform-resource-cleanup.ts", + "default": "./src/platform-resource-cleanup.ts" + }, "./platform-runtime": { "types": "./src/platform-runtime.ts", "default": "./src/platform-runtime.ts" @@ -307,34 +335,18 @@ "types": "./src/platform-runtime-operations.ts", "default": "./src/platform-runtime-operations.ts" }, - "./platform-resource-cleanup": { - "types": "./src/platform-resource-cleanup.ts", - "default": "./src/platform-resource-cleanup.ts" - }, "./platform-runtime-unavailable": { "types": "./src/platform-runtime-unavailable.ts", "default": "./src/platform-runtime-unavailable.ts" }, - "./perf-runtime": { - "types": "./src/perf-runtime.ts", - "default": "./src/perf-runtime.ts" - }, - "./perf-runtime-host": { - "types": "./src/perf-runtime-host.ts", - "default": "./src/perf-runtime-host.ts" - }, - "./perf-runtime-operation-builder": { - "types": "./src/perf-runtime-operation-builder.ts", - "default": "./src/perf-runtime-operation-builder.ts" - }, - "./perf-runtime-plan": { - "types": "./src/perf-runtime-plan.ts", - "default": "./src/perf-runtime-plan.ts" - }, "./progress": { "types": "./src/facades/progress.ts", "default": "./src/facades/progress.ts" }, + "./react-native-overlay": { + "types": "./src/react-native-overlay.ts", + "default": "./src/react-native-overlay.ts" + }, "./record-runtime-execution": { "types": "./src/record-runtime-execution.ts", "default": "./src/record-runtime-execution.ts" @@ -351,10 +363,6 @@ "types": "./src/facades/replay.ts", "default": "./src/facades/replay.ts" }, - "./react-native-overlay": { - "types": "./src/react-native-overlay.ts", - "default": "./src/react-native-overlay.ts" - }, "./runner-lease-context": { "types": "./src/runner-lease-context.ts", "default": "./src/runner-lease-context.ts" @@ -423,6 +431,10 @@ "types": "./src/startup-recovery-fence.ts", "default": "./src/startup-recovery-fence.ts" }, + "./touch-runtime": { + "types": "./src/touch-runtime.ts", + "default": "./src/touch-runtime.ts" + }, "./tv-remote": { "types": "./src/tv-remote.ts", "default": "./src/tv-remote.ts" @@ -435,10 +447,6 @@ "types": "./src/type-text-runtime.ts", "default": "./src/type-text-runtime.ts" }, - "./touch-runtime": { - "types": "./src/touch-runtime.ts", - "default": "./src/touch-runtime.ts" - }, "./viewport-runtime": { "types": "./src/viewport-runtime.ts", "default": "./src/viewport-runtime.ts" @@ -450,14 +458,6 @@ "./wait-runtime-plan": { "types": "./src/wait-runtime-plan.ts", "default": "./src/wait-runtime-plan.ts" - }, - "./android-helper-artifacts": { - "types": "./src/android-helper-artifacts.ts", - "default": "./src/android-helper-artifacts.ts" - }, - "./android-touch-plan": { - "types": "./src/android-touch-plan.ts", - "default": "./src/android-touch-plan.ts" } } } diff --git a/packages/host-kit/package.json b/packages/host-kit/package.json new file mode 100644 index 0000000000..07666e7b80 --- /dev/null +++ b/packages/host-kit/package.json @@ -0,0 +1,51 @@ +{ + "name": "@agent-device/host-kit", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Private host-mechanics package: each export is one narrow capability port over the host machine \u2014 running commands, observing and owning processes, diagnostics, retry/deadline, archive extraction, durable files and locks, request-scoped plumbing, and the installed version.", + "dependencies": { + "@agent-device/contracts": "workspace:*", + "@agent-device/kernel": "workspace:*", + "tar-stream": "^3.2.0", + "yauzl": "^3.4.0" + }, + "devDependencies": { + "@types/tar-stream": "^3.1.4", + "@types/yauzl": "^3.4.0" + }, + "exports": { + "./archive": { + "types": "./src/archive.ts", + "default": "./src/archive.ts" + }, + "./command": { + "types": "./src/command.ts", + "default": "./src/command.ts" + }, + "./diagnostics": { + "types": "./src/diagnostics.ts", + "default": "./src/diagnostics.ts" + }, + "./file": { + "types": "./src/file.ts", + "default": "./src/file.ts" + }, + "./process": { + "types": "./src/process.ts", + "default": "./src/process.ts" + }, + "./request": { + "types": "./src/request.ts", + "default": "./src/request.ts" + }, + "./retry": { + "types": "./src/retry.ts", + "default": "./src/retry.ts" + }, + "./version": { + "types": "./src/version.ts", + "default": "./src/version.ts" + } + } +} diff --git a/packages/host-kit/src/archive.ts b/packages/host-kit/src/archive.ts new file mode 100644 index 0000000000..3300b1f413 --- /dev/null +++ b/packages/host-kit/src/archive.ts @@ -0,0 +1,4 @@ +export { archiveTypeFromPath, extractArchiveSafely } from './internal/archive-extraction.ts'; +export { ArchiveBudget, type ArchiveManifestEntry } from './internal/archive-safety.ts'; +export { MAX_ARTIFACT_COMPRESSED_BYTES } from './internal/artifact-limits.ts'; +export { createByteLimitStream } from './internal/byte-limit-stream.ts'; diff --git a/packages/host-kit/src/command.ts b/packages/host-kit/src/command.ts new file mode 100644 index 0000000000..8c56f2d0ed --- /dev/null +++ b/packages/host-kit/src/command.ts @@ -0,0 +1,24 @@ +export { + coerceExecResult, + type CommandExecutorOverride, + type ExecBackgroundOptions, + type ExecBackgroundResult, + type ExecDetachedExit, + execFailureDetails, + type ExecOptions, + type ExecResult, + isExecutablePath, + requireExecSuccess, + resolveExecutableOverridePath, + resolveFileOverridePath, + runCmd, + runCmdBackground, + runCmdDetached, + runCmdDetachedMonitored, + runCmdStreaming, + runCmdSync, + whichCmd, + withCommandExecutorOverride, + withoutCommandExecutorOverride, +} from './internal/exec.ts'; +export { shellQuote, shellQuoteIfNeeded } from './internal/shell-quote.ts'; diff --git a/packages/host-kit/src/diagnostics.ts b/packages/host-kit/src/diagnostics.ts new file mode 100644 index 0000000000..2b0d0e5247 --- /dev/null +++ b/packages/host-kit/src/diagnostics.ts @@ -0,0 +1,11 @@ +export { + countDiagnosticEventsByPhase, + createRequestId, + emitDiagnostic, + flushDiagnosticsToSessionFile, + getDiagnosticsMeta, + registerDiagnosticSensitiveValue, + updateDiagnosticsScope, + withDiagnosticsScope, + withDiagnosticTimer, +} from './internal/diagnostics.ts'; diff --git a/packages/host-kit/src/file.ts b/packages/host-kit/src/file.ts new file mode 100644 index 0000000000..fe8c310555 --- /dev/null +++ b/packages/host-kit/src/file.ts @@ -0,0 +1,7 @@ +export { + isAtomicPublishTemporaryPath, + publishFileSync, + withAtomicPublishTempPathSync, +} from './internal/atomic-file.ts'; +export { expandUserHomePath, resolveUserPath } from './internal/path-resolution.ts'; +export { acquireProcessLock, type ProcessLockOwner } from './internal/process-lock.ts'; diff --git a/src/utils/__tests__/archive-extraction-tar.test.ts b/packages/host-kit/src/internal/archive-extraction-tar.test.ts similarity index 90% rename from src/utils/__tests__/archive-extraction-tar.test.ts rename to packages/host-kit/src/internal/archive-extraction-tar.test.ts index 05333db87e..54bb3b9e89 100644 --- a/src/utils/__tests__/archive-extraction-tar.test.ts +++ b/packages/host-kit/src/internal/archive-extraction-tar.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert/strict'; import { promises as fs } from 'node:fs'; import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; -import { extractTarArchive } from '../archive-extraction-tar.ts'; -import { ArchiveBudget } from '../archive-safety.ts'; +import { extractTarArchive } from './archive-extraction-tar.ts'; +import { ArchiveBudget } from './archive-safety.ts'; import { createArchiveWorkspace, createTruncatedTgz } from './archive-extraction.fixtures.ts'; test('tar inspection rejects declared bytes before draining an over-budget entry', async () => { diff --git a/src/utils/archive-extraction-tar.ts b/packages/host-kit/src/internal/archive-extraction-tar.ts similarity index 100% rename from src/utils/archive-extraction-tar.ts rename to packages/host-kit/src/internal/archive-extraction-tar.ts diff --git a/src/utils/__tests__/archive-extraction-zip.test.ts b/packages/host-kit/src/internal/archive-extraction-zip.test.ts similarity index 90% rename from src/utils/__tests__/archive-extraction-zip.test.ts rename to packages/host-kit/src/internal/archive-extraction-zip.test.ts index 40a5e26699..9ad4aa0116 100644 --- a/src/utils/__tests__/archive-extraction-zip.test.ts +++ b/packages/host-kit/src/internal/archive-extraction-zip.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert/strict'; import { promises as fs } from 'node:fs'; import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; -import { extractZipArchive } from '../archive-extraction-zip.ts'; -import { ArchiveBudget } from '../archive-safety.ts'; +import { extractZipArchive } from './archive-extraction-zip.ts'; +import { ArchiveBudget } from './archive-safety.ts'; import { createArchiveWorkspace, createZipWithEncryptedSecondEntry, diff --git a/src/utils/archive-extraction-zip.ts b/packages/host-kit/src/internal/archive-extraction-zip.ts similarity index 100% rename from src/utils/archive-extraction-zip.ts rename to packages/host-kit/src/internal/archive-extraction-zip.ts diff --git a/src/utils/__tests__/archive-extraction.fixtures.ts b/packages/host-kit/src/internal/archive-extraction.fixtures.ts similarity index 97% rename from src/utils/__tests__/archive-extraction.fixtures.ts rename to packages/host-kit/src/internal/archive-extraction.fixtures.ts index 694ea53dfc..1471882fca 100644 --- a/src/utils/__tests__/archive-extraction.fixtures.ts +++ b/packages/host-kit/src/internal/archive-extraction.fixtures.ts @@ -3,7 +3,7 @@ import os from 'node:os'; import path from 'node:path'; import { gzipSync } from 'node:zlib'; import * as tar from 'tar-stream'; -import { runCmdSync } from '../exec.ts'; +import { runCmdSync } from './exec.ts'; export async function createArchiveWorkspace(): Promise<{ archivePath: string; diff --git a/src/utils/archive-extraction.ts b/packages/host-kit/src/internal/archive-extraction.ts similarity index 100% rename from src/utils/archive-extraction.ts rename to packages/host-kit/src/internal/archive-extraction.ts diff --git a/src/utils/__tests__/archive-safety.test.ts b/packages/host-kit/src/internal/archive-safety.test.ts similarity index 98% rename from src/utils/__tests__/archive-safety.test.ts rename to packages/host-kit/src/internal/archive-safety.test.ts index 701d6a3432..9b0f4f8ae8 100644 --- a/src/utils/__tests__/archive-safety.test.ts +++ b/packages/host-kit/src/internal/archive-safety.test.ts @@ -5,7 +5,7 @@ import { ArchiveBudget, normalizeArchiveEntryName, resolveArchiveOutputPath, -} from '../archive-safety.ts'; +} from './archive-safety.ts'; function reason(error: unknown): string | undefined { return error instanceof AppError ? (error.details?.reason as string | undefined) : undefined; diff --git a/src/utils/archive-safety.ts b/packages/host-kit/src/internal/archive-safety.ts similarity index 100% rename from src/utils/archive-safety.ts rename to packages/host-kit/src/internal/archive-safety.ts diff --git a/src/utils/artifact-limits.ts b/packages/host-kit/src/internal/artifact-limits.ts similarity index 100% rename from src/utils/artifact-limits.ts rename to packages/host-kit/src/internal/artifact-limits.ts diff --git a/src/utils/atomic-file.ts b/packages/host-kit/src/internal/atomic-file.ts similarity index 100% rename from src/utils/atomic-file.ts rename to packages/host-kit/src/internal/atomic-file.ts diff --git a/src/utils/__tests__/byte-limit-stream.test.ts b/packages/host-kit/src/internal/byte-limit-stream.test.ts similarity index 96% rename from src/utils/__tests__/byte-limit-stream.test.ts rename to packages/host-kit/src/internal/byte-limit-stream.test.ts index cf070a52dd..d050c6b653 100644 --- a/src/utils/__tests__/byte-limit-stream.test.ts +++ b/packages/host-kit/src/internal/byte-limit-stream.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { Readable, Writable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { test } from 'vitest'; -import { createByteLimitStream } from '../byte-limit-stream.ts'; +import { createByteLimitStream } from './byte-limit-stream.ts'; test('byte limit rejects before forwarding overflow and reports accepted bytes only', async () => { const limitError = new Error('limit exceeded'); diff --git a/src/utils/byte-limit-stream.ts b/packages/host-kit/src/internal/byte-limit-stream.ts similarity index 100% rename from src/utils/byte-limit-stream.ts rename to packages/host-kit/src/internal/byte-limit-stream.ts diff --git a/src/utils/__tests__/diagnostics.test.ts b/packages/host-kit/src/internal/diagnostics.test.ts similarity index 98% rename from src/utils/__tests__/diagnostics.test.ts rename to packages/host-kit/src/internal/diagnostics.test.ts index 966aae9c05..088c9e23db 100644 --- a/src/utils/__tests__/diagnostics.test.ts +++ b/packages/host-kit/src/internal/diagnostics.test.ts @@ -7,8 +7,8 @@ import { flushDiagnosticsToSessionFile, registerDiagnosticSensitiveValue, withDiagnosticsScope, -} from '../diagnostics.ts'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +} from './diagnostics.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; test('diagnostics redacts sensitive fields', async () => { const previousHome = process.env.HOME; diff --git a/src/utils/diagnostics.ts b/packages/host-kit/src/internal/diagnostics.ts similarity index 100% rename from src/utils/diagnostics.ts rename to packages/host-kit/src/internal/diagnostics.ts diff --git a/src/utils/__tests__/exec-windows-hide.test.ts b/packages/host-kit/src/internal/exec-windows-hide.test.ts similarity index 98% rename from src/utils/__tests__/exec-windows-hide.test.ts rename to packages/host-kit/src/internal/exec-windows-hide.test.ts index 0cbff61fe5..0f1e4e74f1 100644 --- a/src/utils/__tests__/exec-windows-hide.test.ts +++ b/packages/host-kit/src/internal/exec-windows-hide.test.ts @@ -21,7 +21,7 @@ vi.mock('node:child_process', () => ({ spawnSync: spawnSyncMock, })); -import { runCmd, runCmdBackground, runCmdDetached, runCmdSync } from '../exec.ts'; +import { runCmd, runCmdBackground, runCmdDetached, runCmdSync } from './exec.ts'; beforeEach(() => { spawnMock.mockReset(); diff --git a/src/utils/__tests__/exec.test.ts b/packages/host-kit/src/internal/exec.test.ts similarity index 99% rename from src/utils/__tests__/exec.test.ts rename to packages/host-kit/src/internal/exec.test.ts index d4d89fef9b..dfd8dc0af4 100644 --- a/src/utils/__tests__/exec.test.ts +++ b/packages/host-kit/src/internal/exec.test.ts @@ -2,7 +2,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../diagnostics.ts'; +import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from './diagnostics.ts'; import { coerceExecResult, requireExecSuccess, @@ -13,9 +13,9 @@ import { runCmdSync, whichCmd, type ExecResult, -} from '../exec.ts'; +} from './exec.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; test('runCmd enforces timeoutMs and rejects with COMMAND_FAILED', async () => { await assert.rejects( diff --git a/src/utils/exec.ts b/packages/host-kit/src/internal/exec.ts similarity index 99% rename from src/utils/exec.ts rename to packages/host-kit/src/internal/exec.ts index 3bf80eb7d7..166853c7b9 100644 --- a/src/utils/exec.ts +++ b/packages/host-kit/src/internal/exec.ts @@ -7,7 +7,7 @@ import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { AppError, createRequestCanceledError } from '@agent-device/kernel/errors'; import { emitDiagnostic, getDiagnosticsMeta, updateDiagnosticsScope } from './diagnostics.ts'; -import { parseBooleanLiteral } from './source-value.ts'; +import { parseBooleanLiteral } from '@agent-device/kernel/source-value'; export type ExecResult = { stdout: string; @@ -282,6 +282,7 @@ export async function resolveFileOverridePath( return candidate; } +// fallow-ignore-next-line complexity export function runCmdSync(cmd: string, args: string[], options: ExecOptions = {}): ExecResult { const executable = normalizeExecutableCommand(cmd); const result = spawnSync(executable, args, { diff --git a/src/utils/__tests__/host-process-liveness.test.ts b/packages/host-kit/src/internal/host-process-liveness.test.ts similarity index 92% rename from src/utils/__tests__/host-process-liveness.test.ts rename to packages/host-kit/src/internal/host-process-liveness.test.ts index 5b7f6d108f..fab9d2173c 100644 --- a/src/utils/__tests__/host-process-liveness.test.ts +++ b/packages/host-kit/src/internal/host-process-liveness.test.ts @@ -3,12 +3,12 @@ import { beforeEach, test, vi } from 'vitest'; const { mockRunCmdSync } = vi.hoisted(() => ({ mockRunCmdSync: vi.fn() })); -vi.mock('../exec.ts', async () => { - const actual = await vi.importActual('../exec.ts'); +vi.mock('./exec.ts', async () => { + const actual = await vi.importActual('./exec.ts'); return { ...actual, runCmdSync: mockRunCmdSync }; }); -import { isProcessZombie, readHostProcessIdentityObservations } from '../host-process.ts'; +import { isProcessZombie, readHostProcessIdentityObservations } from './host-process.ts'; function psReturns(stdout: string, exitCode = 0): void { mockRunCmdSync.mockReturnValue({ stdout, stderr: '', exitCode }); diff --git a/src/utils/__tests__/host-process.test.ts b/packages/host-kit/src/internal/host-process.test.ts similarity index 99% rename from src/utils/__tests__/host-process.test.ts rename to packages/host-kit/src/internal/host-process.test.ts index 3ced0c86bf..6236d74b4d 100644 --- a/src/utils/__tests__/host-process.test.ts +++ b/packages/host-kit/src/internal/host-process.test.ts @@ -12,7 +12,7 @@ import { signalProcessGroupBestEffort, stopPidsWithEscalation, uniquePositivePids, -} from '../host-process.ts'; +} from './host-process.ts'; test('isProcessAlive returns false for invalid pid', () => { assert.equal(isProcessAlive(-1), false); diff --git a/src/utils/host-process.ts b/packages/host-kit/src/internal/host-process.ts similarity index 100% rename from src/utils/host-process.ts rename to packages/host-kit/src/internal/host-process.ts diff --git a/src/utils/owned-process-reaper.ts b/packages/host-kit/src/internal/owned-process-reaper.ts similarity index 100% rename from src/utils/owned-process-reaper.ts rename to packages/host-kit/src/internal/owned-process-reaper.ts diff --git a/src/utils/owned-process-record.test.ts b/packages/host-kit/src/internal/owned-process-record.test.ts similarity index 99% rename from src/utils/owned-process-record.test.ts rename to packages/host-kit/src/internal/owned-process-record.test.ts index 8daf342750..38b651b5d6 100644 --- a/src/utils/owned-process-record.test.ts +++ b/packages/host-kit/src/internal/owned-process-record.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { afterEach, beforeEach, test, vi } from 'vitest'; -import { mkdtempForTestSync } from '../__tests__/test-utils/tmp-dir.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; const processState = vi.hoisted(() => ({ alive: new Set(), diff --git a/src/utils/owned-process-record.ts b/packages/host-kit/src/internal/owned-process-record.ts similarity index 100% rename from src/utils/owned-process-record.ts rename to packages/host-kit/src/internal/owned-process-record.ts diff --git a/src/utils/__tests__/owner-identity-liveness.test.ts b/packages/host-kit/src/internal/owner-identity-liveness.test.ts similarity index 96% rename from src/utils/__tests__/owner-identity-liveness.test.ts rename to packages/host-kit/src/internal/owner-identity-liveness.test.ts index 4f103062a6..fd5278afa8 100644 --- a/src/utils/__tests__/owner-identity-liveness.test.ts +++ b/packages/host-kit/src/internal/owner-identity-liveness.test.ts @@ -7,13 +7,13 @@ const { mockIsProcessAlive, mockIsProcessZombie, mockReadProcessStartTime } = vi mockReadProcessStartTime: vi.fn(), })); -vi.mock('../host-process.ts', () => ({ +vi.mock('./host-process.ts', () => ({ isProcessAlive: mockIsProcessAlive, isProcessZombie: mockIsProcessZombie, readProcessStartTime: mockReadProcessStartTime, })); -import { classifyOwnerLiveness, classifyOwnerLivenessFromObservation } from '../owner-identity.ts'; +import { classifyOwnerLiveness, classifyOwnerLivenessFromObservation } from './owner-identity.ts'; const OWNER_PID = 4242; diff --git a/src/utils/__tests__/owner-identity.test.ts b/packages/host-kit/src/internal/owner-identity.test.ts similarity index 96% rename from src/utils/__tests__/owner-identity.test.ts rename to packages/host-kit/src/internal/owner-identity.test.ts index 53b9b1a645..6b6c867d8d 100644 --- a/src/utils/__tests__/owner-identity.test.ts +++ b/packages/host-kit/src/internal/owner-identity.test.ts @@ -8,8 +8,8 @@ import { ownerIdentityDiffers, ownerIdentityMatches, type OwnerIdentity, -} from '../owner-identity.ts'; -import { readProcessStartTime } from '../host-process.ts'; +} from './owner-identity.ts'; +import { readProcessStartTime } from './host-process.ts'; afterEach(() => { vi.restoreAllMocks(); diff --git a/src/utils/owner-identity.ts b/packages/host-kit/src/internal/owner-identity.ts similarity index 100% rename from src/utils/owner-identity.ts rename to packages/host-kit/src/internal/owner-identity.ts diff --git a/src/utils/__tests__/path-resolution.test.ts b/packages/host-kit/src/internal/path-resolution.test.ts similarity index 91% rename from src/utils/__tests__/path-resolution.test.ts rename to packages/host-kit/src/internal/path-resolution.test.ts index a691be3d66..f76a503041 100644 --- a/src/utils/__tests__/path-resolution.test.ts +++ b/packages/host-kit/src/internal/path-resolution.test.ts @@ -1,7 +1,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import path from 'node:path'; -import { expandUserHomePath, resolveUserPath } from '../path-resolution.ts'; +import { expandUserHomePath, resolveUserPath } from './path-resolution.ts'; test('expandUserHomePath expands the current user home prefix', () => { const env = { HOME: '/tmp/agent-device-home' }; diff --git a/src/utils/path-resolution.ts b/packages/host-kit/src/internal/path-resolution.ts similarity index 92% rename from src/utils/path-resolution.ts rename to packages/host-kit/src/internal/path-resolution.ts index 0cedadc1f8..8ce8bd143d 100644 --- a/src/utils/path-resolution.ts +++ b/packages/host-kit/src/internal/path-resolution.ts @@ -1,6 +1,6 @@ import os from 'node:os'; import path from 'node:path'; -import type { EnvMap } from './env-map.ts'; +import type { EnvMap } from '@agent-device/kernel/source-value'; type PathResolutionOptions = { cwd?: string; diff --git a/src/utils/__tests__/process-lock.test.ts b/packages/host-kit/src/internal/process-lock.test.ts similarity index 92% rename from src/utils/__tests__/process-lock.test.ts rename to packages/host-kit/src/internal/process-lock.test.ts index af3399fad7..5e1d393147 100644 --- a/src/utils/__tests__/process-lock.test.ts +++ b/packages/host-kit/src/internal/process-lock.test.ts @@ -6,14 +6,14 @@ import { AppError } from '@agent-device/kernel/errors'; const { zombiePids } = vi.hoisted(() => ({ zombiePids: new Set() })); -vi.mock('../host-process.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('./host-process.ts', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, isProcessZombie: (pid: number) => zombiePids.has(pid) }; }); -import { acquireProcessLock, type ProcessLockOwner } from '../process-lock.ts'; -import { readProcessStartTime } from '../host-process.ts'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +import { acquireProcessLock, type ProcessLockOwner } from './process-lock.ts'; +import { readProcessStartTime } from './host-process.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; let tmpDir: string; diff --git a/src/utils/process-lock.ts b/packages/host-kit/src/internal/process-lock.ts similarity index 100% rename from src/utils/process-lock.ts rename to packages/host-kit/src/internal/process-lock.ts diff --git a/packages/host-kit/src/internal/project-root.ts b/packages/host-kit/src/internal/project-root.ts new file mode 100644 index 0000000000..7a14754025 --- /dev/null +++ b/packages/host-kit/src/internal/project-root.ts @@ -0,0 +1,19 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +export function resolveAgentDeviceProjectRoot(startDirectory: string): string { + let current = startDirectory; + let nearest: string | null = null; + for (let i = 0; i < 8; i += 1) { + const pkgPath = path.join(current, 'package.json'); + if (fs.existsSync(pkgPath)) { + nearest ??= current; + try { + const name = (JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as { name?: unknown }).name; + if (name === 'agent-device') return current; + } catch {} + } + current = path.dirname(current); + } + return nearest ?? startDirectory; +} diff --git a/src/request/cancel.test.ts b/packages/host-kit/src/internal/request-cancel.test.ts similarity index 99% rename from src/request/cancel.test.ts rename to packages/host-kit/src/internal/request-cancel.test.ts index 0104b58a89..915a9d0087 100644 --- a/src/request/cancel.test.ts +++ b/packages/host-kit/src/internal/request-cancel.test.ts @@ -10,7 +10,7 @@ import { markRequestCanceled, registerRequestAbort, resolveRequestTrackingId, -} from './cancel.ts'; +} from './request-cancel.ts'; test('resolveRequestTrackingId generates unique ids for fallback seeds', () => { const first = resolveRequestTrackingId(undefined, 42); diff --git a/src/request/cancel.ts b/packages/host-kit/src/internal/request-cancel.ts similarity index 100% rename from src/request/cancel.ts rename to packages/host-kit/src/internal/request-cancel.ts diff --git a/src/request/progress.ts b/packages/host-kit/src/internal/request-progress.ts similarity index 100% rename from src/request/progress.ts rename to packages/host-kit/src/internal/request-progress.ts diff --git a/src/utils/__tests__/retry.test.ts b/packages/host-kit/src/internal/retry.test.ts similarity index 96% rename from src/utils/__tests__/retry.test.ts rename to packages/host-kit/src/internal/retry.test.ts index 91c60077aa..c5cfdfb2f3 100644 --- a/src/utils/__tests__/retry.test.ts +++ b/packages/host-kit/src/internal/retry.test.ts @@ -2,9 +2,9 @@ import { test, vi } from 'vitest'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { AppError } from '@agent-device/kernel/errors'; -import { retryWithPolicy } from '../retry.ts'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../diagnostics.ts'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; +import { retryWithPolicy } from './retry.ts'; +import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from './diagnostics.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; test('retryWithPolicy retries until success', async () => { let attempts = 0; diff --git a/src/utils/retry.ts b/packages/host-kit/src/internal/retry.ts similarity index 99% rename from src/utils/retry.ts rename to packages/host-kit/src/internal/retry.ts index fe10078ac7..46d17b7b21 100644 --- a/src/utils/retry.ts +++ b/packages/host-kit/src/internal/retry.ts @@ -77,6 +77,7 @@ export class Deadline { } } +// fallow-ignore-next-line complexity export async function retryWithPolicy( fn: (context: RetryAttemptContext) => Promise, policy: Partial = {}, diff --git a/src/utils/__tests__/shell-quote.test.ts b/packages/host-kit/src/internal/shell-quote.test.ts similarity index 92% rename from src/utils/__tests__/shell-quote.test.ts rename to packages/host-kit/src/internal/shell-quote.test.ts index ed0cfd1aa7..17094de555 100644 --- a/src/utils/__tests__/shell-quote.test.ts +++ b/packages/host-kit/src/internal/shell-quote.test.ts @@ -1,6 +1,6 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; -import { shellQuote, shellQuoteIfNeeded } from '../shell-quote.ts'; +import { shellQuote, shellQuoteIfNeeded } from './shell-quote.ts'; test('shellQuote single-quotes and escapes POSIX shell arguments', () => { assert.equal(shellQuote('plain'), "'plain'"); diff --git a/src/utils/shell-quote.ts b/packages/host-kit/src/internal/shell-quote.ts similarity index 100% rename from src/utils/shell-quote.ts rename to packages/host-kit/src/internal/shell-quote.ts diff --git a/src/utils/timeouts.ts b/packages/host-kit/src/internal/timeouts.ts similarity index 100% rename from src/utils/timeouts.ts rename to packages/host-kit/src/internal/timeouts.ts diff --git a/packages/host-kit/src/internal/tmp-dir.fixtures.ts b/packages/host-kit/src/internal/tmp-dir.fixtures.ts new file mode 100644 index 0000000000..d26147671c --- /dev/null +++ b/packages/host-kit/src/internal/tmp-dir.fixtures.ts @@ -0,0 +1,13 @@ +import fs from 'node:fs'; +import fsPromises from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +// fallow-ignore-next-line code-duplication +export async function mkdtempForTest(prefix: string): Promise { + return fsPromises.mkdtemp(path.join(os.tmpdir(), prefix)); +} + +export function mkdtempForTestSync(prefix: string): string { + return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); +} diff --git a/src/utils/__tests__/version.test.ts b/packages/host-kit/src/internal/version.test.ts similarity index 56% rename from src/utils/__tests__/version.test.ts rename to packages/host-kit/src/internal/version.test.ts index 7f041213e4..6e7e164f59 100644 --- a/src/utils/__tests__/version.test.ts +++ b/packages/host-kit/src/internal/version.test.ts @@ -2,9 +2,10 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { afterEach, test, vi } from 'vitest'; -import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -import { resetAllProcessMemosForTests } from '../ttl-memo.ts'; -import { findProjectRoot, readVersion } from '../version.ts'; +import { mkdtempForTestSync } from './tmp-dir.fixtures.ts'; +import { resetAllProcessMemosForTests } from '@agent-device/kernel/ttl-memo'; +import { resolveAgentDeviceProjectRoot } from './project-root.ts'; +import { findProjectRoot, readVersion } from './version.ts'; afterEach(() => { vi.restoreAllMocks(); @@ -19,12 +20,6 @@ function countPackageJsonReads(): () => number { ).length; } -// The package version and the project root are immutable for the life of a -// process: nothing can rewrite the running code's own package.json in a way -// the process is allowed to observe. Every CLI invocation reads both several -// times (daemon reuse check, transport client version, help header), so they -// are computed once per process and reset between tests. - test('readVersion parses each root package.json once per process', () => { const root = mkdtempForTestSync('agent-device-version-memo-'); try { @@ -84,3 +79,52 @@ test('findProjectRoot walks the ancestor chain once per process', () => { assert.equal(findProjectRoot(), root); assert.equal(existsSpy.mock.calls.length, walkCalls); }); + +test('the resolver walks past a workspace-package manifest to the agent-device root', () => { + const root = mkdtempForTestSync('agent-device-project-root-'); + try { + fs.writeFileSync( + path.join(root, 'package.json'), + JSON.stringify({ name: 'agent-device', version: '9.9.9' }), + ); + const moduleDir = path.join(root, 'packages', 'capture-kit', 'src'); + fs.mkdirSync(moduleDir, { recursive: true }); + fs.writeFileSync( + path.join(root, 'packages', 'capture-kit', 'package.json'), + JSON.stringify({ name: '@agent-device/capture-kit', version: '0.0.0' }), + ); + + assert.equal(resolveAgentDeviceProjectRoot(moduleDir), root); + assert.equal(readVersion(resolveAgentDeviceProjectRoot(moduleDir)), '9.9.9'); + } finally { + resetAllProcessMemosForTests(); + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test('the resolver falls back to the nearest manifest when none names agent-device', () => { + const root = mkdtempForTestSync('agent-device-project-root-fallback-'); + try { + fs.writeFileSync( + path.join(root, 'package.json'), + JSON.stringify({ name: 'vendored-fork', version: '1.0.0' }), + ); + const nested = path.join(root, 'lib', 'deep'); + fs.mkdirSync(nested, { recursive: true }); + + assert.equal(resolveAgentDeviceProjectRoot(nested), root); + } finally { + resetAllProcessMemosForTests(); + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test('from this source tree, the project root is the agent-device manifest, not this package', () => { + const resolved = findProjectRoot(); + const manifest = JSON.parse(fs.readFileSync(path.join(resolved, 'package.json'), 'utf8')) as { + name?: string; + version?: string; + }; + assert.equal(manifest.name, 'agent-device'); + assert.equal(readVersion(), manifest.version); +}); diff --git a/packages/host-kit/src/internal/version.ts b/packages/host-kit/src/internal/version.ts new file mode 100644 index 0000000000..bc10587fea --- /dev/null +++ b/packages/host-kit/src/internal/version.ts @@ -0,0 +1,33 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { resolveAgentDeviceProjectRoot } from './project-root.ts'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; + +const versionMemo = createTtlMemo(); +const projectRootMemo = createTtlMemo<'self', string>(); + +export function readVersion(root: string = findProjectRoot()): string { + const memoized = versionMemo.get(root); + if (memoized !== undefined) return memoized; + + let pkg: { version?: unknown }; + try { + pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as { + version?: unknown; + }; + } catch { + return '0.0.0'; + } + const version = typeof pkg.version === 'string' ? pkg.version : '0.0.0'; + versionMemo.set(root, version); + return version; +} + +export function findProjectRoot(): string { + const memoized = projectRootMemo.get('self'); + if (memoized !== undefined) return memoized; + const resolved = resolveAgentDeviceProjectRoot(path.dirname(fileURLToPath(import.meta.url))); + projectRootMemo.set('self', resolved); + return resolved; +} diff --git a/packages/host-kit/src/process.ts b/packages/host-kit/src/process.ts new file mode 100644 index 0000000000..e4e77c1e99 --- /dev/null +++ b/packages/host-kit/src/process.ts @@ -0,0 +1,32 @@ +export { + expandProcessTree, + type HostProcessIdentityObservation, + type HostProcessInfo, + isProcessAlive, + isProcessGroupAlive, + isProcessZombie, + listHostProcesses, + readHostProcessIdentityObservations, + readProcessCommand, + readProcessStartTime, + signalPidsBestEffort, + signalProcessGroupBestEffort, + stopPidsWithEscalation, + uniquePositivePids, + waitForProcessExit, +} from './internal/host-process.ts'; +export { reapOwnedProcessRecordsAtStartup } from './internal/owned-process-reaper.ts'; +export { + createOwnedProcessRecordStore, + type OwnedProcessRecordStore, + readOwnedProcessRecordFile, +} from './internal/owned-process-record.ts'; +export { + classifyOwnerLiveness, + classifyOwnerLivenessFromObservation, + type OwnerIdentity, + ownerIdentityDiffers, + ownerIdentityMatches, + type OwnerLiveness, + readCurrentOwnerIdentity, +} from './internal/owner-identity.ts'; diff --git a/packages/host-kit/src/request.ts b/packages/host-kit/src/request.ts new file mode 100644 index 0000000000..ee98a01e7b --- /dev/null +++ b/packages/host-kit/src/request.ts @@ -0,0 +1,11 @@ +export { + clearRequestAbortRegistration, + clearRequestCanceled, + getRequestSignal, + isRequestCanceled, + markRequestCanceled, + registerRequestAbort, + resolveRequestTrackingId, + throwIfRequestCanceled, +} from './internal/request-cancel.ts'; +export { emitRequestProgress, withRequestProgressSink } from './internal/request-progress.ts'; diff --git a/packages/host-kit/src/retry.ts b/packages/host-kit/src/retry.ts new file mode 100644 index 0000000000..a4aee8ade6 --- /dev/null +++ b/packages/host-kit/src/retry.ts @@ -0,0 +1,2 @@ +export { Deadline, isEnvTruthy, retryWithPolicy } from './internal/retry.ts'; +export { sleep } from './internal/timeouts.ts'; diff --git a/packages/host-kit/src/version.ts b/packages/host-kit/src/version.ts new file mode 100644 index 0000000000..ca16de62bf --- /dev/null +++ b/packages/host-kit/src/version.ts @@ -0,0 +1 @@ +export { findProjectRoot, readVersion } from './internal/version.ts'; diff --git a/packages/host-kit/tsconfig.json b/packages/host-kit/tsconfig.json new file mode 100644 index 0000000000..935c871a4d --- /dev/null +++ b/packages/host-kit/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist-types", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/packages/kernel/package.json b/packages/kernel/package.json index c4b0dee2ac..a3b741b2a3 100644 --- a/packages/kernel/package.json +++ b/packages/kernel/package.json @@ -5,41 +5,77 @@ "type": "module", "description": "Foundational vocabulary for agent-device: errors, device identity, snapshot model, shared contracts. Internal workspace package bundled into the published agent-device artifact.", "exports": { - "./errors": { - "types": "./src/errors.ts", - "default": "./src/errors.ts" + "./bounds": { + "types": "./src/bounds.ts", + "default": "./src/bounds.ts" + }, + "./collections": { + "types": "./src/collections.ts", + "default": "./src/collections.ts" + }, + "./contracts": { + "types": "./src/contracts.ts", + "default": "./src/contracts.ts" }, "./device": { "types": "./src/device.ts", "default": "./src/device.ts" }, - "./snapshot": { - "types": "./src/snapshot.ts", - "default": "./src/snapshot.ts" + "./device-isolation": { + "types": "./src/device-isolation.ts", + "default": "./src/device-isolation.ts" }, - "./contracts": { - "types": "./src/contracts.ts", - "default": "./src/contracts.ts" + "./errors": { + "types": "./src/errors.ts", + "default": "./src/errors.ts" }, - "./collections": { - "types": "./src/collections.ts", - "default": "./src/collections.ts" + "./keyed-lock": { + "types": "./src/keyed-lock.ts", + "default": "./src/keyed-lock.ts" + }, + "./location-coordinates": { + "types": "./src/location-coordinates.ts", + "default": "./src/location-coordinates.ts" + }, + "./record": { + "types": "./src/record.ts", + "default": "./src/record.ts" }, "./rect": { "types": "./src/rect.ts", "default": "./src/rect.ts" }, + "./rect-center": { + "types": "./src/rect-center.ts", + "default": "./src/rect-center.ts" + }, "./redaction": { "types": "./src/redaction.ts", "default": "./src/redaction.ts" }, - "./bounds": { - "types": "./src/bounds.ts", - "default": "./src/bounds.ts" + "./scoped-provider": { + "types": "./src/scoped-provider.ts", + "default": "./src/scoped-provider.ts" }, - "./keyed-lock": { - "types": "./src/keyed-lock.ts", - "default": "./src/keyed-lock.ts" + "./scroll-indicator": { + "types": "./src/scroll-indicator.ts", + "default": "./src/scroll-indicator.ts" + }, + "./snapshot": { + "types": "./src/snapshot.ts", + "default": "./src/snapshot.ts" + }, + "./source-value": { + "types": "./src/source-value.ts", + "default": "./src/source-value.ts" + }, + "./success-text": { + "types": "./src/success-text.ts", + "default": "./src/success-text.ts" + }, + "./ttl-memo": { + "types": "./src/ttl-memo.ts", + "default": "./src/ttl-memo.ts" } } } diff --git a/packages/kernel/src/collections.ts b/packages/kernel/src/collections.ts index 64538f193a..4722319bd2 100644 --- a/packages/kernel/src/collections.ts +++ b/packages/kernel/src/collections.ts @@ -1,3 +1,27 @@ export function uniqueStrings(values: readonly string[]): string[] { return Array.from(new Set(values)); } + +export function canonicalJson(value: unknown): string { + return JSON.stringify(sortKeysDeep(value)); +} + +function sortKeysDeep(value: unknown): unknown { + if (Array.isArray(value)) return value.map(sortKeysDeep); + if (!value || typeof value !== 'object') return value; + const record = value as Record; + return Object.fromEntries( + Object.keys(record) + .sort() + .map((key) => [key, sortKeysDeep(record[key])]), + ); +} + +// Linear-time trim; the regex form trips CodeQL js/polynomial-redos. +export function trimEdgeDashes(value: string): string { + let start = 0; + let end = value.length; + while (start < end && value[start] === '-') start += 1; + while (end > start && value[end - 1] === '-') end -= 1; + return value.slice(start, end); +} diff --git a/src/utils/device-isolation.ts b/packages/kernel/src/device-isolation.ts similarity index 100% rename from src/utils/device-isolation.ts rename to packages/kernel/src/device-isolation.ts diff --git a/packages/kernel/src/errors.ts b/packages/kernel/src/errors.ts index c6d756f8d5..5b611c2942 100644 --- a/packages/kernel/src/errors.ts +++ b/packages/kernel/src/errors.ts @@ -69,7 +69,7 @@ export type ErrorCause = { * lifted onto the normalized error, stripped from details. * - `processExitError` + `stdout`/`stderr`/`exitCode` — marks a wrap of a real * process exit so normalizeError can surface the first meaningful stderr line; - * build these via `execFailureDetails`/`requireExecSuccess` in src/utils/exec.ts + * build these via `execFailureDetails`/`requireExecSuccess` in @agent-device/host-kit/command * rather than by hand. * - `retriable` — typed retry signal hoisted to the wire error shape. * - `reason` — machine-dispatchable sub-classification within a code. diff --git a/src/utils/__tests__/keyed-lock.test.ts b/packages/kernel/src/keyed-lock.test.ts similarity index 97% rename from src/utils/__tests__/keyed-lock.test.ts rename to packages/kernel/src/keyed-lock.test.ts index d5941d138a..5fc2bf8259 100644 --- a/src/utils/__tests__/keyed-lock.test.ts +++ b/packages/kernel/src/keyed-lock.test.ts @@ -1,6 +1,6 @@ import { test, expect, vi } from 'vitest'; import assert from 'node:assert/strict'; -import { withKeyedLock } from '../keyed-lock.ts'; +import { withKeyedLock } from './keyed-lock.ts'; test('withKeyedLock serializes work per key', async () => { const locks = new Map>(); diff --git a/src/utils/location-coordinates.ts b/packages/kernel/src/location-coordinates.ts similarity index 100% rename from src/utils/location-coordinates.ts rename to packages/kernel/src/location-coordinates.ts diff --git a/src/utils/parsing.ts b/packages/kernel/src/record.ts similarity index 100% rename from src/utils/parsing.ts rename to packages/kernel/src/record.ts diff --git a/src/utils/__tests__/rect-center.test.ts b/packages/kernel/src/rect-center.test.ts similarity index 82% rename from src/utils/__tests__/rect-center.test.ts rename to packages/kernel/src/rect-center.test.ts index be5004466c..a6e025c825 100644 --- a/src/utils/__tests__/rect-center.test.ts +++ b/packages/kernel/src/rect-center.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest'; -import { interiorCoordinate, pointInsideRect } from '../rect-center.ts'; +import { interiorCoordinate, pointInsideRect } from './rect-center.ts'; test('interiorCoordinate preserves one-pixel edge controls', () => { expect(interiorCoordinate(0, 1)).toBe(0); diff --git a/src/utils/rect-center.ts b/packages/kernel/src/rect-center.ts similarity index 100% rename from src/utils/rect-center.ts rename to packages/kernel/src/rect-center.ts diff --git a/src/utils/scoped-provider.ts b/packages/kernel/src/scoped-provider.ts similarity index 66% rename from src/utils/scoped-provider.ts rename to packages/kernel/src/scoped-provider.ts index ba4b2a7d33..b6e64a8239 100644 --- a/src/utils/scoped-provider.ts +++ b/packages/kernel/src/scoped-provider.ts @@ -25,3 +25,16 @@ export function createScopedProvider( }, }; } + +export function withMethodScope( + target: T, + runScope: (task: () => Promise) => Promise, +): T { + return new Proxy(target, { + get(proxyTarget, property, receiver) { + const value = Reflect.get(proxyTarget, property, receiver); + if (typeof value !== 'function') return value; + return (...args: unknown[]) => runScope(async () => await value.apply(proxyTarget, args)); + }, + }); +} diff --git a/src/utils/scroll-indicator.ts b/packages/kernel/src/scroll-indicator.ts similarity index 100% rename from src/utils/scroll-indicator.ts rename to packages/kernel/src/scroll-indicator.ts diff --git a/src/utils/source-value.ts b/packages/kernel/src/source-value.ts similarity index 97% rename from src/utils/source-value.ts rename to packages/kernel/src/source-value.ts index 64d1f22a27..0020cc7084 100644 --- a/src/utils/source-value.ts +++ b/packages/kernel/src/source-value.ts @@ -19,6 +19,7 @@ export function buildPrimaryEnvVarName(key: string): string { .toUpperCase()}`; } +// fallow-ignore-next-line complexity export function parseSourceValue( definition: SourceValueDefinition, value: unknown, @@ -115,6 +116,7 @@ export function parseBooleanLiteral(value: string): boolean | undefined { return undefined; } +// fallow-ignore-next-line complexity function parseEnumSetValue( definition: SourceValueDefinition, value: unknown, @@ -135,3 +137,5 @@ function parseEnumSetValue( `Invalid value for "${rawKey}" in ${sourceLabel}. Expected boolean-like value for enum flag.`, ); } + +export type EnvMap = Record; diff --git a/src/utils/success-text.ts b/packages/kernel/src/success-text.ts similarity index 100% rename from src/utils/success-text.ts rename to packages/kernel/src/success-text.ts diff --git a/src/utils/__tests__/ttl-memo.test.ts b/packages/kernel/src/ttl-memo.test.ts similarity index 96% rename from src/utils/__tests__/ttl-memo.test.ts rename to packages/kernel/src/ttl-memo.test.ts index 5e81380d9c..8b889676c8 100644 --- a/src/utils/__tests__/ttl-memo.test.ts +++ b/packages/kernel/src/ttl-memo.test.ts @@ -1,6 +1,6 @@ import { expect, test, vi } from 'vitest'; -import { createTtlMemo } from '../ttl-memo.ts'; +import { createTtlMemo } from './ttl-memo.ts'; const crossTestMemo = createTtlMemo({ ttlMs: 60_000 }); diff --git a/src/utils/ttl-memo.ts b/packages/kernel/src/ttl-memo.ts similarity index 100% rename from src/utils/ttl-memo.ts rename to packages/kernel/src/ttl-memo.ts diff --git a/packages/maestro/src/internal/shared.ts b/packages/maestro/src/internal/shared.ts index 1b9b0d841c..fa9dd74c66 100644 --- a/packages/maestro/src/internal/shared.ts +++ b/packages/maestro/src/internal/shared.ts @@ -1,37 +1,11 @@ -import type { Point, Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; +import type { SnapshotNode } from '@agent-device/kernel/snapshot'; export function stripUndefined>(value: T): T { return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as T; } -export function canonicalJson(value: unknown): string { - return JSON.stringify(sortKeysDeep(value)); -} - -function sortKeysDeep(value: unknown): unknown { - if (Array.isArray(value)) return value.map(sortKeysDeep); - if (!value || typeof value !== 'object') return value; - const record = value as Record; - return Object.fromEntries( - Object.keys(record) - .sort() - .map((key) => [key, sortKeysDeep(record[key])]), - ); -} - -export function pointInsideRect(rect: Rect): Point { - return { - x: interiorCoordinate(rect.x, rect.width), - y: interiorCoordinate(rect.y, rect.height), - }; -} - -function interiorCoordinate(origin: number, size: number): number { - if (size <= 1) return Math.floor(origin); - const min = Math.ceil(origin); - const max = Math.floor(origin + size - 1); - return Math.round(Math.min(max, Math.max(min, origin + size / 2))); -} +export { canonicalJson } from '@agent-device/kernel/collections'; +export { pointInsideRect } from '@agent-device/kernel/rect-center'; export function normalizeText(value: string): string { return value.trim().toLowerCase().replace(/\s+/g, ' '); diff --git a/packages/platform-apple/src/runner/__tests__/runner-lease-filesystem-boundary-faults.test.ts b/packages/platform-apple/src/runner/__tests__/runner-lease-filesystem-boundary-faults.test.ts index d3ffe80326..e4a6de8428 100644 --- a/packages/platform-apple/src/runner/__tests__/runner-lease-filesystem-boundary-faults.test.ts +++ b/packages/platform-apple/src/runner/__tests__/runner-lease-filesystem-boundary-faults.test.ts @@ -24,7 +24,7 @@ afterEach(() => { delete process.env.AGENT_DEVICE_IOS_RUNNER_LEASE_DIR; }); -/** Mirrors the canonical same-directory temp-path shape of src/utils/atomic-file.ts. */ +/** Mirrors the canonical same-directory temp-path shape of @agent-device/host-kit/file. */ function isPublishTemporaryPath(value: unknown, destination: string): boolean { if (typeof value !== 'string') return false; if (path.dirname(value) !== path.dirname(destination)) return false; diff --git a/packages/platform-apple/src/runner/__tests__/runner-session-fixtures.ts b/packages/platform-apple/src/runner/__tests__/runner-session-fixtures.ts index 4ea1efabbe..56a192f941 100644 --- a/packages/platform-apple/src/runner/__tests__/runner-session-fixtures.ts +++ b/packages/platform-apple/src/runner/__tests__/runner-session-fixtures.ts @@ -9,7 +9,7 @@ import type { RunnerSession } from '../runner-session-types.ts'; // Fabricated runner sessions, leases, background children, and transport // payloads shared by the runner-session tests. The child pids here are made up // (`4242`): nothing in a test may deliver a real signal to them, so the owning -// tests mock the signal seam in `src/utils/host-process.ts` — see +// tests mock the signal seam in `@agent-device/host-kit/process` — see // `src/__tests__/hermetic-signal-setup.ts` and #1824. export function makeRunnerSession(overrides: Partial = {}): RunnerSession { @@ -150,7 +150,7 @@ type OwnerLivenessVerdict = | 'owner-state-dir-gone' | 'unknown'; -// classifyOwnerLiveness's real default (src/utils/owner-identity.ts) calls its +// classifyOwnerLiveness's real default (@agent-device/host-kit/process) calls its // OWN direct imports of isProcessAlive/readProcessStartTime rather than going // through the package host, so overriding those two host slots does not reach // it. This double is built over the SAME mocks a suite configures for them, so diff --git a/packages/platform-apple/src/runner/host.ts b/packages/platform-apple/src/runner/host.ts index ab5d181b6a..95af11fff8 100644 --- a/packages/platform-apple/src/runner/host.ts +++ b/packages/platform-apple/src/runner/host.ts @@ -136,7 +136,7 @@ export type IosPhysicalDeviceRunnerControl = { }; export type AppleRunnerHost = { - // Process execution (src/utils/exec.ts) + // Process execution (@agent-device/host-kit/command) runCmdStreaming(cmd: string, args: string[], options?: ExecStreamOptions): Promise; runCmdSync(cmd: string, args: string[], options?: ExecOptions): ExecResult; runCmdBackground( @@ -149,14 +149,14 @@ export type AppleRunnerHost = { message: string, extra?: Record | ((result: ExecResult) => Record), ): ExecResult; - // Diagnostics (src/utils/diagnostics.ts) + // Diagnostics (@agent-device/host-kit/diagnostics) emitDiagnostic(event: DiagnosticEventInput): void; withDiagnosticTimer( phase: string, fn: () => Promise | T, data?: Record, ): Promise; - // Retry (src/utils/retry.ts) + // Retry (@agent-device/host-kit/retry) retryWithPolicy( fn: (context: RetryAttemptContext) => Promise, policy?: RetryPolicy, @@ -164,17 +164,17 @@ export type AppleRunnerHost = { ): Promise; isEnvTruthy(value: string | undefined): boolean; deadlineFromTimeoutMs(timeoutMs: number, nowMs?: number): Deadline; - // Host process probes and signals (src/utils/host-process.ts) + // Host process probes and signals (@agent-device/host-kit/process) isProcessAlive(pid: number): boolean; isProcessGroupAlive(pid: number): boolean; readProcessStartTime(pid: number): string | null; readProcessCommand(pid: number): string | null; signalPidsBestEffort(pidsToSignal: readonly number[], signal: NodeJS.Signals): number; signalProcessGroupBestEffort(pid: number, signal: NodeJS.Signals): boolean; - // Project identity (src/utils/version.ts) + // Project identity (@agent-device/host-kit/version) findProjectRoot(): string; readVersion(root?: string): string; - // Locks (src/utils/process-lock.ts, src/utils/keyed-lock.ts) + // Locks (@agent-device/host-kit/file, @agent-device/kernel/keyed-lock) acquireProcessLock(params: { lockDirPath: string; owner: ProcessLockOwner; @@ -188,26 +188,26 @@ export type AppleRunnerHost = { key: string, task: () => Promise, ): Promise; - // Atomic publish (src/utils/atomic-file.ts) + // Atomic publish (@agent-device/host-kit/file) publishFileSync(options: { destination: string; contents: string; mode?: number; publish?: 'replace' | 'link-exclusive'; }): void; - // Owner liveness (src/utils/owner-identity.ts) + // Owner liveness (@agent-device/host-kit/process) classifyOwnerLiveness(params: { owner: { pid: number; startTime: string | null }; stateDir?: string; }): OwnerLiveness; - // Memoization (src/utils/ttl-memo.ts) + // Memoization (@agent-device/kernel/ttl-memo) createTtlMemo(options?: TtlMemoOptions): TtlMemo; - // Parsing helpers (src/utils/source-value.ts, src/utils/parsing.ts) + // Parsing helpers (@agent-device/kernel/source-value, @agent-device/kernel/record) parseBooleanLiteral(value: string): boolean | undefined; isRecord(value: unknown): value is Record; - // Simulator device-set isolation (src/utils/device-isolation.ts) + // Simulator device-set isolation (@agent-device/kernel/device-isolation) resolveIosSimulatorDeviceSetPath(flagValue: string | undefined): string | undefined; - // Request progress and cancellation (src/request/progress.ts, src/request/cancel.ts) + // Request progress and cancellation (@agent-device/host-kit/request) emitRequestProgress(event: RequestProgressEvent): void; getRequestSignal(requestId: string | undefined): AbortSignal | undefined; isRequestCanceled(requestId: string | undefined): boolean; diff --git a/packages/replay-test/src/internal/__tests__/session-test-discovery.test.ts b/packages/replay-test/src/internal/__tests__/session-test-discovery.test.ts index 4d26cc6801..9abad17d51 100644 --- a/packages/replay-test/src/internal/__tests__/session-test-discovery.test.ts +++ b/packages/replay-test/src/internal/__tests__/session-test-discovery.test.ts @@ -6,7 +6,8 @@ import { buildReplayTestSessionName, discoverReplayTestEntries, } from '../session-test-discovery.ts'; -import { buildReplayTestArtifactSlug, trimEdgeDashes } from '../session-test-artifacts.ts'; +import { trimEdgeDashes } from '@agent-device/kernel/collections'; +import { buildReplayTestArtifactSlug } from '../session-test-artifacts.ts'; import type { ReplayTestManifest, ReplayTestSource } from '../session-test-types.ts'; // Scheduler-owned discovery policy (#1478 P3b): which sources a --platform filter runs, which diff --git a/packages/replay-test/src/internal/session-test-artifacts.ts b/packages/replay-test/src/internal/session-test-artifacts.ts index 1c65e4c9dc..90dbc459c4 100644 --- a/packages/replay-test/src/internal/session-test-artifacts.ts +++ b/packages/replay-test/src/internal/session-test-artifacts.ts @@ -1,5 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; +import { trimEdgeDashes } from '@agent-device/kernel/collections'; import type { ReplayTestAttemptOutcome } from '@agent-device/replay-test'; const DEFAULT_TEST_ARTIFACTS_ROOT = '.agent-device/test-artifacts'; @@ -136,16 +137,3 @@ function isExistingFile(filePath: string): boolean { return false; } } - -/** - * Linear-time edge trim. The regex form (`/^-+|-+$/g`) backtracks - * polynomially on long dash runs (CodeQL js/polynomial-redos #27/#28), and - * these slugs are built from caller-supplied file paths. - */ -export function trimEdgeDashes(value: string): string { - let start = 0; - let end = value.length; - while (start < end && value[start] === '-') start += 1; - while (end > start && value[end - 1] === '-') end -= 1; - return value.slice(start, end); -} diff --git a/packages/replay-test/src/internal/session-test-discovery.ts b/packages/replay-test/src/internal/session-test-discovery.ts index 3dc97a766f..2e7cebb520 100644 --- a/packages/replay-test/src/internal/session-test-discovery.ts +++ b/packages/replay-test/src/internal/session-test-discovery.ts @@ -1,5 +1,5 @@ import path from 'node:path'; -import { trimEdgeDashes } from './session-test-artifacts.ts'; +import { trimEdgeDashes } from '@agent-device/kernel/collections'; import { AppError } from '@agent-device/kernel/errors'; import { isApplePlatform, type PlatformSelector } from '@agent-device/kernel/device'; import type { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26478f568f..711fd4d7d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: '@agent-device/contracts': specifier: workspace:* version: link:packages/contracts + '@agent-device/host-kit': + specifier: workspace:* + version: link:packages/host-kit '@agent-device/kernel': specifier: workspace:* version: link:packages/kernel @@ -183,9 +186,19 @@ importers: '@agent-device/contracts': specifier: workspace:* version: link:../contracts + '@agent-device/host-kit': + specifier: workspace:* + version: link:../host-kit '@agent-device/kernel': specifier: workspace:* version: link:../kernel + devDependencies: + '@types/pngjs': + specifier: ^6.0.5 + version: 6.0.5 + pngjs: + specifier: ^7.0.0 + version: 7.0.0 packages/contracts: dependencies: @@ -197,6 +210,28 @@ importers: specifier: ^4.9.0 version: 4.9.0 + packages/host-kit: + dependencies: + '@agent-device/contracts': + specifier: workspace:* + version: link:../contracts + '@agent-device/kernel': + specifier: workspace:* + version: link:../kernel + tar-stream: + specifier: ^3.2.0 + version: 3.2.0 + yauzl: + specifier: ^3.4.0 + version: 3.4.0 + devDependencies: + '@types/tar-stream': + specifier: ^3.1.4 + version: 3.1.4 + '@types/yauzl': + specifier: ^3.4.0 + version: 3.4.0 + packages/kernel: {} packages/maestro: diff --git a/scripts/__tests__/fallow-fixture-policy.test.ts b/scripts/__tests__/fallow-fixture-policy.test.ts index 00f2c000e8..1b798de120 100644 --- a/scripts/__tests__/fallow-fixture-policy.test.ts +++ b/scripts/__tests__/fallow-fixture-policy.test.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { test } from 'vitest'; import { mkdtempForTest } from '../../src/__tests__/test-utils/tmp-dir.ts'; -import { runCmd } from '../../src/utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; const REPOSITORY_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const FALLOW_CLI = path.join(REPOSITORY_ROOT, 'node_modules', 'fallow', 'bin', 'fallow'); diff --git a/scripts/__tests__/test-file-size-ratchet.test.ts b/scripts/__tests__/test-file-size-ratchet.test.ts index 2f65584834..7e23d3c3d9 100644 --- a/scripts/__tests__/test-file-size-ratchet.test.ts +++ b/scripts/__tests__/test-file-size-ratchet.test.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { expect, test } from 'vitest'; import { walkFiles } from '../lib/walk-files.ts'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; /** * Test-file size ratchet (AGENTS.md "Scope & shape": past 1,000 lines is architecture debt, @@ -44,7 +44,7 @@ const PINNED_TEST_FILE_LINES: Readonly> = Object.freeze({ 'packages/platform-apple/src/runner/__tests__/runner-client.test.ts': 1577, 'src/__tests__/client.test.ts': 1592, 'test/integration/provider-scenarios/android-lifecycle.test.ts': 1556, - 'src/utils/__tests__/daemon-client-lifecycle.test.ts': 1414, + 'src/utils/__tests__/daemon-client-lifecycle.test.ts': 1413, 'packages/platform-apple/src/runner/__tests__/runner-command-retry.test.ts': 1325, 'src/__tests__/cli-client-commands.test.ts': 1317, 'src/__tests__/cli-config.test.ts': 1282, diff --git a/scripts/check-affected/run.test.ts b/scripts/check-affected/run.test.ts index 58402cb696..4aecec7e59 100644 --- a/scripts/check-affected/run.test.ts +++ b/scripts/check-affected/run.test.ts @@ -8,7 +8,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { test } from 'node:test'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { STALE_NODE_MODULES_MESSAGE } from './lockfile-install-sync.ts'; import { CHECK_CATALOG } from './checks.ts'; import { DEFAULT_VITEST_MAX_WORKERS } from '../lib/vitest-concurrency.ts'; diff --git a/scripts/check-affected/run.ts b/scripts/check-affected/run.ts index 7c4d3a2759..6387d4535e 100644 --- a/scripts/check-affected/run.ts +++ b/scripts/check-affected/run.ts @@ -8,7 +8,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { runCmdStreaming, runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdStreaming, runCmdSync } from '@agent-device/host-kit/command'; import { checkLockfileInstallSync, STALE_NODE_MODULES_MESSAGE, diff --git a/scripts/check-replay-compat-provenance.ts b/scripts/check-replay-compat-provenance.ts index df4dcd8fda..ef0b52048a 100644 --- a/scripts/check-replay-compat-provenance.ts +++ b/scripts/check-replay-compat-provenance.ts @@ -16,20 +16,14 @@ import { REPLAY_COMPAT_RELEASED_TAGS, } from '../test/replay-compat/manifest.ts'; import { findProvenanceKindViolations } from '../test/replay-compat/provenance-rules.ts'; -import { runCmdSync } from '../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; +import { repoGit, requireUnshallowHistory } from './lib/repo-git.ts'; const repoRoot = path.resolve(import.meta.dirname, '..'); const corpusRoot = path.join(repoRoot, 'test', 'replay-compat'); -function git(args: string[]): string { - return runCmdSync('git', args, { cwd: repoRoot, allowFailure: true }).stdout.trim(); -} - -if (git(['rev-parse', '--is-shallow-repository']) === 'true') { - throw new Error( - 'Corpus provenance needs full history and tags. Run `git fetch --unshallow --tags` first.', - ); -} +const git = repoGit(repoRoot); +requireUnshallowHistory(git, 'Corpus provenance'); // An entry that escaped `mined` would also escape the history check below, so // the area rule is enforced here as well as in the unit lane. diff --git a/scripts/check-tmpdir-leaks-model.ts b/scripts/check-tmpdir-leaks-model.ts index 9d4ecfbfad..541c6fc1fb 100644 --- a/scripts/check-tmpdir-leaks-model.ts +++ b/scripts/check-tmpdir-leaks-model.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; -import { runCmdSync } from '../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; // Every test run (Vitest via scripts/vitest-tmpdir-global-setup.ts, node --test // via scripts/node-test-tmpdir.ts) redirects TMPDIR into one disposable, diff --git a/scripts/check-xctest-selection.ts b/scripts/check-xctest-selection.ts index 7d6977d301..6b8c36bbd1 100644 --- a/scripts/check-xctest-selection.ts +++ b/scripts/check-xctest-selection.ts @@ -35,7 +35,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { runCmdSync } from '../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import type { Platform } from './swift-conditional-compilation.ts'; import { parseDeclaredTestsByPlatform, diff --git a/scripts/coverage-changed/run.ts b/scripts/coverage-changed/run.ts index 0802d63cc3..8d7644b07c 100644 --- a/scripts/coverage-changed/run.ts +++ b/scripts/coverage-changed/run.ts @@ -12,7 +12,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { parseScriptArgs } from '../lib/cli-args.ts'; import { computeChangedCoverage, diff --git a/scripts/depgraph/affected.test.ts b/scripts/depgraph/affected.test.ts index 1a4737b17c..9b751b2627 100644 --- a/scripts/depgraph/affected.test.ts +++ b/scripts/depgraph/affected.test.ts @@ -218,7 +218,7 @@ test('text output bounds every list and says what it hid', () => { }); test('blast radius over the real tree agrees with check:affected and stays under 10s', async () => { - const file = 'src/utils/exec.ts'; + const file = 'packages/host-kit/src/internal/exec.ts'; const started = Date.now(); const radius = await computeBlastRadius(repoRoot, file); const elapsed = Date.now() - started; diff --git a/scripts/fuzz/envelope.ts b/scripts/fuzz/envelope.ts index 3df2744773..1dd928dfd0 100644 --- a/scripts/fuzz/envelope.ts +++ b/scripts/fuzz/envelope.ts @@ -14,7 +14,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { laneEnvelope } from '../lib/lane-envelope.ts'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import type { FuzzFailure } from './invariant.ts'; const FILENAME = 'run-envelope.json'; diff --git a/scripts/gate/check.ts b/scripts/gate/check.ts index 91a460da95..a9fe4c20ee 100644 --- a/scripts/gate/check.ts +++ b/scripts/gate/check.ts @@ -8,7 +8,7 @@ // launder a new step past review by regenerating it. import { pathToFileURL } from 'node:url'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { audit, formatFailures } from './audit.ts'; import { MANUAL_ONLY_OWNERS, UNPROVABLE_OWNERS } from './declarations.ts'; import { loadModel } from './model.ts'; diff --git a/scripts/gate/run.ts b/scripts/gate/run.ts index 9710dc4847..b7122b4418 100644 --- a/scripts/gate/run.ts +++ b/scripts/gate/run.ts @@ -10,7 +10,7 @@ import { pathToFileURL } from 'node:url'; import fs from 'node:fs'; import path from 'node:path'; -import { runCmdStreaming, runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdStreaming, runCmdSync } from '@agent-device/host-kit/command'; import { runEntrypoint } from '../lib/cli-entrypoint.ts'; import { CHECK_CATALOG, resolveCommand } from '../check-affected/checks.ts'; diff --git a/scripts/layering/model.ts b/scripts/layering/model.ts index 2dc99f55f0..4ca1aadc91 100644 --- a/scripts/layering/model.ts +++ b/scripts/layering/model.ts @@ -89,6 +89,7 @@ export const UNRANKED_ZONES: ReadonlySet = new Set([ // importer and concrete-platform authority; giving them a spine rank would duplicate that seam. 'platform-runtime', 'kernel', + 'host-kit', 'capture-kit', 'platform-apple', 'platform-android', diff --git a/scripts/layering/package-boundaries.test.ts b/scripts/layering/package-boundaries.test.ts index a48580d7fb..42037013eb 100644 --- a/scripts/layering/package-boundaries.test.ts +++ b/scripts/layering/package-boundaries.test.ts @@ -453,8 +453,44 @@ test('the real tree parses, declares, and passes R11', () => { true, 'capture-kit stays a private implementation package', ); - assert.deepEqual([...captureKitPackage.exportTargets.keys()], ['@agent-device/capture-kit']); + assert.deepEqual([...captureKitPackage.exportTargets.keys()].sort(), [ + '@agent-device/capture-kit', + '@agent-device/capture-kit/mobile-snapshot-semantics', + '@agent-device/capture-kit/png', + '@agent-device/capture-kit/png-resize', + '@agent-device/capture-kit/png-rgb-difference', + '@agent-device/capture-kit/png-size', + '@agent-device/capture-kit/png-worker-client', + '@agent-device/capture-kit/screenshot-density', + '@agent-device/capture-kit/screenshot-diff-pixels', + '@agent-device/capture-kit/snapshot-occlusion', + '@agent-device/capture-kit/snapshot-quality-backend-capabilities', + '@agent-device/capture-kit/snapshot-quality-verdict', + ]); assert.deepEqual([...captureKitPackage.workspaceDependencies].sort(), [ + '@agent-device/contracts', + '@agent-device/host-kit', + '@agent-device/kernel', + ]); + const hostKitPackage = packages.find((pkg) => pkg.name === '@agent-device/host-kit'); + assert.ok(hostKitPackage, 'host-kit package must exist'); + assert.equal( + JSON.parse(fs.readFileSync(path.join(repoRoot, 'packages/host-kit/package.json'), 'utf8')) + .private, + true, + 'host-kit stays a private implementation package', + ); + assert.deepEqual([...hostKitPackage.exportTargets.keys()].sort(), [ + '@agent-device/host-kit/archive', + '@agent-device/host-kit/command', + '@agent-device/host-kit/diagnostics', + '@agent-device/host-kit/file', + '@agent-device/host-kit/process', + '@agent-device/host-kit/request', + '@agent-device/host-kit/retry', + '@agent-device/host-kit/version', + ]); + assert.deepEqual([...hostKitPackage.workspaceDependencies].sort(), [ '@agent-device/contracts', '@agent-device/kernel', ]); diff --git a/scripts/layering/platform-package-policy.test.ts b/scripts/layering/platform-package-policy.test.ts index 307ad1f5ca..8dcf85ad18 100644 --- a/scripts/layering/platform-package-policy.test.ts +++ b/scripts/layering/platform-package-policy.test.ts @@ -385,7 +385,7 @@ test('platform packages may use capture-kit but no unrelated workspace implement ); assert.match( messages(sources).join('\n'), - /may import workspace code only from capture-kit, contracts, kernel, or xml/, + /may import workspace code only from capture-kit, host-kit, contracts, kernel, or xml/, ); } }); diff --git a/scripts/layering/platform-package-policy.ts b/scripts/layering/platform-package-policy.ts index 249dc02029..dca542aef8 100644 --- a/scripts/layering/platform-package-policy.ts +++ b/scripts/layering/platform-package-policy.ts @@ -263,6 +263,7 @@ function checkSource(file: string, source: string): LayeringViolation[] { !site.spec.startsWith('@agent-device/contracts/') && site.spec !== '@agent-device/capture-kit' && !site.spec.startsWith('@agent-device/capture-kit/') && + !site.spec.startsWith('@agent-device/host-kit/') && !site.spec.startsWith('@agent-device/kernel/') && site.spec !== '@agent-device/xml' && !isPackageOwnedFacadeTest(file, ownerFamily, site.spec) @@ -271,7 +272,7 @@ function checkSource(file: string, source: string): LayeringViolation[] { violation( file, site.line, - `platform-${ownerFamily} may import workspace code only from capture-kit, contracts, kernel, or xml; found '${site.spec}'`, + `platform-${ownerFamily} may import workspace code only from capture-kit, host-kit, contracts, kernel, or xml; found '${site.spec}'`, ), ); } diff --git a/scripts/lib/repo-git.ts b/scripts/lib/repo-git.ts new file mode 100644 index 0000000000..f7ea03ebb5 --- /dev/null +++ b/scripts/lib/repo-git.ts @@ -0,0 +1,15 @@ +import { runCmdSync } from '@agent-device/host-kit/command'; + +export type RepoGit = (args: string[]) => string; + +export function repoGit(repoRoot: string): RepoGit { + return (args) => runCmdSync('git', args, { cwd: repoRoot, allowFailure: true }).stdout.trim(); +} + +/** History-derivation checks cannot run against a shallow CI clone. */ +export function requireUnshallowHistory(git: RepoGit, subject: string): void { + if (git(['rev-parse', '--is-shallow-repository']) !== 'true') return; + throw new Error( + `${subject} needs full history and tags. Run \`git fetch --unshallow --tags\` first.`, + ); +} diff --git a/scripts/mutation/envelope.test.ts b/scripts/mutation/envelope.test.ts index ea026942d5..f088c5146e 100644 --- a/scripts/mutation/envelope.test.ts +++ b/scripts/mutation/envelope.test.ts @@ -7,7 +7,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { test } from 'node:test'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { laneEnvelope, LANE_ENVELOPE_SCHEMA_VERSION } from '../lib/lane-envelope.ts'; const repoRoot = path.resolve(import.meta.dirname, '../..'); diff --git a/scripts/mutation/modules.ts b/scripts/mutation/modules.ts index e337fa33ce..81e321e31e 100644 --- a/scripts/mutation/modules.ts +++ b/scripts/mutation/modules.ts @@ -87,8 +87,8 @@ export const KERNEL_MODULES: readonly KernelModule[] = [ { id: 'snapshot-occlusion', label: 'Snapshot occlusion (covered/not-covered) decisions', - mutate: ['src/snapshot/snapshot-occlusion.ts'], - owns: ['src/snapshot/snapshot-occlusion.ts'], + mutate: ['packages/capture-kit/src/snapshot-occlusion.ts'], + owns: ['packages/capture-kit/src/snapshot-occlusion.ts'], }, ]; diff --git a/scripts/mutation/run.ts b/scripts/mutation/run.ts index a4888dfcb0..a54bf59f03 100644 --- a/scripts/mutation/run.ts +++ b/scripts/mutation/run.ts @@ -15,7 +15,7 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { runCmdStreaming, runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdStreaming, runCmdSync } from '@agent-device/host-kit/command'; import { parseScriptArgs } from '../lib/cli-args.ts'; import { laneEnvelope } from '../lib/lane-envelope.ts'; import { diff --git a/scripts/mutation/selection.test.ts b/scripts/mutation/selection.test.ts index 137b102528..da2766e614 100644 --- a/scripts/mutation/selection.test.ts +++ b/scripts/mutation/selection.test.ts @@ -8,7 +8,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { after, test } from 'node:test'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { LANE_CANARY, shardMatrix, type ShardSpec } from './modules.ts'; import { affectedMatrixFor } from './run.ts'; diff --git a/scripts/mutation/test-scope.test.ts b/scripts/mutation/test-scope.test.ts index 9dc565738a..c29145b6af 100644 --- a/scripts/mutation/test-scope.test.ts +++ b/scripts/mutation/test-scope.test.ts @@ -21,7 +21,7 @@ test('the thread-hostile exclusion is derived from imports, not listed', () => { // Both reasons are represented: process.chdir (CLI capture harness) and a // worker inside a worker (the node:worker_threads PNG pipeline). assert.ok(files.includes('src/__tests__/cli-help.test.ts')); - assert.ok(files.includes('src/utils/__tests__/png-worker.test.ts')); + assert.ok(files.includes('packages/capture-kit/src/png-worker.test.ts')); // A pure decision-kernel test is never excluded — that would hide survivors. assert.ok(!files.includes('src/daemon/__tests__/ref-frame.test.ts')); assert.ok(files.every((file) => file.endsWith('.test.ts'))); diff --git a/scripts/mutation/test-scope.ts b/scripts/mutation/test-scope.ts index 9127825d9b..138ab0c8f9 100644 --- a/scripts/mutation/test-scope.ts +++ b/scripts/mutation/test-scope.ts @@ -27,7 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { readWorkspacePackages } from '../layering/package-boundaries.ts'; import { walkFiles } from '../lib/walk-files.ts'; import { isKernelTestFile, normalizePath } from './modules.ts'; diff --git a/scripts/mutation/workspace-aliases.test.ts b/scripts/mutation/workspace-aliases.test.ts index f8f0671493..6940f4d3db 100644 --- a/scripts/mutation/workspace-aliases.test.ts +++ b/scripts/mutation/workspace-aliases.test.ts @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import fs from 'node:fs'; import path from 'node:path'; import { test } from 'node:test'; import { workspaceSpecifierTargets } from '../layering/package-boundaries.ts'; @@ -8,7 +9,7 @@ const repoRoot = path.resolve(import.meta.dirname, '../..'); test('Stryker aliases every exported workspace subpath exactly', () => { const targets = workspaceSpecifierTargets(repoRoot); - const aliases = workspaceSourceAliases(repoRoot); + const aliases = workspaceSourceAliases(repoRoot, 'tracked-manifests'); for (const [specifier, target] of targets) { const matches = aliases.filter(({ find }) => find.test(specifier)); @@ -24,3 +25,40 @@ test('Stryker aliases every exported workspace subpath exactly', () => { false, ); }); + +test('disk manifests alias an untracked sandbox copy the tracked reader cannot see', () => { + fs.mkdirSync(path.join(repoRoot, '.tmp'), { recursive: true }); + const sandboxRoot = fs.mkdtempSync(path.join(repoRoot, '.tmp', 'stryker-sandbox-fixture-')); + try { + const packageDir = path.join(sandboxRoot, 'packages', 'kernel'); + fs.mkdirSync(path.join(packageDir, 'src'), { recursive: true }); + fs.writeFileSync( + path.join(packageDir, 'package.json'), + JSON.stringify({ + name: '@agent-device/kernel', + exports: { './errors': { default: './src/errors.ts' } }, + }), + ); + fs.writeFileSync(path.join(packageDir, 'src', 'errors.ts'), 'export const mutated = true;\n'); + + assert.deepEqual( + workspaceSourceAliases(sandboxRoot, 'tracked-manifests'), + [], + 'the git-backed reader must see nothing in an untracked copy', + ); + + const diskAliases = workspaceSourceAliases(sandboxRoot, 'disk-manifests'); + const match = diskAliases.filter(({ find }) => find.test('@agent-device/kernel/errors')); + assert.deepEqual( + match.map(({ replacement }) => replacement), + [path.join(sandboxRoot, 'packages', 'kernel', 'src', 'errors.ts')], + 'the disk reader must resolve the specifier inside the sandbox copy', + ); + assert.equal( + diskAliases.some(({ find }) => find.test('@agent-device/kernel/not-exported')), + false, + ); + } finally { + fs.rmSync(sandboxRoot, { recursive: true, force: true }); + } +}); diff --git a/scripts/mutation/workspace-aliases.ts b/scripts/mutation/workspace-aliases.ts index 05b50eaee8..0261dec36d 100644 --- a/scripts/mutation/workspace-aliases.ts +++ b/scripts/mutation/workspace-aliases.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs'; import path from 'node:path'; import { workspaceSpecifierTargets } from '../layering/package-boundaries.ts'; @@ -6,13 +7,55 @@ export type WorkspaceSourceAlias = { replacement: string; }; +export type ManifestSource = 'tracked-manifests' | 'disk-manifests'; + function exactSpecifier(specifier: string): RegExp { return new RegExp(`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`); } -/** Exact exported workspace-package aliases for Vitest inside Stryker's sandbox. */ -export function workspaceSourceAliases(repoRoot: string): WorkspaceSourceAlias[] { - return [...workspaceSpecifierTargets(repoRoot)].map(([specifier, target]) => ({ +function specifierTargets(repoRoot: string, source: ManifestSource): ReadonlyMap { + return source === 'tracked-manifests' + ? workspaceSpecifierTargets(repoRoot) + : diskSpecifierTargets(repoRoot); +} + +function diskSpecifierTargets(repoRoot: string): ReadonlyMap { + const targets = new Map(); + const packagesDir = path.join(repoRoot, 'packages'); + if (!fs.existsSync(packagesDir)) return targets; + for (const entry of fs.readdirSync(packagesDir)) { + for (const [specifier, target] of manifestSpecifierTargets(packagesDir, entry)) { + targets.set(specifier, target); + } + } + return targets; +} + +function* manifestSpecifierTargets( + packagesDir: string, + entry: string, +): Generator<[string, string]> { + const manifestPath = path.join(packagesDir, entry, 'package.json'); + if (!fs.existsSync(manifestPath)) return; + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { + name?: string; + exports?: Record; + }; + if (!manifest.name?.startsWith('@agent-device/')) return; + for (const [subpath, targetEntry] of Object.entries(manifest.exports ?? {})) { + const target = targetEntry?.default; + if (!target) continue; + const specifier = subpath === '.' ? manifest.name : `${manifest.name}/${subpath.slice(2)}`; + yield [specifier, path.join('packages', entry, target)]; + } +} + +/** Exact exported workspace-package aliases for Vitest, from the declared manifest source. */ +export function workspaceSourceAliases( + repoRoot: string, + source: ManifestSource, +): WorkspaceSourceAlias[] { + return [...specifierTargets(repoRoot, source)].map(([specifier, target]) => ({ find: exactSpecifier(specifier), replacement: path.join(repoRoot, target), })); diff --git a/scripts/node-test-tmpdir.test.ts b/scripts/node-test-tmpdir.test.ts index 22b44ccab8..cd490d2207 100644 --- a/scripts/node-test-tmpdir.test.ts +++ b/scripts/node-test-tmpdir.test.ts @@ -5,7 +5,7 @@ import os from 'node:os'; import path from 'node:path'; import { test } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { runCmd, runCmdBackground } from '../src/utils/exec.ts'; +import { runCmd, runCmdBackground } from '@agent-device/host-kit/command'; import { liveRunDirectoryConsumers, pruneAbandonedRunDirectories, @@ -318,7 +318,7 @@ test('a run whose owner alone was killed keeps its directory while a child still `import fs from 'node:fs'; import os from 'node:os'; import { test } from 'node:test'; -import { runCmdDetached } from '../src/utils/exec.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; test('probe starts a long-lived detached child that inherits TMPDIR, then waits to be killed', async () => { const childPid = runCmdDetached(process.execPath, ['-e', 'setTimeout(() => {}, 60_000)']); diff --git a/scripts/node-test-tmpdir.ts b/scripts/node-test-tmpdir.ts index bb1af509bc..35cfb89dac 100644 --- a/scripts/node-test-tmpdir.ts +++ b/scripts/node-test-tmpdir.ts @@ -27,7 +27,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { runCmdBackground } from '../src/utils/exec.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; import { TEST_RUN_TMP_PREFIX, TEST_RUN_TMP_ROOT, diff --git a/scripts/perf/cli.ts b/scripts/perf/cli.ts index 486cc5eb83..57ee22c56a 100644 --- a/scripts/perf/cli.ts +++ b/scripts/perf/cli.ts @@ -1,5 +1,5 @@ import { performance } from 'node:perf_hooks'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { resolveCliArgv, REPO_ROOT } from './config.ts'; import type { BatchStepSpec } from './scenario.ts'; import type { CliResult } from './types.ts'; diff --git a/scripts/swift-toolchain-tmpdir.test.ts b/scripts/swift-toolchain-tmpdir.test.ts index edc5d3f8a3..22926193c5 100644 --- a/scripts/swift-toolchain-tmpdir.test.ts +++ b/scripts/swift-toolchain-tmpdir.test.ts @@ -4,7 +4,7 @@ import os from 'node:os'; import path from 'node:path'; import { test } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { runCmd, runCmdBackground } from '../src/utils/exec.ts'; +import { runCmd, runCmdBackground } from '@agent-device/host-kit/command'; const REPOSITORY_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const WRAPPER = path.join(REPOSITORY_ROOT, 'scripts', 'swift-toolchain-tmpdir.ts'); diff --git a/scripts/swift-toolchain-tmpdir.ts b/scripts/swift-toolchain-tmpdir.ts index 23fec43a5a..b4ce23e768 100644 --- a/scripts/swift-toolchain-tmpdir.ts +++ b/scripts/swift-toolchain-tmpdir.ts @@ -5,7 +5,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { runCmdBackground } from '../src/utils/exec.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; const [command, ...args] = process.argv.slice(2); if (!command) { diff --git a/scripts/vitest-tmpdir-global-setup.test.ts b/scripts/vitest-tmpdir-global-setup.test.ts index f151107845..0154ae7328 100644 --- a/scripts/vitest-tmpdir-global-setup.test.ts +++ b/scripts/vitest-tmpdir-global-setup.test.ts @@ -5,7 +5,7 @@ import os from 'node:os'; import path from 'node:path'; import { test } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { runCmd } from '../src/utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { TEST_RUN_TMP_PREFIX, TEST_RUN_TMP_ROOT } from './check-tmpdir-leaks-model.ts'; const REPOSITORY_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); diff --git a/scripts/wire-compat/run.ts b/scripts/wire-compat/run.ts index b7b38b1f6b..ff5f86112e 100644 --- a/scripts/wire-compat/run.ts +++ b/scripts/wire-compat/run.ts @@ -19,7 +19,7 @@ */ import path from 'node:path'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { repoGit, requireUnshallowHistory } from '../lib/repo-git.ts'; import { digestDeclaration } from '../../test/wire-compat/declaration-digest.ts'; import { digestWireSurface, @@ -32,15 +32,8 @@ import { compareWireLedgers } from './model.ts'; const repoRoot = path.resolve(import.meta.dirname, '..', '..'); -function git(args: string[]): string { - return runCmdSync('git', args, { cwd: repoRoot, allowFailure: true }).stdout.trim(); -} - -if (git(['rev-parse', '--is-shallow-repository']) === 'true') { - throw new Error( - 'Daemon wire compatibility needs full history and tags. Run `git fetch --unshallow --tags` first.', - ); -} +const git = repoGit(repoRoot); +requireUnshallowHistory(git, 'Daemon wire compatibility'); /** Released tags newest-first by semver, not by tag-creation order. */ function releasedTagsNewestFirst(): string[] { diff --git a/src/__tests__/apple-runner-package-source.test.ts b/src/__tests__/apple-runner-package-source.test.ts index 64bd5d972e..b9d86012db 100644 --- a/src/__tests__/apple-runner-package-source.test.ts +++ b/src/__tests__/apple-runner-package-source.test.ts @@ -3,7 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { onTestFinished, test } from 'vitest'; -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts index 45cc29fa86..d9a5f6d952 100644 --- a/src/__tests__/cli-agent-cdp.test.ts +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -2,11 +2,11 @@ import fs from 'node:fs'; import assert from 'node:assert/strict'; import { afterEach, test, vi } from 'vitest'; -vi.mock('../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', () => ({ runCmdStreaming: vi.fn(), })); -import { runCmdStreaming } from '../utils/exec.ts'; +import { runCmdStreaming } from '@agent-device/host-kit/command'; import { AGENT_CDP_PACKAGE, buildAgentCdpNpmExecArgs, diff --git a/src/__tests__/cli-auth-diagnostics.test.ts b/src/__tests__/cli-auth-diagnostics.test.ts index d8f6eed3b1..0df3a38443 100644 --- a/src/__tests__/cli-auth-diagnostics.test.ts +++ b/src/__tests__/cli-auth-diagnostics.test.ts @@ -1,7 +1,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import fs from 'node:fs'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { runCliCapture } from './cli-capture.ts'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; diff --git a/src/__tests__/cli-client-commands.test.ts b/src/__tests__/cli-client-commands.test.ts index 6f267346db..42866b11bd 100644 --- a/src/__tests__/cli-client-commands.test.ts +++ b/src/__tests__/cli-client-commands.test.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { test } from 'vitest'; import assert from 'node:assert/strict'; -import { PNG } from '../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { tryRunClientBackedCommand } from '../cli/commands/router.ts'; import { runCliCapture } from './cli-capture.ts'; import type { diff --git a/src/__tests__/cli-debug-symbols.test.ts b/src/__tests__/cli-debug-symbols.test.ts index 4f6d6fd738..8803040b58 100644 --- a/src/__tests__/cli-debug-symbols.test.ts +++ b/src/__tests__/cli-debug-symbols.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import path from 'node:path'; import { test } from 'vitest'; -import { withCommandExecutorOverride } from '../utils/exec.ts'; +import { withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { runCliCapture } from './cli-capture.ts'; import { mkdtempForTest } from './test-utils/tmp-dir.ts'; diff --git a/src/__tests__/cli-device-status.test.ts b/src/__tests__/cli-device-status.test.ts index 59c1b7adcd..34ac4f1221 100644 --- a/src/__tests__/cli-device-status.test.ts +++ b/src/__tests__/cli-device-status.test.ts @@ -2,12 +2,12 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { test, vi } from 'vitest'; -import { readCurrentOwnerIdentity } from '../utils/owner-identity.ts'; +import { readCurrentOwnerIdentity } from '@agent-device/host-kit/process'; import { runCliCapture } from './cli-capture.ts'; import { publishDaemonRegistration } from './test-utils/device-claim-store.ts'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; -vi.mock('../utils/host-process.ts', async (importOriginal) => +vi.mock('@agent-device/host-kit/process', async (importOriginal) => (await import('./test-utils/host-process-mock.ts')).pinOwnProcessStartTime(importOriginal), ); diff --git a/src/__tests__/cli-diff.test.ts b/src/__tests__/cli-diff.test.ts index 6dfe82de65..8a26b992d6 100644 --- a/src/__tests__/cli-diff.test.ts +++ b/src/__tests__/cli-diff.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { PNG } from '../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import type { DaemonResponse } from '../daemon/client/daemon-client.ts'; import { runCliCapture as captureCli, diff --git a/src/__tests__/cli-react-devtools.test.ts b/src/__tests__/cli-react-devtools.test.ts index f9896250fa..76b21cd51c 100644 --- a/src/__tests__/cli-react-devtools.test.ts +++ b/src/__tests__/cli-react-devtools.test.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import { afterEach, test, vi } from 'vitest'; import assert from 'node:assert/strict'; -vi.mock('../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', () => ({ runCmdStreaming: vi.fn(), })); @@ -11,7 +11,7 @@ vi.mock('../client/client-react-devtools-companion.ts', () => ({ stopReactDevtoolsCompanion: vi.fn(), })); -import { runCmdStreaming } from '../utils/exec.ts'; +import { runCmdStreaming } from '@agent-device/host-kit/command'; import { ensureReactDevtoolsCompanion, stopReactDevtoolsCompanion, diff --git a/src/__tests__/cli-web.test.ts b/src/__tests__/cli-web.test.ts index 9f74552a03..66f34010d6 100644 --- a/src/__tests__/cli-web.test.ts +++ b/src/__tests__/cli-web.test.ts @@ -3,7 +3,7 @@ import fs from 'node:fs'; import { test } from 'vitest'; import { runCliCapture } from './cli-capture.ts'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; -import { withCommandExecutorOverride } from '../utils/exec.ts'; +import { withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { installFakeManagedAgentBrowser, withNodeRuntime, diff --git a/src/__tests__/client-metro-companion.test.ts b/src/__tests__/client-metro-companion.test.ts index 15e221d820..cc3485029c 100644 --- a/src/__tests__/client-metro-companion.test.ts +++ b/src/__tests__/client-metro-companion.test.ts @@ -4,24 +4,26 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; -vi.mock('../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), runCmdDetached: vi.fn(), })); - -vi.mock('../utils/host-process.ts', () => ({ +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: vi.fn(), readProcessCommand: vi.fn(), readProcessStartTime: vi.fn(), waitForProcessExit: vi.fn(), })); -import { runCmdDetached } from '../utils/exec.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; import { isProcessAlive, readProcessCommand, readProcessStartTime, waitForProcessExit, -} from '../utils/host-process.ts'; +} from '@agent-device/host-kit/process'; + import { ensureMetroCompanion, stopMetroCompanion } from '../metro/client-metro-companion.ts'; import { ensureReactDevtoolsCompanion } from '../client/client-react-devtools-companion.ts'; diff --git a/src/__tests__/client-metro-startup-cleanup.test.ts b/src/__tests__/client-metro-startup-cleanup.test.ts index cd25a945da..b2adc994f9 100644 --- a/src/__tests__/client-metro-startup-cleanup.test.ts +++ b/src/__tests__/client-metro-startup-cleanup.test.ts @@ -4,17 +4,19 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; -vi.mock('../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), runCmdDetached: vi.fn(), runCmdSync: vi.fn(), })); - -vi.mock('../utils/host-process.ts', () => ({ +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), waitForProcessExit: vi.fn(), })); -import { runCmdDetached } from '../utils/exec.ts'; -import { waitForProcessExit } from '../utils/host-process.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; +import { waitForProcessExit } from '@agent-device/host-kit/process'; + import { prepareMetroRuntime } from '../metro/client-metro.ts'; afterEach(() => { diff --git a/src/__tests__/client-metro.test.ts b/src/__tests__/client-metro.test.ts index 0c57ef6dc9..97b404851d 100644 --- a/src/__tests__/client-metro.test.ts +++ b/src/__tests__/client-metro.test.ts @@ -14,7 +14,7 @@ import { createAgentDeviceClient } from '../agent-device-client.ts'; import { readMetroSessionHints } from '../metro/metro-session-hints.ts'; import { resolveDaemonPaths } from '../daemon/config.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { isProcessAlive, waitForProcessExit } from '../utils/host-process.ts'; +import { isProcessAlive, waitForProcessExit } from '@agent-device/host-kit/process'; const TEST_TOKEN = 'agent-device-proxy-test-token'; diff --git a/src/__tests__/daemon-entrypoint.test.ts b/src/__tests__/daemon-entrypoint.test.ts index 87e8de8f4b..d88c62a617 100644 --- a/src/__tests__/daemon-entrypoint.test.ts +++ b/src/__tests__/daemon-entrypoint.test.ts @@ -10,8 +10,9 @@ import { cleanupDownloadableArtifact, trackDownloadableArtifact, } from '../daemon/artifact-tracking.ts'; -import { runCmdBackground } from '../utils/exec.ts'; -import { isProcessAlive, waitForProcessExit } from '../utils/host-process.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; +import { isProcessAlive, waitForProcessExit } from '@agent-device/host-kit/process'; + import { closeLoopbackServer, listenOnLoopback, waitForHttpOk } from './test-utils/loopback.ts'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; diff --git a/src/__tests__/eager-closure-budgets.ts b/src/__tests__/eager-closure-budgets.ts index b3108d9f6f..a8cc04988a 100644 --- a/src/__tests__/eager-closure-budgets.ts +++ b/src/__tests__/eager-closure-budgets.ts @@ -106,7 +106,7 @@ export function discoverFacadeEntryFiles(repoRoot: string): string[] { */ export const FACADE_BUDGETS: Readonly> = Object.freeze({ // --- @agent-device/ad-replay --- - 'packages/ad-replay/src/index.ts': 58, + 'packages/ad-replay/src/index.ts': 57, // --- @agent-device/ad-script --- 'packages/ad-script/src/index.ts': 37, @@ -122,6 +122,27 @@ export const FACADE_BUDGETS: Readonly> = Object.freeze({ // --- @agent-device/capture-kit --- // R60 review: audio-probe split into descriptor/status/recovery/live-process modules (+3 files). 'packages/capture-kit/src/index.ts': 32, + 'packages/capture-kit/src/png-resize.ts': 18, + 'packages/capture-kit/src/png-rgb-difference.ts': 1, + 'packages/capture-kit/src/png-size.ts': 3, + 'packages/capture-kit/src/png-worker-client.ts': 10, + 'packages/capture-kit/src/png.ts': 3, + 'packages/capture-kit/src/screenshot-density.ts': 6, + 'packages/capture-kit/src/screenshot-diff-pixels.ts': 1, + 'packages/capture-kit/src/mobile-snapshot-semantics.ts': 10, + 'packages/capture-kit/src/snapshot-occlusion.ts': 10, + 'packages/capture-kit/src/snapshot-quality-backend-capabilities.ts': 1, + 'packages/capture-kit/src/snapshot-quality-verdict.ts': 2, + + // --- @agent-device/host-kit --- + 'packages/host-kit/src/archive.ts': 9, + 'packages/host-kit/src/command.ts': 7, + 'packages/host-kit/src/diagnostics.ts': 3, + 'packages/host-kit/src/file.ts': 12, + 'packages/host-kit/src/process.ts': 12, + 'packages/host-kit/src/request.ts': 5, + 'packages/host-kit/src/retry.ts': 6, + 'packages/host-kit/src/version.ts': 4, // --- @agent-device/contracts --- 'packages/contracts/src/alert-contract.ts': 1, @@ -235,12 +256,21 @@ export const FACADE_BUDGETS: Readonly> = Object.freeze({ 'packages/kernel/src/errors.ts': 2, // Added by #2041: keyed async lock moved from src/utils for the extracted IME lifecycle. 'packages/kernel/src/keyed-lock.ts': 1, + 'packages/kernel/src/rect-center.ts': 2, 'packages/kernel/src/rect.ts': 1, + 'packages/kernel/src/device-isolation.ts': 1, + 'packages/kernel/src/location-coordinates.ts': 3, + 'packages/kernel/src/record.ts': 3, + 'packages/kernel/src/scoped-provider.ts': 1, + 'packages/kernel/src/source-value.ts': 3, + 'packages/kernel/src/success-text.ts': 1, + 'packages/kernel/src/ttl-memo.ts': 1, 'packages/kernel/src/redaction.ts': 1, + 'packages/kernel/src/scroll-indicator.ts': 1, 'packages/kernel/src/snapshot.ts': 1, // --- @agent-device/maestro --- - 'packages/maestro/src/index.ts': 105, + 'packages/maestro/src/index.ts': 106, // --- @agent-device/platform-*: ADR-0019's metadata-eager/implementation-lazy façades. Each // evaluates only itself; every implementation sits behind a function-scoped `await import`. @@ -276,7 +306,7 @@ export const FACADE_BUDGETS: Readonly> = Object.freeze({ 'packages/provider-webdriver/src/index.ts': 49, // --- @agent-device/replay-test --- - 'packages/replay-test/src/index.ts': 19, + 'packages/replay-test/src/index.ts': 20, // --- @agent-device/selectors --- 'packages/selectors/src/ast.ts': 16, @@ -323,7 +353,7 @@ export const FACADE_BUDGETS: Readonly> = Object.freeze({ */ export const HUB_BUDGETS: Readonly> = Object.freeze({ // 363 -> 365 in #2004, which cuts per-invocation work and pays two modules for it: - // `src/utils/ttl-memo.ts` (version.ts now resolves the package version and the project root + // `@agent-device/kernel/ttl-memo` (version.ts now resolves the package version and the project root // once per process instead of re-reading package.json several times an invocation) and // `src/daemon/client/daemon-launch-spec.ts` (the launch-entry probe, split out of the 726-line // daemon-client-lifecycle.ts). Both run on the path every local command already takes, so @@ -337,7 +367,7 @@ export const HUB_BUDGETS: Readonly> = Object.freeze({ // readers), `input-audience.ts` (who may write a key), and `common-input-fields.ts` (the table // itself). Every command schema already evaluated all three concerns; the growth is three more // module records for the same code, with no new subtree behind any of them. - 'src/cli.ts': 368, + 'src/cli.ts': 380, 'src/platform-runtime.ts': 47, 'src/core/command-descriptor/registry.ts': 67, 'src/core/command-descriptor/platform-execution-entry.ts': 3, @@ -345,7 +375,7 @@ export const HUB_BUDGETS: Readonly> = Object.freeze({ // R64 removes the perf plugin facet and keeps collector binding behind the selected runtime // operation. Teardown now owns only neutral durable-resource cleanup; platform collectors load // through the perf host when an admitted operation actually runs. - 'src/daemon/session-teardown.ts': 60, + 'src/daemon/session-teardown.ts': 68, }); /** diff --git a/src/__tests__/exec-wrap-guard.test.ts b/src/__tests__/exec-wrap-guard.test.ts index 3410af03ea..f49ff92259 100644 --- a/src/__tests__/exec-wrap-guard.test.ts +++ b/src/__tests__/exec-wrap-guard.test.ts @@ -12,7 +12,7 @@ import { test } from 'vitest'; // whose details carry `processExitError: true`. Before PR #1072 ~49 call sites // spread the trio by hand and silently missed the flag, so users saw // " exited with code N" with no cause. The fix is centralized in -// `execFailureDetails` / `requireExecSuccess` (src/utils/exec.ts); this guard +// `execFailureDetails` / `requireExecSuccess` (@agent-device/host-kit/command); this guard // keeps new call sites from drifting back to inline spreads. // // A site that intentionally rebuilds the trio WITHOUT the flag (reachable at @@ -99,7 +99,7 @@ test('AppError details never rebuild the exec stdout/stderr/exitCode trio inline [], `Inline exec stdout/stderr/exitCode spreads in AppError details miss the processExitError flag, ` + `so normalizeError cannot surface the stderr excerpt. Build the details with ` + - `execFailureDetails()/requireExecSuccess() from src/utils/exec.ts, or — when the throw is ` + + `execFailureDetails()/requireExecSuccess() from @agent-device/host-kit/command, or — when the throw is ` + `reachable at exit 0 or the excerpt would degrade the message — opt out with a ` + `"// ${ALLOW_MARKER} " comment above the new AppError(...) call. Violations:\n` + violations.join('\n'), diff --git a/src/__tests__/hermetic-signal-setup.ts b/src/__tests__/hermetic-signal-setup.ts index 2fbaf09035..f381b37219 100644 --- a/src/__tests__/hermetic-signal-setup.ts +++ b/src/__tests__/hermetic-signal-setup.ts @@ -20,7 +20,7 @@ import { afterAll, afterEach, expect } from 'vitest'; // Signal 0 is a liveness probe (`isProcessAlive`), not a signal; it stays free. // Tests that drive a real kill path against a fabricated pid mock the seam — // `signalPidsBestEffort` / `signalProcessGroupBestEffort` in -// `src/utils/host-process.ts` for the direct writes, the exec/tool-provider +// `@agent-device/host-kit/process` for the direct writes, the exec/tool-provider // seam for a spawned `pkill` — the same way they already mock the liveness // reads. @@ -151,7 +151,7 @@ function failOnRefusedSignals(scope: string): void { `${scope} tried to signal ${count} process(es) this vitest worker did not spawn. ` + 'A unit test may signal only itself and its own direct children (and their process ' + 'groups); mock the seam instead — signalPidsBestEffort / signalProcessGroupBestEffort in ' + - 'src/utils/host-process.ts, the exec or tool-provider seam for a spawned pkill, or ' + + '@agent-device/host-kit/process, the exec or tool-provider seam for a spawned pkill, or ' + `vi.spyOn(process, 'kill'). If the pid is a descendant this test really owns, signal the ` + `direct child (or its group via the negative pid) rather than the grandchild.\n${report}`, ); diff --git a/src/__tests__/limrun-runtime.test.ts b/src/__tests__/limrun-runtime.test.ts index 83d77d4ed0..dd3446fbeb 100644 --- a/src/__tests__/limrun-runtime.test.ts +++ b/src/__tests__/limrun-runtime.test.ts @@ -6,8 +6,8 @@ import { LimrunRuntime } from '../sdk/limrun.ts'; import { createExpiredProviderLeaseReleaser } from '../daemon/provider-lease-expiry.ts'; import type { SimulatorLease } from '../daemon/lease-registry.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { runCmd } from '../utils/exec.ts'; -import { readVersion } from '../utils/version.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { readVersion } from '@agent-device/host-kit/version'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; type LimrunInstancePage = { @@ -114,8 +114,8 @@ vi.mock('@limrun/api/instance-client', () => ({ })), })); -vi.mock('../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(async () => ({ stdout: '', stderr: '', exitCode: 0 })), diff --git a/src/__tests__/process-memo-setup.ts b/src/__tests__/process-memo-setup.ts index 12319b1287..10f9a6f807 100644 --- a/src/__tests__/process-memo-setup.ts +++ b/src/__tests__/process-memo-setup.ts @@ -1,6 +1,6 @@ import { afterEach } from 'vitest'; -import { resetAllProcessMemosForTests } from '../utils/ttl-memo.ts'; +import { resetAllProcessMemosForTests } from '@agent-device/kernel/ttl-memo'; afterEach(() => { resetAllProcessMemosForTests(); diff --git a/src/__tests__/test-utils/device-claim-store.ts b/src/__tests__/test-utils/device-claim-store.ts index 84bcfe2736..f1acf204cb 100644 --- a/src/__tests__/test-utils/device-claim-store.ts +++ b/src/__tests__/test-utils/device-claim-store.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import { afterEach } from 'vitest'; import { mkdtempForTestSync } from './tmp-dir.ts'; import type { DeviceClaimReconciler } from '../../daemon/device-claims.ts'; -import type { OwnerIdentity } from '../../utils/owner-identity.ts'; +import { type OwnerIdentity } from '@agent-device/host-kit/process'; export type IsolatedDeviceClaimStore = { /** Temporary root holding both the claim store and the daemon state dir. */ diff --git a/src/__tests__/test-utils/exec-faults.ts b/src/__tests__/test-utils/exec-faults.ts index 7ab4ac762b..102ed552c3 100644 --- a/src/__tests__/test-utils/exec-faults.ts +++ b/src/__tests__/test-utils/exec-faults.ts @@ -1,4 +1,8 @@ -import type { CommandExecutorOverride, ExecOptions, ExecResult } from '../../utils/exec.ts'; +import { + type CommandExecutorOverride, + type ExecOptions, + type ExecResult, +} from '@agent-device/host-kit/command'; export type RecordedCommand = Readonly<{ command: string; diff --git a/src/__tests__/test-utils/fake-apple-tool.ts b/src/__tests__/test-utils/fake-apple-tool.ts index 613dec75e2..384d65313b 100644 --- a/src/__tests__/test-utils/fake-apple-tool.ts +++ b/src/__tests__/test-utils/fake-apple-tool.ts @@ -2,7 +2,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import type { AppleToolProvider } from '../../platforms/apple/core/tool-provider.ts'; import { withAppleToolProvider } from '../../platforms/apple/core/tool-provider.ts'; -import { execFailureDetails, type ExecResult } from '../../utils/exec.ts'; +import { execFailureDetails, type ExecResult } from '@agent-device/host-kit/command'; import { IOS_DEVICE } from './device-fixtures.ts'; /** diff --git a/src/__tests__/test-utils/filesystem-boundary-faults.ts b/src/__tests__/test-utils/filesystem-boundary-faults.ts index 029f029bfc..fb8dcf8833 100644 --- a/src/__tests__/test-utils/filesystem-boundary-faults.ts +++ b/src/__tests__/test-utils/filesystem-boundary-faults.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { test, vi } from 'vitest'; -import { isAtomicPublishTemporaryPath } from '../../utils/atomic-file.ts'; +import { isAtomicPublishTemporaryPath } from '@agent-device/host-kit/file'; import { mkdtempForTestSync } from './tmp-dir.ts'; export type FilesystemErrno = 'EIO' | 'ENOSPC' | 'EMFILE'; diff --git a/src/__tests__/test-utils/host-process-mock.ts b/src/__tests__/test-utils/host-process-mock.ts index f40a5b0b1e..95f9b8098a 100644 --- a/src/__tests__/test-utils/host-process-mock.ts +++ b/src/__tests__/test-utils/host-process-mock.ts @@ -1,28 +1,7 @@ -type HostProcessModule = typeof import('../../utils/host-process.ts'); +type HostProcessModule = typeof import('@agent-device/host-kit/process'); type HostProcessIdentityObservation = - import('../../utils/host-process.ts').HostProcessIdentityObservation; + import('@agent-device/host-kit/process').HostProcessIdentityObservation; -/** - * `vi.mock` factory for `utils/host-process.ts` in tests that seed a "live" - * owner from the test process's own identity (readCurrentOwnerIdentity and - * friends) and later have production code re-read it during classification. - * - * readProcessStartTime shells out to `ps` with a 1s timeout; under full-suite - * CPU contention a re-read can miss that deadline and return null, mismatching - * the seeded value and flipping a genuinely-live owner to 'owner-process-dead'. - * This factory assigns our own pid a stable synthetic start time, then serves - * that value so every read is self-consistent without a subprocess. Other pids read as null, same - * as a real `ps` miss. isProcessAlive stays real (a plain `kill(pid, 0)` - * syscall, not a subprocess), so fabricated dead-pid fixtures still classify - * as dead through that non-flaky check. - * - * Usage: `vi.mock('/utils/host-process.ts', async (importOriginal) => - * (await import('/test-utils/host-process-mock.ts')).pinOwnProcessStartTime(importOriginal))` - */ -// Consumed by three suites, but only through `(await import(...)).pinOwnProcessStartTime` -// inside `vi.mock` factories — vitest hoists those above static imports, so the dynamic -// form is required and fallow cannot trace the consumers statically. -// fallow-ignore-next-line unused-export export async function pinOwnProcessStartTime( importOriginal: () => Promise, ): Promise { @@ -30,6 +9,7 @@ export async function pinOwnProcessStartTime( const ownStartTime = 'test-process-start-time'; return { ...actual, + readCurrentOwnerIdentity: () => ({ pid: process.pid, startTime: ownStartTime }), readProcessStartTime: (pid: number) => { if (pid !== process.pid) return null; return ownStartTime; diff --git a/src/__tests__/update-check.test.ts b/src/__tests__/update-check.test.ts index 247bfed833..19744d5b42 100644 --- a/src/__tests__/update-check.test.ts +++ b/src/__tests__/update-check.test.ts @@ -4,11 +4,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; -vi.mock('../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', () => ({ runCmdDetached: vi.fn(), })); -import { runCmdDetached } from '../utils/exec.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; import { maybeRunUpgradeNotifier, runUpdateCheckWorker } from '../utils/update-check.ts'; function makeTempStateDir(): string { diff --git a/src/__tests__/upload-client.test.ts b/src/__tests__/upload-client.test.ts index 4633e381d1..f9de9991dc 100644 --- a/src/__tests__/upload-client.test.ts +++ b/src/__tests__/upload-client.test.ts @@ -7,7 +7,7 @@ import path from 'node:path'; import { once } from 'node:events'; import { uploadArtifact } from '../remote/upload-client.ts'; import type { UploadProgressEvent } from '../remote/upload-progress.ts'; -import { runCmdSync, withCommandExecutorOverride } from '../utils/exec.ts'; +import { runCmdSync, withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; const TEST_TOKEN = 'agent-device-upload-test-token'; diff --git a/src/agent-device-client.ts b/src/agent-device-client.ts index 124222f541..1793da8209 100644 --- a/src/agent-device-client.ts +++ b/src/agent-device-client.ts @@ -78,7 +78,7 @@ import { writeMetroSessionHints, type MetroSessionHints, } from './metro/metro-session-hints.ts'; -import { isRecord } from './utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import { readScreenshotResultData } from './utils/screenshot-result.ts'; type ProjectedSystemCommandClient = ProjectedNavigationCommandClient & diff --git a/src/bin.ts b/src/bin.ts index 7797305bab..beef52ef75 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -24,7 +24,7 @@ function runVersionFastPath(argv: string[]): boolean { process.stdout.write(`${__AGENT_DEVICE_VERSION__}\n`); return true; } - import('./utils/version.ts') + import('@agent-device/host-kit/version') .then(({ readVersion }) => { process.stdout.write(`${readVersion()}\n`); }) diff --git a/src/cli-schema/cli-config.ts b/src/cli-schema/cli-config.ts index 31358ebe6b..4d6d4027c8 100644 --- a/src/cli-schema/cli-config.ts +++ b/src/cli-schema/cli-config.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; import { mergeDefinedFlags } from '../utils/merge-flags.ts'; import { type FlagKey } from '../commands/cli-grammar/flag-types.ts'; -import { expandUserHomePath, resolveUserPath } from '../utils/path-resolution.ts'; +import { expandUserHomePath, resolveUserPath } from '@agent-device/host-kit/file'; import { getConfigurableOptionSpecs, getOptionSpec, @@ -12,7 +12,7 @@ import { } from './option-schema.ts'; import { parseInstallSourceConfig } from '../utils/install-source-config.ts'; import { RETIRED_SCREENSHOT_MAX_SIZE } from '@agent-device/contracts/capture'; -import type { EnvMap } from '../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; export function resolveConfigBackedFlagDefaults(options: { command: string | null; diff --git a/src/cli-schema/cli-help-examples.test.ts b/src/cli-schema/cli-help-examples.test.ts index 5a00507393..7dc67824a1 100644 --- a/src/cli-schema/cli-help-examples.test.ts +++ b/src/cli-schema/cli-help-examples.test.ts @@ -7,7 +7,7 @@ import { buildCommandUsageText, buildUsageText, helpTopicIds } from './cli-help. import { listCliCommandNames } from '../command-catalog.ts'; import { readInputFromCli } from '../commands/cli-grammar.ts'; import { findCommandMetadata, isCommandName } from '../commands/command-metadata.ts'; -import { readVersion } from '../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; // Help is the agent-facing contract, and agents copy its example lines verbatim, so an example the // CLI schema rejects is a broken command surface — not a docs nit (#1765: `help react-native` diff --git a/src/cli-schema/cli-help.ts b/src/cli-schema/cli-help.ts index 83647f9ec5..36bbc4ca97 100644 --- a/src/cli-schema/cli-help.ts +++ b/src/cli-schema/cli-help.ts @@ -16,7 +16,7 @@ import { type FlagKey, } from './command-schema.ts'; import { buildCommandUsage } from './usage.ts'; -import { readVersion } from '../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; import { renderCliHelpOverview } from './cli-help-overview.ts'; const CONFIGURATION_LINES = [ diff --git a/src/cli-schema/option-schema.ts b/src/cli-schema/option-schema.ts index 9a00797148..9f92cddb72 100644 --- a/src/cli-schema/option-schema.ts +++ b/src/cli-schema/option-schema.ts @@ -1,4 +1,4 @@ -import { buildPrimaryEnvVarName, parseSourceValue } from '../utils/source-value.ts'; +import { buildPrimaryEnvVarName, parseSourceValue } from '@agent-device/kernel/source-value'; import { listCliCommandNames } from '../command-catalog.ts'; import { getCliCommandSchema, diff --git a/src/cli.ts b/src/cli.ts index dfa39cff55..ca639e69d7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -10,7 +10,7 @@ import { import { resolveRemoteRequestDiagnosticsPath } from './daemon/session-store.ts'; import { printHumanError, printJson } from './utils/output.ts'; import { exitAfterFlush } from './utils/process-exit.ts'; -import { readVersion } from './utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; import { pathToFileURL } from 'node:url'; import { sendToDaemon } from './daemon/client/daemon-client.ts'; import fs from 'node:fs'; @@ -34,7 +34,7 @@ import { getDiagnosticsMeta, registerDiagnosticSensitiveValue, withDiagnosticsScope, -} from './utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import { resolveDaemonPaths } from './daemon/config.ts'; import { applyDefaultPlatformBinding, resolveBindingSettings } from './utils/session-binding.ts'; import { resolveCliOptions } from './cli/resolve-cli-options.ts'; diff --git a/src/cli/auth-session.ts b/src/cli/auth-session.ts index 725a7162c8..a58becae2d 100644 --- a/src/cli/auth-session.ts +++ b/src/cli/auth-session.ts @@ -1,9 +1,9 @@ import fs from 'node:fs'; import path from 'node:path'; -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; import type { CliFlags } from '@agent-device/contracts/command'; -import type { EnvMap } from '../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import { readCloudJsonResponse } from './cloud-response.ts'; const DEFAULT_CLOUD_BASE_URL = 'https://cloud.agent-device.dev'; diff --git a/src/cli/commands/agent-cdp.ts b/src/cli/commands/agent-cdp.ts index b651da22f5..092d8c2cbc 100644 --- a/src/cli/commands/agent-cdp.ts +++ b/src/cli/commands/agent-cdp.ts @@ -1,4 +1,4 @@ -import { runCmdStreaming } from '../../utils/exec.ts'; +import { runCmdStreaming } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; import { isRemoteBridgeBackend } from './remote-bridge.ts'; import type { SessionRuntimeHints } from '@agent-device/kernel/contracts'; diff --git a/src/cli/commands/connection-presentation.ts b/src/cli/commands/connection-presentation.ts index 34e83478cc..7257a8f184 100644 --- a/src/cli/commands/connection-presentation.ts +++ b/src/cli/commands/connection-presentation.ts @@ -1,7 +1,7 @@ import { fingerprint, type RemoteConnectionState } from '../../remote/remote-connection-state.ts'; import type { ConnectVerification } from '../connection/connect-provider-adapters.ts'; import { connectionProviderLeaseKind } from '../connection/provider-policy.ts'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; export type ConnectReadiness = ConnectVerification & { preparationMessage: string; diff --git a/src/cli/commands/connection.ts b/src/cli/commands/connection.ts index 102f039655..2efdff6ca2 100644 --- a/src/cli/commands/connection.ts +++ b/src/cli/commands/connection.ts @@ -32,7 +32,7 @@ import { stopReactDevtoolsCleanup, } from './connection-runtime.ts'; import { writeCommandOutput } from './shared.ts'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; import type { LeaseBackend } from '@agent-device/kernel/contracts'; import type { CliFlags } from '@agent-device/contracts/command'; import type { ClientCommandHandler } from './router-types.ts'; diff --git a/src/cli/commands/device.ts b/src/cli/commands/device.ts index a928c6c363..92b261c99f 100644 --- a/src/cli/commands/device.ts +++ b/src/cli/commands/device.ts @@ -5,7 +5,7 @@ import { inspectDeviceClaims, type InspectedDeviceClaim, } from '../../daemon/device-claim-inspection.ts'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; import { writeCommandOutput } from './shared.ts'; import type { ClientCommandHandler } from './router-types.ts'; diff --git a/src/cli/commands/generic.ts b/src/cli/commands/generic.ts index ea5aeafaa4..25bc7f7c9a 100644 --- a/src/cli/commands/generic.ts +++ b/src/cli/commands/generic.ts @@ -4,7 +4,7 @@ import type { CommandName } from '../../commands/command-metadata.ts'; import type { CliOutput } from '../../commands/command-contract.ts'; import type { ReplaySuiteResult } from '@agent-device/contracts/replay'; import type { CliFlags } from '@agent-device/contracts/command'; -import { readCommandMessage } from '../../utils/success-text.ts'; +import { readCommandMessage } from '@agent-device/kernel/success-text'; import { isNonDefaultResponseLevel } from '@agent-device/kernel/contracts'; import { writeCommandOutput } from './shared.ts'; import type { ClientBackedCliCommandName } from './client-backed.ts'; diff --git a/src/cli/commands/react-devtools.ts b/src/cli/commands/react-devtools.ts index 1b0dcfd6d3..c0ce8ba032 100644 --- a/src/cli/commands/react-devtools.ts +++ b/src/cli/commands/react-devtools.ts @@ -1,4 +1,4 @@ -import { runCmdStreaming } from '../../utils/exec.ts'; +import { runCmdStreaming } from '@agent-device/host-kit/command'; import { ensureReactDevtoolsCompanion, stopReactDevtoolsCompanion, diff --git a/src/cli/commands/replay.ts b/src/cli/commands/replay.ts index 1773f4d6e0..933596ff60 100644 --- a/src/cli/commands/replay.ts +++ b/src/cli/commands/replay.ts @@ -4,7 +4,7 @@ import { exportReplayActionsToMaestro, MAESTRO_SELECTOR_PROJECTION } from '@agen import { AppError } from '@agent-device/kernel/errors'; import { parseReplayScriptDetailed, readReplayScriptMetadata } from '@agent-device/ad-script'; import { projectSelectorExpression } from '@agent-device/selectors'; -import { resolveUserPath } from '../../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; import { writeCommandOutput } from './shared.ts'; import type { ClientCommandHandler } from './router-types.ts'; diff --git a/src/cli/commands/screenshot.ts b/src/cli/commands/screenshot.ts index 02ca0ffc54..554f232322 100644 --- a/src/cli/commands/screenshot.ts +++ b/src/cli/commands/screenshot.ts @@ -2,7 +2,7 @@ import { formatScreenshotDiffText, formatSnapshotDiffText } from '../../utils/ou import type { ScreenshotDiffResult } from '../../screenshot-diff/screenshot-diff.ts'; import { AppError } from '@agent-device/kernel/errors'; import { isNonDefaultResponseLevel } from '@agent-device/kernel/contracts'; -import { resolveUserPath } from '../../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; import type { AgentDeviceBackend } from '../../backend.ts'; import type { AgentDeviceClient, CaptureScreenshotResult } from '../../agent-device-client.ts'; import { runCliCommand } from '../../commands/cli-runner.ts'; diff --git a/src/cli/connection/cloud-profile.ts b/src/cli/connection/cloud-profile.ts index 7028dd3400..37a8017b3a 100644 --- a/src/cli/connection/cloud-profile.ts +++ b/src/cli/connection/cloud-profile.ts @@ -1,7 +1,7 @@ import type { RemoteConfigProfile } from '../../remote/remote-config-schema.ts'; import { AppError } from '@agent-device/kernel/errors'; import type { CliFlags } from '@agent-device/contracts/command'; -import type { EnvMap } from '../../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import { resolveCloudAccessForConnect } from '../auth-session.ts'; import { readCloudJsonResponse } from '../cloud-response.ts'; import { persistAndResolveGeneratedProfile } from './generated-config.ts'; diff --git a/src/cli/connection/cloud-webdriver-profile.ts b/src/cli/connection/cloud-webdriver-profile.ts index c7fec3b9d4..ffa64093af 100644 --- a/src/cli/connection/cloud-webdriver-profile.ts +++ b/src/cli/connection/cloud-webdriver-profile.ts @@ -10,7 +10,7 @@ import type { PlatformSelector } from '@agent-device/kernel/device'; import type { CliFlags } from '@agent-device/contracts/command'; import fs from 'node:fs'; import path from 'node:path'; -import type { EnvMap } from '../../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import { readCloudDeviceFeatureProfileFields, readMetroProfileFields } from './profile-fields.ts'; import { persistAndResolveGeneratedProfile } from './generated-config.ts'; import { resolveRequestedLeaseBackend } from '../commands/connection-runtime.ts'; diff --git a/src/cli/connection/connect-provider-adapters.ts b/src/cli/connection/connect-provider-adapters.ts index f65bfd742c..572fbf5b63 100644 --- a/src/cli/connection/connect-provider-adapters.ts +++ b/src/cli/connection/connect-provider-adapters.ts @@ -4,8 +4,9 @@ import { verifyLimrunConnection } from '@agent-device/provider-limrun'; import { AppError } from '@agent-device/kernel/errors'; import { providerWebDriver } from '../../provider-webdriver.ts'; import { resolveRemoteConfigProfile } from '../../remote/remote-config.ts'; -import type { EnvMap } from '../../utils/env-map.ts'; -import { readVersion } from '../../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; +import { type EnvMap } from '@agent-device/kernel/source-value'; + import { resolveCloudConnectProfile } from './cloud-profile.ts'; import { resolveCloudWebDriverConnectProfile } from './cloud-webdriver-profile.ts'; import { resolveLimrunConnectProfile } from './limrun-profile.ts'; diff --git a/src/cli/connection/generated-config.ts b/src/cli/connection/generated-config.ts index c3127914c2..aebdf467aa 100644 --- a/src/cli/connection/generated-config.ts +++ b/src/cli/connection/generated-config.ts @@ -7,7 +7,7 @@ import type { ResolvedRemoteConfigProfile, } from '../../remote/remote-config-schema.ts'; import { AppError, asAppError } from '@agent-device/kernel/errors'; -import type { EnvMap } from '../../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import type { CliFlags } from '@agent-device/contracts/command'; import { profileToCliFlags } from '../remote-config-flags.ts'; diff --git a/src/cli/connection/limrun-profile.ts b/src/cli/connection/limrun-profile.ts index 9a50fe7d7b..20ad38b56d 100644 --- a/src/cli/connection/limrun-profile.ts +++ b/src/cli/connection/limrun-profile.ts @@ -2,7 +2,7 @@ import { resolveDaemonPaths } from '../../daemon/config.ts'; import type { RemoteConfigProfile } from '../../remote/remote-config-schema.ts'; import { AppError } from '@agent-device/kernel/errors'; import type { CliFlags } from '@agent-device/contracts/command'; -import type { EnvMap } from '../../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import { readMetroProfileFields } from './profile-fields.ts'; import { persistAndResolveGeneratedProfile } from './generated-config.ts'; import { resolveRequestedLeaseBackend } from '../commands/connection-runtime.ts'; diff --git a/src/cli/connection/proxy-profile.ts b/src/cli/connection/proxy-profile.ts index d8d4a22890..22f2658e9c 100644 --- a/src/cli/connection/proxy-profile.ts +++ b/src/cli/connection/proxy-profile.ts @@ -1,7 +1,7 @@ import type { RemoteConfigProfile } from '../../remote/remote-config-schema.ts'; import { AppError } from '@agent-device/kernel/errors'; import type { CliFlags } from '@agent-device/contracts/command'; -import type { EnvMap } from '../../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import { readMetroProfileFields } from './profile-fields.ts'; import { persistAndResolveGeneratedProfile } from './generated-config.ts'; import { resolveRequestedLeaseBackend } from '../commands/connection-runtime.ts'; diff --git a/src/cli/resolve-cli-options.ts b/src/cli/resolve-cli-options.ts index 9f75f5dd2c..e1fe640039 100644 --- a/src/cli/resolve-cli-options.ts +++ b/src/cli/resolve-cli-options.ts @@ -3,7 +3,7 @@ import { mergeDefinedFlags } from '../utils/merge-flags.ts'; import { finalizeParsedArgs, parseRawArgs } from './parser/args.ts'; import { resolveConfigBackedFlagDefaults } from '../cli-schema/cli-config.ts'; import { resolveRemoteConfigDefaults } from './remote-config-flags.ts'; -import type { EnvMap } from '../utils/env-map.ts'; +import { type EnvMap } from '@agent-device/kernel/source-value'; export function resolveCliOptions( argv: string[], diff --git a/src/client/client-companion-tunnel.ts b/src/client/client-companion-tunnel.ts index b3098385b3..f19427d0ee 100644 --- a/src/client/client-companion-tunnel.ts +++ b/src/client/client-companion-tunnel.ts @@ -18,13 +18,13 @@ import { ENV_COMPANION_TUNNEL_UNREGISTER_PATH, } from './client-companion-tunnel-contract.ts'; import { normalizeBaseUrl } from '../utils/url.ts'; -import { runCmdDetached } from '../utils/exec.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; import { isProcessAlive, readProcessCommand, readProcessStartTime, waitForProcessExit, -} from '../utils/host-process.ts'; +} from '@agent-device/host-kit/process'; const COMPANION_TUNNEL_TERM_TIMEOUT_MS = 1_000; const COMPANION_TUNNEL_KILL_TIMEOUT_MS = 1_000; diff --git a/src/client/client-normalizers.ts b/src/client/client-normalizers.ts index ea5494fad1..d0962c89f4 100644 --- a/src/client/client-normalizers.ts +++ b/src/client/client-normalizers.ts @@ -33,10 +33,10 @@ import { readRequiredPlatform, readRequiredString, stripUndefined, -} from '../utils/parsing.ts'; +} from '@agent-device/kernel/record'; import { buildAppIdentifiers, buildDeviceIdentifiers } from '../utils/result-serialization.ts'; -export { readOptionalString, readRequiredString } from '../utils/parsing.ts'; +export { readOptionalString, readRequiredString } from '@agent-device/kernel/record'; const DEFAULT_SESSION_NAME = 'default'; diff --git a/src/commands/batch/output.ts b/src/commands/batch/output.ts index f6b9448ae2..4dc6da9461 100644 --- a/src/commands/batch/output.ts +++ b/src/commands/batch/output.ts @@ -1,5 +1,5 @@ import type { BatchRunResult, BatchStepResult } from '../../core/batch.ts'; -import { readCommandMessage } from '../../utils/success-text.ts'; +import { readCommandMessage } from '@agent-device/kernel/success-text'; import type { CliOutput } from '../command-contract.ts'; import { resultOutput, type CliOutputFormatter } from '../output-common.ts'; diff --git a/src/commands/capture/runtime/diff-screenshot.test.ts b/src/commands/capture/runtime/diff-screenshot.test.ts index bdf8204ef7..0c8a329319 100644 --- a/src/commands/capture/runtime/diff-screenshot.test.ts +++ b/src/commands/capture/runtime/diff-screenshot.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import { PNG } from '../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { test } from 'vitest'; import type { AgentDeviceBackend, diff --git a/src/commands/capture/runtime/screenshot.ts b/src/commands/capture/runtime/screenshot.ts index 250c42cb95..b71f62d8d8 100644 --- a/src/commands/capture/runtime/screenshot.ts +++ b/src/commands/capture/runtime/screenshot.ts @@ -1,7 +1,7 @@ import { AppError } from '@agent-device/kernel/errors'; import { validateScreenshotScale } from '@agent-device/contracts/capture'; -import { successText } from '../../../utils/success-text.ts'; -import { resizePngFileToScale } from '../../../utils/png-resize.ts'; +import { successText } from '@agent-device/kernel/success-text'; +import { resizePngFileToScale } from '@agent-device/capture-kit/png-resize'; import type { ArtifactDescriptor } from '../../../io.ts'; import type { RuntimeCommand, ScreenshotCommandOptions } from '../../runtime-types.ts'; import { reserveCommandOutput } from '../../io-policy.ts'; diff --git a/src/commands/capture/settings.ts b/src/commands/capture/settings.ts index 6d7b077b13..9e2673b013 100644 --- a/src/commands/capture/settings.ts +++ b/src/commands/capture/settings.ts @@ -4,7 +4,7 @@ import { SETTINGS_USAGE_OVERRIDE } from '@agent-device/contracts/settings'; import type { CommandSchemaOverride } from '../../cli-schema/types.ts'; import type { CliFlags } from '@agent-device/contracts/command'; import { AppError } from '@agent-device/kernel/errors'; -import { readLocationCoordinate } from '../../utils/location-coordinates.ts'; +import { readLocationCoordinate } from '@agent-device/kernel/location-coordinates'; import { defineExecutableCommand } from '../command-contract.ts'; import { enumField, numberField, requiredField, stringField } from '../command-input.ts'; import { diff --git a/src/commands/capture/snapshot.ts b/src/commands/capture/snapshot.ts index 30505c8385..9c4a19adb6 100644 --- a/src/commands/capture/snapshot.ts +++ b/src/commands/capture/snapshot.ts @@ -1,5 +1,5 @@ import { PUBLIC_COMMANDS } from '../../command-catalog.ts'; -import { SNAPSHOT_BACKEND_CAPABILITIES } from '../../snapshot-quality/backend-capabilities.ts'; +import { SNAPSHOT_BACKEND_CAPABILITIES } from '@agent-device/capture-kit/snapshot-quality-backend-capabilities'; import { SNAPSHOT_FLAGS } from '../cli-grammar/flag-groups.ts'; import { booleanField, integerField, stringField } from '../command-input.ts'; import { defineExecutableCommand } from '../command-contract.ts'; diff --git a/src/commands/command-flags.ts b/src/commands/command-flags.ts index 8fa88063d9..21f3d9b8ea 100644 --- a/src/commands/command-flags.ts +++ b/src/commands/command-flags.ts @@ -1,7 +1,7 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import { screenshotFlagsFromOptions } from '@agent-device/contracts/capture'; import { leaseScopeFromOptions, leaseScopeToCommandFlags } from '../core/lease-scope.ts'; -import { stripUndefined } from '../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; import { getFlagDefinitions } from './cli-grammar/flag-registry.ts'; import type { InternalRequestOptions } from '@agent-device/contracts/client'; import type { CommandMetadata } from './command-contract.ts'; diff --git a/src/commands/input-audience.ts b/src/commands/input-audience.ts index 67e9200434..17edee193f 100644 --- a/src/commands/input-audience.ts +++ b/src/commands/input-audience.ts @@ -1,4 +1,4 @@ -import { buildPrimaryEnvVarName } from '../utils/source-value.ts'; +import { buildPrimaryEnvVarName } from '@agent-device/kernel/source-value'; /** * Where an operator supplies a value that no model-facing surface may accept as diff --git a/src/commands/interaction/output.ts b/src/commands/interaction/output.ts index 81f584aeac..2e19675669 100644 --- a/src/commands/interaction/output.ts +++ b/src/commands/interaction/output.ts @@ -2,7 +2,7 @@ import type { CommandRequestResult } from '@agent-device/contracts/client'; import type { SnapshotNode } from '@agent-device/kernel/snapshot'; import type { CliOutput } from '../command-contract.ts'; import { displayLabel, formatRole } from '../../snapshot/snapshot-lines.ts'; -import { readCommandMessage } from '../../utils/success-text.ts'; +import { readCommandMessage } from '@agent-device/kernel/success-text'; import { messageCliOutput, messageOutput, diff --git a/src/commands/interaction/runtime/gesture-command.ts b/src/commands/interaction/runtime/gesture-command.ts index c6d0d1dc2b..abfef1120a 100644 --- a/src/commands/interaction/runtime/gesture-command.ts +++ b/src/commands/interaction/runtime/gesture-command.ts @@ -7,7 +7,7 @@ import type { import { buildGesturePlan } from '@agent-device/contracts/gesture-plan'; import type { Point } from '@agent-device/kernel/snapshot'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { toBackendContext } from '../../runtime-common.ts'; import { toBackendResult, diff --git a/src/commands/interaction/runtime/gestures.ts b/src/commands/interaction/runtime/gestures.ts index 363d6bba89..d643a73f90 100644 --- a/src/commands/interaction/runtime/gestures.ts +++ b/src/commands/interaction/runtime/gestures.ts @@ -27,7 +27,7 @@ import { type ScrollEdgeState, type ScrollEdgeTarget, } from '../../../utils/scroll-edge-state.ts'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { requireIntInRange } from '../../../utils/validation.ts'; import { toBackendContext } from '../../runtime-common.ts'; import { diff --git a/src/commands/interaction/runtime/resolution.ts b/src/commands/interaction/runtime/resolution.ts index 741a7d145c..8d07266ee2 100644 --- a/src/commands/interaction/runtime/resolution.ts +++ b/src/commands/interaction/runtime/resolution.ts @@ -1,7 +1,7 @@ import { AppError } from '@agent-device/kernel/errors'; import type { Point, SnapshotNode, SnapshotState } from '@agent-device/kernel/snapshot'; import { findNodeByRef, normalizeRef } from '@agent-device/kernel/snapshot'; -import { resolveRectCenter } from '../../../utils/rect-center.ts'; +import { resolveRectCenter } from '@agent-device/kernel/rect-center'; import type { AgentDeviceRuntime, CommandContext, @@ -37,7 +37,7 @@ import { import { classifyOffscreenScrollDirection, type OffscreenScrollDirection, -} from '../../../snapshot/mobile-snapshot-semantics.ts'; +} from '@agent-device/capture-kit/mobile-snapshot-semantics'; import { truncateUtf8 } from '../../../utils/truncate-utf8.ts'; import type { InteractionTarget, diff --git a/src/commands/interaction/runtime/selector-read-shared.ts b/src/commands/interaction/runtime/selector-read-shared.ts index 79cd6e6808..d50cfa68b5 100644 --- a/src/commands/interaction/runtime/selector-read-shared.ts +++ b/src/commands/interaction/runtime/selector-read-shared.ts @@ -10,7 +10,7 @@ import type { SnapshotState, } from '@agent-device/kernel/snapshot'; import { findNodeByRef, normalizeRef } from '@agent-device/kernel/snapshot'; -import { isSparseSnapshotQualityVerdict } from '../../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { extractReadableText } from '../../../utils/text-surface.ts'; import { now, toBackendContext } from '../../runtime-common.ts'; import { findNodeByLabel } from './selector-read-utils.ts'; diff --git a/src/commands/interaction/runtime/selector-read.ts b/src/commands/interaction/runtime/selector-read.ts index a074724c6b..ebd79dc34a 100644 --- a/src/commands/interaction/runtime/selector-read.ts +++ b/src/commands/interaction/runtime/selector-read.ts @@ -19,7 +19,7 @@ import { } from '../../../core/selector-pipeline.ts'; import { SELECTOR_PIPELINE_POLICIES } from '../../../core/selector-pipeline-policy.ts'; import type { SnapshotNode } from '@agent-device/kernel/snapshot'; -import { isSparseSnapshotQualityVerdict } from '../../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import type { AgentDeviceRuntime, CommandContext } from '../../../runtime-contract.ts'; import { AppError } from '@agent-device/kernel/errors'; import type { diff --git a/src/commands/interaction/runtime/settle.test.ts b/src/commands/interaction/runtime/settle.test.ts index eae5f1e9a4..cc8fd86e17 100644 --- a/src/commands/interaction/runtime/settle.test.ts +++ b/src/commands/interaction/runtime/settle.test.ts @@ -11,7 +11,7 @@ import { import { makeSnapshotState } from '../../../__tests__/test-utils/snapshot-builders.ts'; import { ref, selector } from './selector-read-utils.ts'; import { buildSettleTailEntries, NEVER_SETTLED_HINT } from './settle.ts'; -import { readSnapshotQualityVerdict } from '../../../snapshot-quality/verdict.ts'; +import { readSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; // #1101 --settle: quiet-window settle loop composition on the interaction // commands. Budgets are injected (fake clock) — no real waiting. diff --git a/src/commands/interaction/runtime/settle.ts b/src/commands/interaction/runtime/settle.ts index b90327edc6..849f2cc8dc 100644 --- a/src/commands/interaction/runtime/settle.ts +++ b/src/commands/interaction/runtime/settle.ts @@ -4,7 +4,7 @@ import type { CommandContext, CommandSessionRecord, } from '../../../runtime-contract.ts'; -import { isSparseSnapshotQualityVerdict } from '../../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { buildSnapshotDiff } from '../../../snapshot/snapshot-diff.ts'; import { displayLabel, formatRole } from '../../../snapshot/snapshot-lines.ts'; import { collectSettleChromeRefs, withoutSettleChrome } from '../../../core/snapshot-chrome.ts'; diff --git a/src/commands/interaction/runtime/stable-capture.ts b/src/commands/interaction/runtime/stable-capture.ts index ddf61130f2..1e7fcb5415 100644 --- a/src/commands/interaction/runtime/stable-capture.ts +++ b/src/commands/interaction/runtime/stable-capture.ts @@ -18,7 +18,7 @@ import { stableCaptureSignalsEqual, type StableCaptureSignal, } from './stable-capture-signal.ts'; -import { preferredSnapshotBackendForVerdict } from '../../../snapshot-quality/verdict.ts'; +import { preferredSnapshotBackendForVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; /** * The quiet-window stable-capture loop shared by `wait stable` and the diff --git a/src/commands/management/output.ts b/src/commands/management/output.ts index 74296c102d..79eb03c3a2 100644 --- a/src/commands/management/output.ts +++ b/src/commands/management/output.ts @@ -28,7 +28,7 @@ import { serializeOpenResult, serializeSessionListEntry, } from '../../utils/result-serialization.ts'; -import { readCommandMessage } from '../../utils/success-text.ts'; +import { readCommandMessage } from '@agent-device/kernel/success-text'; import { snapshotCliOutput } from '../capture/output.ts'; import type { CliOutput } from '../command-contract.ts'; import { diff --git a/src/commands/management/runtime/admin.ts b/src/commands/management/runtime/admin.ts index 625e74f6be..0613b7ba72 100644 --- a/src/commands/management/runtime/admin.ts +++ b/src/commands/management/runtime/admin.ts @@ -8,7 +8,7 @@ import type { } from '../../../backend.ts'; import type { AgentDeviceRuntime, CommandContext } from '../../../runtime-contract.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { toBackendResult, type BackendResultEnvelope, diff --git a/src/commands/management/runtime/apps.ts b/src/commands/management/runtime/apps.ts index a8e324ae17..c131a2e5fa 100644 --- a/src/commands/management/runtime/apps.ts +++ b/src/commands/management/runtime/apps.ts @@ -11,7 +11,7 @@ import type { FileInputRef } from '../../../io.ts'; import type { AgentDeviceRuntime, CommandContext } from '../../../runtime-contract.ts'; import { assertResolvedAppsFilter } from '../app-inventory-contract.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { resolveCommandInput } from '../../io-policy.ts'; import { toBackendResult, diff --git a/src/commands/output-common.ts b/src/commands/output-common.ts index 53125ae608..e6802326d7 100644 --- a/src/commands/output-common.ts +++ b/src/commands/output-common.ts @@ -1,4 +1,4 @@ -import { readCommandMessage } from '../utils/success-text.ts'; +import { readCommandMessage } from '@agent-device/kernel/success-text'; import type { CliOutput } from './command-contract.ts'; export type CliOutputFormatter = (params: { diff --git a/src/commands/perf/output.ts b/src/commands/perf/output.ts index 94badeb4a7..9ec488cd90 100644 --- a/src/commands/perf/output.ts +++ b/src/commands/perf/output.ts @@ -1,5 +1,5 @@ import type { CommandRequestResult } from '@agent-device/contracts/client'; -import { isRecord } from '../../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import type { CliOutput } from '../command-contract.ts'; import { resultOutput, type CliOutputFormatter } from '../output-common.ts'; diff --git a/src/commands/recording/runtime/recording.ts b/src/commands/recording/runtime/recording.ts index 67388355a6..d184da2129 100644 --- a/src/commands/recording/runtime/recording.ts +++ b/src/commands/recording/runtime/recording.ts @@ -7,7 +7,7 @@ import type { import type { ArtifactDescriptor, FileOutputRef } from '../../../io.ts'; import type { CommandContext } from '../../../runtime-contract.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { requireIntInRange } from '../../../utils/validation.ts'; import { recordingQualityInputToExportQuality, diff --git a/src/commands/system/runtime/system.ts b/src/commands/system/runtime/system.ts index 6ed4799ebe..38f2c5058a 100644 --- a/src/commands/system/runtime/system.ts +++ b/src/commands/system/runtime/system.ts @@ -10,7 +10,7 @@ import type { CommandContext } from '../../../runtime-contract.ts'; import type { BackMode } from '@agent-device/contracts/back-mode'; import { parseTvRemoteButton } from '@agent-device/contracts/tv-remote'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { requireIntInRange } from '../../../utils/validation.ts'; import { isKeyboardAction } from '../../../utils/keyboard-actions.ts'; import { diff --git a/src/core/batch.ts b/src/core/batch.ts index 7ec94e73f2..c66fe805c8 100644 --- a/src/core/batch.ts +++ b/src/core/batch.ts @@ -8,7 +8,7 @@ import { isNonDefaultResponseLevel, } from '@agent-device/kernel/contracts'; import { AppError, asAppError, type DaemonError } from '@agent-device/kernel/errors'; -import { isRecord } from '../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import { DEFAULT_BATCH_MAX_STEPS, assertBatchStepCount, diff --git a/src/core/dispatch-resolve.ts b/src/core/dispatch-resolve.ts index 6859057f2a..f0d8553a4d 100644 --- a/src/core/dispatch-resolve.ts +++ b/src/core/dispatch-resolve.ts @@ -12,8 +12,8 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { resolveAndroidSerialAllowlist, resolveIosSimulatorDeviceSetPath, -} from '../utils/device-isolation.ts'; -import { withDiagnosticTimer } from '../utils/diagnostics.ts'; +} from '@agent-device/kernel/device-isolation'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { listLocalDeviceInventory, readDeviceInventory, diff --git a/src/core/interaction-targeting.ts b/src/core/interaction-targeting.ts index 9004df0706..2ebcb4853a 100644 --- a/src/core/interaction-targeting.ts +++ b/src/core/interaction-targeting.ts @@ -7,12 +7,12 @@ import { normalizeType, isViewportRootNode, } from '@agent-device/contracts/snapshot'; -import { isSnapshotNodeInteractionBlocked } from '../snapshot/snapshot-occlusion.ts'; +import { isSnapshotNodeInteractionBlocked } from '@agent-device/capture-kit/snapshot-occlusion'; import { areRectsApproximatelyEqual, normalizeRect, resolveRectCenter, -} from '../utils/rect-center.ts'; +} from '@agent-device/kernel/rect-center'; import { intersectArea } from '../utils/screenshot-geometry.ts'; import { isSemanticTouchTarget } from './touch-semantics.ts'; diff --git a/src/core/interaction-touch-point.ts b/src/core/interaction-touch-point.ts index f2751a4c30..6c23d2ec12 100644 --- a/src/core/interaction-touch-point.ts +++ b/src/core/interaction-touch-point.ts @@ -4,7 +4,7 @@ import { areRectsApproximatelyEqual, normalizeRect, resolveRectCenter, -} from '../utils/rect-center.ts'; +} from '@agent-device/kernel/rect-center'; import { isSemanticTouchTarget } from './touch-semantics.ts'; export type InteractionTouchPointResolution = diff --git a/src/core/interactors/android.ts b/src/core/interactors/android.ts index 6fba4e7f58..b59529f765 100644 --- a/src/core/interactors/android.ts +++ b/src/core/interactors/android.ts @@ -34,8 +34,8 @@ import { setAndroidSetting } from '../../platforms/android/settings.ts'; import { snapshotAndroid } from '../../platforms/android/snapshot.ts'; import type { AndroidHelperSessionScope } from '../../platforms/android/snapshot-helper-types.ts'; import { screenshotAndroid } from '../../platforms/android/screenshot.ts'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; -import { withMethodScope } from '../../utils/method-scope.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; +import { withMethodScope } from '@agent-device/kernel/scoped-provider'; import type { DeviceInfo } from '@agent-device/kernel/device'; import type { Interactor, RunnerContext } from '@agent-device/contracts/interactor-types'; import { androidSnapshotPublicationInput } from '../../platforms/android/snapshot-capture.ts'; diff --git a/src/core/interactors/harmonyos.ts b/src/core/interactors/harmonyos.ts index 96f30915bb..2b966425c7 100644 --- a/src/core/interactors/harmonyos.ts +++ b/src/core/interactors/harmonyos.ts @@ -1,7 +1,7 @@ import { AppError } from '@agent-device/kernel/errors'; import type { DeviceInfo } from '@agent-device/kernel/device'; import type { Interactor, RunnerContext } from '@agent-device/contracts/interactor-types'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { closeHarmonyApp, openHarmonyApp } from '../../platforms/harmonyos/app-lifecycle.ts'; import { appSwitcherHarmony, diff --git a/src/core/interactors/linux.ts b/src/core/interactors/linux.ts index 50be016efd..cd8eac4fa7 100644 --- a/src/core/interactors/linux.ts +++ b/src/core/interactors/linux.ts @@ -1,5 +1,5 @@ import { AppError } from '@agent-device/kernel/errors'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { backLinux, closeLinuxApp, diff --git a/src/core/interactors/web.ts b/src/core/interactors/web.ts index e9462d94dd..914da409e0 100644 --- a/src/core/interactors/web.ts +++ b/src/core/interactors/web.ts @@ -1,7 +1,7 @@ import type { Interactor } from '@agent-device/contracts/interactor-types'; import { AppError } from '@agent-device/kernel/errors'; import { stripAtPrefix } from '../interaction-positionals.ts'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { resolveWebProvider, type WebProvider } from '../../platforms/web/provider.ts'; import { createUnsupportedInteractor } from '../../platforms/unsupported-interactor.ts'; diff --git a/src/core/lease-scope.ts b/src/core/lease-scope.ts index 36ba9212f7..530fa97c10 100644 --- a/src/core/lease-scope.ts +++ b/src/core/lease-scope.ts @@ -1,5 +1,5 @@ import type { LeaseBackend } from '@agent-device/kernel/contracts'; -import { stripUndefined } from '../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; const PROXY_LEASE_PROVIDER = 'proxy'; export const DEFAULT_PROXY_LEASE_TTL_MS = 300_000; diff --git a/src/core/press-retarget.ts b/src/core/press-retarget.ts index b062fb77e2..29c145e435 100644 --- a/src/core/press-retarget.ts +++ b/src/core/press-retarget.ts @@ -14,7 +14,7 @@ */ import type { SnapshotNode } from '@agent-device/kernel/snapshot'; import { containsPoint } from '@agent-device/kernel/rect'; -import { resolveRectCenter } from '../utils/rect-center.ts'; +import { resolveRectCenter } from '@agent-device/kernel/rect-center'; import { demoteNonUniqueLocalIdentity, readNodeLocalIdentity } from '@agent-device/ad-script'; import { buildIndexMap } from '../replay/target-evidence-tree.ts'; import { normalizeSelectorText } from '@agent-device/selectors'; diff --git a/src/core/selector-pipeline.ts b/src/core/selector-pipeline.ts index 7b596a265f..39e5ab3ace 100644 --- a/src/core/selector-pipeline.ts +++ b/src/core/selector-pipeline.ts @@ -4,7 +4,7 @@ import { listSelectorChainMatches, resolveSelectorChainWithPolicy, } from '@agent-device/selectors/engine'; -import { isSnapshotNodeInteractionBlocked } from '../snapshot/snapshot-occlusion.ts'; +import { isSnapshotNodeInteractionBlocked } from '@agent-device/capture-kit/snapshot-occlusion'; import { isRootInteractionContainer, resolveActionableTouchResolution, diff --git a/src/core/snapshot-state.ts b/src/core/snapshot-state.ts index 1285b85539..b90f78fcf1 100644 --- a/src/core/snapshot-state.ts +++ b/src/core/snapshot-state.ts @@ -17,7 +17,7 @@ import { import { annotateCoveredSnapshotNodes, annotateSnapshotNodesCoveredByPolicy, -} from '../snapshot/snapshot-occlusion.ts'; +} from '@agent-device/capture-kit/snapshot-occlusion'; import { coveredAndroidReplacementNodeIndexes } from '../snapshot/android-replacement-surface-occlusion.ts'; import { scopeSnapshotNodes } from '../snapshot/snapshot-desktop-surface.ts'; import { normalizeSnapshotTree, pruneGroupNodes } from '../core/snapshot-tree-ingestion.ts'; diff --git a/src/daemon/__tests__/android-dialog-readiness.test.ts b/src/daemon/__tests__/android-dialog-readiness.test.ts index d88cea570c..8697c139a5 100644 --- a/src/daemon/__tests__/android-dialog-readiness.test.ts +++ b/src/daemon/__tests__/android-dialog-readiness.test.ts @@ -24,7 +24,10 @@ vi.mock('../../platforms/android/app-lifecycle.ts', async (importOriginal) => { import { makeAndroidSession } from '../../__tests__/test-utils/session-factories.ts'; import { withFakeAdb, type FakeAdbScript } from '../../__tests__/test-utils/fake-adb.ts'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { ensureAndroidBlockingSystemDialogReady as ensureOwnedAndroidBlockingSystemDialogReady } from '../android-system-dialog.ts'; import { androidObservation } from '../../platform-runtime.ts'; diff --git a/src/daemon/__tests__/daemon-exit-wait.test.ts b/src/daemon/__tests__/daemon-exit-wait.test.ts index 47a1e5f8bd..b6c65943d6 100644 --- a/src/daemon/__tests__/daemon-exit-wait.test.ts +++ b/src/daemon/__tests__/daemon-exit-wait.test.ts @@ -15,8 +15,8 @@ const state = vi.hoisted(() => ({ commands: new Map(), })); -vi.mock('../../utils/host-process.ts', async (importOriginal) => ({ - ...(await importOriginal()), +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: (pid: number) => state.alive.get(pid) ?? false, readProcessStartTime: (pid: number) => state.starts.get(pid) ?? null, readProcessCommand: (pid: number) => state.commands.get(pid) ?? null, diff --git a/src/daemon/__tests__/daemon-process-takeover.test.ts b/src/daemon/__tests__/daemon-process-takeover.test.ts index e513119336..f82bd9382e 100644 --- a/src/daemon/__tests__/daemon-process-takeover.test.ts +++ b/src/daemon/__tests__/daemon-process-takeover.test.ts @@ -4,7 +4,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { afterEach, test } from 'vitest'; -import { isProcessAlive, readProcessStartTime } from '../../utils/host-process.ts'; +import { isProcessAlive, readProcessStartTime } from '@agent-device/host-kit/process'; import { isAgentDeviceDaemonProcess, stopProcessForTakeover } from '../daemon-process.ts'; const TAKEOVER_TIMEOUTS = { termTimeoutMs: 5_000, killTimeoutMs: 2_000 }; diff --git a/src/daemon/__tests__/daemon-stop.test.ts b/src/daemon/__tests__/daemon-stop.test.ts index cceec7ebd1..e9f258852a 100644 --- a/src/daemon/__tests__/daemon-stop.test.ts +++ b/src/daemon/__tests__/daemon-stop.test.ts @@ -16,10 +16,14 @@ vi.mock('../daemon-process.ts', () => ({ trySignalProcess: mocks.trySignalProcess, waitForDaemonExit: mocks.waitForDaemonExit, })); -vi.mock('../../utils/host-process.ts', () => ({ +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: mocks.isProcessAlive, })); -vi.mock('../../utils/timeouts.ts', () => ({ sleep: mocks.sleep })); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => ({ + ...(await importOriginal()), + sleep: mocks.sleep, +})); import { resolveDaemonPaths } from '../config.ts'; import { stopDaemon } from '../daemon-stop.ts'; diff --git a/src/daemon/__tests__/deferred-interaction-outcome.test.ts b/src/daemon/__tests__/deferred-interaction-outcome.test.ts index e588f52f04..56a2689ac1 100644 --- a/src/daemon/__tests__/deferred-interaction-outcome.test.ts +++ b/src/daemon/__tests__/deferred-interaction-outcome.test.ts @@ -7,7 +7,10 @@ import { type DeferredOutcomeSnapshotAttempt, } from '../deferred-interaction-outcome.ts'; import type { InteractionRetryTap } from '../interaction-outcome-policy.ts'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import type { SessionState } from '../types.ts'; import { deliverySnapshot, diff --git a/src/daemon/__tests__/device-claim-prune.test.ts b/src/daemon/__tests__/device-claim-prune.test.ts index 95c4a12e2e..7598915174 100644 --- a/src/daemon/__tests__/device-claim-prune.test.ts +++ b/src/daemon/__tests__/device-claim-prune.test.ts @@ -5,12 +5,12 @@ import path from 'node:path'; import { afterEach, test, vi } from 'vitest'; import { reconcileOrphanedDeviceClaims } from '../device-claims.ts'; import { resolveDeviceClaimPath } from '../device-claim-paths.ts'; -import { acquireProcessLock } from '../../utils/process-lock.ts'; -import { readCurrentOwnerIdentity } from '../../utils/owner-identity.ts'; +import { acquireProcessLock } from '@agent-device/host-kit/file'; +import { readCurrentOwnerIdentity } from '@agent-device/host-kit/process'; import { publishDaemonRegistration } from '../../__tests__/test-utils/device-claim-store.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../utils/host-process.ts', async (importOriginal) => +vi.mock('@agent-device/host-kit/process', async (importOriginal) => (await import('../../__tests__/test-utils/host-process-mock.ts')).pinOwnProcessStartTime( importOriginal, ), diff --git a/src/daemon/__tests__/device-claims.test.ts b/src/daemon/__tests__/device-claims.test.ts index 956673f2f1..995964636a 100644 --- a/src/daemon/__tests__/device-claims.test.ts +++ b/src/daemon/__tests__/device-claims.test.ts @@ -12,9 +12,9 @@ import { inspectDeviceClaims } from '../device-claim-inspection.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; import { publishDaemonRegistration } from '../../__tests__/test-utils/device-claim-store.ts'; -import { readCurrentOwnerIdentity } from '../../utils/owner-identity.ts'; +import { readCurrentOwnerIdentity } from '@agent-device/host-kit/process'; -vi.mock('../../utils/host-process.ts', async (importOriginal) => +vi.mock('@agent-device/host-kit/process', async (importOriginal) => (await import('../../__tests__/test-utils/host-process-mock.ts')).pinOwnProcessStartTime( importOriginal, ), diff --git a/src/daemon/__tests__/device-ready.test.ts b/src/daemon/__tests__/device-ready.test.ts index 650bd48d5c..a332b7b8d4 100644 --- a/src/daemon/__tests__/device-ready.test.ts +++ b/src/daemon/__tests__/device-ready.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, expect, test, vi } from 'vitest'; import { promises as fs } from 'node:fs'; import type { DeviceInfo } from '@agent-device/kernel/device'; -vi.mock('../../utils/exec.ts', () => ({ +vi.mock('@agent-device/host-kit/command', () => ({ runCmd: vi.fn(), runCmdSync: vi.fn(), whichCmd: vi.fn(async () => true), @@ -14,7 +14,7 @@ vi.mock('../../platforms/android/emulator-lifecycle.ts', () => ({ waitForAndroidBoot: vi.fn(async () => {}), })); -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { waitForAndroidBoot } from '../../platforms/android/emulator-lifecycle.ts'; import { ensureBootedSimulator } from '../../platforms/apple/core/simulator.ts'; import { diff --git a/src/daemon/__tests__/durable-capture-resource-transitions.test.ts b/src/daemon/__tests__/durable-capture-resource-transitions.test.ts index 9516ec779d..be300cbe33 100644 --- a/src/daemon/__tests__/durable-capture-resource-transitions.test.ts +++ b/src/daemon/__tests__/durable-capture-resource-transitions.test.ts @@ -1,5 +1,8 @@ import { expect, test } from 'vitest'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { createTestCaptureResource, makeDurableCaptureContext, diff --git a/src/daemon/__tests__/filesystem-boundary-faults.test.ts b/src/daemon/__tests__/filesystem-boundary-faults.test.ts index f8a7a852b4..e91acf8f05 100644 --- a/src/daemon/__tests__/filesystem-boundary-faults.test.ts +++ b/src/daemon/__tests__/filesystem-boundary-faults.test.ts @@ -21,7 +21,7 @@ import { type FilesystemBoundaryFixture, } from '../../__tests__/test-utils/filesystem-boundary-faults.ts'; -vi.mock('../../utils/host-process.ts', async (importOriginal) => +vi.mock('@agent-device/host-kit/process', async (importOriginal) => (await import('../../__tests__/test-utils/host-process-mock.ts')).pinOwnProcessStartTime( importOriginal, ), diff --git a/src/daemon/__tests__/http-server-artifacts.test.ts b/src/daemon/__tests__/http-server-artifacts.test.ts index 91fbfc9fa5..52adf08048 100644 --- a/src/daemon/__tests__/http-server-artifacts.test.ts +++ b/src/daemon/__tests__/http-server-artifacts.test.ts @@ -9,7 +9,7 @@ import { trackDownloadableArtifact, } from '../artifact-tracking.ts'; import type { DaemonResponse } from '../types.ts'; -import { runCmdSync, withCommandExecutorOverride } from '../../utils/exec.ts'; +import { runCmdSync, withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { closeLoopbackServer, listenOnLoopback, diff --git a/src/daemon/__tests__/post-gesture-no-effect-claim.test.ts b/src/daemon/__tests__/post-gesture-no-effect-claim.test.ts index a936c82005..258bb9fefe 100644 --- a/src/daemon/__tests__/post-gesture-no-effect-claim.test.ts +++ b/src/daemon/__tests__/post-gesture-no-effect-claim.test.ts @@ -4,7 +4,10 @@ import path from 'node:path'; import { afterEach, test, vi } from 'vitest'; import { makeSnapshotState } from '../../__tests__/test-utils/snapshot-builders.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { buildInteractionSurfaceSignature, summarizeDiscriminatingSurfaceDivergence, diff --git a/src/daemon/__tests__/post-gesture-stabilization.test.ts b/src/daemon/__tests__/post-gesture-stabilization.test.ts index 680bbb4a2d..3cdc90d20f 100644 --- a/src/daemon/__tests__/post-gesture-stabilization.test.ts +++ b/src/daemon/__tests__/post-gesture-stabilization.test.ts @@ -2,7 +2,10 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import assert from 'node:assert/strict'; import { afterEach, test, vi } from 'vitest'; import { makeSnapshotState } from '../../__tests__/test-utils/snapshot-builders.ts'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { buildInteractionSurfaceSignature } from '../interaction-outcome-policy.ts'; import { capturePostGestureStabilizedResult, diff --git a/src/daemon/__tests__/request-execution-scope.test.ts b/src/daemon/__tests__/request-execution-scope.test.ts index f24360b052..86870102ff 100644 --- a/src/daemon/__tests__/request-execution-scope.test.ts +++ b/src/daemon/__tests__/request-execution-scope.test.ts @@ -5,7 +5,7 @@ import { emitDiagnostic, flushDiagnosticsToSessionFile, withDiagnosticsScope, -} from '../../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import { makeAndroidSession, makeIosSession, @@ -14,7 +14,7 @@ import { import { LINUX_DEVICE } from '../../__tests__/test-utils/device-fixtures.ts'; import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; import { LeaseRegistry } from '../lease-registry.ts'; -import { clearRequestCanceled, markRequestCanceled } from '../../request/cancel.ts'; +import { clearRequestCanceled, markRequestCanceled } from '@agent-device/host-kit/request'; import { createRequestExecutionScope, prepareLockedRequestScope, diff --git a/src/daemon/__tests__/request-handler-catalog.test.ts b/src/daemon/__tests__/request-handler-catalog.test.ts index 0c875fefd0..e58c966fb6 100644 --- a/src/daemon/__tests__/request-handler-catalog.test.ts +++ b/src/daemon/__tests__/request-handler-catalog.test.ts @@ -16,7 +16,7 @@ import { clearRequestAbortRegistration, markRequestCanceled, registerRequestAbort, -} from '../../request/cancel.ts'; +} from '@agent-device/host-kit/request'; import { unavailableBindDevice, unavailableBindExactDevice, diff --git a/src/daemon/__tests__/request-router-android-modal.test.ts b/src/daemon/__tests__/request-router-android-modal.test.ts index 8ccfc2da12..985b8fc5b2 100644 --- a/src/daemon/__tests__/request-router-android-modal.test.ts +++ b/src/daemon/__tests__/request-router-android-modal.test.ts @@ -113,8 +113,8 @@ const modalObservation: AndroidObservationAdapter = { }, }; -vi.mock('../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(async (_cmd: string, args: string[]) => { diff --git a/src/daemon/__tests__/request-router-cost.test.ts b/src/daemon/__tests__/request-router-cost.test.ts index 29c4113b68..5d917a76a2 100644 --- a/src/daemon/__tests__/request-router-cost.test.ts +++ b/src/daemon/__tests__/request-router-cost.test.ts @@ -16,7 +16,7 @@ import { gestureDeviceRuntimeGateway, gestureRuntimeSpies, } from './test-device-runtime-gateway.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { DaemonRequest, SessionState } from '../types.ts'; import { LeaseRegistry } from '../lease-registry.ts'; import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; diff --git a/src/daemon/__tests__/request-router-open.test.ts b/src/daemon/__tests__/request-router-open.test.ts index 862ba4bb14..4793489df4 100644 --- a/src/daemon/__tests__/request-router-open.test.ts +++ b/src/daemon/__tests__/request-router-open.test.ts @@ -9,8 +9,8 @@ import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; import { replayScriptSourceBundleFor } from '../../__tests__/test-utils/replay-script-source.ts'; vi.mock('../device-ready.ts', () => ({ ensureDeviceReady: vi.fn(async () => {}) })); -vi.mock('../../utils/host-process.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, readProcessStartTime: vi.fn(() => 'test-process-start') }; }); // Opening a session runs the owned-lease cleanup, which pattern-kills stale diff --git a/src/daemon/__tests__/request-router-screenshot.test.ts b/src/daemon/__tests__/request-router-screenshot.test.ts index 6a1803bc77..55f5f63d2d 100644 --- a/src/daemon/__tests__/request-router-screenshot.test.ts +++ b/src/daemon/__tests__/request-router-screenshot.test.ts @@ -26,7 +26,7 @@ import { import type { DaemonRequest, SessionState } from '../types.ts'; import { LeaseRegistry } from '../lease-registry.ts'; import { attachRefs } from '@agent-device/kernel/snapshot'; -import { PNG } from '../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { ANDROID_EMULATOR, IOS_SIMULATOR } from '../../__tests__/test-utils/device-fixtures.ts'; import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; import { makeSession as makeBaseSession } from '../../__tests__/test-utils/session-factories.ts'; diff --git a/src/daemon/__tests__/request-router-session-address.test.ts b/src/daemon/__tests__/request-router-session-address.test.ts index 0dc88a5370..a46a90dcdb 100644 --- a/src/daemon/__tests__/request-router-session-address.test.ts +++ b/src/daemon/__tests__/request-router-session-address.test.ts @@ -7,8 +7,8 @@ import { getResolveTargetDeviceMock } from './request-router-dispatch-mocks.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; vi.mock('../device-ready.ts', () => ({ ensureDeviceReady: vi.fn(async () => {}) })); -vi.mock('../../utils/host-process.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, readProcessStartTime: vi.fn(() => 'test-process-start') }; }); // Opening a session runs owned-lease cleanup, which pattern-kills stale xcodebuild runners with a diff --git a/src/daemon/__tests__/resumable-upload.test.ts b/src/daemon/__tests__/resumable-upload.test.ts index fbc4774946..818566f989 100644 --- a/src/daemon/__tests__/resumable-upload.test.ts +++ b/src/daemon/__tests__/resumable-upload.test.ts @@ -12,7 +12,7 @@ import { receiveResumableUploadChunk, } from '../resumable-upload.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -import { runCmdSync } from '../../utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; test('finalizing an unknown upload reports expiry with a recovery hint', async () => { const error = await finalizeResumableUpload('missing-upload-id').then( diff --git a/src/daemon/__tests__/runtime-session.test.ts b/src/daemon/__tests__/runtime-session.test.ts index c0ed07bacd..60426909d6 100644 --- a/src/daemon/__tests__/runtime-session.test.ts +++ b/src/daemon/__tests__/runtime-session.test.ts @@ -1,7 +1,10 @@ import { test, expect } from 'vitest'; import fs from 'node:fs'; import { makeIosSession } from '../../__tests__/test-utils/session-factories.ts'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + flushDiagnosticsToSessionFile, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { createDaemonRuntimeSessionStore } from '../runtime-session.ts'; import type { CommandSessionRecord } from '../../runtime-contract.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; diff --git a/src/daemon/__tests__/screen-recording-boundary-faults.test.ts b/src/daemon/__tests__/screen-recording-boundary-faults.test.ts index c02f489880..6cb8155755 100644 --- a/src/daemon/__tests__/screen-recording-boundary-faults.test.ts +++ b/src/daemon/__tests__/screen-recording-boundary-faults.test.ts @@ -14,7 +14,10 @@ import type { import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; -import { countDiagnosticEventsByPhase, withDiagnosticsScope } from '../../utils/diagnostics.ts'; +import { + countDiagnosticEventsByPhase, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { createDurableCaptureAdmissionLedger } from '../durable-capture-admission-ledger.ts'; import { createDurableCaptureResource } from '../durable-capture-resource.ts'; import type { DurableCaptureResourceStore } from '../durable-capture-resource-store.ts'; diff --git a/src/daemon/__tests__/screenshot-overlay.test.ts b/src/daemon/__tests__/screenshot-overlay.test.ts index a4d821e590..7d75724691 100644 --- a/src/daemon/__tests__/screenshot-overlay.test.ts +++ b/src/daemon/__tests__/screenshot-overlay.test.ts @@ -2,7 +2,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import { PNG } from '../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { annotateScreenshotWithRefs, buildScreenshotOverlayRefs } from '../screenshot-overlay.ts'; import { makeSnapshotState } from '../../__tests__/test-utils/snapshot-builders.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; diff --git a/src/daemon/__tests__/screenshot-runtime-fixture.ts b/src/daemon/__tests__/screenshot-runtime-fixture.ts index 694587de70..ecb462ba49 100644 --- a/src/daemon/__tests__/screenshot-runtime-fixture.ts +++ b/src/daemon/__tests__/screenshot-runtime-fixture.ts @@ -18,7 +18,7 @@ import { import { deviceShape, type DeviceInfo } from '@agent-device/kernel/device'; import fs from 'node:fs'; import { vi, type Mock } from 'vitest'; -import { PNG } from '../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-runtime-binding.ts'; import { unavailableDeviceRuntimeGateway } from './test-device-runtime-gateway.ts'; import { diff --git a/src/daemon/__tests__/snapshot-command-runtime.test.ts b/src/daemon/__tests__/snapshot-command-runtime.test.ts index 4715550341..b43dafba60 100644 --- a/src/daemon/__tests__/snapshot-command-runtime.test.ts +++ b/src/daemon/__tests__/snapshot-command-runtime.test.ts @@ -5,7 +5,7 @@ import { clearRequestAbortRegistration, markRequestCanceled, registerRequestAbort, -} from '../../request/cancel.ts'; +} from '@agent-device/host-kit/request'; import { dispatchSnapshotDiffViaRuntime } from '../snapshot-diff-runtime.ts'; import { dispatchSnapshotViaRuntime } from '../snapshot-runtime.ts'; import { legacyDispatchCapture } from './legacy-snapshot-capture-fixture.ts'; diff --git a/src/daemon/__tests__/snapshot-runtime-fixture.ts b/src/daemon/__tests__/snapshot-runtime-fixture.ts index 1ac41f3d90..eab84d6cca 100644 --- a/src/daemon/__tests__/snapshot-runtime-fixture.ts +++ b/src/daemon/__tests__/snapshot-runtime-fixture.ts @@ -32,7 +32,7 @@ import { } from '@agent-device/kernel/device'; import { actOnAppleAlert, awaitAppleAlert, readAppleAlert } from '../../platforms/apple/alert.ts'; import { type DispatchContext } from '../../core/dispatch-context.ts'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { isActiveProviderDevice } from '../../provider-device-runtime.ts'; import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-runtime-binding.ts'; import { unavailableDeviceRuntimeGateway } from './test-device-runtime-gateway.ts'; diff --git a/src/daemon/__tests__/upload.test.ts b/src/daemon/__tests__/upload.test.ts index 4676e32e09..4ed7f60836 100644 --- a/src/daemon/__tests__/upload.test.ts +++ b/src/daemon/__tests__/upload.test.ts @@ -6,7 +6,7 @@ import { Readable } from 'node:stream'; import type { IncomingMessage } from 'node:http'; import { receiveUpload } from '../upload.ts'; import { streamReadableToFile, validateArtifactContentLength } from '../artifact-download.ts'; -import { runCmdSync } from '../../utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; function makeUploadRequest(body: Buffer, headers: Record): IncomingMessage { diff --git a/src/daemon/adapters/maestro/__tests__/daemon-runtime-port-targets.test.ts b/src/daemon/adapters/maestro/__tests__/daemon-runtime-port-targets.test.ts index 6d2de1f11a..7c4649064e 100644 --- a/src/daemon/adapters/maestro/__tests__/daemon-runtime-port-targets.test.ts +++ b/src/daemon/adapters/maestro/__tests__/daemon-runtime-port-targets.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import { promises as fs } from 'node:fs'; -import { PNG } from '../../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import type { DaemonInvokeFn, DaemonRequest } from '../../../types.ts'; import { createDaemonMaestroRuntimePort } from '../daemon-runtime-port.ts'; import { makeBaseRequest, makeDependencies } from './daemon-runtime-port-fixtures.ts'; diff --git a/src/daemon/adapters/maestro/__tests__/daemon-runtime-port.test.ts b/src/daemon/adapters/maestro/__tests__/daemon-runtime-port.test.ts index f902ea14f2..4d9cac2e75 100644 --- a/src/daemon/adapters/maestro/__tests__/daemon-runtime-port.test.ts +++ b/src/daemon/adapters/maestro/__tests__/daemon-runtime-port.test.ts @@ -4,12 +4,12 @@ import { expect, test } from 'vitest'; import { noMaestroIncludeSources } from '../../../../__tests__/test-utils/replay-script-source.ts'; import { executeMaestroFlow, inspectMaestroFlow } from '@agent-device/maestro'; import type { DaemonInvokeFn, DaemonRequest } from '../../../types.ts'; -import { PNG } from '../../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { emitDiagnostic, flushDiagnosticsToSessionFile, withDiagnosticsScope, -} from '../../../../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import { createDaemonMaestroRuntimePort } from '../daemon-runtime-port.ts'; import { MAESTRO_OBSERVATION_POLL_MS } from '../daemon-runtime-port-observation.ts'; import { makeBaseRequest, makeDependencies, makeSnapshot } from './daemon-runtime-port-fixtures.ts'; diff --git a/src/daemon/adapters/maestro/__tests__/wait-for-animation-to-end.test.ts b/src/daemon/adapters/maestro/__tests__/wait-for-animation-to-end.test.ts index f1d1a5d345..4741c9b43a 100644 --- a/src/daemon/adapters/maestro/__tests__/wait-for-animation-to-end.test.ts +++ b/src/daemon/adapters/maestro/__tests__/wait-for-animation-to-end.test.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { expect, test } from 'vitest'; -import { PNG } from '../../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { waitForMaestroAnimationToEnd } from '../wait-for-animation-to-end.ts'; test('captures an explicit zero-timeout pair and accepts exactly 0.005% RGB difference', async () => { diff --git a/src/daemon/adapters/maestro/daemon-runtime-port-support.ts b/src/daemon/adapters/maestro/daemon-runtime-port-support.ts index bd2dfc6e72..87bb3720d8 100644 --- a/src/daemon/adapters/maestro/daemon-runtime-port-support.ts +++ b/src/daemon/adapters/maestro/daemon-runtime-port-support.ts @@ -16,7 +16,7 @@ import type { import { AppError } from '@agent-device/kernel/errors'; import type { Rect } from '@agent-device/kernel/snapshot'; import type { DaemonMaestroRuntimeDependencies } from './daemon-runtime-port-observation.ts'; -import { stripUndefined } from '../../../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; import { projectMaestroPublicOperation, type MaestroPublicOperation, diff --git a/src/daemon/adapters/maestro/daemon-runtime-port.ts b/src/daemon/adapters/maestro/daemon-runtime-port.ts index c8c702eaee..b3ed9b8c3f 100644 --- a/src/daemon/adapters/maestro/daemon-runtime-port.ts +++ b/src/daemon/adapters/maestro/daemon-runtime-port.ts @@ -8,8 +8,8 @@ import { type MaestroRuntimeOperations, type MaestroRuntimePort, } from '@agent-device/maestro'; -import { registerDiagnosticSensitiveValue } from '../../../utils/diagnostics.ts'; -import { stripUndefined } from '../../../utils/parsing.ts'; +import { registerDiagnosticSensitiveValue } from '@agent-device/host-kit/diagnostics'; +import { stripUndefined } from '@agent-device/kernel/record'; import { executeRunScriptFile } from './run-script-execution.ts'; import { waitForMaestroAnimationToEnd } from './wait-for-animation-to-end.ts'; import { diff --git a/src/daemon/adapters/maestro/daemon-runtime-tap.ts b/src/daemon/adapters/maestro/daemon-runtime-tap.ts index 2220521584..c08d0c9243 100644 --- a/src/daemon/adapters/maestro/daemon-runtime-tap.ts +++ b/src/daemon/adapters/maestro/daemon-runtime-tap.ts @@ -10,8 +10,8 @@ import { type MaestroTargetMatch, type MaestroTargetQuery, } from '@agent-device/maestro'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; -import { pointInsideRect } from '../../../utils/rect-center.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { pointInsideRect } from '@agent-device/kernel/rect-center'; import { withMaestroScreenshotBaseline, type MaestroScreenshotBaseline, diff --git a/src/daemon/adapters/maestro/maestro-screenshot-comparison.ts b/src/daemon/adapters/maestro/maestro-screenshot-comparison.ts index adb1535e14..167b03b4bc 100644 --- a/src/daemon/adapters/maestro/maestro-screenshot-comparison.ts +++ b/src/daemon/adapters/maestro/maestro-screenshot-comparison.ts @@ -3,9 +3,9 @@ import os from 'node:os'; import path from 'node:path'; import { MAESTRO_RUNTIME_ADAPTER_POLICY } from '@agent-device/maestro'; import { createRequestCanceledError, isRequestCanceledError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; -import { computePngRgbDifferenceAsync } from '../../../utils/png-worker-client.ts'; -import type { PngRgbDifferenceResult } from '../../../utils/png-rgb-difference.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { computePngRgbDifferenceAsync } from '@agent-device/capture-kit/png-worker-client'; +import type { PngRgbDifferenceResult } from '@agent-device/capture-kit/png-rgb-difference'; export type MaestroScreenshotBaseline = { readonly matchesCurrent: () => Promise; diff --git a/src/daemon/adapters/maestro/run-script-execution.ts b/src/daemon/adapters/maestro/run-script-execution.ts index 618bd21dde..15427f56b3 100644 --- a/src/daemon/adapters/maestro/run-script-execution.ts +++ b/src/daemon/adapters/maestro/run-script-execution.ts @@ -1,8 +1,8 @@ import fs from 'node:fs'; import vm from 'node:vm'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; -import { runCmdSync } from '../../../utils/exec.ts'; -import { stripUndefined } from '../../../utils/parsing.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; +import { stripUndefined } from '@agent-device/kernel/record'; const RUN_SCRIPT_TIMEOUT_MS = 30_000; const RUN_SCRIPT_DIAGNOSTIC_PREVIEW_CHARS = 1_000; diff --git a/src/daemon/android-snapshot-timeout-evidence.ts b/src/daemon/android-snapshot-timeout-evidence.ts index f2c57249f3..d08bb336d3 100644 --- a/src/daemon/android-snapshot-timeout-evidence.ts +++ b/src/daemon/android-snapshot-timeout-evidence.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import type { DaemonResponse, SessionState } from './types.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; import { snapshotTimeoutCaptureFailed, diff --git a/src/daemon/android-system-dialog.ts b/src/daemon/android-system-dialog.ts index 18b0addc85..daaa45b5e2 100644 --- a/src/daemon/android-system-dialog.ts +++ b/src/daemon/android-system-dialog.ts @@ -2,11 +2,12 @@ import type { AndroidBlockingDialogFocus, AndroidObservationAdapter, } from '@agent-device/contracts/android-observation'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; import { AppError, normalizeError, type NormalizedError } from '@agent-device/kernel/errors'; import { centerOfRect, type SnapshotNode } from '@agent-device/kernel/snapshot'; -import { sleep } from '../utils/timeouts.ts'; -import { isSnapshotNodeInteractionBlocked } from '../snapshot/snapshot-occlusion.ts'; + +import { isSnapshotNodeInteractionBlocked } from '@agent-device/capture-kit/snapshot-occlusion'; import { expireRefFrame } from './ref-frame.ts'; import type { SessionState } from './types.ts'; import { isActiveProviderDevice } from '../provider-device-runtime.ts'; diff --git a/src/daemon/app-event-runtime.ts b/src/daemon/app-event-runtime.ts index 0f5cd3ee1f..6b83ed980a 100644 --- a/src/daemon/app-event-runtime.ts +++ b/src/daemon/app-event-runtime.ts @@ -3,7 +3,7 @@ import { appEventRuntimeUse } from '@agent-device/contracts/platform-runtime-ope import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { parseTriggerAppEventArgs, resolveAppEventUrl } from '../core/app-events.ts'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import { admitRuntimeUse, type RuntimeAdmissionBindings } from './runtime-admission.ts'; import { runtimeExecutionFromContext } from './snapshot-runtime-capture-input.ts'; diff --git a/src/daemon/app-switcher-runtime.ts b/src/daemon/app-switcher-runtime.ts index f678bc43a2..127bb882b8 100644 --- a/src/daemon/app-switcher-runtime.ts +++ b/src/daemon/app-switcher-runtime.ts @@ -2,7 +2,7 @@ import type { AppSwitcherInput } from '@agent-device/contracts/app-switcher-runt import { appSwitcherRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; import { resolveBoundGenericRuntime, type RuntimeAdmissionBindings } from './runtime-admission.ts'; diff --git a/src/daemon/artifact-archive.ts b/src/daemon/artifact-archive.ts index 32774b7cba..41492d3185 100644 --- a/src/daemon/artifact-archive.ts +++ b/src/daemon/artifact-archive.ts @@ -1,8 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { extractArchiveSafely } from '../utils/archive-extraction.ts'; -import type { ArchiveManifestEntry } from '../utils/archive-safety.ts'; +import { extractArchiveSafely, type ArchiveManifestEntry } from '@agent-device/host-kit/archive'; export async function extractTarInstallableArtifact(params: { archivePath: string; diff --git a/src/daemon/artifact-download.ts b/src/daemon/artifact-download.ts index b9d507f30e..2506d195f8 100644 --- a/src/daemon/artifact-download.ts +++ b/src/daemon/artifact-download.ts @@ -4,8 +4,10 @@ import path from 'node:path'; import { once } from 'node:events'; import { pipeline } from 'node:stream/promises'; import { AppError } from '@agent-device/kernel/errors'; -import { MAX_ARTIFACT_COMPRESSED_BYTES } from '../utils/artifact-limits.ts'; -import { createByteLimitStream } from '../utils/byte-limit-stream.ts'; +import { + MAX_ARTIFACT_COMPRESSED_BYTES, + createByteLimitStream, +} from '@agent-device/host-kit/archive'; const TEMP_PREFIX = 'agent-device-artifact-'; const REQUEST_IDLE_TIMEOUT_MS = 60_000; diff --git a/src/daemon/artifact-tracking.ts b/src/daemon/artifact-tracking.ts index df40440031..4a8fff31ea 100644 --- a/src/daemon/artifact-tracking.ts +++ b/src/daemon/artifact-tracking.ts @@ -4,7 +4,7 @@ import os from 'node:os'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; import type { DaemonArtifactType } from '@agent-device/kernel/contracts'; -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { expiredTenantOwnedEntryError, requireTenantOwnedEntry, diff --git a/src/daemon/back-runtime.ts b/src/daemon/back-runtime.ts index a57802f6ec..b3eedeca78 100644 --- a/src/daemon/back-runtime.ts +++ b/src/daemon/back-runtime.ts @@ -3,7 +3,7 @@ import type { BackMode } from '@agent-device/contracts/back-mode'; import { backRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; import { resolveBoundGenericRuntime, type RuntimeAdmissionBindings } from './runtime-admission.ts'; diff --git a/src/daemon/client/__tests__/boundary-fault-transport.test.ts b/src/daemon/client/__tests__/boundary-fault-transport.test.ts index 4d2c9bf273..a0db14ad4c 100644 --- a/src/daemon/client/__tests__/boundary-fault-transport.test.ts +++ b/src/daemon/client/__tests__/boundary-fault-transport.test.ts @@ -17,9 +17,10 @@ import { sendRequest } from '../daemon-client-transport.ts'; const { mockRunCmdSync } = vi.hoisted(() => ({ mockRunCmdSync: vi.fn() })); -vi.mock('../../../utils/exec.ts', async () => { - const actual = - await vi.importActual('../../../utils/exec.ts'); +vi.mock('@agent-device/host-kit/command', async () => { + const actual = await vi.importActual( + '@agent-device/host-kit/command', + ); return { ...actual, runCmdSync: mockRunCmdSync }; }); diff --git a/src/daemon/client/__tests__/daemon-client-timeout-route.test.ts b/src/daemon/client/__tests__/daemon-client-timeout-route.test.ts index a19d430698..d01562e128 100644 --- a/src/daemon/client/__tests__/daemon-client-timeout-route.test.ts +++ b/src/daemon/client/__tests__/daemon-client-timeout-route.test.ts @@ -4,7 +4,7 @@ // as a pure formatter, but a pure-formatter test cannot catch a bug in // CLEANUP ELIGIBILITY: whether `cleanupTimedOutIosRunnerBuilds` (the Apple // xcodebuild pkill sweep) actually runs. This file spies on the real -// process-execution seam (`runCmdSync`, src/utils/exec.ts) and drives an +// process-execution seam (`runCmdSync`, @agent-device/host-kit/command) and drives an // actual socket/HTTP timeout through `sendRequest` so the assertions exercise // the same code path a real client does. // @@ -31,9 +31,10 @@ import { beforeEach, test, vi } from 'vitest'; const { mockRunCmdSync } = vi.hoisted(() => ({ mockRunCmdSync: vi.fn() })); -vi.mock('../../../utils/exec.ts', async () => { - const actual = - await vi.importActual('../../../utils/exec.ts'); +vi.mock('@agent-device/host-kit/command', async () => { + const actual = await vi.importActual( + '@agent-device/host-kit/command', + ); return { ...actual, runCmdSync: mockRunCmdSync }; }); diff --git a/src/daemon/client/__tests__/daemon-launch-spec.test.ts b/src/daemon/client/__tests__/daemon-launch-spec.test.ts index dae6bf6f9a..91644541a6 100644 --- a/src/daemon/client/__tests__/daemon-launch-spec.test.ts +++ b/src/daemon/client/__tests__/daemon-launch-spec.test.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { afterEach, test, vi } from 'vitest'; import { computeDaemonCodeSignature } from '../../code-signature.ts'; import { resolveDaemonLaunchSpec, resolveLocalDaemonCodeSignature } from '../daemon-launch-spec.ts'; -import { resetAllProcessMemosForTests } from '../../../utils/ttl-memo.ts'; +import { resetAllProcessMemosForTests } from '@agent-device/kernel/ttl-memo'; afterEach(() => { vi.restoreAllMocks(); diff --git a/src/daemon/client/daemon-client-lifecycle.ts b/src/daemon/client/daemon-client-lifecycle.ts index ce4f9965ea..ab5b0ac92d 100644 --- a/src/daemon/client/daemon-client-lifecycle.ts +++ b/src/daemon/client/daemon-client-lifecycle.ts @@ -5,9 +5,15 @@ import path from 'node:path'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; import { readReplayDivergenceResume } from '@agent-device/contracts/divergence'; import type { DaemonRequest, DaemonResponse } from '../types.ts'; -import { runCmdDetachedMonitored, type ExecDetachedExit } from '../../utils/exec.ts'; -import { readVersion } from '../../utils/version.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { + runCmdDetachedMonitored, + type ExecDetachedExit, + shellQuoteIfNeeded, +} from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; +import { readVersion } from '@agent-device/host-kit/version'; + import { findUnrecoveredRepairCommitFailure } from '../session-store.ts'; import { resolveDaemonPaths, @@ -19,8 +25,7 @@ import { } from '../config.ts'; import { resolveDaemonLaunchSpec, resolveLocalDaemonCodeSignature } from './daemon-launch-spec.ts'; import { PUBLIC_COMMANDS } from '../../command-catalog.ts'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; -import { sleep } from '../../utils/timeouts.ts'; + import { cleanupFailedDaemonStartupMetadata, cleanupStaleDaemonLockIfSafe, @@ -527,7 +532,7 @@ export function isActiveReplaySessionResponse( * emitted when a name is available, explicit state dir or not. Attached to * both a structured `hint` field (for `--json` consumers) and appended to * `message` — the only field the default text renderer surfaces - * (`src/utils/success-text.ts`) — so the hint reaches a caller in either mode. + * (`@agent-device/kernel/success-text`) — so the hint reaches a caller in either mode. * * `data.session` is used verbatim, never reconstructed as `default`: an * EXPLICIT `--session ` is used as-is by `resolveEffectiveSessionName`, diff --git a/src/daemon/client/daemon-client-metadata.ts b/src/daemon/client/daemon-client-metadata.ts index 1c67f8bb83..9c1549d4f8 100644 --- a/src/daemon/client/daemon-client-metadata.ts +++ b/src/daemon/client/daemon-client-metadata.ts @@ -1,7 +1,8 @@ import fs from 'node:fs'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { shellQuote } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { isAgentDeviceDaemonProcess, stopProcessForTakeover } from '../daemon-process.ts'; -import { shellQuote } from '../../utils/shell-quote.ts'; + import { resolveDaemonPaths, type DaemonPaths, type DaemonServerMode } from '../config.ts'; export type DaemonInfo = { diff --git a/src/daemon/client/daemon-client-rpc.ts b/src/daemon/client/daemon-client-rpc.ts index 36092c526c..3ccc05adbe 100644 --- a/src/daemon/client/daemon-client-rpc.ts +++ b/src/daemon/client/daemon-client-rpc.ts @@ -5,7 +5,7 @@ import { type DaemonError, } from '@agent-device/kernel/errors'; import { sanitizeErrorCause } from '@agent-device/kernel/redaction'; -import { createRequestId } from '../../utils/diagnostics.ts'; +import { createRequestId } from '@agent-device/host-kit/diagnostics'; import type { DaemonRequest, DaemonResponse } from '../types.ts'; import { materializeRemoteArtifacts } from '../../remote/daemon-artifacts.ts'; import { localizeRemoteDaemonError } from '../../remote/remote-request-diagnostics.ts'; diff --git a/src/daemon/client/daemon-client-timeout.ts b/src/daemon/client/daemon-client-timeout.ts index 0f5a7b6ac6..e7e1359113 100644 --- a/src/daemon/client/daemon-client-timeout.ts +++ b/src/daemon/client/daemon-client-timeout.ts @@ -1,6 +1,7 @@ import { AppError } from '@agent-device/kernel/errors'; -import { runCmdSync } from '../../utils/exec.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; + import { isAgentDeviceDaemonProcess } from '../daemon-process.ts'; import { PUBLIC_COMMANDS } from '../../command-catalog.ts'; import { resolveCommandTimeoutPolicy } from '../../core/command-descriptor/registry.ts'; diff --git a/src/daemon/client/daemon-client-transport.ts b/src/daemon/client/daemon-client-transport.ts index 3305fc552d..93f95dd0ae 100644 --- a/src/daemon/client/daemon-client-transport.ts +++ b/src/daemon/client/daemon-client-transport.ts @@ -3,7 +3,7 @@ import net from 'node:net'; import { AppError } from '@agent-device/kernel/errors'; import { loadNodeHttpRequester, readNodeHttpResponseBody } from '../../utils/node-http.ts'; import type { DaemonRequest, DaemonResponse } from '../types.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { DaemonPaths, DaemonTransportPreference } from '../config.ts'; import { readDaemonHttpProgressResponse, @@ -15,7 +15,7 @@ import { buildHttpRpcPayload, handleDaemonHttpResponseBody } from './daemon-clie import { handleRequestTimeout } from './daemon-client-timeout.ts'; import { isRemoteDaemon, type DaemonInfo } from './daemon-client-metadata.ts'; import { DAEMON_RPC_PROTOCOL_VERSION } from '../http-health.ts'; -import { readVersion } from '../../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; type ResolvedDaemonTransport = 'socket' | 'http'; type SendRequestOptions = { diff --git a/src/daemon/client/daemon-client.ts b/src/daemon/client/daemon-client.ts index 8379d5dbc0..a3d8f85259 100644 --- a/src/daemon/client/daemon-client.ts +++ b/src/daemon/client/daemon-client.ts @@ -5,7 +5,11 @@ import type { } from '../types.ts'; import type { AgentDeviceDaemonTransportContext } from '@agent-device/contracts/client'; import { AppError } from '@agent-device/kernel/errors'; -import { createRequestId, emitDiagnostic, withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { + createRequestId, + emitDiagnostic, + withDiagnosticTimer, +} from '@agent-device/host-kit/diagnostics'; import { INTERNAL_COMMANDS, PUBLIC_COMMANDS } from '../../command-catalog.ts'; import { prepareRemoteRequestArtifacts } from '../../remote/daemon-artifacts.ts'; import { diff --git a/src/daemon/client/daemon-launch-spec.ts b/src/daemon/client/daemon-launch-spec.ts index aad6289fb0..1159aa9eea 100644 --- a/src/daemon/client/daemon-launch-spec.ts +++ b/src/daemon/client/daemon-launch-spec.ts @@ -1,8 +1,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { createTtlMemo } from '../../utils/ttl-memo.ts'; -import { findProjectRoot } from '../../utils/version.ts'; +import { findProjectRoot } from '@agent-device/host-kit/version'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; + import { computeDaemonCodeSignature } from '../code-signature.ts'; export type DaemonLaunchSpec = { diff --git a/src/daemon/code-signature-cache.ts b/src/daemon/code-signature-cache.ts index 8763de7035..a361f3f7a4 100644 --- a/src/daemon/code-signature-cache.ts +++ b/src/daemon/code-signature-cache.ts @@ -2,7 +2,7 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { publishFileSync } from '../utils/atomic-file.ts'; +import { publishFileSync } from '@agent-device/host-kit/file'; import { buildDaemonCodeFileLabel, formatDaemonCodeSignature, diff --git a/src/daemon/code-signature.ts b/src/daemon/code-signature.ts index 8702f86aa7..823f72e601 100644 --- a/src/daemon/code-signature.ts +++ b/src/daemon/code-signature.ts @@ -1,7 +1,7 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; -import { findProjectRoot } from '../utils/version.ts'; +import { findProjectRoot } from '@agent-device/host-kit/version'; // Any quoted, relative-path-shaped string literal is treated as a module // specifier, rather than matching the `import`/`export`/`from` grammar diff --git a/src/daemon/config.ts b/src/daemon/config.ts index 53ed43842e..63a1ceb32a 100644 --- a/src/daemon/config.ts +++ b/src/daemon/config.ts @@ -1,9 +1,9 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; -import { expandUserHomePath, resolveUserPath } from '../utils/path-resolution.ts'; -import { findProjectRoot } from '../utils/version.ts'; -import type { EnvMap } from '../utils/env-map.ts'; +import { expandUserHomePath, resolveUserPath } from '@agent-device/host-kit/file'; +import { findProjectRoot } from '@agent-device/host-kit/version'; +import { type EnvMap } from '@agent-device/kernel/source-value'; import type { DaemonServerMode, diff --git a/src/daemon/context.ts b/src/daemon/context.ts index b10991c290..e022854df4 100644 --- a/src/daemon/context.ts +++ b/src/daemon/context.ts @@ -5,7 +5,7 @@ import { screenshotFlagsFromOptions, type ScreenshotRuntimeFlags, } from '@agent-device/contracts/capture'; -import { getDiagnosticsMeta } from '../utils/diagnostics.ts'; +import { getDiagnosticsMeta } from '@agent-device/host-kit/diagnostics'; import { resolveRunnerLogicalLeaseContext } from './lease-context.ts'; import type { DaemonRequest } from './types.ts'; diff --git a/src/daemon/daemon-process.ts b/src/daemon/daemon-process.ts index 0aea115baf..3f0fd19c80 100644 --- a/src/daemon/daemon-process.ts +++ b/src/daemon/daemon-process.ts @@ -3,8 +3,8 @@ import { readHostProcessIdentityObservations, readProcessCommand, readProcessStartTime, -} from '../utils/host-process.ts'; -import { sleep } from '../utils/timeouts.ts'; +} from '@agent-device/host-kit/process'; +import { sleep } from '@agent-device/host-kit/retry'; const DAEMON_COMMAND_PATTERNS = [ /\/dist\/src\/daemon\.js($|[\s"'])/, diff --git a/src/daemon/daemon-registration.ts b/src/daemon/daemon-registration.ts index b34fe64f7a..d0c2dcbbcd 100644 --- a/src/daemon/daemon-registration.ts +++ b/src/daemon/daemon-registration.ts @@ -1,5 +1,5 @@ import fs from 'node:fs'; -import { ownerIdentityDiffers, type OwnerIdentity } from '../utils/owner-identity.ts'; +import { ownerIdentityDiffers, type OwnerIdentity } from '@agent-device/host-kit/process'; import { resolveDaemonPaths } from './config.ts'; /** diff --git a/src/daemon/daemon-shutdown-report.ts b/src/daemon/daemon-shutdown-report.ts index 5e4fe81c7f..2ec19ce684 100644 --- a/src/daemon/daemon-shutdown-report.ts +++ b/src/daemon/daemon-shutdown-report.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import type { DeviceLease } from '@agent-device/contracts/device'; -import { publishFileSync } from '../utils/atomic-file.ts'; +import { publishFileSync } from '@agent-device/host-kit/file'; const SHUTDOWN_REPORT_FILE = 'daemon-shutdown.json'; diff --git a/src/daemon/daemon-stop.ts b/src/daemon/daemon-stop.ts index ac9667bc51..79ca7bd6d5 100644 --- a/src/daemon/daemon-stop.ts +++ b/src/daemon/daemon-stop.ts @@ -6,8 +6,9 @@ import { waitForDaemonExit, type DaemonProcessIdentity, } from './daemon-process.ts'; -import { isProcessAlive } from '../utils/host-process.ts'; -import { sleep } from '../utils/timeouts.ts'; +import { isProcessAlive } from '@agent-device/host-kit/process'; +import { sleep } from '@agent-device/host-kit/retry'; + import type { DaemonPaths } from './config.ts'; import { readRegisteredDaemonIdentity } from './daemon-registration.ts'; import type { DeviceClaimRecord, ProviderReleaseRecord } from './daemon-shutdown-report.ts'; diff --git a/src/daemon/deferred-interaction-outcome.ts b/src/daemon/deferred-interaction-outcome.ts index 61886382f5..575b741af6 100644 --- a/src/daemon/deferred-interaction-outcome.ts +++ b/src/daemon/deferred-interaction-outcome.ts @@ -2,7 +2,7 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import type { SnapshotCaptureAnnotations } from '@agent-device/contracts/capture'; import { isApplePlatform, isMobilePlatform } from '@agent-device/kernel/device'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; -import { sleep } from '../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { captureAndroidFreshnessRecoveredAttempt, clearAndroidSnapshotFreshness, diff --git a/src/daemon/device-claim-admission.ts b/src/daemon/device-claim-admission.ts index da444e4cee..69a256182c 100644 --- a/src/daemon/device-claim-admission.ts +++ b/src/daemon/device-claim-admission.ts @@ -1,7 +1,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import type { RuntimeOwnerRef } from '@agent-device/contracts/platform-runtime'; import type { DeviceClaimPolicy } from '../core/command-descriptor/types.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { deviceClaimConflictError } from './device-claim-conflict.ts'; import { acquireTransientDeviceClaim, diff --git a/src/daemon/device-claim-conflict.ts b/src/daemon/device-claim-conflict.ts index d7bb719ff6..95d62953dc 100644 --- a/src/daemon/device-claim-conflict.ts +++ b/src/daemon/device-claim-conflict.ts @@ -4,7 +4,7 @@ import { type DeviceInfo, } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { shellQuoteIfNeeded } from '../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; import { deviceClaimRequiresStaleInspection, type DeviceClaimClassification, diff --git a/src/daemon/device-claim-inspection.ts b/src/daemon/device-claim-inspection.ts index be2e2b4247..0b5d94107b 100644 --- a/src/daemon/device-claim-inspection.ts +++ b/src/daemon/device-claim-inspection.ts @@ -11,8 +11,9 @@ import { decodeDeviceIdentity } from '@agent-device/capture-kit'; import { classifyOwnerLivenessFromObservation, type OwnerLiveness, -} from '../utils/owner-identity.ts'; -import { readHostProcessIdentityObservations } from '../utils/host-process.ts'; + readHostProcessIdentityObservations, +} from '@agent-device/host-kit/process'; + import { isSupersededDaemonOwner } from './daemon-registration.ts'; import { canonicalLocalDeviceKey, resolveDeviceClaimRoot } from './device-claim-paths.ts'; import type { DeviceClaim } from './device-claims.ts'; diff --git a/src/daemon/device-claim-reconciliation.ts b/src/daemon/device-claim-reconciliation.ts index b63f9fa10b..0bd3bd6bd3 100644 --- a/src/daemon/device-claim-reconciliation.ts +++ b/src/daemon/device-claim-reconciliation.ts @@ -3,7 +3,7 @@ import type { DeviceRuntimeGateway } from '@agent-device/contracts/platform-runt import type { PlatformRequestScope } from '@agent-device/contracts/platform-runtime-host'; import type { PlatformRuntimeOperations } from '@agent-device/contracts/platform-runtime-operations'; import { sameDeviceIdentity } from '@agent-device/kernel/device'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { resolveDaemonPaths } from './config.ts'; import { appLogDurableResource } from './app-log-session-resource.ts'; import { recoverAppLogResourceAfterDaemonLock } from './app-log-resource-recovery.ts'; diff --git a/src/daemon/device-claims.ts b/src/daemon/device-claims.ts index e7c3588518..c84d3fa0fc 100644 --- a/src/daemon/device-claims.ts +++ b/src/daemon/device-claims.ts @@ -8,11 +8,11 @@ import { type DeviceIdentity, type DeviceInfo, } from '@agent-device/kernel/device'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { ownerIdentityMatches, readCurrentOwnerIdentity } from '@agent-device/host-kit/process'; import type { RuntimeOwnerRef } from '@agent-device/contracts/platform-runtime'; -import { publishFileSync } from '../utils/atomic-file.ts'; -import { acquireProcessLock } from '../utils/process-lock.ts'; -import { ownerIdentityMatches, readCurrentOwnerIdentity } from '../utils/owner-identity.ts'; +import { publishFileSync, acquireProcessLock } from '@agent-device/host-kit/file'; + import { deviceClaimOwnerCannotRelease, inspectDeviceClaimFile, diff --git a/src/daemon/device-ready.ts b/src/daemon/device-ready.ts index 2137c227cb..15338570e9 100644 --- a/src/daemon/device-ready.ts +++ b/src/daemon/device-ready.ts @@ -1,7 +1,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { ensureLocalPlatformDeviceReady } from '../platform-runtime-device-ready.ts'; import { isActiveProviderDevice } from '../provider-device-runtime.ts'; -import { createTtlMemo } from '../utils/ttl-memo.ts'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; // Exported so unit tests can assert TTL behavior without duplicating the value. export const DEVICE_READY_CACHE_TTL_MS = 5_000; diff --git a/src/daemon/durable-capture-resource-adoption.ts b/src/daemon/durable-capture-resource-adoption.ts index 1d84f77752..a190a6fbb7 100644 --- a/src/daemon/durable-capture-resource-adoption.ts +++ b/src/daemon/durable-capture-resource-adoption.ts @@ -11,7 +11,7 @@ import { } from '@agent-device/capture-kit'; import { deviceIdentity, sameDeviceIdentity, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { AdoptStartedDurableCaptureParams, DurableCaptureResourceDefinition, diff --git a/src/daemon/durable-capture-resource-recovery.ts b/src/daemon/durable-capture-resource-recovery.ts index fad77edaf3..0e3e58070b 100644 --- a/src/daemon/durable-capture-resource-recovery.ts +++ b/src/daemon/durable-capture-resource-recovery.ts @@ -6,7 +6,7 @@ import { isConfirmedCleanup, } from '@agent-device/contracts/durable-resource'; import type { PlatformRequestScope } from '@agent-device/contracts/platform-runtime-host'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { acquireDurableCaptureRecoveryAuthorityBeforeDeadline, DurableCaptureRecoveryDeadlineError, diff --git a/src/daemon/durable-capture-resource-store.ts b/src/daemon/durable-capture-resource-store.ts index 354703f80b..8f7ac301e6 100644 --- a/src/daemon/durable-capture-resource-store.ts +++ b/src/daemon/durable-capture-resource-store.ts @@ -5,7 +5,7 @@ import type { DurableResourceEnvelope, } from '@agent-device/contracts/durable-resource-envelope'; import { decodeDurableResourceEnvelope } from '@agent-device/capture-kit'; -import { withAtomicPublishTempPathSync } from '../utils/atomic-file.ts'; +import { withAtomicPublishTempPathSync } from '@agent-device/host-kit/file'; import { openVerifiedFileForRead } from '../utils/verified-file.ts'; export type DurableCaptureResourceRecord = diff --git a/src/daemon/durable-capture-resource-transitions.ts b/src/daemon/durable-capture-resource-transitions.ts index 6e6368de92..6e46c2ba54 100644 --- a/src/daemon/durable-capture-resource-transitions.ts +++ b/src/daemon/durable-capture-resource-transitions.ts @@ -5,7 +5,7 @@ import { isConfirmedCleanup, } from '@agent-device/contracts/durable-resource'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { withDurableCaptureResourceFence, type DurableCaptureResourceFenceLease, diff --git a/src/daemon/focus-runtime.ts b/src/daemon/focus-runtime.ts index e9790fd175..f9701487f4 100644 --- a/src/daemon/focus-runtime.ts +++ b/src/daemon/focus-runtime.ts @@ -2,7 +2,7 @@ import type { FocusPointInput } from '@agent-device/contracts/focus-runtime'; import { focusRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; import type { DeviceInfo } from '@agent-device/kernel/device'; import type { Point } from '@agent-device/kernel/snapshot'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { readPointPositionals } from '../utils/validation.ts'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; diff --git a/src/daemon/handlers/__tests__/interaction-settle.test.ts b/src/daemon/handlers/__tests__/interaction-settle.test.ts index d547a9d70e..21939cbb92 100644 --- a/src/daemon/handlers/__tests__/interaction-settle.test.ts +++ b/src/daemon/handlers/__tests__/interaction-settle.test.ts @@ -11,7 +11,10 @@ import { activateCompleteRefFrame } from '../../ref-frame.ts'; import { makeSessionStore } from '../../../__tests__/test-utils/store-factory.ts'; import { makeIosSession } from '../../../__tests__/test-utils/session-factories.ts'; import { createInteractionRuntime } from '../interaction-runtime.ts'; -import { clearRequestAbortRegistration, registerRequestAbort } from '../../../request/cancel.ts'; +import { + clearRequestAbortRegistration, + registerRequestAbort, +} from '@agent-device/host-kit/request'; import { IOS_SIMULATOR } from '../../../__tests__/test-utils/device-fixtures.ts'; import { getRuntimeBindings, diff --git a/src/daemon/handlers/__tests__/session-close-shutdown.fixtures.ts b/src/daemon/handlers/__tests__/session-close-shutdown.fixtures.ts index e8871a54c6..17c3f8173a 100644 --- a/src/daemon/handlers/__tests__/session-close-shutdown.fixtures.ts +++ b/src/daemon/handlers/__tests__/session-close-shutdown.fixtures.ts @@ -12,8 +12,8 @@ vi.mock('../../../platforms/apple/core/simulator.ts', async (importOriginal) => await importOriginal(); return { ...actual, shutdownSimulator: vi.fn() }; }); -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../../../platforms/apple/core/runner-client.ts', async (importOriginal) => { @@ -54,7 +54,11 @@ import { teardownSessionResources as teardownProductionSessionResources } from ' import { platformResourceCleanup } from '../../../platform-runtime-resource-cleanup.ts'; import { LeaseRegistry } from '../../lease-registry.ts'; import { shutdownSimulator } from '../../../platforms/apple/core/simulator.ts'; -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { + flushDiagnosticsToSessionFile, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { cleanupAppleXctracePerfCapture } from '../../../platforms/apple/core/perf-xctrace.ts'; import { cleanupAndroidNativePerfSession } from '../../../platforms/android/perf-native.ts'; import { stopAndroidSnapshotHelperSessionForDevice } from '../../../platforms/android/snapshot-helper.ts'; @@ -62,7 +66,7 @@ import { stopIosRunnerSession } from '../../../platforms/apple/core/runner-clien import { WEB_DESKTOP_DEVICE } from '../../../__tests__/test-utils/device-fixtures.ts'; import { acquireDeviceClaim } from '../../device-claims.ts'; import { inspectDeviceClaims } from '../../device-claim-inspection.ts'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../../../utils/diagnostics.ts'; + import { type DeviceBinding, localRuntimeOwner, diff --git a/src/daemon/handlers/__tests__/session-device-claims.test.ts b/src/daemon/handlers/__tests__/session-device-claims.test.ts index 6cecb29d07..b6ddcea2df 100644 --- a/src/daemon/handlers/__tests__/session-device-claims.test.ts +++ b/src/daemon/handlers/__tests__/session-device-claims.test.ts @@ -29,7 +29,7 @@ vi.mock('../../../platforms/android/ime-lifecycle.ts', () => ({ activateAndroidTestIme: vi.fn(async () => ({ activated: false })), restoreAndroidTestIme: vi.fn(async () => ({ restored: false, reason: 'no-record' })), })); -vi.mock('../../../utils/host-process.ts', async (importOriginal) => +vi.mock('@agent-device/host-kit/process', async (importOriginal) => (await import('../../../__tests__/test-utils/host-process-mock.ts')).pinOwnProcessStartTime( importOriginal, ), @@ -44,7 +44,7 @@ import { discoverReadyAndroidEmulators, dispatchApplicationLifecycleEffect, } from '../../__tests__/application-lifecycle-runtime-fixture.ts'; -import { clearRequestCanceled, markRequestCanceled } from '../../../request/cancel.ts'; +import { clearRequestCanceled, markRequestCanceled } from '@agent-device/host-kit/request'; import { acquireDeviceClaim as acquireProductionDeviceClaim } from '../../device-claims.ts'; import { inspectDeviceClaims } from '../../device-claim-inspection.ts'; import { LeaseRegistry } from '../../lease-registry.ts'; diff --git a/src/daemon/handlers/__tests__/session-open-execution-runtime.test.ts b/src/daemon/handlers/__tests__/session-open-execution-runtime.test.ts index 590ef27037..aad33d91c3 100644 --- a/src/daemon/handlers/__tests__/session-open-execution-runtime.test.ts +++ b/src/daemon/handlers/__tests__/session-open-execution-runtime.test.ts @@ -51,8 +51,8 @@ vi.mock('../../../platforms/android/ime-lifecycle.ts', async (importOriginal) => await importOriginal(); return { ...actual, activateAndroidTestIme: vi.fn(async () => ({ activated: false })) }; }); -vi.mock('../../../utils/host-process.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, readProcessStartTime: vi.fn(() => 'test-process-start') }; }); diff --git a/src/daemon/handlers/__tests__/session-open-runtime.test.ts b/src/daemon/handlers/__tests__/session-open-runtime.test.ts index 02ce130ea2..8c65c99cbe 100644 --- a/src/daemon/handlers/__tests__/session-open-runtime.test.ts +++ b/src/daemon/handlers/__tests__/session-open-runtime.test.ts @@ -49,8 +49,8 @@ vi.mock('../../../platforms/android/ime-lifecycle.ts', async (importOriginal) => await importOriginal(); return { ...actual, activateAndroidTestIme: vi.fn(async () => ({ activated: false })) }; }); -vi.mock('../../../utils/host-process.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, readProcessStartTime: vi.fn(() => 'test-process-start') }; }); diff --git a/src/daemon/handlers/__tests__/session-replay-divergence.test.ts b/src/daemon/handlers/__tests__/session-replay-divergence.test.ts index 692824a1a4..ccdce4e7e0 100644 --- a/src/daemon/handlers/__tests__/session-replay-divergence.test.ts +++ b/src/daemon/handlers/__tests__/session-replay-divergence.test.ts @@ -18,8 +18,8 @@ vi.mock('../snapshot-interactor-capture.ts', () => ({ // re-captures — so the test still proves two dispatches and use of the retried // tree. The #1385 `retryLaunchRace` retry loop (`captureDivergenceObservation`) // awaits this SAME `sleep`, further down in this file. -vi.mock('../../../utils/timeouts.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, sleep: vi.fn(async () => {}) }; }); diff --git a/src/daemon/handlers/__tests__/session-replay-repair-record-exclusion.test.ts b/src/daemon/handlers/__tests__/session-replay-repair-record-exclusion.test.ts index a56b62ca64..60cc9f6982 100644 --- a/src/daemon/handlers/__tests__/session-replay-repair-record-exclusion.test.ts +++ b/src/daemon/handlers/__tests__/session-replay-repair-record-exclusion.test.ts @@ -25,8 +25,8 @@ vi.mock('../../../platforms/apple/core/simulator.ts', async (importOriginal) => await importOriginal(); return { ...actual, shutdownSimulator: vi.fn() }; }); -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../../../platforms/apple/core/runner-client.ts', async (importOriginal) => { diff --git a/src/daemon/handlers/__tests__/session-replay-repair-transaction.test.ts b/src/daemon/handlers/__tests__/session-replay-repair-transaction.test.ts index c3c4b07117..06d4bd6b1d 100644 --- a/src/daemon/handlers/__tests__/session-replay-repair-transaction.test.ts +++ b/src/daemon/handlers/__tests__/session-replay-repair-transaction.test.ts @@ -25,8 +25,8 @@ vi.mock('../../../platforms/apple/core/simulator.ts', async (importOriginal) => await importOriginal(); return { ...actual, shutdownSimulator: vi.fn() }; }); -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../../../platforms/apple/core/runner-client.ts', async (importOriginal) => { diff --git a/src/daemon/handlers/__tests__/session-replay-runtime-maestro.test.ts b/src/daemon/handlers/__tests__/session-replay-runtime-maestro.test.ts index 34be07eded..7c18c5b73b 100644 --- a/src/daemon/handlers/__tests__/session-replay-runtime-maestro.test.ts +++ b/src/daemon/handlers/__tests__/session-replay-runtime-maestro.test.ts @@ -51,7 +51,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { Worker } from 'node:worker_threads'; -import { PNG } from '../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import type { DaemonInvokeFn, DaemonRequest, DaemonResponse } from '../../types.ts'; import { SessionStore } from '../../session-store.ts'; import { runReplayScriptSource } from '../session-replay-runtime.ts'; diff --git a/src/daemon/handlers/__tests__/session-replay-target-verification-runtime.test.ts b/src/daemon/handlers/__tests__/session-replay-target-verification-runtime.test.ts index e0aee3fb42..b630f50f8b 100644 --- a/src/daemon/handlers/__tests__/session-replay-target-verification-runtime.test.ts +++ b/src/daemon/handlers/__tests__/session-replay-target-verification-runtime.test.ts @@ -23,8 +23,8 @@ vi.mock('../snapshot-interactor-capture.ts', () => ({ // no-op so the retry tests below exercise the retry BRANCH (still runs each // mocked capture attempt) without a real wall-clock wait (repo guidance: unit // tests must not wait real time). -vi.mock('../../../utils/timeouts.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, sleep: vi.fn(async () => {}) }; }); diff --git a/src/daemon/handlers/__tests__/session-test-harness.ts b/src/daemon/handlers/__tests__/session-test-harness.ts index 7b6ab0e0d2..7811f95aca 100644 --- a/src/daemon/handlers/__tests__/session-test-harness.ts +++ b/src/daemon/handlers/__tests__/session-test-harness.ts @@ -69,13 +69,8 @@ vi.mock('../../../platforms/android/ime-lifecycle.ts', async (importOriginal) => restoreAndroidTestIme: vi.fn(async () => ({ restored: false, reason: 'no-record' })), }; }); -vi.mock('../../../platforms/apple/core/simulator.ts', async (importOriginal) => { - const actual = - await importOriginal(); - return { ...actual, getSimulatorState: vi.fn(async () => null) }; -}); -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(async () => ({ stdout: '', stderr: '', exitCode: 0 })) }; }); vi.mock('../../materialized-path-registry.ts', async (importOriginal) => { @@ -111,7 +106,7 @@ import { } from '../../../platforms/apple/core/runner-client.ts'; import { runMacOsAlertAction } from '../../../platforms/apple/os/macos/helper.ts'; import { resolveAndroidPackageForOpen } from '../../../platform-runtime-open-target.ts'; -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { resolveIosApp, resolveIosSimulatorDeepLinkBundleId, diff --git a/src/daemon/handlers/__tests__/session-test-reporter-values-maestro.test.ts b/src/daemon/handlers/__tests__/session-test-reporter-values-maestro.test.ts index 1944637632..4fe8a68ff6 100644 --- a/src/daemon/handlers/__tests__/session-test-reporter-values-maestro.test.ts +++ b/src/daemon/handlers/__tests__/session-test-reporter-values-maestro.test.ts @@ -42,7 +42,7 @@ import type { ReplaySuiteResult } from '@agent-device/contracts/replay'; import { handleSessionCommands } from './session-command-harness.ts'; import { SessionStore } from '../../session-store.ts'; import type { DaemonRequest, DaemonResponse } from '../../types.ts'; -import { withRequestProgressSink } from '../../../request/progress.ts'; +import { withRequestProgressSink } from '@agent-device/host-kit/request'; import { getReplayTestReporterExitCode, runReplayTestReporterProgress, diff --git a/src/daemon/handlers/__tests__/session-test-reporter-values.test.ts b/src/daemon/handlers/__tests__/session-test-reporter-values.test.ts index 63db1e878f..43cb366f20 100644 --- a/src/daemon/handlers/__tests__/session-test-reporter-values.test.ts +++ b/src/daemon/handlers/__tests__/session-test-reporter-values.test.ts @@ -26,7 +26,7 @@ import type { ReplaySuiteResult } from '@agent-device/contracts/replay'; import { handleSessionCommands } from './session-command-harness.ts'; import { SessionStore } from '../../session-store.ts'; import type { DaemonRequest, DaemonResponse } from '../../types.ts'; -import { withRequestProgressSink } from '../../../request/progress.ts'; +import { withRequestProgressSink } from '@agent-device/host-kit/request'; import { withTestDeviceInventoryProvider as withDeviceInventoryProvider } from '../../../__tests__/test-utils/device-inventory-gateways.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { diff --git a/src/daemon/handlers/__tests__/session-test-runner.test.ts b/src/daemon/handlers/__tests__/session-test-runner.test.ts index cf9b78c0a8..7378b99c61 100644 --- a/src/daemon/handlers/__tests__/session-test-runner.test.ts +++ b/src/daemon/handlers/__tests__/session-test-runner.test.ts @@ -2,7 +2,7 @@ import { test, expect, vi } from 'vitest'; import fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; -import { clearRequestCanceled, markRequestCanceled } from '../../../request/cancel.ts'; +import { clearRequestCanceled, markRequestCanceled } from '@agent-device/host-kit/request'; import { mockResolveTargetDevice, makeSessionStore, diff --git a/src/daemon/handlers/__tests__/session-test-suite-command-cancellation.test.ts b/src/daemon/handlers/__tests__/session-test-suite-command-cancellation.test.ts index d6c6173cc7..ae94c81c46 100644 --- a/src/daemon/handlers/__tests__/session-test-suite-command-cancellation.test.ts +++ b/src/daemon/handlers/__tests__/session-test-suite-command-cancellation.test.ts @@ -5,7 +5,7 @@ import { isRequestCanceled, markRequestCanceled, registerRequestAbort, -} from '../../../request/cancel.ts'; +} from '@agent-device/host-kit/request'; import { bindReplayTestAttemptCancellation } from '../session-test-suite-command.ts'; // The daemon half of the replay-test cancellation seam (#1478 P3b). The scheduler only says diff --git a/src/daemon/handlers/__tests__/session-test-suite.test.ts b/src/daemon/handlers/__tests__/session-test-suite.test.ts index b3150faf3d..e9d59dc1c4 100644 --- a/src/daemon/handlers/__tests__/session-test-suite.test.ts +++ b/src/daemon/handlers/__tests__/session-test-suite.test.ts @@ -20,13 +20,14 @@ import path from 'node:path'; import { handleSessionCommands, mockInspectDeviceRuntimeFacts } from './session-command-harness.ts'; import type { DaemonRequest } from '../../types.ts'; import { expectOkData, makeSessionStore } from './session-test-suite.fixtures.ts'; -import { withRequestProgressSink } from '../../../request/progress.ts'; import { + withRequestProgressSink, clearRequestCanceled, getRequestSignal, markRequestCanceled, registerRequestAbort, -} from '../../../request/cancel.ts'; +} from '@agent-device/host-kit/request'; + import { withTestDeviceInventoryProvider as withDeviceInventoryProvider } from '../../../__tests__/test-utils/device-inventory-gateways.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { diff --git a/src/daemon/handlers/find.ts b/src/daemon/handlers/find.ts index 76fe61c91c..2d7412b70d 100644 --- a/src/daemon/handlers/find.ts +++ b/src/daemon/handlers/find.ts @@ -12,7 +12,7 @@ import { expireRefFrame } from '../ref-frame.ts'; import type { DaemonInvokeFn, DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { SessionStore } from '../session-store.ts'; import { contextFromFlags } from '../context.ts'; -import { readCommandMessage, successText } from '../../utils/success-text.ts'; +import { readCommandMessage, successText } from '@agent-device/kernel/success-text'; import { errorResponse, noActiveSessionError } from './response.ts'; import { withSystemSurfaceDisclosure } from './system-surface-disclosure.ts'; import { recordSessionAction } from './handler-utils.ts'; @@ -27,7 +27,7 @@ import { resolveSelectorCaptureRuntimePlan } from '@agent-device/contracts/platf import type { TypeTextRuntimeOperations } from '@agent-device/contracts/type-text-runtime'; import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-runtime-binding.ts'; import { createFindTargetCapture, sparseFindSnapshotResponse } from './find-target-capture.ts'; -import { isSparseSnapshotQualityVerdict } from '../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; type FindContext = { req: DaemonRequest; diff --git a/src/daemon/handlers/interaction-gesture.ts b/src/daemon/handlers/interaction-gesture.ts index dfb26f53ef..1fc84f2eb9 100644 --- a/src/daemon/handlers/interaction-gesture.ts +++ b/src/daemon/handlers/interaction-gesture.ts @@ -25,7 +25,7 @@ import { } from '@agent-device/kernel/snapshot'; import { resolveBoundGestureRuntime, type BoundGestureExecutor } from '../gesture-runtime.ts'; import { isActiveProviderDevice } from '../../provider-device-runtime.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { ensureAndroidBlockingSystemDialogReady } from '../android-system-dialog.ts'; import type { DaemonResponse, SessionState } from '../types.ts'; import type { InteractionHandlerParams } from './interaction-common.ts'; diff --git a/src/daemon/handlers/interaction-ios-tap-outcome.ts b/src/daemon/handlers/interaction-ios-tap-outcome.ts index fff6677b05..19a787ef8a 100644 --- a/src/daemon/handlers/interaction-ios-tap-outcome.ts +++ b/src/daemon/handlers/interaction-ios-tap-outcome.ts @@ -5,11 +5,11 @@ import { asAppError } from '@agent-device/kernel/errors'; import { isSparseSnapshotQualityVerdict, preferredSnapshotBackendForVerdict, -} from '../../snapshot-quality/verdict.ts'; +} from '@agent-device/capture-kit/snapshot-quality-verdict'; import { summarizeAxEvidence } from '../../utils/ax-digest.ts'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { isLocalIosRunnerSession } from '../direct-ios-selector.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionStore } from '../session-store.ts'; import type { SessionState } from '../types.ts'; import type { ContextFromFlags } from './interaction-common.ts'; diff --git a/src/daemon/handlers/interaction-read.ts b/src/daemon/handlers/interaction-read.ts index 65746105b7..eda77362cb 100644 --- a/src/daemon/handlers/interaction-read.ts +++ b/src/daemon/handlers/interaction-read.ts @@ -5,7 +5,7 @@ import type { } from '@agent-device/contracts/element-text-runtime'; import { isIosFamily } from '@agent-device/kernel/device'; import { runtimeExecutionFromContext } from '../snapshot-runtime-capture-input.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionState } from '../types.ts'; import type { SnapshotNode } from '@agent-device/kernel/snapshot'; import { extractReadableText, prefersValueForReadableText } from '../../utils/text-surface.ts'; diff --git a/src/daemon/handlers/interaction-runtime.ts b/src/daemon/handlers/interaction-runtime.ts index 6e9bbe4882..f178968066 100644 --- a/src/daemon/handlers/interaction-runtime.ts +++ b/src/daemon/handlers/interaction-runtime.ts @@ -15,7 +15,7 @@ import { createDaemonRuntimePolicy } from '../runtime-policy.ts'; import { createDaemonRuntimeSessionStore } from '../runtime-session.ts'; import { NO_ACTIVE_SESSION_MESSAGE } from './response.ts'; import type { Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { buildAppleRunnerRequestOptions } from '../apple-runner-options.ts'; import { isLocalIosRunnerSession } from '../direct-ios-selector.ts'; import { confirmIosOffscreenTargetVisible } from '../offscreen-target-probe.ts'; diff --git a/src/daemon/handlers/interaction-snapshot.ts b/src/daemon/handlers/interaction-snapshot.ts index fd0ff44cc3..49ab5ed061 100644 --- a/src/daemon/handlers/interaction-snapshot.ts +++ b/src/daemon/handlers/interaction-snapshot.ts @@ -5,7 +5,7 @@ import type { SnapshotPreferredBackend, SnapshotState } from '@agent-device/kern import type { ContextFromFlags } from './interaction-common.ts'; import { captureSnapshot } from './snapshot-capture.ts'; import { setSessionSnapshot } from '../session-snapshot.ts'; -import { isSparseSnapshotQualityVerdict } from '../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { snapshotOptionsToFlags } from '../../backend-snapshot-options.ts'; import type { CaptureSnapshotInput, diff --git a/src/daemon/handlers/interaction-targeting.ts b/src/daemon/handlers/interaction-targeting.ts index ec8c8d8c8e..88987e9820 100644 --- a/src/daemon/handlers/interaction-targeting.ts +++ b/src/daemon/handlers/interaction-targeting.ts @@ -1,4 +1,4 @@ -import { resolveRectCenter } from '../../utils/rect-center.ts'; +import { resolveRectCenter } from '@agent-device/kernel/rect-center'; export function parseCoordinateTarget(positionals: string[]): { x: number; y: number } | null { if (positionals.length < 2) return null; diff --git a/src/daemon/handlers/interaction-touch-android-freshness.ts b/src/daemon/handlers/interaction-touch-android-freshness.ts index 42cbe175ea..64ccb128cc 100644 --- a/src/daemon/handlers/interaction-touch-android-freshness.ts +++ b/src/daemon/handlers/interaction-touch-android-freshness.ts @@ -1,4 +1,4 @@ -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { getActiveAndroidSnapshotFreshness } from '../session-snapshot-freshness.ts'; import type { SessionState } from '../types.ts'; import type { InteractionHandlerParams } from './interaction-common.ts'; diff --git a/src/daemon/handlers/interaction-touch-direct-ios.ts b/src/daemon/handlers/interaction-touch-direct-ios.ts index c4a0faea26..bf9aff71c2 100644 --- a/src/daemon/handlers/interaction-touch-direct-ios.ts +++ b/src/daemon/handlers/interaction-touch-direct-ios.ts @@ -1,6 +1,6 @@ import type { GestureReferenceFrame } from '@agent-device/contracts/scroll-gesture'; import { normalizeError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { isDirectIosSelectorFallbackError, type DirectIosSelectorTarget, diff --git a/src/daemon/handlers/interaction-touch-payload.ts b/src/daemon/handlers/interaction-touch-payload.ts index 971b97879f..d1e55b0191 100644 --- a/src/daemon/handlers/interaction-touch-payload.ts +++ b/src/daemon/handlers/interaction-touch-payload.ts @@ -1,5 +1,5 @@ import type { GestureReferenceFrame } from '@agent-device/contracts/scroll-gesture'; -import { successText } from '../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; /** * What a built touch payload looks like: the field merge (backend data, point, diff --git a/src/daemon/handlers/interaction-touch-reference-frame.ts b/src/daemon/handlers/interaction-touch-reference-frame.ts index 79e573233a..eb9e523139 100644 --- a/src/daemon/handlers/interaction-touch-reference-frame.ts +++ b/src/daemon/handlers/interaction-touch-reference-frame.ts @@ -2,7 +2,7 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import type { AndroidObservationAdapter } from '@agent-device/contracts/android-observation'; import type { GestureReferenceFrame } from '@agent-device/contracts/scroll-gesture'; import type { SnapshotNode } from '@agent-device/kernel/snapshot'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionStore } from '../session-store.ts'; import { getSnapshotReferenceFrame } from '../touch-reference-frame.ts'; import type { SessionState } from '../types.ts'; diff --git a/src/daemon/handlers/lease.ts b/src/daemon/handlers/lease.ts index 9b235d878b..fa7293f831 100644 --- a/src/daemon/handlers/lease.ts +++ b/src/daemon/handlers/lease.ts @@ -23,7 +23,7 @@ import { } from '../../core/lease-scope.ts'; import { AppError, createRequestCanceledError, errorMessage } from '@agent-device/kernel/errors'; import { LEASE_ALLOCATION_BUDGET_MS } from '../../core/command-descriptor/timeout-policy.ts'; -import { getRequestSignal, isRequestCanceled } from '../../request/cancel.ts'; +import { getRequestSignal, isRequestCanceled } from '@agent-device/host-kit/request'; import { listDownloadableArtifacts } from '../artifact-tracking.ts'; type LeaseHandlerArgs = { diff --git a/src/daemon/handlers/react-native.ts b/src/daemon/handlers/react-native.ts index e02ab5c0e4..4c6111f128 100644 --- a/src/daemon/handlers/react-native.ts +++ b/src/daemon/handlers/react-native.ts @@ -9,10 +9,11 @@ import { type ReactNativeOverlayDismissTarget, } from '../../core/react-native-overlay.ts'; import { normalizeError } from '@agent-device/kernel/errors'; -import { stripUndefined } from '../../utils/parsing.ts'; -import { successText } from '../../utils/success-text.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; +import { successText } from '@agent-device/kernel/success-text'; + import type { SnapshotQualityVerdict, SnapshotState } from '@agent-device/kernel/snapshot'; -import { isSparseSnapshotQualityVerdict } from '../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import type { DaemonResponse, SessionState } from '../types.ts'; import { errorResponse, noActiveSessionError } from './response.ts'; import { captureSnapshotForSession } from './interaction-snapshot.ts'; diff --git a/src/daemon/handlers/session-app-deployment.ts b/src/daemon/handlers/session-app-deployment.ts index adde3e6aed..11830fa46b 100644 --- a/src/daemon/handlers/session-app-deployment.ts +++ b/src/daemon/handlers/session-app-deployment.ts @@ -13,7 +13,7 @@ import { SessionStore } from '../session-store.ts'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { resolvePayloadInput } from '../../utils/payload-input.ts'; import { resolveDeployResultTarget } from '../../utils/result-serialization.ts'; -import { withSuccessText } from '../../utils/success-text.ts'; +import { withSuccessText } from '@agent-device/kernel/success-text'; import { recordSessionAction } from './handler-utils.ts'; import { errorResponse } from './response.ts'; import { requireSessionOrExplicitSelector, resolveCommandDevice } from './session-device-utils.ts'; diff --git a/src/daemon/handlers/session-app-source-deployment.ts b/src/daemon/handlers/session-app-source-deployment.ts index d6952970ec..0f00dabb5c 100644 --- a/src/daemon/handlers/session-app-source-deployment.ts +++ b/src/daemon/handlers/session-app-source-deployment.ts @@ -17,7 +17,7 @@ import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-ru import { SessionStore } from '../session-store.ts'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { resolveInstallFromSourceResultTarget } from '../../utils/result-serialization.ts'; -import { withSuccessText } from '../../utils/success-text.ts'; +import { withSuccessText } from '@agent-device/kernel/success-text'; import { recordSessionAction } from './handler-utils.ts'; import { resolveCommandDevice } from './session-device-utils.ts'; import { diff --git a/src/daemon/handlers/session-clipboard.ts b/src/daemon/handlers/session-clipboard.ts index e275295ae2..d44e6462d5 100644 --- a/src/daemon/handlers/session-clipboard.ts +++ b/src/daemon/handlers/session-clipboard.ts @@ -16,7 +16,7 @@ import type { SessionStore } from '../session-store.ts'; import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-runtime-binding.ts'; import { admitRuntimeUse, type RuntimeAdmissionBindings } from '../runtime-admission.ts'; import { runtimeExecutionFromContext } from '../snapshot-runtime-capture-input.ts'; -import { successText } from '../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { errorResponse, type DaemonFailureResponse } from './response.ts'; import { recordSessionAction } from './handler-utils.ts'; import { requireSessionOrExplicitSelector, resolveCommandDevice } from './session-device-utils.ts'; diff --git a/src/daemon/handlers/session-close-script.ts b/src/daemon/handlers/session-close-script.ts index ce51ce715d..9e051e9c36 100644 --- a/src/daemon/handlers/session-close-script.ts +++ b/src/daemon/handlers/session-close-script.ts @@ -1,5 +1,5 @@ import { AppError, normalizeError } from '@agent-device/kernel/errors'; -import { successText } from '../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { SessionStore } from '../session-store.ts'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { recordSessionAction } from './handler-utils.ts'; diff --git a/src/daemon/handlers/session-close.ts b/src/daemon/handlers/session-close.ts index 0acbc6a867..9d6ca0adda 100644 --- a/src/daemon/handlers/session-close.ts +++ b/src/daemon/handlers/session-close.ts @@ -1,9 +1,9 @@ -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; import type { LeaseLifecycleProvider, TargetShutdownResult } from '@agent-device/contracts/device'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { SessionStore } from '../session-store.ts'; -import { successText, withSuccessText } from '../../utils/success-text.ts'; +import { successText, withSuccessText } from '@agent-device/kernel/success-text'; import { resolveCommandDevice } from './session-device-utils.ts'; import { errorResponse } from './response.ts'; import { expireRefFrame } from '../ref-frame.ts'; diff --git a/src/daemon/handlers/session-doctor-metro.ts b/src/daemon/handlers/session-doctor-metro.ts index cf8240441c..ed4d8578f4 100644 --- a/src/daemon/handlers/session-doctor-metro.ts +++ b/src/daemon/handlers/session-doctor-metro.ts @@ -1,5 +1,5 @@ import type { DoctorCheck, DoctorKind } from '@agent-device/contracts/observability'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; const METRO_PROBE_TIMEOUT_MS = 1500; const METRO_PROCESS_LOOKUP_TIMEOUT_MS = 1500; diff --git a/src/daemon/handlers/session-doctor-output.ts b/src/daemon/handlers/session-doctor-output.ts index 191e8f4e29..3b7d54fa7c 100644 --- a/src/daemon/handlers/session-doctor-output.ts +++ b/src/daemon/handlers/session-doctor-output.ts @@ -1,4 +1,4 @@ -import { emitRequestProgress } from '../../request/progress.ts'; +import { emitRequestProgress } from '@agent-device/host-kit/request'; import { formatDoctorCheckDetailLines, formatDoctorCheckSummaryLine, diff --git a/src/daemon/handlers/session-doctor.ts b/src/daemon/handlers/session-doctor.ts index ec40668ef8..f14019e5af 100644 --- a/src/daemon/handlers/session-doctor.ts +++ b/src/daemon/handlers/session-doctor.ts @@ -2,13 +2,13 @@ import path from 'node:path'; import { PUBLIC_COMMANDS } from '../../command-catalog.ts'; import { isIosFamily, publicPlatformString, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitRequestProgress } from '../../request/progress.ts'; +import { emitRequestProgress } from '@agent-device/host-kit/request'; import { isActiveProviderDevice } from '../../provider-device-runtime.ts'; import { listLocalDeviceInventory, shouldPropagateDeviceInventoryProbeError, } from '../../request/device-inventory-context.ts'; -import { readVersion } from '../../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { SessionStore } from '../session-store.ts'; import { appendAppChecks, type DoctorAppInventory } from './session-doctor-app.ts'; @@ -36,7 +36,7 @@ import type { HostDiagnostics, HostDiagnosticsContext, } from '@agent-device/contracts/host-diagnostics'; -import { resolveAndroidSerialAllowlist } from '../../utils/device-isolation.ts'; +import { resolveAndroidSerialAllowlist } from '@agent-device/kernel/device-isolation'; import type { InstalledAppInfo } from '@agent-device/contracts/app-inventory-runtime'; import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import { appsRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; diff --git a/src/daemon/handlers/session-inventory.ts b/src/daemon/handlers/session-inventory.ts index 6def4d9682..ab14ffea8e 100644 --- a/src/daemon/handlers/session-inventory.ts +++ b/src/daemon/handlers/session-inventory.ts @@ -18,7 +18,7 @@ import { import { resolveAndroidSerialAllowlist, resolveIosSimulatorDeviceSetPath, -} from '../../utils/device-isolation.ts'; +} from '@agent-device/kernel/device-isolation'; import type { DaemonRequest, DaemonResponse, SessionRef } from '../types.ts'; import { resolveSessionRunnerLogPath, SessionStore } from '../session-store.ts'; import { diff --git a/src/daemon/handlers/session-open-execution.ts b/src/daemon/handlers/session-open-execution.ts index 74d65b2ecd..0144926d84 100644 --- a/src/daemon/handlers/session-open-execution.ts +++ b/src/daemon/handlers/session-open-execution.ts @@ -16,7 +16,7 @@ import { armAuthoringOnOpen, isAuthoringArmedSession, } from '../session-script-publication-capability.ts'; -import { isRequestCanceled } from '../../request/cancel.ts'; +import { isRequestCanceled } from '@agent-device/host-kit/request'; import { createRequestCanceledError } from '@agent-device/kernel/errors'; import { resolveSessionRequestLogPath, @@ -31,7 +31,7 @@ import { import { STARTUP_SAMPLE_METHOD, type StartupPerfSample } from './session-startup-metrics.ts'; import { buildNextOpenSession, buildOpenResult } from './session-open-surface.ts'; import { markDeferredInteractionOutcome } from '../deferred-interaction-outcome.ts'; -import { emitDiagnostic, getDiagnosticsMeta } from '../../utils/diagnostics.ts'; +import { emitDiagnostic, getDiagnosticsMeta } from '@agent-device/host-kit/diagnostics'; import { prepareOpenCommandDetails, type ResolvedOpenRuntimeHintPlan, diff --git a/src/daemon/handlers/session-open-surface.ts b/src/daemon/handlers/session-open-surface.ts index d990241727..fc7262a300 100644 --- a/src/daemon/handlers/session-open-surface.ts +++ b/src/daemon/handlers/session-open-surface.ts @@ -6,7 +6,7 @@ import { type DeviceInfo, } from '@agent-device/kernel/device'; import type { SessionRuntimeHints, SessionState } from '../types.ts'; -import { successText } from '../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { StartupPerfSample } from './session-startup-metrics.ts'; import type { DeviceSelectionResult } from '../../core/device-selection-resolver.ts'; diff --git a/src/daemon/handlers/session-open.ts b/src/daemon/handlers/session-open.ts index 27c06b0289..1f4dd5d883 100644 --- a/src/daemon/handlers/session-open.ts +++ b/src/daemon/handlers/session-open.ts @@ -10,7 +10,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { SessionStore } from '../session-store.ts'; import { refreshSessionDeviceIfNeeded } from './session-device-utils.ts'; -import { withKeyedLock } from '../../utils/keyed-lock.ts'; +import { withKeyedLock } from '@agent-device/kernel/keyed-lock'; import { buildOpenTargetDeviceResolutionOptions } from '../open-device-selection.ts'; import { invalidOpenArgs, diff --git a/src/daemon/handlers/session-replay-divergence.ts b/src/daemon/handlers/session-replay-divergence.ts index f760eba2ce..3b71272194 100644 --- a/src/daemon/handlers/session-replay-divergence.ts +++ b/src/daemon/handlers/session-replay-divergence.ts @@ -1,8 +1,8 @@ import type { SessionAction } from '@agent-device/contracts/session'; import type { CommandFlags } from '@agent-device/contracts/command'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { isUnreadableCaptureContentError } from '@agent-device/contracts/android-snapshot-quality'; -import { isSparseSnapshotQualityVerdict } from '../../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { displayLabel, formatRole } from '../../snapshot/snapshot-lines.ts'; import type { ResponseLevel } from '@agent-device/kernel/contracts'; import type { DaemonError } from '@agent-device/kernel/errors'; diff --git a/src/daemon/handlers/session-replay-maestro-failure.ts b/src/daemon/handlers/session-replay-maestro-failure.ts index 399be8d215..ed550dbd29 100644 --- a/src/daemon/handlers/session-replay-maestro-failure.ts +++ b/src/daemon/handlers/session-replay-maestro-failure.ts @@ -9,7 +9,7 @@ import { type ReplayVarScrubEntry, } from '@agent-device/contracts/divergence'; import { formatScriptArg } from '@agent-device/ad-script'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { SessionStore } from '../session-store.ts'; import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import type { ReplayReportAction } from './session-replay-report-action.ts'; diff --git a/src/daemon/handlers/session-replay-maestro-observer.ts b/src/daemon/handlers/session-replay-maestro-observer.ts index 92658549e9..89b7dded01 100644 --- a/src/daemon/handlers/session-replay-maestro-observer.ts +++ b/src/daemon/handlers/session-replay-maestro-observer.ts @@ -6,7 +6,7 @@ import type { } from '@agent-device/maestro'; import { AppError } from '@agent-device/kernel/errors'; import type { ReplayTestAttemptStepSink } from '@agent-device/replay-test'; -import { stripUndefined } from '../../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; import { appendReplayTraceEvent } from './session-replay-trace.ts'; export function createMaestroReplayObserver(params: { diff --git a/src/daemon/handlers/session-replay-maestro-runtime.ts b/src/daemon/handlers/session-replay-maestro-runtime.ts index a64cd17490..90ac983e33 100644 --- a/src/daemon/handlers/session-replay-maestro-runtime.ts +++ b/src/daemon/handlers/session-replay-maestro-runtime.ts @@ -8,8 +8,8 @@ import { } from '@agent-device/maestro'; import { AppError } from '@agent-device/kernel/errors'; import { resolveTargetDevice } from '../../core/dispatch-resolve.ts'; -import { getRequestSignal } from '../../request/cancel.ts'; -import { stripUndefined } from '../../utils/parsing.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; +import { stripUndefined } from '@agent-device/kernel/record'; import { collectReplayShellEnv, parseReplayCliEnvEntries, diff --git a/src/daemon/handlers/session-replay-runtime-failure.ts b/src/daemon/handlers/session-replay-runtime-failure.ts index c02ddb9e6d..ef4e02abca 100644 --- a/src/daemon/handlers/session-replay-runtime-failure.ts +++ b/src/daemon/handlers/session-replay-runtime-failure.ts @@ -13,7 +13,7 @@ import { buildReplayDivergenceFailureResponse, hoistReplayFailureCauseDiagnosticMeta, } from './session-replay-runtime-failure-response.ts'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; export async function withReplayFailureDiagnostics(params: { response: DaemonResponse; diff --git a/src/daemon/handlers/session-replay-runtime.ts b/src/daemon/handlers/session-replay-runtime.ts index 425e4d1e5f..2aec107346 100644 --- a/src/daemon/handlers/session-replay-runtime.ts +++ b/src/daemon/handlers/session-replay-runtime.ts @@ -7,7 +7,7 @@ import type { SnapshotTimingSample } from '@agent-device/contracts/capture'; import { summarizeSnapshotTimingSamples } from '@agent-device/contracts/capture'; import type { ReplayCommandResult } from '@agent-device/contracts/replay'; import { isMaestroYamlPath, maestroBackendRequiredMessage } from '../../replay/format.ts'; -import { getRequestSignal } from '../../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { createReplayCoordinator, type ReplayCoordinator } from '../session-replay-coordinator.ts'; import { createAdReplayStepRuntime, diff --git a/src/daemon/handlers/session-replay-trace.ts b/src/daemon/handlers/session-replay-trace.ts index 2feb38db96..2f314bfaee 100644 --- a/src/daemon/handlers/session-replay-trace.ts +++ b/src/daemon/handlers/session-replay-trace.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import { redactDiagnosticData } from '@agent-device/kernel/redaction'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; export function appendReplayTraceEvent( tracePath: string | undefined, diff --git a/src/daemon/handlers/session-replay-video-recording.ts b/src/daemon/handlers/session-replay-video-recording.ts index be79ccf90b..0084bb0903 100644 --- a/src/daemon/handlers/session-replay-video-recording.ts +++ b/src/daemon/handlers/session-replay-video-recording.ts @@ -1,8 +1,9 @@ import path from 'node:path'; import type { DaemonOpenLifecycle, DaemonRequest, DaemonResponse } from '../types.ts'; import type { SessionStore } from '../session-store.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; + import { handleRecordCommand } from './record-runtime.ts'; import type { BindDeviceRuntime, BindExactDeviceRuntime } from '../request-runtime-binding.ts'; import type { ScreenRecordingAdmissionLedger } from '../screen-recording-admission-ledger.ts'; diff --git a/src/daemon/handlers/session-script-publication.ts b/src/daemon/handlers/session-script-publication.ts index 585713354d..fea4d2549f 100644 --- a/src/daemon/handlers/session-script-publication.ts +++ b/src/daemon/handlers/session-script-publication.ts @@ -1,6 +1,6 @@ import { INTERNAL_COMMANDS } from '../../command-catalog.ts'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; -import { successText } from '../../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { effectiveWriteForce, isAuthoringArmedSession, diff --git a/src/daemon/handlers/session-state.ts b/src/daemon/handlers/session-state.ts index 7fa1eff83e..5bda3acb1a 100644 --- a/src/daemon/handlers/session-state.ts +++ b/src/daemon/handlers/session-state.ts @@ -15,7 +15,7 @@ import { } from '@agent-device/kernel/device'; import type { DaemonRequest, DaemonResponse } from '../types.ts'; import { SessionStore } from '../session-store.ts'; -import { resolveAndroidSerialAllowlist } from '../../utils/device-isolation.ts'; +import { resolveAndroidSerialAllowlist } from '@agent-device/kernel/device-isolation'; import { hasExplicitSessionFlag, requireSessionOrExplicitSelector, diff --git a/src/daemon/handlers/session-test-shard-devices.ts b/src/daemon/handlers/session-test-shard-devices.ts index 6cd3d46511..03ef5adc3d 100644 --- a/src/daemon/handlers/session-test-shard-devices.ts +++ b/src/daemon/handlers/session-test-shard-devices.ts @@ -4,7 +4,7 @@ import { listDeviceInventory } from '../../request/device-inventory-context.ts'; import { resolveAndroidSerialAllowlist, resolveIosSimulatorDeviceSetPath, -} from '../../utils/device-isolation.ts'; +} from '@agent-device/kernel/device-isolation'; import { isMobilePlatform, matchesPlatformSelector, diff --git a/src/daemon/handlers/session-test-suite-command.ts b/src/daemon/handlers/session-test-suite-command.ts index bde0219012..94b4147e00 100644 --- a/src/daemon/handlers/session-test-suite-command.ts +++ b/src/daemon/handlers/session-test-suite-command.ts @@ -23,15 +23,16 @@ import { runReplayScriptSource } from './session-replay-runtime.ts'; import { collectReplayActionArtifactPaths } from './session-replay-runtime-artifacts.ts'; import { errorResponse } from './response.ts'; import { AppError, asAppError } from '@agent-device/kernel/errors'; -import { emitRequestProgress } from '../../request/progress.ts'; import { + emitRequestProgress, clearRequestCanceled, getRequestSignal, isRequestCanceled, markRequestCanceled, registerRequestAbort, -} from '../../request/cancel.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +} from '@agent-device/host-kit/request'; + +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { buildReplayTestSourceDiscovery } from './session-test-source-discovery.ts'; import { buildReplayTestShardFlags, diff --git a/src/daemon/handlers/snapshot-settings.ts b/src/daemon/handlers/snapshot-settings.ts index ae9ba1f8cd..47f270544d 100644 --- a/src/daemon/handlers/snapshot-settings.ts +++ b/src/daemon/handlers/snapshot-settings.ts @@ -14,9 +14,10 @@ import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; import { recordIfSession } from './snapshot-session.ts'; import { errorResponse, type DaemonFailureResponse } from './response.ts'; import { expireRefFrame } from '../ref-frame.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import { readLocationCoordinate } from '../../utils/location-coordinates.ts'; -import { successText, withSuccessText } from '../../utils/success-text.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { readLocationCoordinate } from '@agent-device/kernel/location-coordinates'; +import { successText, withSuccessText } from '@agent-device/kernel/success-text'; + import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from '../request-runtime-binding.ts'; import { admitRuntimeUse } from '../runtime-admission.ts'; import { runtimeExecutionFromContext } from '../snapshot-runtime-capture-input.ts'; diff --git a/src/daemon/home-runtime.ts b/src/daemon/home-runtime.ts index 8006010429..b20b2fe9e0 100644 --- a/src/daemon/home-runtime.ts +++ b/src/daemon/home-runtime.ts @@ -2,7 +2,7 @@ import type { HomeInput } from '@agent-device/contracts/home-runtime'; import { homeRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; import { resolveBoundGenericRuntime, type RuntimeAdmissionBindings } from './runtime-admission.ts'; diff --git a/src/daemon/http-health.ts b/src/daemon/http-health.ts index a7932df40c..7c07183695 100644 --- a/src/daemon/http-health.ts +++ b/src/daemon/http-health.ts @@ -1,4 +1,4 @@ -import { readVersion } from '../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; // See docs/adr/0006-daemon-rpc-protocol-version.md before changing this value. // Enforced, not just documented: `test/wire-compat/` digests the declarations diff --git a/src/daemon/interaction-outcome-policy.ts b/src/daemon/interaction-outcome-policy.ts index 020b2c65d9..693e6211bf 100644 --- a/src/daemon/interaction-outcome-policy.ts +++ b/src/daemon/interaction-outcome-policy.ts @@ -2,7 +2,7 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import { isMobilePlatform } from '@agent-device/kernel/device'; import type { SnapshotNode, SnapshotState } from '@agent-device/kernel/snapshot'; import { collectKeyboardChromeRefs } from '../core/snapshot-chrome.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { isViewportRootNode } from '@agent-device/contracts/snapshot'; import { contextFromFlags, type DaemonCommandContext } from './context.ts'; import type { SessionState } from './types.ts'; diff --git a/src/daemon/ios-app-session-hint.ts b/src/daemon/ios-app-session-hint.ts index 91697f4c87..8afa0d63d7 100644 --- a/src/daemon/ios-app-session-hint.ts +++ b/src/daemon/ios-app-session-hint.ts @@ -1,6 +1,6 @@ import { isIosFamily, type DeviceInfo } from '@agent-device/kernel/device'; import { resolveSoleForegroundIosApp } from '../platform-runtime-open-target.ts'; -import { shellQuoteIfNeeded } from '../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; export { resolveSoleForegroundIosApp } from '../platform-runtime-open-target.ts'; diff --git a/src/daemon/keyboard-runtime.ts b/src/daemon/keyboard-runtime.ts index c3a43d26ba..ab33d1d522 100644 --- a/src/daemon/keyboard-runtime.ts +++ b/src/daemon/keyboard-runtime.ts @@ -18,7 +18,7 @@ import type { import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import { isKeyboardAction, type KeyboardAction } from '../utils/keyboard-actions.ts'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import { admitRuntimeUse, diff --git a/src/daemon/lease-context.ts b/src/daemon/lease-context.ts index 1cfd9c044e..f96af5a390 100644 --- a/src/daemon/lease-context.ts +++ b/src/daemon/lease-context.ts @@ -2,7 +2,7 @@ import type { DaemonRequest } from './types.ts'; import type { LeaseBackend } from '@agent-device/kernel/contracts'; import type { DeviceLease } from '@agent-device/contracts/device'; import type { RunnerLogicalLeaseContext } from '@agent-device/contracts/runner-lease-context'; -import { stripUndefined } from '../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; import { DEFAULT_PROXY_LEASE_TTL_MS, findMissingProxyLeaseFields, diff --git a/src/daemon/lease-lifecycle.ts b/src/daemon/lease-lifecycle.ts index 695bcb4be1..daba39eaab 100644 --- a/src/daemon/lease-lifecycle.ts +++ b/src/daemon/lease-lifecycle.ts @@ -1,4 +1,4 @@ -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { leaseScopeToReleaseRequest } from '../core/lease-scope.ts'; import { clearDeviceClaim } from './device-claims.ts'; import type { LeaseRegistry } from './lease-registry.ts'; diff --git a/src/daemon/offscreen-target-probe.ts b/src/daemon/offscreen-target-probe.ts index fe2132b573..eddebc5ec3 100644 --- a/src/daemon/offscreen-target-probe.ts +++ b/src/daemon/offscreen-target-probe.ts @@ -1,5 +1,5 @@ import type { Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; -import { isConfirmedOnScreenProbe } from '../snapshot/mobile-snapshot-semantics.ts'; +import { isConfirmedOnScreenProbe } from '@agent-device/capture-kit/mobile-snapshot-semantics'; import { deriveDirectIosNodeSelector } from './direct-ios-selector.ts'; import { queryDirectIosSelector } from './selector-runtime.ts'; import type { AppleRunnerRequestOptions } from './apple-runner-options.ts'; diff --git a/src/daemon/orientation-runtime.ts b/src/daemon/orientation-runtime.ts index 071dd0451a..0d409c9fcd 100644 --- a/src/daemon/orientation-runtime.ts +++ b/src/daemon/orientation-runtime.ts @@ -3,7 +3,7 @@ import { parseDeviceRotation, type DeviceRotation } from '@agent-device/contract import { orientationRuntimeUse } from '@agent-device/contracts/platform-runtime-operations'; import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; import { resolveBoundGenericRuntime, type RuntimeAdmissionBindings } from './runtime-admission.ts'; diff --git a/src/daemon/platform-request-scope.ts b/src/daemon/platform-request-scope.ts index bf280477a7..201a92e9c6 100644 --- a/src/daemon/platform-request-scope.ts +++ b/src/daemon/platform-request-scope.ts @@ -3,9 +3,9 @@ import type { PlatformProgressUpdate, PlatformRequestScope, } from '@agent-device/contracts/platform-runtime-host'; -import { getRequestSignal } from '../request/cancel.ts'; -import { emitRequestProgress } from '../request/progress.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { getRequestSignal, emitRequestProgress } from '@agent-device/host-kit/request'; + +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { DaemonRequest } from './types.ts'; const processLifetimeSignal = new AbortController().signal; diff --git a/src/daemon/post-gesture-stability.ts b/src/daemon/post-gesture-stability.ts index 60d6216f41..cad874f7c8 100644 --- a/src/daemon/post-gesture-stability.ts +++ b/src/daemon/post-gesture-stability.ts @@ -1,5 +1,5 @@ -import { emitDiagnostic } from '../utils/diagnostics.ts'; -import { sleep } from '../utils/timeouts.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; /** * Pure post-gesture stability mechanics: the quiet-window polling loop and the diff --git a/src/daemon/provider-lease-expiry.ts b/src/daemon/provider-lease-expiry.ts index 3fadb6780a..ded6cb40e3 100644 --- a/src/daemon/provider-lease-expiry.ts +++ b/src/daemon/provider-lease-expiry.ts @@ -5,10 +5,10 @@ import type { LeaseLifecycleProvider, ProviderExpiredLeaseRecovery, } from '@agent-device/contracts/device'; -import { publishFileSync } from '../utils/atomic-file.ts'; +import { publishFileSync } from '@agent-device/host-kit/file'; import { releaseExpiredProviderLease } from './lease-lifecycle.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; -import { sleep } from '../utils/timeouts.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; const DEFAULT_RETRY_DELAY_MS = 1_000; const PENDING_RELEASES_FILE = 'expired-provider-leases.json'; diff --git a/src/daemon/recording-gestures.ts b/src/daemon/recording-gestures.ts index f3a150cb69..313f2ecd3e 100644 --- a/src/daemon/recording-gestures.ts +++ b/src/daemon/recording-gestures.ts @@ -7,7 +7,7 @@ import { resolveGestureOffsetMs, resolveTapVisualizationOffsetMs, } from './recording-timing.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { DEFAULT_MOBILE_SCROLL_DURATION_MS } from '@agent-device/contracts/scroll-command'; import { type ScrollDirection, diff --git a/src/daemon/request-execution-locks.ts b/src/daemon/request-execution-locks.ts index 99f8c1e46b..b91c3b39a8 100644 --- a/src/daemon/request-execution-locks.ts +++ b/src/daemon/request-execution-locks.ts @@ -1,4 +1,4 @@ -import { withKeyedLock } from '../utils/keyed-lock.ts'; +import { withKeyedLock } from '@agent-device/kernel/keyed-lock'; import type { RequestExecutionLockKey } from './request-binding.ts'; type RetainedExecutionLock = { diff --git a/src/daemon/request-execution-scope.ts b/src/daemon/request-execution-scope.ts index a04a90e4c4..b66fe746e4 100644 --- a/src/daemon/request-execution-scope.ts +++ b/src/daemon/request-execution-scope.ts @@ -4,7 +4,7 @@ import { emitDiagnostic, getDiagnosticsMeta, updateDiagnosticsScope, -} from '../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import { applyCommandDefaults } from '../cli-schema/command-schema.ts'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; import { @@ -21,7 +21,7 @@ import { } from './lease-lifecycle.ts'; import { prepareLockedRequestBinding, resolveRequestExecutionLockKeys } from './request-binding.ts'; import { createRequestExecutionLocks } from './request-execution-locks.ts'; -import { throwIfRequestCanceled } from '../request/cancel.ts'; +import { throwIfRequestCanceled } from '@agent-device/host-kit/request'; import { finalizeDaemonResponse } from './request-finalization.ts'; import { refreshRecordingHealth } from './request-recording-health.ts'; import { diff --git a/src/daemon/request-finalization.ts b/src/daemon/request-finalization.ts index 962d6b904c..d2bad90c89 100644 --- a/src/daemon/request-finalization.ts +++ b/src/daemon/request-finalization.ts @@ -4,7 +4,7 @@ import { emitDiagnostic, flushDiagnosticsToSessionFile, getDiagnosticsMeta, -} from '../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import type { DaemonRequest, DaemonResponse, DaemonResponseData } from './types.ts'; import type { DaemonArtifact, DaemonArtifactType } from '@agent-device/kernel/contracts'; diff --git a/src/daemon/request-lock-policy.ts b/src/daemon/request-lock-policy.ts index 093d5a9160..512be48c10 100644 --- a/src/daemon/request-lock-policy.ts +++ b/src/daemon/request-lock-policy.ts @@ -13,7 +13,7 @@ import { type PlatformSelector, } from '@agent-device/kernel/device'; import { buildSessionRecoveryHint, describeSessionDevice } from './session-recovery-hints.ts'; -import { shellQuoteIfNeeded } from '../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; import { hasLockableDeviceSelector, hasSelectorValue } from './device-selector-intent.ts'; import { canOverrideLockPolicySelector } from './daemon-command-registry.ts'; diff --git a/src/daemon/request-router.ts b/src/daemon/request-router.ts index dc6f832216..4228a4b80e 100644 --- a/src/daemon/request-router.ts +++ b/src/daemon/request-router.ts @@ -29,7 +29,7 @@ import { getDiagnosticsMeta, registerDiagnosticSensitiveValue, withDiagnosticsScope, -} from '../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import type { LeaseRegistry } from './lease-registry.ts'; import { loadGenericRequestHandlerModule, diff --git a/src/daemon/request-save-script-policy.ts b/src/daemon/request-save-script-policy.ts index 4f04cf2d77..a5d133d060 100644 --- a/src/daemon/request-save-script-policy.ts +++ b/src/daemon/request-save-script-policy.ts @@ -3,7 +3,7 @@ import { emitDiagnostic, flushDiagnosticsToSessionFile, getDiagnosticsMeta, -} from '../utils/diagnostics.ts'; +} from '@agent-device/host-kit/diagnostics'; import { listSaveScriptFlagOwnerCommands, ownsSaveScriptFlag } from './daemon-command-registry.ts'; import type { DaemonRequest, DaemonResponse } from './types.ts'; diff --git a/src/daemon/resumable-upload-transfer.ts b/src/daemon/resumable-upload-transfer.ts index 37cd30eaa6..7106c53efe 100644 --- a/src/daemon/resumable-upload-transfer.ts +++ b/src/daemon/resumable-upload-transfer.ts @@ -3,7 +3,7 @@ import crypto from 'node:crypto'; import type { IncomingMessage } from 'node:http'; import { pipeline } from 'node:stream/promises'; import { AppError } from '@agent-device/kernel/errors'; -import { createByteLimitStream } from '../utils/byte-limit-stream.ts'; +import { createByteLimitStream } from '@agent-device/host-kit/archive'; import { parseUploadContentLength, parseUploadContentRange } from './resumable-upload-range.ts'; export type ResumableTransferEntry = { diff --git a/src/daemon/runtime-session.ts b/src/daemon/runtime-session.ts index 32591e984b..80ab3fbcd9 100644 --- a/src/daemon/runtime-session.ts +++ b/src/daemon/runtime-session.ts @@ -1,5 +1,5 @@ import type { CommandSessionRecord, CommandSessionStore } from '../runtime-contract.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionState } from './types.ts'; export type RuntimeSessionRecordOptions = { diff --git a/src/daemon/screenshot-overlay-draw.ts b/src/daemon/screenshot-overlay-draw.ts index 0d0b0ee115..1b01d56fc5 100644 --- a/src/daemon/screenshot-overlay-draw.ts +++ b/src/daemon/screenshot-overlay-draw.ts @@ -1,5 +1,5 @@ import type { Rect, ScreenshotOverlayRef } from '@agent-device/kernel/snapshot'; -import type { PNG } from '../utils/png.ts'; +import type { PNG } from '@agent-device/capture-kit/png'; import { clamp } from '../snapshot/screenshot-overlay/rects.ts'; /** diff --git a/src/daemon/screenshot-overlay.ts b/src/daemon/screenshot-overlay.ts index 1c835721b8..d3bf634722 100644 --- a/src/daemon/screenshot-overlay.ts +++ b/src/daemon/screenshot-overlay.ts @@ -6,7 +6,7 @@ import { type SnapshotNode, type SnapshotState, } from '@agent-device/kernel/snapshot'; -import { decodePngAsync, encodePngAsync } from '../utils/png-worker-client.ts'; +import { decodePngAsync, encodePngAsync } from '@agent-device/capture-kit/png-worker-client'; import { analyzeReactNativeOverlay } from '../core/react-native-overlay.ts'; import { findNearestAncestor, diff --git a/src/daemon/screenshot-runtime.ts b/src/daemon/screenshot-runtime.ts index 9d0ddee20e..0e05453eb5 100644 --- a/src/daemon/screenshot-runtime.ts +++ b/src/daemon/screenshot-runtime.ts @@ -17,7 +17,7 @@ import { createAgentDevice, localCommandPolicy } from '../runtime.ts'; import { assertSupportedScreenshotPixelDensity, readScreenshotResultMetadata, -} from '../utils/screenshot-density.ts'; +} from '@agent-device/capture-kit/screenshot-density'; import { runtimeExecutionFromContext } from './snapshot-runtime-capture-input.ts'; import type { DaemonCommandContext } from './context.ts'; import { captureSnapshotData } from './handlers/snapshot-capture.ts'; diff --git a/src/daemon/scroll-runtime.ts b/src/daemon/scroll-runtime.ts index ea83085513..5e0d987acf 100644 --- a/src/daemon/scroll-runtime.ts +++ b/src/daemon/scroll-runtime.ts @@ -22,7 +22,7 @@ import { type ScrollEdge, type ScrollEdgeState, } from '../utils/scroll-edge-state.ts'; -import { withSuccessText } from '../utils/success-text.ts'; +import { withSuccessText } from '@agent-device/kernel/success-text'; import type { DaemonCommandContext } from './context.ts'; import { errorResponse } from './handlers/response.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; diff --git a/src/daemon/selector-capture-runtime.ts b/src/daemon/selector-capture-runtime.ts index 5f78cb2e70..f06747679d 100644 --- a/src/daemon/selector-capture-runtime.ts +++ b/src/daemon/selector-capture-runtime.ts @@ -5,7 +5,7 @@ import { snapshotPresentationOptionsFromFlags, type SnapshotState, } from '@agent-device/kernel/snapshot'; -import { isSparseSnapshotQualityVerdict } from '../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import type { DaemonRequest, SessionState } from './types.ts'; import { SessionStore } from './session-store.ts'; import { captureSnapshot } from './handlers/snapshot-capture.ts'; diff --git a/src/daemon/selector-runtime-backend.ts b/src/daemon/selector-runtime-backend.ts index 627c9f75bf..ced8d7fda6 100644 --- a/src/daemon/selector-runtime-backend.ts +++ b/src/daemon/selector-runtime-backend.ts @@ -27,7 +27,7 @@ import { import type { BindDeviceRuntime, InspectDeviceRuntimeFacts } from './request-runtime-binding.ts'; import type { AndroidObservationAdapter } from '@agent-device/contracts/android-observation'; import type { PlatformResourceCleanup } from '@agent-device/contracts/platform-resource-cleanup'; -import { getRequestSignal } from '../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { snapshotOptionsToFlags } from '../backend-snapshot-options.ts'; export type SelectorRuntimeParams = { diff --git a/src/daemon/server/daemon-idle-reap.ts b/src/daemon/server/daemon-idle-reap.ts index de7313badb..8cf63cfb62 100644 --- a/src/daemon/server/daemon-idle-reap.ts +++ b/src/daemon/server/daemon-idle-reap.ts @@ -1,4 +1,4 @@ -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionStore } from '../session-store.ts'; import type { SessionState } from '../types.ts'; import { isUncommittedRepairSession } from '../session-replay-transaction.ts'; diff --git a/src/daemon/server/daemon-runtime-web-close-teardown.test.ts b/src/daemon/server/daemon-runtime-web-close-teardown.test.ts index 84f390a100..53286eee5b 100644 --- a/src/daemon/server/daemon-runtime-web-close-teardown.test.ts +++ b/src/daemon/server/daemon-runtime-web-close-teardown.test.ts @@ -1,8 +1,8 @@ import path from 'node:path'; import { afterEach, expect, test, vi } from 'vitest'; -vi.mock('../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); @@ -10,7 +10,7 @@ import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; import { WEB_DESKTOP_DEVICE } from '../../__tests__/test-utils/device-fixtures.ts'; import { AGENT_BROWSER_TIMEOUT_MS } from '../../platforms/web/agent-browser-provider.ts'; import { installFakeManagedAgentBrowser } from '../../platforms/web/__tests__/test-utils.ts'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { SessionStore } from '../session-store.ts'; import type { SessionState } from '../types.ts'; import { diff --git a/src/daemon/server/daemon-runtime.ts b/src/daemon/server/daemon-runtime.ts index c28c38fe1d..aa22576195 100644 --- a/src/daemon/server/daemon-runtime.ts +++ b/src/daemon/server/daemon-runtime.ts @@ -35,8 +35,14 @@ import { emitDiagnostic, flushDiagnosticsToSessionFile, withDiagnosticsScope, -} from '../../utils/diagnostics.ts'; -import { isEnvTruthy } from '../../utils/retry.ts'; +} from '@agent-device/host-kit/diagnostics'; +import { + createOwnedProcessRecordStore, + type OwnedProcessRecordStore, + reapOwnedProcessRecordsAtStartup, +} from '@agent-device/host-kit/process'; +import { isEnvTruthy, sleep } from '@agent-device/host-kit/retry'; + import { acquireDaemonLock, parseIntegerEnv, @@ -53,8 +59,8 @@ import { listenNetServer, type DaemonServer, } from './transport.ts'; -import { prewarmPngWorker, terminatePngWorker } from '../../utils/png-worker-client.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { prewarmPngWorker, terminatePngWorker } from '@agent-device/capture-kit/png-worker-client'; + import { configureAppleRunnerLeaseOwnerStateDir } from '../../platform-runtime-apple-runner-owner.ts'; import { cleanupManagedWebRuntimeOrphans, @@ -70,11 +76,6 @@ import { createDaemonRecoveryPlatformScope } from '../platform-request-scope.ts' import { createAppLogAdmissionLedger } from '../app-log-admission-ledger.ts'; import { createAudioProbeAdmissionLedger } from '../audio-probe-admission-ledger.ts'; import { createScreenRecordingAdmissionLedger } from '../screen-recording-admission-ledger.ts'; -import { - createOwnedProcessRecordStore, - type OwnedProcessRecordStore, -} from '../../utils/owned-process-record.ts'; -import { reapOwnedProcessRecordsAtStartup } from '../../utils/owned-process-reaper.ts'; const DAEMON_SESSION_TEARDOWN_TIMEOUT_MS = 5_000; export const SCREEN_RECORDING_SESSION_TEARDOWN_BUDGET_MS = 11_000; diff --git a/src/daemon/server/daemon-session-lease-finalizer.ts b/src/daemon/server/daemon-session-lease-finalizer.ts index 4e48b10292..ee01e540ce 100644 --- a/src/daemon/server/daemon-session-lease-finalizer.ts +++ b/src/daemon/server/daemon-session-lease-finalizer.ts @@ -1,5 +1,5 @@ import { leaseScopeToReleaseRequest } from '../../core/lease-scope.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { ExpiredProviderLeaseReleaser } from '../provider-lease-expiry.ts'; import type { LeaseRegistry } from '../lease-registry.ts'; import type { SessionState } from '../types.ts'; diff --git a/src/daemon/server/daemon-shutdown-claims.ts b/src/daemon/server/daemon-shutdown-claims.ts index ffa7cb260f..126aab4b1a 100644 --- a/src/daemon/server/daemon-shutdown-claims.ts +++ b/src/daemon/server/daemon-shutdown-claims.ts @@ -1,5 +1,5 @@ import { publicPlatformString } from '@agent-device/kernel/device'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { clearDeviceClaim, type DeviceClaimClearOutcome } from '../device-claims.ts'; import type { DeviceClaimRecord } from '../daemon-shutdown-report.ts'; import type { SessionState } from '../types.ts'; diff --git a/src/daemon/server/http-server.ts b/src/daemon/server/http-server.ts index cbac9a2056..6bd7317b6e 100644 --- a/src/daemon/server/http-server.ts +++ b/src/daemon/server/http-server.ts @@ -6,7 +6,7 @@ import { toAppErrorCode, type DiagnosticsRecordRef, } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { timingSafeStringEqual } from '../../utils/timing-safe-equal.ts'; import type { CommandRpcParams, @@ -22,10 +22,11 @@ import { markRequestCanceled, registerRequestAbort, resolveRequestTrackingId, -} from '../../request/cancel.ts'; + withRequestProgressSink, +} from '@agent-device/host-kit/request'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { withRequestProgressSink } from '../../request/progress.ts'; + import { serializeDaemonProgressEnvelope, serializeDaemonRpcResponseEnvelope, diff --git a/src/daemon/server/server-lifecycle.ts b/src/daemon/server/server-lifecycle.ts index 911b0fc7ec..77adf71657 100644 --- a/src/daemon/server/server-lifecycle.ts +++ b/src/daemon/server/server-lifecycle.ts @@ -1,8 +1,8 @@ import fs from 'node:fs'; import { isAgentDeviceDaemonProcess } from '../daemon-process.ts'; -export { readVersion } from '../../utils/version.ts'; -export { readProcessStartTime } from '../../utils/host-process.ts'; +export { readVersion } from '@agent-device/host-kit/version'; +export { readProcessStartTime } from '@agent-device/host-kit/process'; export { resolveDaemonCodeSignature } from '../code-signature.ts'; export type DaemonLockInfo = { diff --git a/src/daemon/server/transport.ts b/src/daemon/server/transport.ts index d5cbe7595a..49fd036389 100644 --- a/src/daemon/server/transport.ts +++ b/src/daemon/server/transport.ts @@ -8,10 +8,11 @@ import { markRequestCanceled, registerRequestAbort, resolveRequestTrackingId, -} from '../../request/cancel.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; + withRequestProgressSink, +} from '@agent-device/host-kit/request'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { consumeTextLines } from '../../utils/line-stream.ts'; -import { withRequestProgressSink } from '../../request/progress.ts'; + import { serializeDaemonProgressEnvelope, serializeDaemonResponseEnvelope, diff --git a/src/daemon/session-action-recorder.ts b/src/daemon/session-action-recorder.ts index 89f31ce4aa..1627815a33 100644 --- a/src/daemon/session-action-recorder.ts +++ b/src/daemon/session-action-recorder.ts @@ -1,7 +1,7 @@ import type { SessionAction } from '@agent-device/contracts/session'; import type { CommandFlags } from '@agent-device/contracts/command'; import { SCREENSHOT_ACTION_FLAG_KEYS } from '@agent-device/contracts/capture'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { DaemonRequest, SessionRuntimeHints, SessionState } from './types.ts'; import { applyRecordedSaveScriptFlags } from './session-script-publication-capability.ts'; import { repairSessionBoundary } from './session-replay-transaction.ts'; diff --git a/src/daemon/session-event-log-window.ts b/src/daemon/session-event-log-window.ts index 5ba5e73e86..d0960044b1 100644 --- a/src/daemon/session-event-log-window.ts +++ b/src/daemon/session-event-log-window.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import { AppError } from '@agent-device/kernel/errors'; -import { isRecord } from '../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import { measureEventLogFile, readFirstLineDigest, diff --git a/src/daemon/session-event-log.ts b/src/daemon/session-event-log.ts index 09719b5e30..d81793c5d7 100644 --- a/src/daemon/session-event-log.ts +++ b/src/daemon/session-event-log.ts @@ -4,8 +4,8 @@ import path from 'node:path'; import { PUBLIC_COMMANDS } from '../command-catalog.ts'; import { AppError } from '@agent-device/kernel/errors'; import { redactDiagnosticData } from '@agent-device/kernel/redaction'; -import { emitDiagnostic, getDiagnosticsMeta } from '../utils/diagnostics.ts'; -import { isRecord } from '../utils/parsing.ts'; +import { emitDiagnostic, getDiagnosticsMeta } from '@agent-device/host-kit/diagnostics'; +import { isRecord } from '@agent-device/kernel/record'; import type { DaemonRequest, DaemonResponse } from './types.ts'; import { buildActionDetails, buildActionSummary } from './session-event-action.ts'; import { buildRequestSuccessEventPresentation } from './session-event-request.ts'; diff --git a/src/daemon/session-event-request.ts b/src/daemon/session-event-request.ts index 55a0198d14..f3c5b8be3c 100644 --- a/src/daemon/session-event-request.ts +++ b/src/daemon/session-event-request.ts @@ -1,5 +1,5 @@ import { PUBLIC_COMMANDS } from '../command-catalog.ts'; -import { isRecord } from '../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import type { DaemonRequest, DaemonResponseData } from './types.ts'; const EVENT_LIST_PREVIEW_LIMIT = 5; diff --git a/src/daemon/session-paths.ts b/src/daemon/session-paths.ts index 4ac0f3a765..544f9dd3ac 100644 --- a/src/daemon/session-paths.ts +++ b/src/daemon/session-paths.ts @@ -1,4 +1,4 @@ -import { resolveUserPath } from '../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; export function safeSessionName(name: string): string { return name.replace(/[^a-zA-Z0-9._-]/g, '_'); diff --git a/src/daemon/session-recovery-hints.ts b/src/daemon/session-recovery-hints.ts index a0e7892780..353caa63db 100644 --- a/src/daemon/session-recovery-hints.ts +++ b/src/daemon/session-recovery-hints.ts @@ -1,5 +1,5 @@ import type { SessionRef, SessionState } from './types.ts'; -import { shellQuoteIfNeeded } from '../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; export type SessionRecoveryContext = 'device-in-use' | 'selector-conflict'; diff --git a/src/daemon/session-script-writer.ts b/src/daemon/session-script-writer.ts index 1eb5bbbf3f..acf914850c 100644 --- a/src/daemon/session-script-writer.ts +++ b/src/daemon/session-script-writer.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { publicPlatformString } from '@agent-device/kernel/device'; import { dragGesturePayloadFromPositionals } from '@agent-device/contracts/gesture-normalization'; import { inferFillText } from './action-utils.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { AppError } from '@agent-device/kernel/errors'; import { appendScriptSeriesFlags, @@ -17,7 +17,7 @@ import { stripRecordedRefGeneration, } from '@agent-device/ad-script'; import { expandSessionPath, safeSessionName } from './session-paths.ts'; -import { publishFileSync } from '../utils/atomic-file.ts'; +import { publishFileSync } from '@agent-device/host-kit/file'; import type { SessionState } from './types.ts'; import { NO_SCRIPT_PUBLICATION, diff --git a/src/daemon/session-selector.ts b/src/daemon/session-selector.ts index cf646e439b..4018b1df57 100644 --- a/src/daemon/session-selector.ts +++ b/src/daemon/session-selector.ts @@ -6,7 +6,7 @@ import { isSerialAddressablePlatform, matchesPlatformSelector, } from '@agent-device/kernel/device'; -import { parseSerialAllowlist } from '../utils/device-isolation.ts'; +import { parseSerialAllowlist } from '@agent-device/kernel/device-isolation'; import { buildSessionRecoveryHint, describeSessionDevice } from './session-recovery-hints.ts'; export type SessionSelectorConflictKey = diff --git a/src/daemon/session-store.ts b/src/daemon/session-store.ts index 24779e6c46..e848b2e20d 100644 --- a/src/daemon/session-store.ts +++ b/src/daemon/session-store.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import fs from 'node:fs'; import { AppError, type DiagnosticsRecordRef } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { SessionRef, SessionRuntimeHints, SessionState } from './types.ts'; import { recordActionEntry, type RecordActionEntry } from './session-action-recorder.ts'; import { expandSessionPath, isSafeSessionSegment, safeSessionName } from './session-paths.ts'; @@ -16,7 +16,7 @@ import { type SessionScriptWriteOptions, type SessionScriptWriteResult, } from './session-script-writer.ts'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { appendActionEvent, appendSessionEvent, diff --git a/src/daemon/session-target-evidence.ts b/src/daemon/session-target-evidence.ts index c816216779..d7f1b5b638 100644 --- a/src/daemon/session-target-evidence.ts +++ b/src/daemon/session-target-evidence.ts @@ -21,7 +21,7 @@ */ import type { SnapshotNode } from '@agent-device/kernel/snapshot'; -import { resolveRectCenter } from '../utils/rect-center.ts'; +import { resolveRectCenter } from '@agent-device/kernel/rect-center'; import { findNearestScrollableContainer } from '../snapshot/snapshot-presentation/tree.ts'; import { classifyTargetBindingMatch, diff --git a/src/daemon/session-teardown.ts b/src/daemon/session-teardown.ts index 43a981608b..e461918fad 100644 --- a/src/daemon/session-teardown.ts +++ b/src/daemon/session-teardown.ts @@ -1,5 +1,5 @@ import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { cleanupRetainedMaterializedPathsForSession } from './materialized-path-registry.ts'; import type { SessionState } from './types.ts'; import type { SessionStore } from './session-store.ts'; diff --git a/src/daemon/snapshot-command-runtime.ts b/src/daemon/snapshot-command-runtime.ts index e8fd3007db..4f767e65e0 100644 --- a/src/daemon/snapshot-command-runtime.ts +++ b/src/daemon/snapshot-command-runtime.ts @@ -9,7 +9,7 @@ import { publicPlatformString } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import type { AgentDeviceBackend, BackendSnapshotResult } from '../backend.ts'; import { type CommandSessionRecord, createAgentDevice } from '../runtime.ts'; -import { getRequestSignal } from '../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import type { RuntimeAdmissionBindings } from './request-runtime-binding.ts'; import { maybeBuildAndroidSnapshotTimeoutFailure } from './android-snapshot-timeout-evidence.ts'; import { captureSnapshot } from './handlers/snapshot-capture.ts'; diff --git a/src/daemon/sparse-fallback-screenshot.ts b/src/daemon/sparse-fallback-screenshot.ts index e1f4effd45..b6a1bde400 100644 --- a/src/daemon/sparse-fallback-screenshot.ts +++ b/src/daemon/sparse-fallback-screenshot.ts @@ -1,5 +1,5 @@ import type { SnapshotQualityVerdict } from '@agent-device/kernel/snapshot'; -import { isSparseSnapshotQualityVerdict } from '../snapshot-quality/verdict.ts'; +import { isSparseSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { contextFromFlags } from './context.ts'; import { captureScreenshotArtifact } from './screenshot-runtime.ts'; import { diff --git a/src/daemon/touch-runtime.ts b/src/daemon/touch-runtime.ts index 02a9675802..7a3fbc66be 100644 --- a/src/daemon/touch-runtime.ts +++ b/src/daemon/touch-runtime.ts @@ -20,7 +20,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import type { Point } from '@agent-device/kernel/snapshot'; import { readFillBackendResult } from '../core/fill-backend-result.ts'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { requireIntInRange } from '../utils/validation.ts'; import type { DaemonCommandContext } from './context.ts'; import type { DirectIosSelectorTarget } from './direct-ios-selector.ts'; diff --git a/src/daemon/tv-remote-runtime.ts b/src/daemon/tv-remote-runtime.ts index 7f807f8e54..cea981bba2 100644 --- a/src/daemon/tv-remote-runtime.ts +++ b/src/daemon/tv-remote-runtime.ts @@ -4,7 +4,7 @@ import type { BoundDeviceRuntime } from '@agent-device/contracts/platform-runtim import { parseTvRemoteButton, type TvRemoteButton } from '@agent-device/contracts/tv-remote'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { requireIntInRange } from '../utils/validation.ts'; import type { DaemonCommandContext } from './context.ts'; import type { ResolvedGenericExecution } from './request-generic-dispatch.ts'; diff --git a/src/daemon/type-text-runtime.ts b/src/daemon/type-text-runtime.ts index d1caf430ff..74d4360241 100644 --- a/src/daemon/type-text-runtime.ts +++ b/src/daemon/type-text-runtime.ts @@ -5,7 +5,7 @@ import type { } from '@agent-device/contracts/type-text-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { successText } from '../utils/success-text.ts'; +import { successText } from '@agent-device/kernel/success-text'; import { findMistargetedTypeRefToken } from '../utils/type-target-warning.ts'; import { requireIntInRange } from '../utils/validation.ts'; import type { DaemonCommandContext } from './context.ts'; diff --git a/src/daemon/wait-conditional-selector.ts b/src/daemon/wait-conditional-selector.ts index 2f1373a6a6..4381951a43 100644 --- a/src/daemon/wait-conditional-selector.ts +++ b/src/daemon/wait-conditional-selector.ts @@ -1,7 +1,7 @@ import type { FindSelectorRuntimeOperations } from '@agent-device/contracts/selector-observation-runtime'; import type { TargetAnnotationV1 } from '@agent-device/contracts/replay'; import { readSimpleSelectorTarget } from '@agent-device/selectors'; -import { getRequestSignal } from '../request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { buildRuntimeCaptureInput } from './snapshot-runtime-capture-input.ts'; import { recordIfSession, stripSelectorChain } from './selector-recording.ts'; import { isSessionRecording } from './session-script-publication-capability.ts'; diff --git a/src/mcp/command-tools.ts b/src/mcp/command-tools.ts index 130640d043..81b85c78a3 100644 --- a/src/mcp/command-tools.ts +++ b/src/mcp/command-tools.ts @@ -30,7 +30,7 @@ import { mcpToolConfigProperties, } from './tool-control-fields.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { isRecord } from '../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import { formatToolErrorText, normalizeToolError } from './tool-error.ts'; import { resolveMcpConfigDefaults } from './tool-input-config.ts'; import { projectStructuredContent } from './tool-result.ts'; diff --git a/src/mcp/protocol-era.ts b/src/mcp/protocol-era.ts index f21fa6d703..9991016f70 100644 --- a/src/mcp/protocol-era.ts +++ b/src/mcp/protocol-era.ts @@ -1,5 +1,5 @@ import { AppError } from '@agent-device/kernel/errors'; -import { readVersion } from '../utils/version.ts'; +import { readVersion } from '@agent-device/host-kit/version'; /** * MCP has two protocol eras, and agent-device serves both from one stdio process diff --git a/src/mcp/tool-ref-pins.ts b/src/mcp/tool-ref-pins.ts index e32d86fce2..21d12248f1 100644 --- a/src/mcp/tool-ref-pins.ts +++ b/src/mcp/tool-ref-pins.ts @@ -6,7 +6,7 @@ import { commandDescriptors, commandSupportsSettleObservation, } from '../core/command-descriptor/registry.ts'; -import { asOptionalRecord } from '../utils/parsing.ts'; +import { asOptionalRecord } from '@agent-device/kernel/record'; import { readElementMatchCandidateRefs } from '../utils/error-candidates.ts'; export type ToolRefPinStore = { diff --git a/src/metro/client-metro.ts b/src/metro/client-metro.ts index 9138e71f17..648a5eac62 100644 --- a/src/metro/client-metro.ts +++ b/src/metro/client-metro.ts @@ -10,7 +10,9 @@ import type { } from '@agent-device/contracts/remote'; import fs from 'node:fs'; import path from 'node:path'; -import { sleep } from '../utils/timeouts.ts'; +import { runCmdSync, runCmdDetached } from '@agent-device/host-kit/command'; +import { waitForProcessExit } from '@agent-device/host-kit/process'; +import { sleep } from '@agent-device/host-kit/retry'; import { ensureMetroCompanion } from './client-metro-companion.ts'; import type { MetroBridgeDescriptor, @@ -19,9 +21,9 @@ import type { MetroRuntimeHints, } from './metro-types.ts'; import { AppError, asAppError } from '@agent-device/kernel/errors'; -import { runCmdSync, runCmdDetached } from '../utils/exec.ts'; -import { resolveUserPath } from '../utils/path-resolution.ts'; -import { waitForProcessExit } from '../utils/host-process.ts'; + +import { resolveUserPath } from '@agent-device/host-kit/file'; + import { detectProjectRuntimeKindFromPackageJson, readProjectPackageJson, diff --git a/src/platform-runtime-android-application-tools.ts b/src/platform-runtime-android-application-tools.ts index 83b34d19f7..fd7d68cb26 100644 --- a/src/platform-runtime-android-application-tools.ts +++ b/src/platform-runtime-android-application-tools.ts @@ -5,7 +5,7 @@ import type { } from '@agent-device/contracts/application-lifecycle-runtime'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from './utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; /** Lazy Android tools; the Android package owns lifecycle sequencing and durable IME policy. */ export function createAndroidApplicationTools(): AndroidApplicationTools { diff --git a/src/platform-runtime-android-emulator-host.ts b/src/platform-runtime-android-emulator-host.ts index bb2cefc8de..5670e44190 100644 --- a/src/platform-runtime-android-emulator-host.ts +++ b/src/platform-runtime-android-emulator-host.ts @@ -1,7 +1,7 @@ import type { DeviceReadinessRuntimeHost } from '@agent-device/contracts/device-readiness-runtime'; import { listLocalDeviceInventory } from './request/device-inventory-context.ts'; -import { runCmdDetached } from './utils/exec.ts'; -import { stopPidsWithEscalation } from './utils/host-process.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; +import { stopPidsWithEscalation } from '@agent-device/host-kit/process'; export function createAndroidEmulatorHost(): DeviceReadinessRuntimeHost['androidEmulator'] { return Object.freeze({ diff --git a/src/platform-runtime-app-log-process.test.ts b/src/platform-runtime-app-log-process.test.ts index e3754d8335..6393440532 100644 --- a/src/platform-runtime-app-log-process.test.ts +++ b/src/platform-runtime-app-log-process.test.ts @@ -12,8 +12,12 @@ const processIdentity = vi.hoisted(() => ({ startTime: 'Sun Aug 9 22:00:00 2026' as string | null, })); -vi.mock('./utils/exec.ts', () => ({ runCmdBackground: exec.run })); -vi.mock('./utils/host-process.ts', () => ({ +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), + runCmdBackground: exec.run, +})); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: () => processIdentity.alive, readProcessCommand: () => processIdentity.command, readProcessStartTime: () => processIdentity.startTime, diff --git a/src/platform-runtime-app-log-process.ts b/src/platform-runtime-app-log-process.ts index 4731b2125e..9f8b6eda37 100644 --- a/src/platform-runtime-app-log-process.ts +++ b/src/platform-runtime-app-log-process.ts @@ -14,13 +14,14 @@ import { decodeAppLogProcessMarker } from '@agent-device/capture-kit'; import type { DeviceIdentity } from '@agent-device/kernel/device'; import fs from 'node:fs'; import path from 'node:path'; -import { runCmdBackground } from './utils/exec.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; import { isProcessAlive, readProcessCommand, readProcessStartTime, waitForProcessExit, -} from './utils/host-process.ts'; +} from '@agent-device/host-kit/process'; + import { requireManagedSessionArtifactPath } from './utils/managed-session-artifact-path.ts'; import { openVerifiedFileForRead } from './utils/verified-file.ts'; diff --git a/src/platform-runtime-apple-application-tools.ts b/src/platform-runtime-apple-application-tools.ts index 5fb90af790..57dd1087dd 100644 --- a/src/platform-runtime-apple-application-tools.ts +++ b/src/platform-runtime-apple-application-tools.ts @@ -38,7 +38,7 @@ export function createAppleApplicationTools(): AppleApplicationTools { ); }, prepareRunner: async (device, input, signal) => { - const { Deadline } = await import('./utils/retry.ts'); + const { Deadline } = await import('@agent-device/host-kit/retry'); const { prepareIosRunner } = await import('./platforms/apple/core/runner-client.ts'); const startedAtMs = Date.now(); return await prepareIosRunner(device, { diff --git a/src/platform-runtime-host.ts b/src/platform-runtime-host.ts index 80013fd0fb..812167b24f 100644 --- a/src/platform-runtime-host.ts +++ b/src/platform-runtime-host.ts @@ -11,7 +11,7 @@ import os from 'node:os'; import path from 'node:path'; import { createAppleToolHost } from './platform-runtime-apple-tool-host.ts'; import { createHostToolchainPreparer } from './platform-runtime-toolchain-host.ts'; -import { runCmd, whichCmd } from './utils/exec.ts'; +import { runCmd, whichCmd } from '@agent-device/host-kit/command'; export function createDeviceInventoryHost(): DeviceInventoryHost { return Object.freeze({ diff --git a/src/platform-runtime-operation-host.ts b/src/platform-runtime-operation-host.ts index 4941e8191b..6f4c7d04c1 100644 --- a/src/platform-runtime-operation-host.ts +++ b/src/platform-runtime-operation-host.ts @@ -9,7 +9,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { setTimeout as sleep } from 'node:timers/promises'; import { createAppleToolHost } from './platform-runtime-apple-tool-host.ts'; import { createHostToolchainPreparer } from './platform-runtime-toolchain-host.ts'; -import { runCmd, whichCmd } from './utils/exec.ts'; +import { runCmd, whichCmd } from '@agent-device/host-kit/command'; import { openAppLogOutput, readAppLogOutputTail } from './platform-runtime-app-log-output.ts'; import { createManagedAppLogProcesses } from './platform-runtime-app-log-process.ts'; import { createNetworkRuntimeHost } from './platform-runtime-network-host.ts'; diff --git a/src/platform-runtime-resource-cleanup.ts b/src/platform-runtime-resource-cleanup.ts index 9ab506936d..fee44beaac 100644 --- a/src/platform-runtime-resource-cleanup.ts +++ b/src/platform-runtime-resource-cleanup.ts @@ -1,8 +1,8 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { isIosFamily } from '@agent-device/kernel/device'; import type { PlatformResourceCleanup } from '@agent-device/contracts/platform-resource-cleanup'; -import { emitDiagnostic } from './utils/diagnostics.ts'; -import type { OwnedProcessRecordStore } from './utils/owned-process-record.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { type OwnedProcessRecordStore } from '@agent-device/host-kit/process'; /** Focused durable-resource cleanup composed above daemon policy and concrete platforms. */ export async function resetAndroidSnapshotHelperRuntime(): Promise { diff --git a/src/platform-runtime-runtime-hints.ts b/src/platform-runtime-runtime-hints.ts index 5f7a4b96eb..04954f2dbd 100644 --- a/src/platform-runtime-runtime-hints.ts +++ b/src/platform-runtime-runtime-hints.ts @@ -2,7 +2,7 @@ import { isIosFamily, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError, asAppError } from '@agent-device/kernel/errors'; import { escapeXmlTextAndAttribute } from '@agent-device/xml'; import type { RuntimeHintValues } from '@agent-device/contracts/application-lifecycle-runtime'; -import { execFailureDetails, type ExecResult } from './utils/exec.ts'; +import { execFailureDetails, type ExecResult } from '@agent-device/host-kit/command'; import { type ResolvedRuntimeTransport } from './utils/runtime-transport.ts'; // React Native's PackagerConnectionSettings/DevInternalSettings read debug_http_host via diff --git a/src/platform-runtime-screen-recording-android-host.ts b/src/platform-runtime-screen-recording-android-host.ts index 101b9ee2c2..a7370f64f2 100644 --- a/src/platform-runtime-screen-recording-android-host.ts +++ b/src/platform-runtime-screen-recording-android-host.ts @@ -4,7 +4,7 @@ import type { AndroidScreenRecordingTransport, } from '@agent-device/contracts/screen-recording-runtime-host'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { shellQuote } from './utils/shell-quote.ts'; +import { shellQuote } from '@agent-device/host-kit/command'; import { isPlayableVideo } from './utils/video.ts'; const ANDROID_MANIFEST_NAME = 'agent-device-recording-active.json'; diff --git a/src/platform-runtime-screen-recording-apple-runner-transport.ts b/src/platform-runtime-screen-recording-apple-runner-transport.ts index 9f3ffd8efc..22cfe768da 100644 --- a/src/platform-runtime-screen-recording-apple-runner-transport.ts +++ b/src/platform-runtime-screen-recording-apple-runner-transport.ts @@ -1,8 +1,8 @@ import path from 'node:path'; import type { ManagedProcessOwnership } from '@agent-device/contracts/platform-runtime-host'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { createScopedProvider } from './utils/scoped-provider.ts'; -import { isProcessAlive } from './utils/host-process.ts'; +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; +import { isProcessAlive } from '@agent-device/host-kit/process'; type AppleRunnerScreenRecordingStartRequest = Readonly<{ device: DeviceInfo; diff --git a/src/platform-runtime-screen-recording-apple-simulator-host.test.ts b/src/platform-runtime-screen-recording-apple-simulator-host.test.ts index bc27e916df..ac23f76565 100644 --- a/src/platform-runtime-screen-recording-apple-simulator-host.test.ts +++ b/src/platform-runtime-screen-recording-apple-simulator-host.test.ts @@ -12,8 +12,8 @@ const processes = vi.hoisted(() => ({ commands: new Map(), })); -vi.mock('./utils/host-process.ts', async (importOriginal) => ({ - ...(await importOriginal()), +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: (pid: number) => processes.alive.get(pid) ?? false, isProcessZombie: () => false, readProcessStartTime: (pid: number) => processes.starts.get(pid) ?? null, diff --git a/src/platform-runtime-screen-recording-apple-transport.ts b/src/platform-runtime-screen-recording-apple-transport.ts index 391adad738..4ef8f91c32 100644 --- a/src/platform-runtime-screen-recording-apple-transport.ts +++ b/src/platform-runtime-screen-recording-apple-transport.ts @@ -1,6 +1,6 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; -import type { ExecBackgroundResult, ExecResult } from './utils/exec.ts'; -import { createScopedProvider } from './utils/scoped-provider.ts'; +import { type ExecBackgroundResult, type ExecResult } from '@agent-device/host-kit/command'; +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; export type AppleSimulatorScreenRecordingProcess = Readonly<{ child: Pick; @@ -27,7 +27,7 @@ const localTransport: AppleSimulatorScreenRecordingTransport = Object.freeze({ async start({ device, outputPath, signal }) { const [{ buildSimctlArgsForDevice }, { runCmdBackground }] = await Promise.all([ import('./platforms/apple/core/simctl.ts'), - import('./utils/exec.ts'), + import('@agent-device/host-kit/command'), ]); signal?.throwIfAborted(); return runCmdBackground( diff --git a/src/platform-runtime-screen-recording-process-host.test.ts b/src/platform-runtime-screen-recording-process-host.test.ts index 323bafff52..9174cf4b51 100644 --- a/src/platform-runtime-screen-recording-process-host.test.ts +++ b/src/platform-runtime-screen-recording-process-host.test.ts @@ -11,8 +11,8 @@ const state = vi.hoisted(() => ({ signaled: [] as Array<{ pids: readonly number[]; signal: NodeJS.Signals }>, })); -vi.mock('./utils/host-process.ts', async (importOriginal) => ({ - ...(await importOriginal()), +vi.mock('@agent-device/host-kit/process', async (importOriginal) => ({ + ...(await importOriginal()), isProcessAlive: (pid: number) => state.alive.get(pid) ?? false, isProcessZombie: () => false, readProcessStartTime: (pid: number) => state.starts.get(pid) ?? null, diff --git a/src/platform-runtime-screen-recording-process-host.ts b/src/platform-runtime-screen-recording-process-host.ts index caf76d2b9e..41ec9f6f81 100644 --- a/src/platform-runtime-screen-recording-process-host.ts +++ b/src/platform-runtime-screen-recording-process-host.ts @@ -12,7 +12,7 @@ import { readProcessStartTime, signalPidsBestEffort, waitForProcessExit, -} from './utils/host-process.ts'; +} from '@agent-device/host-kit/process'; const STOP_TIMEOUT_MS = 5_000; const FORCE_STOP_TIMEOUT_MS = 2_000; diff --git a/src/platform-runtime/request-providers.ts b/src/platform-runtime/request-providers.ts index c4c43b6f3e..ba9fbc306b 100644 --- a/src/platform-runtime/request-providers.ts +++ b/src/platform-runtime/request-providers.ts @@ -16,7 +16,7 @@ import type { VegaToolProvider } from '../platforms/vega/tool-provider.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import type { AppleSimulatorScreenRecordingTransport } from '../platform-runtime-screen-recording-apple-transport.ts'; import type { AppleRunnerScreenRecordingTransport } from '../platform-runtime-screen-recording-apple-runner-transport.ts'; -import type { OwnedProcessRecordStore } from '../utils/owned-process-record.ts'; +import { type OwnedProcessRecordStore } from '@agent-device/host-kit/process'; import { tryGetPlugin } from '../core/platform-plugin-registry.ts'; import { registerBuiltinPlatformPlugins } from '../core/interactors/register-builtins.ts'; diff --git a/src/platforms/__tests__/install-source.test.ts b/src/platforms/__tests__/install-source.test.ts index b439d9693c..2ed07aad6c 100644 --- a/src/platforms/__tests__/install-source.test.ts +++ b/src/platforms/__tests__/install-source.test.ts @@ -6,7 +6,7 @@ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { Readable } from 'node:stream'; -import { runCmdSync, withCommandExecutorOverride } from '../../utils/exec.ts'; +import { runCmdSync, withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { ARCHIVE_EXTENSIONS, isTrustedInstallSourceUrl, diff --git a/src/platforms/android/__tests__/adb-executor.test.ts b/src/platforms/android/__tests__/adb-executor.test.ts index 2b5b2cbda7..9322ef7671 100644 --- a/src/platforms/android/__tests__/adb-executor.test.ts +++ b/src/platforms/android/__tests__/adb-executor.test.ts @@ -8,8 +8,8 @@ const { runCmdBackgroundMock } = vi.hoisted(() => ({ })), })); -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(async () => ({ stdout: 'ok', stderr: '', exitCode: 0 })), @@ -32,7 +32,7 @@ import { withAndroidAdbProvider, type AndroidAdbProvider, } from '../adb-executor.ts'; -import { runCmd, runCmdBackground } from '../../../utils/exec.ts'; +import { runCmd, runCmdBackground } from '@agent-device/host-kit/command'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/android/__tests__/adb-provider-scope.test.ts b/src/platforms/android/__tests__/adb-provider-scope.test.ts index ffe7b31fa2..9b81110997 100644 --- a/src/platforms/android/__tests__/adb-provider-scope.test.ts +++ b/src/platforms/android/__tests__/adb-provider-scope.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { test } from 'vitest'; -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { withAndroidAdbProvider } from '../adb-executor.ts'; import { mkdtempForTestSync } from '../../../__tests__/test-utils/tmp-dir.ts'; diff --git a/src/platforms/android/__tests__/device-input-state.test.ts b/src/platforms/android/__tests__/device-input-state.test.ts index 8f47d0aa3c..eabb3fae25 100644 --- a/src/platforms/android/__tests__/device-input-state.test.ts +++ b/src/platforms/android/__tests__/device-input-state.test.ts @@ -10,7 +10,10 @@ import { readAndroidClipboardWithAdb, writeAndroidClipboardWithAdb, } from '../device-input-state.ts'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../../../utils/diagnostics.ts'; +import { + flushDiagnosticsToSessionFile, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { assertRejectsAppError } from '../../../__tests__/test-utils/app-error.ts'; import { withFakeAdb } from '../../../__tests__/test-utils/fake-adb.ts'; import { mkdtempForTest } from '../../../__tests__/test-utils/tmp-dir.ts'; diff --git a/src/platforms/android/__tests__/emulator-lifecycle.test.ts b/src/platforms/android/__tests__/emulator-lifecycle.test.ts index 9777495438..59bf6272f4 100644 --- a/src/platforms/android/__tests__/emulator-lifecycle.test.ts +++ b/src/platforms/android/__tests__/emulator-lifecycle.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert/strict'; import { beforeEach, test, vi } from 'vitest'; import type { DeviceInfo } from '@agent-device/kernel/device'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(), @@ -13,7 +13,7 @@ vi.mock('../../../utils/exec.ts', async (importOriginal) => { }); vi.mock('../sdk.ts', () => ({ ensureAndroidSdkPathConfigured: vi.fn(async () => {}) })); -import { runCmd, runCmdDetached, whichCmd } from '../../../utils/exec.ts'; +import { runCmd, runCmdDetached, whichCmd } from '@agent-device/host-kit/command'; import { ensureAndroidEmulatorBooted } from '../emulator-lifecycle.ts'; const stopped: DeviceInfo = { diff --git a/src/platforms/android/__tests__/ime-helper-security.test.ts b/src/platforms/android/__tests__/ime-helper-security.test.ts index d5ee182e38..dab148ce99 100644 --- a/src/platforms/android/__tests__/ime-helper-security.test.ts +++ b/src/platforms/android/__tests__/ime-helper-security.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { test } from 'vitest'; -import { findProjectRoot } from '../../../utils/version.ts'; +import { findProjectRoot } from '@agent-device/host-kit/version'; // Security guard for #1201: the text-injection broadcast receiver must require a sender permission // that only adb shell / privileged callers hold, so a co-installed third-party app cannot inject diff --git a/src/platforms/android/__tests__/snapshot-capture-failure-reason.test.ts b/src/platforms/android/__tests__/snapshot-capture-failure-reason.test.ts index c861d4244d..3b2e3197d1 100644 --- a/src/platforms/android/__tests__/snapshot-capture-failure-reason.test.ts +++ b/src/platforms/android/__tests__/snapshot-capture-failure-reason.test.ts @@ -24,8 +24,8 @@ import { isAndroidSnapshotTimeoutError } from '../../../snapshot/snapshot-timeou * value it publishes. Split out of `snapshot.test.ts`, which is over the file-size tripwire. */ -vi.mock('../../../utils/exec.ts', async (importOriginal) => ({ - ...(await importOriginal()), +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), runCmd: vi.fn(), })); diff --git a/src/platforms/android/__tests__/snapshot-clickability.test.ts b/src/platforms/android/__tests__/snapshot-clickability.test.ts index 1086ac69eb..ae090c659f 100644 --- a/src/platforms/android/__tests__/snapshot-clickability.test.ts +++ b/src/platforms/android/__tests__/snapshot-clickability.test.ts @@ -15,8 +15,8 @@ import { resetAndroidSnapshotHelperInstallCache } from '../snapshot-helper-insta import { resetAndroidSnapshotHelperSessions } from '../snapshot-helper-session-lifecycle.ts'; import { androidSnapshotPublicationInput } from '../snapshot-capture.ts'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../adb.ts', async (importOriginal) => { diff --git a/src/platforms/android/__tests__/snapshot-helper-session.test.ts b/src/platforms/android/__tests__/snapshot-helper-session.test.ts index abde725aa7..4250d3cc5c 100644 --- a/src/platforms/android/__tests__/snapshot-helper-session.test.ts +++ b/src/platforms/android/__tests__/snapshot-helper-session.test.ts @@ -10,7 +10,7 @@ import { import path from 'node:path'; import { promises as fs } from 'node:fs'; import { mkdtempForTest } from '../../../__tests__/test-utils/tmp-dir.ts'; -import { withDiagnosticsScope } from '../../../utils/diagnostics.ts'; +import { withDiagnosticsScope } from '@agent-device/host-kit/diagnostics'; beforeEach(async () => { await resetAndroidSnapshotHelperSessions(); diff --git a/src/platforms/android/__tests__/snapshot-occlusion-context.test.ts b/src/platforms/android/__tests__/snapshot-occlusion-context.test.ts index af5ab174e3..276956a84e 100644 --- a/src/platforms/android/__tests__/snapshot-occlusion-context.test.ts +++ b/src/platforms/android/__tests__/snapshot-occlusion-context.test.ts @@ -16,8 +16,8 @@ import { isAndroidHelperRuntimeForceStop, } from './snapshot-helper-session.fixtures.ts'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../adb.ts', async (importOriginal) => { diff --git a/src/platforms/android/__tests__/snapshot.test.ts b/src/platforms/android/__tests__/snapshot.test.ts index 04f8557627..70a108ce59 100644 --- a/src/platforms/android/__tests__/snapshot.test.ts +++ b/src/platforms/android/__tests__/snapshot.test.ts @@ -5,8 +5,8 @@ import os from 'node:os'; import path from 'node:path'; import { mkdtempForTest } from '../../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); vi.mock('../adb.ts', async (importOriginal) => { @@ -19,9 +19,9 @@ import { snapshotAndroid } from '../snapshot.ts'; import { readAndroidGestureViewport } from '../touch-executor.ts'; import { buildUiHierarchySnapshot, parseUiHierarchyTree } from '../ui-hierarchy.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../../../utils/diagnostics.ts'; +import * as diagnosticsModule from '@agent-device/host-kit/diagnostics'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { sleep } from '../adb.ts'; import { resetAndroidSnapshotHelperInstallCache } from '../snapshot-helper-install.ts'; import { resetAndroidSnapshotHelperSessions } from '../snapshot-helper-session-lifecycle.ts'; @@ -282,13 +282,13 @@ function isAndroidSdkVersionCommand(args: string[]): boolean { } async function captureDiagnostics( - scope: Parameters[0], + scope: Parameters[0], callback: () => Promise, ): Promise { const previousHome = process.env.HOME; process.env.HOME = await mkdtempForTest('agent-device-android-diag-'); try { - const diagnosticsPath = await withDiagnosticsScope(scope, callback); + const diagnosticsPath = await diagnosticsModule.withDiagnosticsScope(scope, callback); assert.ok(diagnosticsPath); return await fs.readFile(diagnosticsPath, 'utf8'); } finally { @@ -412,7 +412,7 @@ test('snapshotAndroid emits helper phase diagnostics', async () => { helperAdb, helperArtifact, }); - return flushDiagnosticsToSessionFile({ force: true })?.path ?? null; + return diagnosticsModule.flushDiagnosticsToSessionFile({ force: true })?.path ?? null; }, ); @@ -938,7 +938,7 @@ test('snapshotAndroid emits helper failure diagnostics', async () => { return true; }, ); - return flushDiagnosticsToSessionFile({ force: true })?.path ?? null; + return diagnosticsModule.flushDiagnosticsToSessionFile({ force: true })?.path ?? null; }, ); @@ -962,7 +962,7 @@ test('snapshotAndroid emits unavailable diagnostics when helper artifact is miss () => snapshotAndroid(device), /Android snapshot helper is unavailable/, ); - return flushDiagnosticsToSessionFile({ force: true })?.path ?? null; + return diagnosticsModule.flushDiagnosticsToSessionFile({ force: true })?.path ?? null; }, ); @@ -1066,7 +1066,7 @@ test('snapshotAndroid emits timeout diagnostics when helper capture times out', () => snapshotAndroidWithHelper(helperAdb), /Android snapshot helper failed: helper capture timed out/, ); - return flushDiagnosticsToSessionFile({ force: true })?.path ?? null; + return diagnosticsModule.flushDiagnosticsToSessionFile({ force: true })?.path ?? null; }, ); diff --git a/src/platforms/android/__tests__/text-input-test-ime.test.ts b/src/platforms/android/__tests__/text-input-test-ime.test.ts index c3a5da9a4d..e461281985 100644 --- a/src/platforms/android/__tests__/text-input-test-ime.test.ts +++ b/src/platforms/android/__tests__/text-input-test-ime.test.ts @@ -32,7 +32,10 @@ vi.mock('@agent-device/platform-android/ime-helper', async (importOriginal) => { import fs from 'node:fs/promises'; import { ANDROID_EMULATOR } from '../../../__tests__/test-utils/device-fixtures.ts'; import { mkdtempForTest } from '../../../__tests__/test-utils/tmp-dir.ts'; -import { flushDiagnosticsToSessionFile, withDiagnosticsScope } from '../../../utils/diagnostics.ts'; +import { + flushDiagnosticsToSessionFile, + withDiagnosticsScope, +} from '@agent-device/host-kit/diagnostics'; import { ANDROID_SNAPSHOT_HELPER_FIXTURE_ARTIFACT, createAndroidSnapshotHelperExecutor, diff --git a/src/platforms/android/adb-host-binding.ts b/src/platforms/android/adb-host-binding.ts index ca03424fc9..ea1cdb97b6 100644 --- a/src/platforms/android/adb-host-binding.ts +++ b/src/platforms/android/adb-host-binding.ts @@ -6,8 +6,9 @@ import { runCmdBackground, withCommandExecutorOverride, withoutCommandExecutorOverride, -} from '../../utils/exec.ts'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +} from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; + import { clearAndroidTestImeRecoveryMarker, readAndroidTestImeRecoveryMarkers, diff --git a/src/platforms/android/adb.ts b/src/platforms/android/adb.ts index fac14c2097..6d00630bb2 100644 --- a/src/platforms/android/adb.ts +++ b/src/platforms/android/adb.ts @@ -5,7 +5,7 @@ import { type AndroidAdbExecutorResult, } from './adb-executor.ts'; -export { sleep } from '../../utils/timeouts.ts'; +export { sleep } from '@agent-device/host-kit/retry'; export async function runAndroidAdb( device: DeviceInfo, diff --git a/src/platforms/android/alert-detection.ts b/src/platforms/android/alert-detection.ts index 053c106e30..23b70429a4 100644 --- a/src/platforms/android/alert-detection.ts +++ b/src/platforms/android/alert-detection.ts @@ -1,6 +1,6 @@ import { centerOfRect, type RawSnapshotNode } from '@agent-device/kernel/snapshot'; import type { AlertInfo, AlertSource } from '@agent-device/contracts/alert-contract'; -import { isSnapshotNodeInteractionBlocked } from '../../snapshot/snapshot-occlusion.ts'; +import { isSnapshotNodeInteractionBlocked } from '@agent-device/capture-kit/snapshot-occlusion'; type AndroidAlertButtonRole = 'accept' | 'dismiss' | 'neutral'; diff --git a/src/platforms/android/alert.ts b/src/platforms/android/alert.ts index 56f7249cd5..9c406ea12e 100644 --- a/src/platforms/android/alert.ts +++ b/src/platforms/android/alert.ts @@ -5,9 +5,10 @@ import { DEFAULT_ALERT_TIMEOUT_MS, } from '@agent-device/contracts/alert-contract'; import { AppError } from '@agent-device/kernel/errors'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; -import { successText } from '../../utils/success-text.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; +import { successText } from '@agent-device/kernel/success-text'; + import type { DeviceInfo } from '@agent-device/kernel/device'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { diff --git a/src/platforms/android/app-deployment.ts b/src/platforms/android/app-deployment.ts index 47f5233181..34ce75139e 100644 --- a/src/platforms/android/app-deployment.ts +++ b/src/platforms/android/app-deployment.ts @@ -3,7 +3,7 @@ import os from 'node:os'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { resolveFileOverridePath, runCmd, whichCmd } from '../../utils/exec.ts'; +import { resolveFileOverridePath, runCmd, whichCmd } from '@agent-device/host-kit/command'; import { waitForAndroidBoot } from './emulator-lifecycle.ts'; import { installAndroidAdbPackage, resolveAndroidAdbProvider } from './adb-executor.ts'; import { withAndroidAppResolutionCacheInvalidated } from './app-deployment-resolution.ts'; diff --git a/src/platforms/android/app-lifecycle.ts b/src/platforms/android/app-lifecycle.ts index d0265d23d5..cdf0694b37 100644 --- a/src/platforms/android/app-lifecycle.ts +++ b/src/platforms/android/app-lifecycle.ts @@ -1,10 +1,11 @@ import { AppError } from '@agent-device/kernel/errors'; import type { AppStateRuntimeResult } from '@agent-device/contracts/app-state-runtime'; -import { sleep } from '../../utils/timeouts.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; +import { sleep } from '@agent-device/host-kit/retry'; import type { AppsFilter } from '@agent-device/contracts/device'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { isDeepLinkTarget } from '@agent-device/contracts/command'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; + import { waitForAndroidBoot } from './emulator-lifecycle.ts'; import { runAndroidAdb } from './adb.ts'; import { diff --git a/src/platforms/android/device-input-state.ts b/src/platforms/android/device-input-state.ts index 45bade05a3..722b01ba10 100644 --- a/src/platforms/android/device-input-state.ts +++ b/src/platforms/android/device-input-state.ts @@ -1,7 +1,8 @@ -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; + import { isClipboardShellUnsupported, sleep } from './adb.ts'; import { androidAdbResultError, diff --git a/src/platforms/android/emulator-lifecycle.ts b/src/platforms/android/emulator-lifecycle.ts index 4499fce026..cb31c71a9d 100644 --- a/src/platforms/android/emulator-lifecycle.ts +++ b/src/platforms/android/emulator-lifecycle.ts @@ -1,11 +1,11 @@ import type { DeviceInventoryRequest } from '@agent-device/contracts/device'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError, asAppError } from '@agent-device/kernel/errors'; -import type { ExecResult } from '../../utils/exec.ts'; -import { runCmdDetached, whichCmd } from '../../utils/exec.ts'; +import { type ExecResult, runCmdDetached, whichCmd } from '@agent-device/host-kit/command'; +import { Deadline, retryWithPolicy, sleep } from '@agent-device/host-kit/retry'; + import { runAndroidHostAdb } from './adb-executor.ts'; -import { Deadline, retryWithPolicy } from '../../utils/retry.ts'; -import { sleep } from '../../utils/timeouts.ts'; + import { bootFailureHint, classifyBootFailure } from '../boot-diagnostics.ts'; import { ensureAndroidSdkPathConfigured } from './sdk.ts'; diff --git a/src/platforms/android/fill-verification.ts b/src/platforms/android/fill-verification.ts index d452aa0974..145eef7b55 100644 --- a/src/platforms/android/fill-verification.ts +++ b/src/platforms/android/fill-verification.ts @@ -1,6 +1,6 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { containsPoint } from '@agent-device/kernel/rect'; import { buildFillFailureDetails, diff --git a/src/platforms/android/helper-package-install.ts b/src/platforms/android/helper-package-install.ts index a16a6f3fce..4a5a06c416 100644 --- a/src/platforms/android/helper-package-install.ts +++ b/src/platforms/android/helper-package-install.ts @@ -3,7 +3,7 @@ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { AppError, normalizeError } from '@agent-device/kernel/errors'; -import { findProjectRoot, readVersion } from '../../utils/version.ts'; +import { findProjectRoot, readVersion } from '@agent-device/host-kit/version'; // The package subpath (not the root shim) breaks the module cycle the shim route would create: // the adb-host binding reaches this file for the helper port facets, and the shim imports that // binding. Allowed by the transitional #2041 R13 table. diff --git a/src/platforms/android/ime-recovery-marker.ts b/src/platforms/android/ime-recovery-marker.ts index fea7bcd558..b645300aec 100644 --- a/src/platforms/android/ime-recovery-marker.ts +++ b/src/platforms/android/ime-recovery-marker.ts @@ -2,7 +2,7 @@ import fs from 'node:fs/promises'; import { randomUUID } from 'node:crypto'; import path from 'node:path'; import { normalizeError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; // Device-scoped pending-recovery markers, one file per switched device, in the daemon state dir. // The marker is separate from IME mechanics so startup can establish evidence before it loads the diff --git a/src/platforms/android/manifest.ts b/src/platforms/android/manifest.ts index 650f22c7e1..3587723b78 100644 --- a/src/platforms/android/manifest.ts +++ b/src/platforms/android/manifest.ts @@ -1,7 +1,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { TextDecoder } from 'node:util'; -import { isExecutablePath, runCmd } from '../../utils/exec.ts'; +import { isExecutablePath, runCmd } from '@agent-device/host-kit/command'; import { resolveAndroidSdkRoots } from './sdk.ts'; const RES_XML_TYPE = 0x0003; diff --git a/src/platforms/android/perf-native-artifacts.ts b/src/platforms/android/perf-native-artifacts.ts index 831edc3f11..b019350469 100644 --- a/src/platforms/android/perf-native-artifacts.ts +++ b/src/platforms/android/perf-native-artifacts.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { resolveAndroidAdbExecutor, type AndroidAdbExecutor } from './adb-executor.ts'; import { annotateAndroidNativePerfError } from './perf-native-errors.ts'; import { buildAndroidNativePerfStopSummary } from './perf-native-summary.ts'; diff --git a/src/platforms/android/perf-native-report.ts b/src/platforms/android/perf-native-report.ts index 20ee674245..580f0a5431 100644 --- a/src/platforms/android/perf-native-report.ts +++ b/src/platforms/android/perf-native-report.ts @@ -1,4 +1,4 @@ -import { splitNonEmptyTrimmedLines } from '../../utils/parsing.ts'; +import { splitNonEmptyTrimmedLines } from '@agent-device/kernel/record'; import { roundPercent } from '../perf-utils.ts'; const SIMPLEPERF_REPORT_ARTIFACT_ENTRY_LIMIT = 50; diff --git a/src/platforms/android/perf.ts b/src/platforms/android/perf.ts index ec46f81867..4e4c06f038 100644 --- a/src/platforms/android/perf.ts +++ b/src/platforms/android/perf.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { requireExecSuccess } from '../../utils/exec.ts'; +import { requireExecSuccess } from '@agent-device/host-kit/command'; import { androidAdbResultError, resolveAndroidAdbExecutor, diff --git a/src/platforms/android/settings.ts b/src/platforms/android/settings.ts index b4b7825ccf..e62e63e9b1 100644 --- a/src/platforms/android/settings.ts +++ b/src/platforms/android/settings.ts @@ -1,6 +1,6 @@ import { AppError } from '@agent-device/kernel/errors'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { requireLocationCoordinates } from '../../utils/location-coordinates.ts'; +import { requireLocationCoordinates } from '@agent-device/kernel/location-coordinates'; import { summarizeCommandAttemptFailures, type CommandAttemptFailure, diff --git a/src/platforms/android/snapshot-helper-capture.ts b/src/platforms/android/snapshot-helper-capture.ts index 7c7eef446c..f92fdc7e21 100644 --- a/src/platforms/android/snapshot-helper-capture.ts +++ b/src/platforms/android/snapshot-helper-capture.ts @@ -4,7 +4,7 @@ import { androidCaptureFailureReasonFromExitCode, androidCaptureFailureReasonFromHelperResult, } from './snapshot-capture-failure-reason.ts'; -import { execFailureDetails } from '../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; import { parseInstrumentationRecords, readInstrumentationResultBoolean, diff --git a/src/platforms/android/snapshot-helper-runtime.ts b/src/platforms/android/snapshot-helper-runtime.ts index 3a7bf1397b..e34e47f506 100644 --- a/src/platforms/android/snapshot-helper-runtime.ts +++ b/src/platforms/android/snapshot-helper-runtime.ts @@ -1,5 +1,5 @@ import { normalizeError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { sleep } from './adb.ts'; import type { AndroidAdbExecutor } from './adb-executor.ts'; import { stopAndroidSnapshotHelperSession } from './snapshot-helper-session-lifecycle.ts'; diff --git a/src/platforms/android/snapshot-helper-session-lifecycle.ts b/src/platforms/android/snapshot-helper-session-lifecycle.ts index f01b6d6040..a9f2d7b1e1 100644 --- a/src/platforms/android/snapshot-helper-session-lifecycle.ts +++ b/src/platforms/android/snapshot-helper-session-lifecycle.ts @@ -9,7 +9,7 @@ import type { AndroidAdbProcess } from './adb-executor.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { androidAdbForwardsDeviceExitStatus, resetAndroidAdbShellProtocolProbes, diff --git a/src/platforms/android/snapshot-helper-session.ts b/src/platforms/android/snapshot-helper-session.ts index 4718a61203..4014835d5d 100644 --- a/src/platforms/android/snapshot-helper-session.ts +++ b/src/platforms/android/snapshot-helper-session.ts @@ -4,7 +4,7 @@ * `snapshot-helper-session-lifecycle.ts`, which this module acquires through. */ import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { readAndroidCaptureFailureReason } from '@agent-device/contracts/android-snapshot-quality'; import type { AndroidSnapshotHelperCaptureOptions, diff --git a/src/platforms/android/snapshot.ts b/src/platforms/android/snapshot.ts index b4a875616f..37c84c6eb8 100644 --- a/src/platforms/android/snapshot.ts +++ b/src/platforms/android/snapshot.ts @@ -6,16 +6,16 @@ import { toAppErrorCode, type NormalizedError, } from '@agent-device/kernel/errors'; -import { emitDiagnostic, withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { emitDiagnostic, withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { findProjectRoot, readVersion } from '../../utils/version.ts'; +import { findProjectRoot, readVersion } from '@agent-device/host-kit/version'; import { attachRefs, type HiddenContentHint, type RawSnapshotNode, type SnapshotOptions, } from '@agent-device/kernel/snapshot'; -import { deriveMobileSnapshotHiddenContentHints } from '../../snapshot/mobile-snapshot-semantics.ts'; +import { deriveMobileSnapshotHiddenContentHints } from '@agent-device/capture-kit/mobile-snapshot-semantics'; import { buildUiHierarchySnapshot, parseUiHierarchyTree, diff --git a/src/platforms/android/text-input.ts b/src/platforms/android/text-input.ts index 9c26ec6ce3..3502f4f976 100644 --- a/src/platforms/android/text-input.ts +++ b/src/platforms/android/text-input.ts @@ -7,8 +7,9 @@ import type { FillUnconfirmedVerification } from '@agent-device/contracts/interactor-types'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import { shellQuoteIfNeeded } from '../../utils/shell-quote.ts'; +import { shellQuoteIfNeeded } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; + import { resolveAndroidAdbExecutor, resolveAndroidAdbProvider, diff --git a/src/platforms/android/touch-helper.ts b/src/platforms/android/touch-helper.ts index 9bd1ac8272..89052de745 100644 --- a/src/platforms/android/touch-helper.ts +++ b/src/platforms/android/touch-helper.ts @@ -2,8 +2,9 @@ import type { PointerTrajectory } from '@agent-device/contracts/gesture-plan-typ import type { DeviceInfo } from '@agent-device/kernel/device'; import type { Rect } from '@agent-device/kernel/snapshot'; import { AppError } from '@agent-device/kernel/errors'; -import { execFailureDetails } from '../../utils/exec.ts'; -import { emitDiagnostic, withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; +import { emitDiagnostic, withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; + import { resolveAndroidAdbProvider, type AndroidAdbExecutor, diff --git a/src/platforms/apple/alert.ts b/src/platforms/apple/alert.ts index 2d2adec8d4..d72a4399ca 100644 --- a/src/platforms/apple/alert.ts +++ b/src/platforms/apple/alert.ts @@ -11,7 +11,7 @@ import type { } from '@agent-device/contracts/interactor-types'; import { isIosFamily, isMacOs, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { runAppleRunnerCommand } from './core/runner-client.ts'; import { runMacOsAlertAction } from './os/macos/helper.ts'; diff --git a/src/platforms/apple/core/__tests__/app-launch-url.test.ts b/src/platforms/apple/core/__tests__/app-launch-url.test.ts index a94e3c586a..7d560f9db6 100644 --- a/src/platforms/apple/core/__tests__/app-launch-url.test.ts +++ b/src/platforms/apple/core/__tests__/app-launch-url.test.ts @@ -3,8 +3,8 @@ import assert from 'node:assert/strict'; import { IOS_TEST_SIMULATOR } from './apple-core-stub-helpers.ts'; import { IOS_SIMULATOR_TERMINATE_TIMEOUT_MS } from '../config.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd) }; }); vi.mock('../simulator.ts', async (importOriginal) => { @@ -12,7 +12,7 @@ vi.mock('../simulator.ts', async (importOriginal) => { return { ...actual, ensureBootedSimulator: vi.fn(actual.ensureBootedSimulator) }; }); -import { runCmd } from '../../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { ensureBootedSimulator } from '../simulator.ts'; import { openIosApp } from '../app-launch.ts'; diff --git a/src/platforms/apple/core/__tests__/apps.test.ts b/src/platforms/apple/core/__tests__/apps.test.ts index 30a00fe675..5d9b531947 100644 --- a/src/platforms/apple/core/__tests__/apps.test.ts +++ b/src/platforms/apple/core/__tests__/apps.test.ts @@ -4,12 +4,12 @@ import { promises as fs, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd) }; }); -vi.mock('../../../../utils/retry.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, retryWithPolicy: vi.fn(actual.retryWithPolicy) }; }); vi.mock('../simulator.ts', async (importOriginal) => { @@ -17,11 +17,11 @@ vi.mock('../simulator.ts', async (importOriginal) => { return { ...actual, ensureBootedSimulator: vi.fn(actual.ensureBootedSimulator) }; }); -const execActual = await vi.importActual( - '../../../../utils/exec.ts', +const execActual = await vi.importActual( + '@agent-device/host-kit/command', ); -const retryActual = await vi.importActual( - '../../../../utils/retry.ts', +const retryActual = await vi.importActual( + '@agent-device/host-kit/retry', ); const simulatorActual = await vi.importActual('../simulator.ts'); @@ -42,9 +42,9 @@ import { ensureBootedSimulator } from '../simulator.ts'; import { IOS_DEVICE_INSTALL_TIMEOUT_MS, IOS_SIMULATOR_TERMINATE_TIMEOUT_MS } from '../config.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../../../utils/exec.ts'; -import { retryWithPolicy } from '../../../../utils/retry.ts'; -import { PNG } from '../../../../utils/png.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { retryWithPolicy } from '@agent-device/host-kit/retry'; +import { PNG } from '@agent-device/capture-kit/png'; import { assertRejectsAppError } from '../../../../__tests__/test-utils/app-error.ts'; import { withFakeAppleTool, diff --git a/src/platforms/apple/core/__tests__/debug-symbols.test.ts b/src/platforms/apple/core/__tests__/debug-symbols.test.ts index 18d07fc6f3..19f7ec6821 100644 --- a/src/platforms/apple/core/__tests__/debug-symbols.test.ts +++ b/src/platforms/apple/core/__tests__/debug-symbols.test.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { test } from 'vitest'; import { symbolicateCrashArtifact } from '../debug-symbols.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { withCommandExecutorOverride } from '../../../../utils/exec.ts'; +import { withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; const UUID = 'ABCDEFAB-CDEF-ABCD-EFAB-CDEFABCDEFAB'; diff --git a/src/platforms/apple/core/__tests__/install-artifact.fixtures.ts b/src/platforms/apple/core/__tests__/install-artifact.fixtures.ts index b33d6f1ed1..93577bd1a6 100644 --- a/src/platforms/apple/core/__tests__/install-artifact.fixtures.ts +++ b/src/platforms/apple/core/__tests__/install-artifact.fixtures.ts @@ -1,6 +1,6 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { runCmdSync } from '../../../../utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; export async function createZipFixture( archivePath: string, diff --git a/src/platforms/apple/core/__tests__/perf.test.ts b/src/platforms/apple/core/__tests__/perf.test.ts index 069bce8920..939040f4ce 100644 --- a/src/platforms/apple/core/__tests__/perf.test.ts +++ b/src/platforms/apple/core/__tests__/perf.test.ts @@ -4,8 +4,8 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd), @@ -27,7 +27,7 @@ import { type AppleXctracePerfCapture, } from '../perf-xctrace.ts'; import { parseAppleFramePerfSample } from '../perf-frame.ts'; -import { runCmd, runCmdBackground } from '../../../../utils/exec.ts'; +import { runCmd, runCmdBackground } from '@agent-device/host-kit/command'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; diff --git a/src/platforms/apple/core/__tests__/plist.test.ts b/src/platforms/apple/core/__tests__/plist.test.ts index 7b808332d6..a31dd608d0 100644 --- a/src/platforms/apple/core/__tests__/plist.test.ts +++ b/src/platforms/apple/core/__tests__/plist.test.ts @@ -4,12 +4,12 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd) }; }); -import { runCmd } from '../../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { readInfoPlistString } from '../plist.ts'; import { createLocalAppleToolProvider, withAppleToolProvider } from '../tool-provider.ts'; diff --git a/src/platforms/apple/core/__tests__/screenshot-density.test.ts b/src/platforms/apple/core/__tests__/screenshot-density.test.ts index 7857bf923a..7b77d40d24 100644 --- a/src/platforms/apple/core/__tests__/screenshot-density.test.ts +++ b/src/platforms/apple/core/__tests__/screenshot-density.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import { promises as fs } from 'node:fs'; import path from 'node:path'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { PNG } from '../../../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { screenshotIos } from '../screenshot.ts'; import { createLocalAppleToolProvider, withAppleToolProvider } from '../tool-provider.ts'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; diff --git a/src/platforms/apple/core/__tests__/screenshot.test.ts b/src/platforms/apple/core/__tests__/screenshot.test.ts index 0cebc6ed50..1110e0c622 100644 --- a/src/platforms/apple/core/__tests__/screenshot.test.ts +++ b/src/platforms/apple/core/__tests__/screenshot.test.ts @@ -4,12 +4,12 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { mkdtempForTest } from '../../../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd) }; }); -vi.mock('../../../../utils/retry.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, retryWithPolicy: vi.fn(actual.retryWithPolicy) }; }); vi.mock('../runner-client.ts', async (importOriginal) => { @@ -32,11 +32,11 @@ vi.mock('../screenshot-status-bar.ts', async (importOriginal) => { }; }); -const execActual = await vi.importActual( - '../../../../utils/exec.ts', +const execActual = await vi.importActual( + '@agent-device/host-kit/command', ); -const retryActual = await vi.importActual( - '../../../../utils/retry.ts', +const retryActual = await vi.importActual( + '@agent-device/host-kit/retry', ); const runnerActual = await vi.importActual('../runner-client.ts'); @@ -55,10 +55,11 @@ import { import { ensureBootedSimulator, openIosSimulatorApp } from '../simulator.ts'; import { prepareSimulatorStatusBarForScreenshot } from '../screenshot-status-bar.ts'; import { runAppleRunnerCommand } from '../runner-client.ts'; -import { withDiagnosticsScope } from '../../../../utils/diagnostics.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { withDiagnosticsScope } from '@agent-device/host-kit/diagnostics'; +import { retryWithPolicy } from '@agent-device/host-kit/retry'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../../../utils/exec.ts'; -import { retryWithPolicy } from '../../../../utils/retry.ts'; + import { IOS_TEST_SIMULATOR, MACOS_TEST_DEVICE } from './apple-core-stub-helpers.ts'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/apple/core/__tests__/simulator.test.ts b/src/platforms/apple/core/__tests__/simulator.test.ts index 719589d6df..91315c81ae 100644 --- a/src/platforms/apple/core/__tests__/simulator.test.ts +++ b/src/platforms/apple/core/__tests__/simulator.test.ts @@ -3,16 +3,16 @@ import assert from 'node:assert/strict'; import { ensureBootedSimulator, openIosSimulatorApp } from '../simulator.ts'; import { IOS_SIMULATOR_FOCUS_TIMEOUT_MS } from '../config.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { IOS_TEST_SIMULATOR } from './apple-core-stub-helpers.ts'; -vi.mock('../../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(actual.runCmd) }; }); -const execActual = await vi.importActual( - '../../../../utils/exec.ts', +const execActual = await vi.importActual( + '@agent-device/host-kit/command', ); const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/apple/core/app-device-io.ts b/src/platforms/apple/core/app-device-io.ts index 531bea8ea6..912a53a6d3 100644 --- a/src/platforms/apple/core/app-device-io.ts +++ b/src/platforms/apple/core/app-device-io.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { isMacOs, type DeviceInfo } from '@agent-device/kernel/device'; -import { requireExecSuccess } from '../../../utils/exec.ts'; +import { requireExecSuccess } from '@agent-device/host-kit/command'; import { ensureBootedSimulator, requireSimulatorDevice } from './simulator.ts'; import { readMacOsClipboardText, writeMacOsClipboardText } from '../os/macos/apps.ts'; import { runSimctl } from './apps-simctl.ts'; diff --git a/src/platforms/apple/core/app-launch.ts b/src/platforms/apple/core/app-launch.ts index f41b49493e..da5d8f6838 100644 --- a/src/platforms/apple/core/app-launch.ts +++ b/src/platforms/apple/core/app-launch.ts @@ -2,13 +2,15 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { isIosFamily, isMacOs, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; -import { execFailureDetails } from '../../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { Deadline, retryWithPolicy } from '@agent-device/host-kit/retry'; + import { LAUNCH_CONSOLE_DIRECT_APP_ONLY_MESSAGE, LAUNCH_CONSOLE_IOS_SIMULATOR_ONLY_MESSAGE, } from '@agent-device/contracts/observability'; -import { Deadline, retryWithPolicy } from '../../../utils/retry.ts'; + import { isDeepLinkTarget, isWebUrl, diff --git a/src/platforms/apple/core/app-resolution.ts b/src/platforms/apple/core/app-resolution.ts index 2d90abd294..88f283eacf 100644 --- a/src/platforms/apple/core/app-resolution.ts +++ b/src/platforms/apple/core/app-resolution.ts @@ -14,7 +14,7 @@ import { listMacApps, resolveMacOsApp } from '../os/macos/apps.ts'; import { runAppleToolCommand } from './tool-provider.ts'; import { runSimctl } from './apps-simctl.ts'; import { resolveIosPhysicalDeviceControl } from './physical-device-control.ts'; -import { createTtlMemo } from '../../../utils/ttl-memo.ts'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; const ALIASES: Record = { settings: 'com.apple.Preferences', diff --git a/src/platforms/apple/core/app-settings.ts b/src/platforms/apple/core/app-settings.ts index f0da0d061e..246c8ced70 100644 --- a/src/platforms/apple/core/app-settings.ts +++ b/src/platforms/apple/core/app-settings.ts @@ -8,9 +8,9 @@ import { isIosFamily, isMacOs, type DeviceInfo } from '@agent-device/kernel/devi import { AppError } from '@agent-device/kernel/errors'; import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { resolveIosSimulatorDeviceSetPath } from '../../../utils/device-isolation.ts'; -import { requireExecSuccess } from '../../../utils/exec.ts'; -import { requireLocationCoordinates } from '../../../utils/location-coordinates.ts'; +import { resolveIosSimulatorDeviceSetPath } from '@agent-device/kernel/device-isolation'; +import { requireExecSuccess } from '@agent-device/host-kit/command'; +import { requireLocationCoordinates } from '@agent-device/kernel/location-coordinates'; import { parseAppearanceAction } from '../../appearance.ts'; import { summarizeCommandAttemptFailures, diff --git a/src/platforms/apple/core/debug-symbols/crash-artifact.ts b/src/platforms/apple/core/debug-symbols/crash-artifact.ts index a22c4d3785..d897bddc44 100644 --- a/src/platforms/apple/core/debug-symbols/crash-artifact.ts +++ b/src/platforms/apple/core/debug-symbols/crash-artifact.ts @@ -1,5 +1,5 @@ import path from 'node:path'; -import { isRecord } from '../../../../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import type { AppleImage, CrashArtifact, diff --git a/src/platforms/apple/core/debug-symbols/dsym.ts b/src/platforms/apple/core/debug-symbols/dsym.ts index 331727ace8..cbf835cb91 100644 --- a/src/platforms/apple/core/debug-symbols/dsym.ts +++ b/src/platforms/apple/core/debug-symbols/dsym.ts @@ -2,7 +2,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import type { AppleImage, DsymMatch, DsymSlice } from './types.ts'; import { normalizeUuid, unique } from './utils.ts'; -import { requireExecSuccess, runCmd } from '../../../../utils/exec.ts'; +import { requireExecSuccess, runCmd } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; const MAX_SEARCH_ENTRIES = 10_000; diff --git a/src/platforms/apple/core/debug-symbols/report.ts b/src/platforms/apple/core/debug-symbols/report.ts index cee0016b2b..e4809d97c7 100644 --- a/src/platforms/apple/core/debug-symbols/report.ts +++ b/src/platforms/apple/core/debug-symbols/report.ts @@ -9,7 +9,7 @@ import type { DebugSymbolsCrashFrame, DebugSymbolsCrashSummary, } from '@agent-device/contracts/observability'; -import { isRecord } from '../../../../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; import { addressKey, compactJoin, diff --git a/src/platforms/apple/core/debug-symbols/symbolication.ts b/src/platforms/apple/core/debug-symbols/symbolication.ts index 1045b703b1..20c482fe05 100644 --- a/src/platforms/apple/core/debug-symbols/symbolication.ts +++ b/src/platforms/apple/core/debug-symbols/symbolication.ts @@ -1,6 +1,6 @@ import type { DsymMatch, SymbolicatedAddress, SymbolicationGroup } from './types.ts'; import { addressKey, hex, unique } from './utils.ts'; -import { requireExecSuccess, runCmd } from '../../../../utils/exec.ts'; +import { requireExecSuccess, runCmd } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; export async function symbolicateAddresses( diff --git a/src/platforms/apple/core/debug-symbols/utils.ts b/src/platforms/apple/core/debug-symbols/utils.ts index bba3ab3eda..1e3afb4886 100644 --- a/src/platforms/apple/core/debug-symbols/utils.ts +++ b/src/platforms/apple/core/debug-symbols/utils.ts @@ -1,5 +1,5 @@ import { AppError } from '@agent-device/kernel/errors'; -import { isRecord } from '../../../../utils/parsing.ts'; +import { isRecord } from '@agent-device/kernel/record'; const UUID_RE = /^[0-9a-fA-F-]{32,36}$/; diff --git a/src/platforms/apple/core/devicectl.ts b/src/platforms/apple/core/devicectl.ts index 729ecea93a..e0c80933eb 100644 --- a/src/platforms/apple/core/devicectl.ts +++ b/src/platforms/apple/core/devicectl.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { execFailureDetails } from '../../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; import { IOS_DEVICECTL_TIMEOUT_MS } from './config.ts'; import { runXcrun } from './tool-provider.ts'; diff --git a/src/platforms/apple/core/install-artifact.ts b/src/platforms/apple/core/install-artifact.ts index db3caa9d5d..8380db8b46 100644 --- a/src/platforms/apple/core/install-artifact.ts +++ b/src/platforms/apple/core/install-artifact.ts @@ -4,8 +4,8 @@ import path from 'node:path'; import type { LocalInstallSource } from '@agent-device/kernel/contracts'; import { readInfoPlistString } from './plist.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { extractArchiveSafely } from '../../../utils/archive-extraction.ts'; -import { ArchiveBudget } from '../../../utils/archive-safety.ts'; +import { extractArchiveSafely, ArchiveBudget } from '@agent-device/host-kit/archive'; + import { installArtifactArchiveBudget, installArtifactArchiveDepth, diff --git a/src/platforms/apple/core/perf-xctrace.ts b/src/platforms/apple/core/perf-xctrace.ts index 69ed0ee081..b1a4fe0037 100644 --- a/src/platforms/apple/core/perf-xctrace.ts +++ b/src/platforms/apple/core/perf-xctrace.ts @@ -17,7 +17,7 @@ import { runCmdBackground, type ExecBackgroundResult, type ExecResult, -} from '../../../utils/exec.ts'; +} from '@agent-device/host-kit/command'; import { uniqueStrings } from '@agent-device/kernel/collections'; import { findAllXmlNodes } from './perf-xml.ts'; import { diff --git a/src/platforms/apple/core/perf.ts b/src/platforms/apple/core/perf.ts index 5d9d4ccf0f..175de0d322 100644 --- a/src/platforms/apple/core/perf.ts +++ b/src/platforms/apple/core/perf.ts @@ -11,8 +11,12 @@ import { } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; import { parseXmlDocumentSync, type XmlNode } from '@agent-device/xml'; -import { execFailureDetails, requireExecSuccess, type ExecResult } from '../../../utils/exec.ts'; -import { splitNonEmptyTrimmedLines } from '../../../utils/parsing.ts'; +import { + execFailureDetails, + requireExecSuccess, + type ExecResult, +} from '@agent-device/host-kit/command'; +import { splitNonEmptyTrimmedLines } from '@agent-device/kernel/record'; import { uniqueStrings } from '@agent-device/kernel/collections'; import { IOS_DEVICECTL_DEFAULT_HINT, resolveIosDevicectlHint } from './devicectl.ts'; import type { IosDeviceProcessInfo } from './app-info.ts'; @@ -36,9 +40,7 @@ import { const APPLE_MEMORY_SAMPLE_METHOD = 'ps-process-snapshot'; const IOS_DEVICE_MEMORY_SAMPLE_METHOD = 'xctrace-activity-monitor'; -export const APPLE_MEMGRAPH_SNAPSHOT_METHOD = 'leaks-output-graph'; -export const APPLE_MEMGRAPH_SNAPSHOT_DESCRIPTION = - 'Memory graph captured with leaks --outputGraph for host-visible Apple app processes.'; +const APPLE_MEMGRAPH_SNAPSHOT_METHOD = 'leaks-output-graph'; const APPLE_PERF_TIMEOUT_MS = 15_000; const APPLE_MEMORY_SNAPSHOT_TIMEOUT_MS = 120_000; diff --git a/src/platforms/apple/core/physical-device-control.ts b/src/platforms/apple/core/physical-device-control.ts index c96492d045..5cab541eaa 100644 --- a/src/platforms/apple/core/physical-device-control.ts +++ b/src/platforms/apple/core/physical-device-control.ts @@ -1,6 +1,6 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { execFailureDetails } from '../../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; import type { AppsFilter } from '@agent-device/contracts/device'; import type { IosAppInfo, IosDeviceAppProcesses } from './app-info.ts'; import { diff --git a/src/platforms/apple/core/physical-device-coredevice.ts b/src/platforms/apple/core/physical-device-coredevice.ts index 1051f3ea8a..0dfebfa587 100644 --- a/src/platforms/apple/core/physical-device-coredevice.ts +++ b/src/platforms/apple/core/physical-device-coredevice.ts @@ -3,7 +3,7 @@ import os from 'node:os'; import path from 'node:path'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { execFailureDetails } from '../../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; import { IOS_DEVICECTL_DEFAULT_HINT, resolveIosDevicectlHint, diff --git a/src/platforms/apple/core/physical-device-files.ts b/src/platforms/apple/core/physical-device-files.ts index af9d93bdff..2ccb3c49a9 100644 --- a/src/platforms/apple/core/physical-device-files.ts +++ b/src/platforms/apple/core/physical-device-files.ts @@ -1,6 +1,6 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { Deadline } from '../../../utils/retry.ts'; +import { Deadline } from '@agent-device/host-kit/retry'; import { IOS_RUNNER_SCREENSHOT_COPY_TIMEOUT_MS } from './config.ts'; import { IOS_RUNNER_CONTAINER_BUNDLE_IDS } from '@agent-device/platform-apple/runner'; import { runXcrun } from './tool-provider.ts'; diff --git a/src/platforms/apple/core/physical-device-screenshot.ts b/src/platforms/apple/core/physical-device-screenshot.ts index 7d3aef96d6..44f3d79b04 100644 --- a/src/platforms/apple/core/physical-device-screenshot.ts +++ b/src/platforms/apple/core/physical-device-screenshot.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'node:fs'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { runIosDevicectl } from './devicectl.ts'; import { copyCoreDeviceRunnerFile } from './physical-device-files.ts'; import type { diff --git a/src/platforms/apple/core/runner-host.ts b/src/platforms/apple/core/runner-host.ts index 30e6560e87..4cdc30b32f 100644 --- a/src/platforms/apple/core/runner-host.ts +++ b/src/platforms/apple/core/runner-host.ts @@ -1,13 +1,14 @@ import type { AppleRunnerHost } from '@agent-device/platform-apple/runner'; -import { publishFileSync } from '../../../utils/atomic-file.ts'; -import { resolveIosSimulatorDeviceSetPath } from '../../../utils/device-isolation.ts'; -import { emitDiagnostic, withDiagnosticTimer } from '../../../utils/diagnostics.ts'; +import { publishFileSync, acquireProcessLock } from '@agent-device/host-kit/file'; +import { resolveIosSimulatorDeviceSetPath } from '@agent-device/kernel/device-isolation'; + import { requireExecSuccess, runCmdBackground, runCmdStreaming, runCmdSync, -} from '../../../utils/exec.ts'; +} from '@agent-device/host-kit/command'; +import { emitDiagnostic, withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { isProcessAlive, isProcessGroupAlive, @@ -15,17 +16,23 @@ import { readProcessStartTime, signalPidsBestEffort, signalProcessGroupBestEffort, -} from '../../../utils/host-process.ts'; -import { withKeyedLock } from '../../../utils/keyed-lock.ts'; -import { classifyOwnerLiveness } from '../../../utils/owner-identity.ts'; -import { isRecord } from '../../../utils/parsing.ts'; -import { acquireProcessLock } from '../../../utils/process-lock.ts'; -import { Deadline, isEnvTruthy, retryWithPolicy } from '../../../utils/retry.ts'; -import { parseBooleanLiteral } from '../../../utils/source-value.ts'; -import { createTtlMemo } from '../../../utils/ttl-memo.ts'; -import { findProjectRoot, readVersion } from '../../../utils/version.ts'; -import { getRequestSignal, isRequestCanceled } from '../../../request/cancel.ts'; -import { emitRequestProgress } from '../../../request/progress.ts'; + classifyOwnerLiveness, +} from '@agent-device/host-kit/process'; +import { Deadline, isEnvTruthy, retryWithPolicy } from '@agent-device/host-kit/retry'; + +import { withKeyedLock } from '@agent-device/kernel/keyed-lock'; + +import { findProjectRoot, readVersion } from '@agent-device/host-kit/version'; +import { isRecord } from '@agent-device/kernel/record'; +import { parseBooleanLiteral } from '@agent-device/kernel/source-value'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; + +import { + getRequestSignal, + isRequestCanceled, + emitRequestProgress, +} from '@agent-device/host-kit/request'; + import { bootFailureHint, classifyBootFailure } from '../../boot-diagnostics.ts'; import { resolveIosPhysicalDeviceControl } from './physical-device-control.ts'; import { visitXmlPlistEntries } from './plist-xml.ts'; diff --git a/src/platforms/apple/core/screenshot-status-bar.ts b/src/platforms/apple/core/screenshot-status-bar.ts index 096a0de42f..0ba2eb5a82 100644 --- a/src/platforms/apple/core/screenshot-status-bar.ts +++ b/src/platforms/apple/core/screenshot-status-bar.ts @@ -1,7 +1,8 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; +import { requireExecSuccess, type ExecOptions } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { AppError } from '@agent-device/kernel/errors'; -import { requireExecSuccess, type ExecOptions } from '../../../utils/exec.ts'; + import { runSimctlForDevice } from './simctl.ts'; import { extractAppleToolErrorMeta } from './tool-diagnostics.ts'; diff --git a/src/platforms/apple/core/screenshot.ts b/src/platforms/apple/core/screenshot.ts index e749ebf8f6..5a5845e336 100644 --- a/src/platforms/apple/core/screenshot.ts +++ b/src/platforms/apple/core/screenshot.ts @@ -1,13 +1,14 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { isMacOs, type DeviceInfo } from '@agent-device/kernel/device'; -import { emitDiagnostic } from '../../../utils/diagnostics.ts'; +import { type ExecOptions } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { Deadline, retryWithPolicy } from '@agent-device/host-kit/retry'; import { AppError } from '@agent-device/kernel/errors'; -import type { ExecOptions } from '../../../utils/exec.ts'; -import { resizePngFile } from '../../../utils/png-resize.ts'; -import { readPngSize } from '../../../utils/png-size.ts'; -import { computeDensityScaledScreenshotSize } from '../../../utils/screenshot-density.ts'; -import { Deadline, retryWithPolicy } from '../../../utils/retry.ts'; + +import { resizePngFile } from '@agent-device/capture-kit/png-resize'; +import { readPngSize } from '@agent-device/capture-kit/png-size'; +import { computeDensityScaledScreenshotSize } from '@agent-device/capture-kit/screenshot-density'; import { IOS_RUNNER_SCREENSHOT_COPY_TIMEOUT_MS, diff --git a/src/platforms/apple/core/simctl.ts b/src/platforms/apple/core/simctl.ts index 91c2052f49..3997a6f4f1 100644 --- a/src/platforms/apple/core/simctl.ts +++ b/src/platforms/apple/core/simctl.ts @@ -1,6 +1,6 @@ import { isIosFamily, type DeviceInfo } from '@agent-device/kernel/device'; -import type { ExecOptions, ExecResult } from '../../../utils/exec.ts'; -import { resolveIosSimulatorDeviceSetPath } from '../../../utils/device-isolation.ts'; +import { type ExecOptions, type ExecResult } from '@agent-device/host-kit/command'; +import { resolveIosSimulatorDeviceSetPath } from '@agent-device/kernel/device-isolation'; import { runXcrun } from './tool-provider.ts'; type SimctlArgsOptions = { diff --git a/src/platforms/apple/core/simulator.ts b/src/platforms/apple/core/simulator.ts index 1f5d1170c7..7a98e508ef 100644 --- a/src/platforms/apple/core/simulator.ts +++ b/src/platforms/apple/core/simulator.ts @@ -1,8 +1,9 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { execFailureDetails, requireExecSuccess } from '../../../utils/exec.ts'; -import { Deadline, retryWithPolicy } from '../../../utils/retry.ts'; -import { createTtlMemo } from '../../../utils/ttl-memo.ts'; +import { execFailureDetails, requireExecSuccess } from '@agent-device/host-kit/command'; +import { Deadline, retryWithPolicy } from '@agent-device/host-kit/retry'; + +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; import { bootFailureHint, classifyBootFailure } from '../../boot-diagnostics.ts'; import { @@ -10,7 +11,7 @@ import { IOS_SIMCTL_LIST_TIMEOUT_MS, IOS_SIMULATOR_FOCUS_TIMEOUT_MS, } from './config.ts'; -import { buildSimctlArgs, buildSimctlArgsForDevice } from './simctl.ts'; +import { buildSimctlArgsForDevice } from './simctl.ts'; import { runAppleToolCommand, runXcrun } from './tool-provider.ts'; const IOS_SIMULATOR_HOST_APPS = ['Simulator'] as const; @@ -235,15 +236,8 @@ export async function shutdownSimulator( }; } -export async function getSimulatorState( - deviceOrUdid: DeviceInfo | string, - signal?: AbortSignal, -): Promise { - const udid = typeof deviceOrUdid === 'string' ? deviceOrUdid : deviceOrUdid.id; - const simctlArgs = - typeof deviceOrUdid === 'string' - ? buildSimctlArgs(['list', 'devices', '-j']) - : buildSimctlArgsForDevice(deviceOrUdid, ['list', 'devices', '-j']); +async function getSimulatorState(device: DeviceInfo, signal?: AbortSignal): Promise { + const simctlArgs = buildSimctlArgsForDevice(device, ['list', 'devices', '-j']); const result = await runXcrun(simctlArgs, { allowFailure: true, signal, @@ -257,7 +251,7 @@ export async function getSimulatorState( }; for (const runtime of Object.values(payload.devices ?? {})) { - const match = runtime.find((entry) => entry.udid === udid); + const match = runtime.find((entry) => entry.udid === device.id); if (match) return match.state; } return null; diff --git a/src/platforms/apple/core/tool-provider-types.ts b/src/platforms/apple/core/tool-provider-types.ts index 7c4b499ddb..51892efa5e 100644 --- a/src/platforms/apple/core/tool-provider-types.ts +++ b/src/platforms/apple/core/tool-provider-types.ts @@ -1,5 +1,5 @@ import type { AppsFilter } from '@agent-device/contracts/device'; -import type { ExecOptions, ExecResult } from '../../../utils/exec.ts'; +import { type ExecOptions, type ExecResult } from '@agent-device/host-kit/command'; import type { IosAppInfo } from './app-info.ts'; export type AppleToolCommandExecutor = ( diff --git a/src/platforms/apple/core/tool-provider.ts b/src/platforms/apple/core/tool-provider.ts index e98bbc8315..0f6f0bd7cc 100644 --- a/src/platforms/apple/core/tool-provider.ts +++ b/src/platforms/apple/core/tool-provider.ts @@ -4,8 +4,8 @@ import { whichCmd, type ExecOptions, type ExecResult, -} from '../../../utils/exec.ts'; -import { createScopedProvider } from '../../../utils/scoped-provider.ts'; +} from '@agent-device/host-kit/command'; +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; import { createLocalAppleMacOsHostProvider } from '../os/macos/host-provider.ts'; import type { AppleMacOsHelperProvider, diff --git a/src/platforms/apple/interactor.ts b/src/platforms/apple/interactor.ts index f47d7d5ee2..5e19b22ae9 100644 --- a/src/platforms/apple/interactor.ts +++ b/src/platforms/apple/interactor.ts @@ -23,10 +23,10 @@ import { toAppleTvRemoteButton } from '@agent-device/contracts/tv-remote'; import type { SessionSurface } from '@agent-device/contracts/session'; import { DEVICE_ROTATIONS, type DeviceRotation } from '@agent-device/contracts/device'; import { normalizeSnapshotScope } from '@agent-device/contracts/snapshot'; -import { withDiagnosticTimer } from '../../utils/diagnostics.ts'; +import { withDiagnosticTimer } from '@agent-device/host-kit/diagnostics'; import { isMacOs, isTvOsDevice, type DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { withMethodScope } from '../../utils/method-scope.ts'; +import { withMethodScope } from '@agent-device/kernel/scoped-provider'; import type { Point, RawSnapshotNode, SnapshotQualityVerdict } from '@agent-device/kernel/snapshot'; import type { Interactor, @@ -35,7 +35,7 @@ import type { ScreenshotOptions, SnapshotOptions, } from '@agent-device/contracts/interactor-types'; -import { readSnapshotQualityVerdict } from '../../snapshot-quality/verdict.ts'; +import { readSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { captureMacOsSurfaceSnapshot } from '../../snapshot/snapshot-desktop-surface.ts'; export function createAppleInteractor( diff --git a/src/platforms/apple/os/macos/helper.ts b/src/platforms/apple/os/macos/helper.ts index 15f1c7ea18..f1143c8450 100644 --- a/src/platforms/apple/os/macos/helper.ts +++ b/src/platforms/apple/os/macos/helper.ts @@ -8,7 +8,7 @@ import { resolveExecutableOverridePath, runCmdBackground, type ExecBackgroundResult, -} from '../../../../utils/exec.ts'; +} from '@agent-device/host-kit/command'; import type { SessionSurface } from '@agent-device/contracts/session'; import { hasScopedAppleToolProvider, diff --git a/src/platforms/apple/os/macos/host-provider.ts b/src/platforms/apple/os/macos/host-provider.ts index 7c7e1f9aef..dce442825c 100644 --- a/src/platforms/apple/os/macos/host-provider.ts +++ b/src/platforms/apple/os/macos/host-provider.ts @@ -3,7 +3,7 @@ import os from 'node:os'; import path from 'node:path'; import type { AppsFilter } from '@agent-device/contracts/device'; import { AppError } from '@agent-device/kernel/errors'; -import { requireExecSuccess } from '../../../../utils/exec.ts'; +import { requireExecSuccess } from '@agent-device/host-kit/command'; import { filterAppleAppsByBundlePrefix } from '../../core/app-filter.ts'; import type { IosAppInfo } from '../../core/app-info.ts'; import type { diff --git a/src/platforms/harmonyos/__tests__/app-lifecycle.test.ts b/src/platforms/harmonyos/__tests__/app-lifecycle.test.ts index 3db05e85e0..698d7bb803 100644 --- a/src/platforms/harmonyos/__tests__/app-lifecycle.test.ts +++ b/src/platforms/harmonyos/__tests__/app-lifecycle.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert/strict'; import { AppError } from '@agent-device/kernel/errors'; import { afterEach, beforeEach, test, vi } from 'vitest'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); @@ -25,7 +25,7 @@ import { parseHarmonyLaunchTarget, resolveHarmonyArchiveBundleName, } from '../app-lifecycle.ts'; -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/harmonyos/__tests__/doctor.test.ts b/src/platforms/harmonyos/__tests__/doctor.test.ts index becc9124bf..132c5aa575 100644 --- a/src/platforms/harmonyos/__tests__/doctor.test.ts +++ b/src/platforms/harmonyos/__tests__/doctor.test.ts @@ -2,12 +2,12 @@ import assert from 'node:assert/strict'; import { beforeEach, test, vi } from 'vitest'; import type { DoctorCheck } from '@agent-device/contracts/observability'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { harmonyToolchainCheck } from '../doctor.ts'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/harmonyos/__tests__/hdc.test.ts b/src/platforms/harmonyos/__tests__/hdc.test.ts index a10b81b05b..a180f24a1d 100644 --- a/src/platforms/harmonyos/__tests__/hdc.test.ts +++ b/src/platforms/harmonyos/__tests__/hdc.test.ts @@ -1,12 +1,12 @@ import assert from 'node:assert/strict'; import { beforeEach, test, vi } from 'vitest'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn() }; }); -import { runCmd } from '../../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { DEFAULT_HARMONY_HDC_TIMEOUT_MS, runHarmonyHdc } from '../hdc.ts'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/harmonyos/__tests__/input-actions.test.ts b/src/platforms/harmonyos/__tests__/input-actions.test.ts index e1210b4435..df1b1b4182 100644 --- a/src/platforms/harmonyos/__tests__/input-actions.test.ts +++ b/src/platforms/harmonyos/__tests__/input-actions.test.ts @@ -12,7 +12,7 @@ const { runHarmonyHdc, sleep, readHarmonyGestureViewport, invalidateHarmonyGestu })); vi.mock('../hdc.ts', () => ({ runHarmonyHdc })); -vi.mock('../../../utils/timeouts.ts', () => ({ sleep })); +vi.mock('@agent-device/host-kit/retry', () => ({ sleep })); vi.mock('../snapshot.ts', () => ({ readHarmonyGestureViewport, invalidateHarmonyGestureViewport, diff --git a/src/platforms/harmonyos/app-lifecycle.ts b/src/platforms/harmonyos/app-lifecycle.ts index 41e07f741c..4727d4b27a 100644 --- a/src/platforms/harmonyos/app-lifecycle.ts +++ b/src/platforms/harmonyos/app-lifecycle.ts @@ -1,6 +1,6 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { runHarmonyHdc } from './hdc.ts'; import { invalidateHarmonyGestureViewport } from './snapshot.ts'; diff --git a/src/platforms/harmonyos/hdc.ts b/src/platforms/harmonyos/hdc.ts index 4afd5c451a..de62b2c521 100644 --- a/src/platforms/harmonyos/hdc.ts +++ b/src/platforms/harmonyos/hdc.ts @@ -1,7 +1,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { runCmd, type ExecOptions, type ExecResult } from '../../utils/exec.ts'; +import { runCmd, type ExecOptions, type ExecResult } from '@agent-device/host-kit/command'; export type HarmonyHdcOptions = Pick< ExecOptions, diff --git a/src/platforms/harmonyos/input-actions.ts b/src/platforms/harmonyos/input-actions.ts index abe9799b5c..d811800dae 100644 --- a/src/platforms/harmonyos/input-actions.ts +++ b/src/platforms/harmonyos/input-actions.ts @@ -7,7 +7,7 @@ import { } from '@agent-device/contracts/scroll-gesture'; import type { DeviceInfo } from '@agent-device/kernel/device'; import { AppError } from '@agent-device/kernel/errors'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import { runHarmonyHdc } from './hdc.ts'; import { invalidateHarmonyGestureViewport, readHarmonyGestureViewport } from './snapshot.ts'; diff --git a/src/platforms/harmonyos/snapshot.ts b/src/platforms/harmonyos/snapshot.ts index 50ba5ccd62..514ce8a7a8 100644 --- a/src/platforms/harmonyos/snapshot.ts +++ b/src/platforms/harmonyos/snapshot.ts @@ -6,7 +6,7 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; import type { RawSnapshotNode, Rect } from '@agent-device/kernel/snapshot'; import { AppError } from '@agent-device/kernel/errors'; import type { SnapshotOptions } from '@agent-device/contracts/interactor-types'; -import { createTtlMemo } from '../../utils/ttl-memo.ts'; +import { createTtlMemo } from '@agent-device/kernel/ttl-memo'; import { runHarmonyHdc } from './hdc.ts'; const MAX_NODES = 5_000; diff --git a/src/platforms/install-artifact-archive-context.ts b/src/platforms/install-artifact-archive-context.ts index 23cb74c873..f254846737 100644 --- a/src/platforms/install-artifact-archive-context.ts +++ b/src/platforms/install-artifact-archive-context.ts @@ -1,5 +1,5 @@ import { AsyncLocalStorage } from 'node:async_hooks'; -import { ArchiveBudget } from '../utils/archive-safety.ts'; +import { ArchiveBudget } from '@agent-device/host-kit/archive'; type ArchiveState = { budget: ArchiveBudget; depth: number }; const ARCHIVE_STATE = new AsyncLocalStorage(); diff --git a/src/platforms/install-source-archive.ts b/src/platforms/install-source-archive.ts index 6a093d73d5..7449c3d480 100644 --- a/src/platforms/install-source-archive.ts +++ b/src/platforms/install-source-archive.ts @@ -2,8 +2,11 @@ import { promises as fs } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { extractArchiveSafely, archiveTypeFromPath } from '../utils/archive-extraction.ts'; -import { ArchiveBudget } from '../utils/archive-safety.ts'; +import { + extractArchiveSafely, + archiveTypeFromPath, + ArchiveBudget, +} from '@agent-device/host-kit/archive'; const MAX_INSTALL_SOURCE_SEARCH_DEPTH = 5; diff --git a/src/platforms/install-source-download.ts b/src/platforms/install-source-download.ts index bf2514370f..25277c9ad5 100644 --- a/src/platforms/install-source-download.ts +++ b/src/platforms/install-source-download.ts @@ -2,8 +2,11 @@ import { createWriteStream, promises as fs } from 'node:fs'; import path from 'node:path'; import { pipeline } from 'node:stream/promises'; import { AppError } from '@agent-device/kernel/errors'; -import { MAX_ARTIFACT_COMPRESSED_BYTES } from '../utils/artifact-limits.ts'; -import { createByteLimitStream } from '../utils/byte-limit-stream.ts'; +import { + MAX_ARTIFACT_COMPRESSED_BYTES, + createByteLimitStream, +} from '@agent-device/host-kit/archive'; + import { approveDownloadSourceUrl } from './install-source-network.ts'; import * as networkTransport from './install-source-network-transport.ts'; diff --git a/src/platforms/install-source.ts b/src/platforms/install-source.ts index 53fd4c930e..e73c4aafdb 100644 --- a/src/platforms/install-source.ts +++ b/src/platforms/install-source.ts @@ -3,8 +3,9 @@ import os from 'node:os'; import path from 'node:path'; import type { LocalInstallSource } from '@agent-device/kernel/contracts'; import { AppError, createRequestCanceledError } from '@agent-device/kernel/errors'; -import { expandUserHomePath } from '../utils/path-resolution.ts'; -import { ArchiveBudget } from '../utils/archive-safety.ts'; +import { ArchiveBudget } from '@agent-device/host-kit/archive'; +import { expandUserHomePath } from '@agent-device/host-kit/file'; + import { resolveInstallableCandidate } from './install-source-archive.ts'; import { installArtifactArchiveBudget, diff --git a/src/platforms/linux/__tests__/atspi-bridge.test.ts b/src/platforms/linux/__tests__/atspi-bridge.test.ts index c993ad3785..49745ccd3a 100644 --- a/src/platforms/linux/__tests__/atspi-bridge.test.ts +++ b/src/platforms/linux/__tests__/atspi-bridge.test.ts @@ -1,13 +1,13 @@ import { afterAll, beforeEach, test, vi } from 'vitest'; import assert from 'node:assert/strict'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(), whichCmd: vi.fn() }; }); import { captureAccessibilityTree } from '../atspi-bridge.ts'; -import { runCmd, whichCmd } from '../../../utils/exec.ts'; +import { runCmd, whichCmd } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/platforms/linux/__tests__/clipboard.test.ts b/src/platforms/linux/__tests__/clipboard.test.ts index 2712c1c9a4..3d41659b87 100644 --- a/src/platforms/linux/__tests__/clipboard.test.ts +++ b/src/platforms/linux/__tests__/clipboard.test.ts @@ -1,12 +1,12 @@ import { afterAll, beforeEach, test, vi } from 'vitest'; import assert from 'node:assert/strict'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(), whichCmd: vi.fn() }; }); -import { runCmd, whichCmd } from '../../../utils/exec.ts'; +import { runCmd, whichCmd } from '@agent-device/host-kit/command'; import { readLinuxClipboard, writeLinuxClipboard } from '../clipboard.ts'; import { AppError } from '@agent-device/kernel/errors'; diff --git a/src/platforms/linux/__tests__/input-actions.test.ts b/src/platforms/linux/__tests__/input-actions.test.ts index e1732d1c15..43c53c9dc8 100644 --- a/src/platforms/linux/__tests__/input-actions.test.ts +++ b/src/platforms/linux/__tests__/input-actions.test.ts @@ -1,12 +1,12 @@ import { afterAll, beforeEach, test, vi } from 'vitest'; import assert from 'node:assert/strict'; -vi.mock('../../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, runCmd: vi.fn(), whichCmd: vi.fn() }; }); -import { runCmd, whichCmd } from '../../../utils/exec.ts'; +import { runCmd, whichCmd } from '@agent-device/host-kit/command'; import { resetInputToolCache } from '../linux-env.ts'; import { fillLinux, pressLinux, scrollLinux, typeLinux, sendKey } from '../input-actions.ts'; diff --git a/src/platforms/linux/input-actions.ts b/src/platforms/linux/input-actions.ts index 114a9bee21..1fb14402d4 100644 --- a/src/platforms/linux/input-actions.ts +++ b/src/platforms/linux/input-actions.ts @@ -1,6 +1,6 @@ import { ensureInputTool } from './linux-env.ts'; import { resolveLinuxToolProvider, type LinuxPointerButton } from './tool-provider.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import type { ScrollDirection } from '@agent-device/contracts/scroll-gesture'; // ── Low-level wrappers ───────────────────────────────────────────────── diff --git a/src/platforms/linux/snapshot.ts b/src/platforms/linux/snapshot.ts index 91ea2ff8c9..80b7bfe39f 100644 --- a/src/platforms/linux/snapshot.ts +++ b/src/platforms/linux/snapshot.ts @@ -1,7 +1,7 @@ import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { captureAccessibilityTree, type SnapshotSurface } from './atspi-bridge.ts'; import type { SessionSurface } from '@agent-device/contracts/session'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; /** * Map the session-level surface to an AT-SPI2 surface. diff --git a/src/platforms/linux/tool-provider.ts b/src/platforms/linux/tool-provider.ts index 8cc9bc4d45..540e30088b 100644 --- a/src/platforms/linux/tool-provider.ts +++ b/src/platforms/linux/tool-provider.ts @@ -1,10 +1,17 @@ import type { ClickButton } from '@agent-device/contracts/click-button'; import type { ScrollDirection } from '@agent-device/contracts/scroll-gesture'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import { runCmd, whichCmd, type ExecOptions, type ExecResult } from '../../utils/exec.ts'; -import { createScopedProvider } from '../../utils/scoped-provider.ts'; -import { sleep } from '../../utils/timeouts.ts'; +import { + runCmd, + whichCmd, + type ExecOptions, + type ExecResult, +} from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { sleep } from '@agent-device/host-kit/retry'; + +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; + import type { LinuxAccessibilityTree, LinuxSnapshotSurface, diff --git a/src/platforms/toolchain-probe.ts b/src/platforms/toolchain-probe.ts index 4bbdfe8b5a..8359d7173a 100644 --- a/src/platforms/toolchain-probe.ts +++ b/src/platforms/toolchain-probe.ts @@ -1,4 +1,4 @@ -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; export const TOOLCHAIN_TIMEOUT_MS = 3_000; diff --git a/src/platforms/vega/app-lifecycle.ts b/src/platforms/vega/app-lifecycle.ts index 67d734bff3..1b8595260d 100644 --- a/src/platforms/vega/app-lifecycle.ts +++ b/src/platforms/vega/app-lifecycle.ts @@ -1,5 +1,5 @@ import type { DeviceInfo } from '@agent-device/kernel/device'; -import { requireExecSuccess, type ExecOptions } from '../../utils/exec.ts'; +import { requireExecSuccess, type ExecOptions } from '@agent-device/host-kit/command'; import { resolveVegaToolProvider } from './tool-provider.ts'; const VEGA_APP_COMMAND_TIMEOUT_MS = 30_000; diff --git a/src/platforms/vega/input-actions.ts b/src/platforms/vega/input-actions.ts index 92937f2829..dcae051927 100644 --- a/src/platforms/vega/input-actions.ts +++ b/src/platforms/vega/input-actions.ts @@ -1,6 +1,6 @@ import { type TvRemoteButton, toVegaTvRemoteKey } from '@agent-device/contracts/tv-remote'; import type { DeviceInfo } from '@agent-device/kernel/device'; -import { requireExecSuccess } from '../../utils/exec.ts'; +import { requireExecSuccess } from '@agent-device/host-kit/command'; import { resolveVegaToolProvider } from './tool-provider.ts'; const VEGA_INPUT_TRANSPORT_OVERHEAD_MS = 10_000; diff --git a/src/platforms/vega/tool-provider.ts b/src/platforms/vega/tool-provider.ts index 84d26dd720..78b080986f 100644 --- a/src/platforms/vega/tool-provider.ts +++ b/src/platforms/vega/tool-provider.ts @@ -3,8 +3,13 @@ import { access } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import type { VegaTvRemoteKey } from '@agent-device/contracts/tv-remote'; -import { runCmd, whichCmd, type ExecOptions, type ExecResult } from '../../utils/exec.ts'; -import { createScopedProvider } from '../../utils/scoped-provider.ts'; +import { + runCmd, + whichCmd, + type ExecOptions, + type ExecResult, +} from '@agent-device/host-kit/command'; +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; export type VegaToolProvider = { isAvailable(): Promise; diff --git a/src/platforms/web/__tests__/agent-browser-boundary-faults.test.ts b/src/platforms/web/__tests__/agent-browser-boundary-faults.test.ts index 86729d3e66..799c271bc2 100644 --- a/src/platforms/web/__tests__/agent-browser-boundary-faults.test.ts +++ b/src/platforms/web/__tests__/agent-browser-boundary-faults.test.ts @@ -4,7 +4,7 @@ import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; import { mkdtempForTestSync } from '../../../__tests__/test-utils/tmp-dir.ts'; import { createFailNthCommandExecutor } from '../../../__tests__/test-utils/exec-faults.ts'; -import { withCommandExecutorOverride } from '../../../utils/exec.ts'; +import { withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { createAgentBrowserWebProvider } from '../agent-browser-provider.ts'; import { installFakeManagedAgentBrowser } from './test-utils.ts'; diff --git a/src/platforms/web/agent-browser-install.ts b/src/platforms/web/agent-browser-install.ts index 0d41e710bf..29ee09a0da 100644 --- a/src/platforms/web/agent-browser-install.ts +++ b/src/platforms/web/agent-browser-install.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; /** * How the managed backend gets onto disk. Kept apart from the tool module, diff --git a/src/platforms/web/agent-browser-lifecycle.test.ts b/src/platforms/web/agent-browser-lifecycle.test.ts index 762d822b7e..d8652e2f87 100644 --- a/src/platforms/web/agent-browser-lifecycle.test.ts +++ b/src/platforms/web/agent-browser-lifecycle.test.ts @@ -8,21 +8,23 @@ const { runCmdMock } = vi.hoisted(() => ({ runCmdMock: vi.fn(), })); -vi.mock('../../utils/exec.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), + runCmd: runCmdMock, + withoutCommandExecutorOverride: async (fn: () => Promise) => await fn(), +})); + +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, - runCmd: runCmdMock, - withoutCommandExecutorOverride: async (fn: () => Promise) => await fn(), + listHostProcesses: async (options: Parameters[0]) => + await actual.listHostProcesses({ ...options, runCommand: runCmdMock }), + readProcessStartTime: (pid: number) => `start-${pid}`, + readProcessCommand: (pid: number) => `command-${pid}`, }; }); -vi.mock('../../utils/host-process.ts', async (importOriginal) => ({ - ...(await importOriginal()), - readProcessStartTime: (pid: number) => `start-${pid}`, - readProcessCommand: (pid: number) => `command-${pid}`, -})); - import { DEFAULT_AGENT_BROWSER_IDLE_TIMEOUT_MS, agentBrowserChromeLaunchMarker, diff --git a/src/platforms/web/agent-browser-lifecycle.ts b/src/platforms/web/agent-browser-lifecycle.ts index b75fdb336d..60aa08cca5 100644 --- a/src/platforms/web/agent-browser-lifecycle.ts +++ b/src/platforms/web/agent-browser-lifecycle.ts @@ -1,9 +1,12 @@ import fs from 'node:fs'; import path from 'node:path'; import { uniqueStrings } from '@agent-device/kernel/collections'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import type { OwnedProcessRecordStore } from '../../utils/owned-process-record.ts'; -import { reapOwnedProcessRecordsAtStartup } from '../../utils/owned-process-reaper.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { + type OwnedProcessRecordStore, + reapOwnedProcessRecordsAtStartup, +} from '@agent-device/host-kit/process'; + import type { AgentBrowserToolStatus } from './agent-browser-tool.ts'; import type { AgentBrowserProcessSummary } from './agent-browser-process-record.ts'; export { diff --git a/src/platforms/web/agent-browser-network.ts b/src/platforms/web/agent-browser-network.ts index 4ae37c7564..7991c3d0a9 100644 --- a/src/platforms/web/agent-browser-network.ts +++ b/src/platforms/web/agent-browser-network.ts @@ -3,7 +3,7 @@ import type { BackendDumpNetworkResult, BackendNetworkEntry, } from '../../backend.ts'; -import { stripUndefined } from '../../utils/parsing.ts'; +import { stripUndefined } from '@agent-device/kernel/record'; import { isJsonObject, readNumberProperty, readStringProperty } from './json-utils.ts'; type AgentBrowserNetworkRequest = { diff --git a/src/platforms/web/agent-browser-process-record.ts b/src/platforms/web/agent-browser-process-record.ts index f60b33ab40..448ec570bf 100644 --- a/src/platforms/web/agent-browser-process-record.ts +++ b/src/platforms/web/agent-browser-process-record.ts @@ -2,15 +2,16 @@ import crypto from 'node:crypto'; import path from 'node:path'; import type { OwnedProcessRecord } from '@agent-device/contracts/platform-runtime-host'; import { uniqueStrings } from '@agent-device/kernel/collections'; -import { withoutCommandExecutorOverride } from '../../utils/exec.ts'; +import { withoutCommandExecutorOverride } from '@agent-device/host-kit/command'; import { expandProcessTree, listHostProcesses, readProcessCommand, readProcessStartTime, type HostProcessInfo, -} from '../../utils/host-process.ts'; -import type { OwnedProcessRecordStore } from '../../utils/owned-process-record.ts'; + type OwnedProcessRecordStore, +} from '@agent-device/host-kit/process'; + import type { AgentBrowserToolStatus } from './agent-browser-tool.ts'; const HOST_PROCESS_LIST_TIMEOUT_MS = 1_500; diff --git a/src/platforms/web/agent-browser-provider.test.ts b/src/platforms/web/agent-browser-provider.test.ts index cc490cd9be..b0bc4275ae 100644 --- a/src/platforms/web/agent-browser-provider.test.ts +++ b/src/platforms/web/agent-browser-provider.test.ts @@ -17,12 +17,12 @@ vi.mock('./agent-browser-lifecycle.ts', async (importOriginal) => { import { createAgentBrowserWebProvider } from './agent-browser-provider.ts'; import type { WebSnapshotResult } from './provider.ts'; -import { withCommandExecutorOverride, type ExecResult } from '../../utils/exec.ts'; +import { withCommandExecutorOverride, type ExecResult } from '@agent-device/host-kit/command'; +import { type OwnedProcessRecordStore } from '@agent-device/host-kit/process'; import { AppError } from '@agent-device/kernel/errors'; import { buildSelectorChainForNode, resolveRecordedTarget } from '@agent-device/selectors'; import { attachRefs } from '@agent-device/kernel/snapshot'; import { installFakeManagedAgentBrowser, withNodeRuntime } from './__tests__/test-utils.ts'; -import type { OwnedProcessRecordStore } from '../../utils/owned-process-record.ts'; type AgentBrowserCall = { cmd: string; diff --git a/src/platforms/web/agent-browser-provider.ts b/src/platforms/web/agent-browser-provider.ts index d62bc44ce2..dd0359cd5d 100644 --- a/src/platforms/web/agent-browser-provider.ts +++ b/src/platforms/web/agent-browser-provider.ts @@ -1,7 +1,9 @@ -import { execFailureDetails } from '../../utils/exec.ts'; +import { execFailureDetails } from '@agent-device/host-kit/command'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; +import { type OwnedProcessRecordStore } from '@agent-device/host-kit/process'; +import { sleep } from '@agent-device/host-kit/retry'; import { AppError } from '@agent-device/kernel/errors'; -import { emitDiagnostic } from '../../utils/diagnostics.ts'; -import { sleep } from '../../utils/timeouts.ts'; + import type { Rect } from '@agent-device/kernel/snapshot'; import { buildAudioProbeEvalScript, @@ -25,7 +27,6 @@ import { cleanupManagedAgentBrowserOrphansForProviderStartup, recordManagedAgentBrowserProcesses, } from './agent-browser-lifecycle.ts'; -import type { OwnedProcessRecordStore } from '../../utils/owned-process-record.ts'; const AGENT_BROWSER = 'agent-browser'; // Exported so daemon shutdown can pin its web-close budget to the ceiling enforced for one diff --git a/src/platforms/web/agent-browser-tool.test.ts b/src/platforms/web/agent-browser-tool.test.ts index 0e1b88e3de..8dcf8d5c21 100644 --- a/src/platforms/web/agent-browser-tool.test.ts +++ b/src/platforms/web/agent-browser-tool.test.ts @@ -14,7 +14,7 @@ import { writeFakeNpmCliScript, } from './__tests__/test-utils.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { withCommandExecutorOverride } from '../../utils/exec.ts'; +import { withCommandExecutorOverride } from '@agent-device/host-kit/command'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; type SpawnedCommand = { cmd: string; args: string[] }; diff --git a/src/platforms/web/agent-browser-tool.ts b/src/platforms/web/agent-browser-tool.ts index f11d1ede97..a3abd74b0d 100644 --- a/src/platforms/web/agent-browser-tool.ts +++ b/src/platforms/web/agent-browser-tool.ts @@ -2,15 +2,16 @@ import fs from 'node:fs'; import crypto from 'node:crypto'; import os from 'node:os'; import path from 'node:path'; -import { runCmd, type ExecResult } from '../../utils/exec.ts'; +import { runCmd, type ExecResult } from '@agent-device/host-kit/command'; +import { readProcessStartTime } from '@agent-device/host-kit/process'; import { AppError, asAppError } from '@agent-device/kernel/errors'; import type { ManagedWebBackendStatus } from '@agent-device/contracts/managed-web-backend'; import { installManagedAgentBrowserPackage, writeManagedAgentBrowserManifest, } from './agent-browser-install.ts'; -import { acquireProcessLock } from '../../utils/process-lock.ts'; -import { readProcessStartTime } from '../../utils/host-process.ts'; +import { acquireProcessLock } from '@agent-device/host-kit/file'; + import { appendAgentDeviceChromeArgs, resolveAgentBrowserIdleTimeoutMs, diff --git a/src/platforms/web/provider.ts b/src/platforms/web/provider.ts index 081382180e..43a1d19f6e 100644 --- a/src/platforms/web/provider.ts +++ b/src/platforms/web/provider.ts @@ -1,6 +1,6 @@ import type { ScrollDirection } from '@agent-device/contracts/scroll-gesture'; import type { SessionSurface } from '@agent-device/contracts/session'; -import { createScopedProvider } from '../../utils/scoped-provider.ts'; +import { createScopedProvider } from '@agent-device/kernel/scoped-provider'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import type { BackendDumpNetworkOptions, BackendDumpNetworkResult } from '../../backend.ts'; import type { AudioProbeResult } from '@agent-device/contracts/audio-probe-result'; diff --git a/src/provider-webdriver.ts b/src/provider-webdriver.ts index dcbe0e5808..389292a1b0 100644 --- a/src/provider-webdriver.ts +++ b/src/provider-webdriver.ts @@ -1,6 +1,6 @@ import { createProviderWebDriver } from '@agent-device/provider-webdriver'; -import { runCmd } from './utils/exec.ts'; -import { readVersion } from './utils/version.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { readVersion } from '@agent-device/host-kit/version'; export const providerWebDriver = createProviderWebDriver({ clientVersion: readVersion(), diff --git a/src/recording/__tests__/overlay.test.ts b/src/recording/__tests__/overlay.test.ts index 6f86d3b7a0..f2034040f6 100644 --- a/src/recording/__tests__/overlay.test.ts +++ b/src/recording/__tests__/overlay.test.ts @@ -3,10 +3,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../utils/exec.ts', async () => { +vi.mock('@agent-device/host-kit/command', async (importOriginal) => { const fs = await import('node:fs'); const path = await import('node:path'); return { + ...(await importOriginal()), runCmd: vi.fn(async (cmd: string, args: string[]) => { if (cmd === 'xcrun') { const outputPath = args[args.indexOf('-o') + 1]!; @@ -29,7 +30,7 @@ vi.mock('../../utils/video.ts', () => ({ import { buildRecordingScriptPathCandidates, overlayRecordingTouches } from '../overlay.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; function helperScriptArgs(): string[] { const helperCall = mockRunCmd.mock.calls.find(([cmd]) => cmd !== 'xcrun'); diff --git a/src/recording/__tests__/recording-scripts.test.ts b/src/recording/__tests__/recording-scripts.test.ts index d8650d60df..5f230feff1 100644 --- a/src/recording/__tests__/recording-scripts.test.ts +++ b/src/recording/__tests__/recording-scripts.test.ts @@ -2,7 +2,7 @@ import { beforeAll, test } from 'vitest'; import assert from 'node:assert/strict'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { runCmd } from '../../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { getRecordingOverlaySupportWarning } from '../overlay.ts'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); diff --git a/src/recording/output-path.ts b/src/recording/output-path.ts index 81813d5f65..d8c5964f69 100644 --- a/src/recording/output-path.ts +++ b/src/recording/output-path.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import type { Platform, PlatformSelector } from '@agent-device/kernel/device'; -import { resolveUserPath } from '../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; const DEFAULT_RECORDING_EXTENSION = '.mp4'; const WEB_RECORDING_EXTENSION = '.webm'; diff --git a/src/recording/overlay.ts b/src/recording/overlay.ts index 40e0acc373..3bb0983f12 100644 --- a/src/recording/overlay.ts +++ b/src/recording/overlay.ts @@ -1,10 +1,10 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { AppError } from '@agent-device/kernel/errors'; import { buildSwiftToolEnv, compileSwiftSourceFile } from '../utils/swift-cache.ts'; -import { findProjectRoot } from '../utils/version.ts'; +import { findProjectRoot } from '@agent-device/host-kit/version'; import { waitForPlayableVideo, waitForStableFile } from '../utils/video.ts'; import { DEFAULT_RECORDING_EXPORT_QUALITY, diff --git a/src/remote/remote-config-core.ts b/src/remote/remote-config-core.ts index 1a768065ad..063e8527eb 100644 --- a/src/remote/remote-config-core.ts +++ b/src/remote/remote-config-core.ts @@ -9,8 +9,8 @@ import { type ResolvedRemoteConfigProfile, } from './remote-config-schema.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { resolveUserPath } from '../utils/path-resolution.ts'; -import { parseSourceValue } from '../utils/source-value.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; +import { parseSourceValue } from '@agent-device/kernel/source-value'; // Deliberately narrower than `resolveRemoteConfigProfile`: this reads only // what the config *file itself* declares, with no ambient environment diff --git a/src/remote/remote-config-schema.ts b/src/remote/remote-config-schema.ts index b2cd81ac16..6c33f206dd 100644 --- a/src/remote/remote-config-schema.ts +++ b/src/remote/remote-config-schema.ts @@ -7,7 +7,7 @@ import { // Declared in contracts/ so zones below remote/ can be stated in terms of the field vocabulary; // re-exported here because this module is where consumers already import it from. export type { RemoteConnectionProfileFields } from '@agent-device/contracts/remote'; -import { buildPrimaryEnvVarName } from '../utils/source-value.ts'; +import { buildPrimaryEnvVarName } from '@agent-device/kernel/source-value'; import { PLATFORM_SELECTORS, type DeviceTarget, diff --git a/src/remote/remote-connection-state.ts b/src/remote/remote-connection-state.ts index deb877db96..f24d6f17de 100644 --- a/src/remote/remote-connection-state.ts +++ b/src/remote/remote-connection-state.ts @@ -3,8 +3,8 @@ import fs from 'node:fs'; import path from 'node:path'; import { resolveRemoteConfigPath, resolveRemoteConfigProfile } from './remote-config-core.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { publishFileSync } from '../utils/atomic-file.ts'; -import { emitDiagnostic } from '../utils/diagnostics.ts'; +import { publishFileSync } from '@agent-device/host-kit/file'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import type { CliFlags } from '@agent-device/contracts/command'; import type { LeaseBackend, SessionRuntimeHints } from '@agent-device/kernel/contracts'; import { diff --git a/src/remote/upload-client-artifact.ts b/src/remote/upload-client-artifact.ts index c0d6dfd102..f0d241ead8 100644 --- a/src/remote/upload-client-artifact.ts +++ b/src/remote/upload-client-artifact.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { Writable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from '../utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; const ARTIFACT_HASH_ALGORITHM = 'sha256'; const DEFAULT_CONTENT_TYPE = 'application/octet-stream'; diff --git a/src/replay/script-source-bundle.ts b/src/replay/script-source-bundle.ts index 07ba00bfe3..ee42516ba1 100644 --- a/src/replay/script-source-bundle.ts +++ b/src/replay/script-source-bundle.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import { AppError } from '@agent-device/kernel/errors'; import type { ReplayScriptSourceBundle } from '@agent-device/contracts/replay'; -import { resolveUserPath } from '../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; import { resolveReplayFormat } from './format.ts'; /** diff --git a/src/replay/source-discovery.ts b/src/replay/source-discovery.ts index fbcafd673f..2fdbd56d0b 100644 --- a/src/replay/source-discovery.ts +++ b/src/replay/source-discovery.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; -import { resolveUserPath } from '../utils/path-resolution.ts'; +import { resolveUserPath } from '@agent-device/host-kit/file'; import { isMaestroYamlPath, maestroBackendRequiredMessage } from './format.ts'; const GLOB_PATTERN_CHARS = /[*?[\]{}]/; diff --git a/src/screenshot-diff/__tests__/screenshot-diff.test.ts b/src/screenshot-diff/__tests__/screenshot-diff.test.ts index b90899da95..f5fbb90c9f 100644 --- a/src/screenshot-diff/__tests__/screenshot-diff.test.ts +++ b/src/screenshot-diff/__tests__/screenshot-diff.test.ts @@ -4,11 +4,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../../utils/png-worker-client.ts', async () => { +vi.mock('@agent-device/capture-kit/png-worker-client', async () => { const [{ PNG }, { decodePng }, { computeScreenshotDiffPixels }] = await Promise.all([ - import('../../utils/png.ts'), - import('../../utils/png.ts'), - import('../../utils/screenshot-diff-pixels.ts'), + import('@agent-device/capture-kit/png'), + import('@agent-device/capture-kit/png'), + import('@agent-device/capture-kit/screenshot-diff-pixels'), ]); return { decodePngAsync: async (buffer: Buffer, label: string) => decodePng(buffer, label), @@ -19,7 +19,7 @@ vi.mock('../../utils/png-worker-client.ts', async () => { }; }); -import { PNG } from '../../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { compareScreenshots } from '../screenshot-diff.ts'; function tmpDir(): string { diff --git a/src/screenshot-diff/screenshot-diff-region-overlay.ts b/src/screenshot-diff/screenshot-diff-region-overlay.ts index 22b74fcb8f..66d7c0cafa 100644 --- a/src/screenshot-diff/screenshot-diff-region-overlay.ts +++ b/src/screenshot-diff/screenshot-diff-region-overlay.ts @@ -1,4 +1,4 @@ -import type { PNG } from '../utils/png.ts'; +import type { PNG } from '@agent-device/capture-kit/png'; import type { ScreenshotDiffRegion } from './screenshot-diff-regions.ts'; const REGION_BORDER_COLOR = [0, 187, 255, 255] as const; diff --git a/src/screenshot-diff/screenshot-diff-region-split.ts b/src/screenshot-diff/screenshot-diff-region-split.ts index bb537b37de..ace6cbbd97 100644 --- a/src/screenshot-diff/screenshot-diff-region-split.ts +++ b/src/screenshot-diff/screenshot-diff-region-split.ts @@ -1,4 +1,4 @@ -import type { PNG } from '../utils/png.ts'; +import type { PNG } from '@agent-device/capture-kit/png'; import type { MutableDiffRegion } from './screenshot-diff-region-types.ts'; // Region splitting is based on screen-relative heights so it works on phone, diff --git a/src/screenshot-diff/screenshot-diff-regions.ts b/src/screenshot-diff/screenshot-diff-regions.ts index ee464b9465..39d5f87be8 100644 --- a/src/screenshot-diff/screenshot-diff-regions.ts +++ b/src/screenshot-diff/screenshot-diff-regions.ts @@ -1,5 +1,5 @@ import type { Rect } from '@agent-device/kernel/snapshot'; -import type { PNG } from '../utils/png.ts'; +import type { PNG } from '@agent-device/capture-kit/png'; import { normalizedRect, type NormalizedRect } from '../utils/screenshot-geometry.ts'; import { findConnectedMaskComponents } from './screenshot-diff-components.ts'; import { splitLargeDiffRegions } from './screenshot-diff-region-split.ts'; diff --git a/src/screenshot-diff/screenshot-diff.ts b/src/screenshot-diff/screenshot-diff.ts index 269389cf96..2aaf0b02cc 100644 --- a/src/screenshot-diff/screenshot-diff.ts +++ b/src/screenshot-diff/screenshot-diff.ts @@ -2,12 +2,12 @@ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { AppError } from '@agent-device/kernel/errors'; import type { Rect } from '@agent-device/kernel/snapshot'; -import { PNG } from '../utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import { computeScreenshotDiffPixelsAsync, decodePngAsync, encodePngAsync, -} from '../utils/png-worker-client.ts'; +} from '@agent-device/capture-kit/png-worker-client'; import { annotateDiffRegions } from './screenshot-diff-region-overlay.ts'; import { summarizeDiffRegions, type ScreenshotDiffRegion } from './screenshot-diff-regions.ts'; import type { ImageDimensions } from '../utils/screenshot-geometry.ts'; diff --git a/src/sdk/limrun-runtime-dependencies.ts b/src/sdk/limrun-runtime-dependencies.ts index ca9ab2c1b0..45009300b7 100644 --- a/src/sdk/limrun-runtime-dependencies.ts +++ b/src/sdk/limrun-runtime-dependencies.ts @@ -4,8 +4,8 @@ import type { LimrunRuntimeDependencies } from '@agent-device/provider-limrun'; // deliberate static edge; making it lazy would require a proxy interactor rather than this seam. import { createAndroidInteractor } from '../core/interactors/android.ts'; import { runAndroidHostAdb } from '../platforms/android/adb-executor.ts'; -import { execFailureDetails, runCmd } from '../utils/exec.ts'; -import { readVersion } from '../utils/version.ts'; +import { execFailureDetails, runCmd } from '@agent-device/host-kit/command'; +import { readVersion } from '@agent-device/host-kit/version'; export function createLimrunRuntimeDependencies(): LimrunRuntimeDependencies { return { diff --git a/src/snapshot-quality/__tests__/warnings.test.ts b/src/snapshot-quality/__tests__/warnings.test.ts index 43e0aa1f11..024586435a 100644 --- a/src/snapshot-quality/__tests__/warnings.test.ts +++ b/src/snapshot-quality/__tests__/warnings.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; -import { readSnapshotQualityVerdict } from '../verdict.ts'; +import { readSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; import { renderSnapshotQualityWarnings } from '../warnings.ts'; const sharedRecoveryReason = diff --git a/src/snapshot/snapshot-freshness/recovery.ts b/src/snapshot/snapshot-freshness/recovery.ts index 9e8414c632..f6fb607fd4 100644 --- a/src/snapshot/snapshot-freshness/recovery.ts +++ b/src/snapshot/snapshot-freshness/recovery.ts @@ -3,7 +3,7 @@ import { type SnapshotCaptureAnnotations, } from '@agent-device/contracts/capture'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; -import { sleep } from '../../utils/timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; import type { SnapshotFreshnessReason, SnapshotFreshnessWindow } from './types.ts'; /** diff --git a/src/snapshot/snapshot-lines.ts b/src/snapshot/snapshot-lines.ts index 1553120579..fe7677fca0 100644 --- a/src/snapshot/snapshot-lines.ts +++ b/src/snapshot/snapshot-lines.ts @@ -1,4 +1,4 @@ -import { isSystemScrollIndicatorLabel } from '../utils/scroll-indicator.ts'; +import { isSystemScrollIndicatorLabel } from '@agent-device/kernel/scroll-indicator'; import type { SnapshotNode } from '@agent-device/kernel/snapshot'; import { buildTextPreview, describeTextSurface, trimText } from '../utils/text-surface.ts'; diff --git a/src/snapshot/snapshot-presentation/ios/rows.ts b/src/snapshot/snapshot-presentation/ios/rows.ts index b3cbd83a31..06613ba7a0 100644 --- a/src/snapshot/snapshot-presentation/ios/rows.ts +++ b/src/snapshot/snapshot-presentation/ios/rows.ts @@ -1,6 +1,6 @@ import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { normalizeType } from '@agent-device/contracts/snapshot'; -import { isSystemScrollIndicatorLabel } from '../../../utils/scroll-indicator.ts'; +import { isSystemScrollIndicatorLabel } from '@agent-device/kernel/scroll-indicator'; import { areRectsApproximatelyEqual, collectDescendants, diff --git a/src/snapshot/snapshot-presentation/ios/scroll.ts b/src/snapshot/snapshot-presentation/ios/scroll.ts index d41eaff147..09ede96247 100644 --- a/src/snapshot/snapshot-presentation/ios/scroll.ts +++ b/src/snapshot/snapshot-presentation/ios/scroll.ts @@ -2,7 +2,7 @@ import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { inferVerticalScrollIndicatorDirections, isSystemScrollIndicatorLabel, -} from '../../../utils/scroll-indicator.ts'; +} from '@agent-device/kernel/scroll-indicator'; import { findNearestScrollableContainer, isScrollableSnapshotType, diff --git a/src/snapshot/snapshot-presentation/tree.ts b/src/snapshot/snapshot-presentation/tree.ts index ac9bce796a..25350a941a 100644 --- a/src/snapshot/snapshot-presentation/tree.ts +++ b/src/snapshot/snapshot-presentation/tree.ts @@ -1,6 +1,6 @@ import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { normalizeType } from '@agent-device/contracts/snapshot'; -export { areRectsApproximatelyEqual } from '../../utils/rect-center.ts'; +export { areRectsApproximatelyEqual } from '@agent-device/kernel/rect-center'; export type SnapshotTreeRuleContext = { replacements: Map; diff --git a/src/snapshot/snapshot-visibility.ts b/src/snapshot/snapshot-visibility.ts index b9dc1dc477..6e96565d62 100644 --- a/src/snapshot/snapshot-visibility.ts +++ b/src/snapshot/snapshot-visibility.ts @@ -1,4 +1,4 @@ -import { buildMobileSnapshotPresentation } from './mobile-snapshot-semantics.ts'; +import { buildMobileSnapshotPresentation } from '@agent-device/capture-kit/mobile-snapshot-semantics'; import { usesMobileSnapshotPresentation, type SnapshotState, diff --git a/src/utils/__tests__/atomic-publish-ownership.test.ts b/src/utils/__tests__/atomic-publish-ownership.test.ts index f5e082c162..5c17a53fa7 100644 --- a/src/utils/__tests__/atomic-publish-ownership.test.ts +++ b/src/utils/__tests__/atomic-publish-ownership.test.ts @@ -9,7 +9,7 @@ const SIMPLE_PUBLISHERS = [ new URL('../../daemon/session-script-writer.ts', import.meta.url), new URL('../../../packages/platform-apple/src/runner/runner-lease.ts', import.meta.url), new URL('../../remote/remote-connection-state.ts', import.meta.url), - new URL('../process-lock.ts', import.meta.url), + new URL('../../../packages/host-kit/src/internal/process-lock.ts', import.meta.url), ] as const; test('simple same-directory publishers use the shared atomic publish owner', () => { diff --git a/src/utils/__tests__/daemon-client-lifecycle.test.ts b/src/utils/__tests__/daemon-client-lifecycle.test.ts index d8af0aa30c..8c893593ac 100644 --- a/src/utils/__tests__/daemon-client-lifecycle.test.ts +++ b/src/utils/__tests__/daemon-client-lifecycle.test.ts @@ -7,20 +7,16 @@ import path from 'node:path'; import { afterEach, test, vi } from 'vitest'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../exec.ts', async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - runCmdDetached: vi.fn(), - runCmdDetachedMonitored: vi.fn(), - runCmdSync: vi.fn(() => ({ exitCode: 1, stdout: '', stderr: '' })), - }; -}); - -vi.mock('../timeouts.ts', async (importOriginal) => { - const actual = await importOriginal(); - return { ...actual, sleep: vi.fn(async () => {}) }; -}); +vi.mock('@agent-device/host-kit/command', async (importOriginal) => ({ + ...(await importOriginal()), + runCmdDetached: vi.fn(), + runCmdDetachedMonitored: vi.fn(), + runCmdSync: vi.fn(() => ({ exitCode: 1, stdout: '', stderr: '' })), +})); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => ({ + ...(await importOriginal()), + sleep: vi.fn(async () => {}), +})); import { resolveDaemonPaths, type DaemonPaths } from '../../daemon/config.ts'; import { @@ -37,11 +33,14 @@ import { supportsLoopbackBind, } from '../../__tests__/test-utils/loopback.ts'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmdDetachedMonitored, runCmdSync } from '../exec.ts'; -import { readProcessStartTime } from '../host-process.ts'; -import { shellQuoteIfNeeded } from '../shell-quote.ts'; -import { sleep } from '../timeouts.ts'; -import { findProjectRoot, readVersion } from '../version.ts'; +import { + runCmdDetachedMonitored, + runCmdSync, + shellQuoteIfNeeded, +} from '@agent-device/host-kit/command'; +import { readProcessStartTime } from '@agent-device/host-kit/process'; +import { sleep } from '@agent-device/host-kit/retry'; +import { findProjectRoot, readVersion } from '@agent-device/host-kit/version'; type DaemonInfoFixture = { port?: number; diff --git a/src/utils/__tests__/daemon-client.test.ts b/src/utils/__tests__/daemon-client.test.ts index 0fed780f1c..29de065b30 100644 --- a/src/utils/__tests__/daemon-client.test.ts +++ b/src/utils/__tests__/daemon-client.test.ts @@ -11,7 +11,13 @@ import { listenOnLoopback, supportsLoopbackBind, } from '../../__tests__/test-utils/loopback.ts'; -import { runCmdBackground } from '../exec.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; +import { + isProcessAlive, + readProcessCommand, + readProcessStartTime, + waitForProcessExit, +} from '@agent-device/host-kit/process'; import { sendToDaemon } from '../../daemon/client/daemon-client.ts'; import { computeDaemonCodeSignature } from '../../daemon/code-signature.ts'; import { downloadRemoteArtifact } from '../../remote/daemon-artifacts.ts'; @@ -27,14 +33,8 @@ import { shouldResetDaemonAfterRequestTimeout, } from '../../daemon/client/daemon-client-timeout.ts'; import { resolveDaemonPaths } from '../../daemon/config.ts'; -import { - isProcessAlive, - readProcessCommand, - readProcessStartTime, - waitForProcessExit, -} from '../host-process.ts'; import { stopProcessForTakeover } from '../../daemon/daemon-process.ts'; -import { findProjectRoot, readVersion } from '../version.ts'; +import { findProjectRoot, readVersion } from '@agent-device/host-kit/version'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; // readProcessStartTime/readProcessCommand shell out to `ps` with a 1s @@ -52,8 +52,8 @@ const { mockReadProcessStartTime, mockReadProcessCommand } = vi.hoisted(() => ({ mockReadProcessCommand: vi.fn<(pid: number) => string | null | undefined>(), })); -vi.mock('../host-process.ts', async () => { - const actual = await vi.importActual('../host-process.ts'); +vi.mock('@agent-device/host-kit/process', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, readProcessStartTime: (pid: number) => { diff --git a/src/utils/__tests__/exec-boundary-faults.test.ts b/src/utils/__tests__/exec-boundary-faults.test.ts index 552d69f5e5..f3cab30b86 100644 --- a/src/utils/__tests__/exec-boundary-faults.test.ts +++ b/src/utils/__tests__/exec-boundary-faults.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; import { createFailNthCommandExecutor } from '../../__tests__/test-utils/exec-faults.ts'; -import { runCmd, withCommandExecutorOverride } from '../exec.ts'; +import { runCmd, withCommandExecutorOverride } from '@agent-device/host-kit/command'; test('fail-Nth executor drives one deterministic command failure without hiding later calls', async () => { const failure = new AppError('COMMAND_FAILED', 'injected command failure', { diff --git a/src/utils/__tests__/process-exit.test.ts b/src/utils/__tests__/process-exit.test.ts index ff9d4c575a..f499a7788a 100644 --- a/src/utils/__tests__/process-exit.test.ts +++ b/src/utils/__tests__/process-exit.test.ts @@ -1,13 +1,13 @@ import assert from 'node:assert/strict'; import { afterEach, test, vi } from 'vitest'; -vi.mock('../timeouts.ts', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('@agent-device/host-kit/retry', async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, sleep: vi.fn(async () => {}) }; }); import { exitAfterFlush } from '../process-exit.ts'; -import { sleep } from '../timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; type FakeWriteStream = { writableLength: number; diff --git a/src/utils/__tests__/swift-cache.test.ts b/src/utils/__tests__/swift-cache.test.ts index a0f691acc7..d19d95e81e 100644 --- a/src/utils/__tests__/swift-cache.test.ts +++ b/src/utils/__tests__/swift-cache.test.ts @@ -3,7 +3,8 @@ import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; -vi.mock('../exec.ts', () => ({ +vi.mock(import('@agent-device/host-kit/command'), async (importOriginal) => ({ + ...(await importOriginal()), runCmd: vi.fn(async (_cmd: string, args: string[]) => { const outputPath = args[args.indexOf('-o') + 1]!; fs.writeFileSync(outputPath, 'compiled'); @@ -12,7 +13,7 @@ vi.mock('../exec.ts', () => ({ }), })); -import { runCmd } from '../exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { compileSwiftSourceFile, compileSwiftSourceText } from '../swift-cache.ts'; const mockRunCmd = vi.mocked(runCmd); diff --git a/src/utils/env-map.ts b/src/utils/env-map.ts deleted file mode 100644 index 43fd717aa4..0000000000 --- a/src/utils/env-map.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type EnvMap = Record; -export type DefinedEnvMap = Record; diff --git a/src/utils/keyed-lock.ts b/src/utils/keyed-lock.ts deleted file mode 100644 index e5eef7881a..0000000000 --- a/src/utils/keyed-lock.ts +++ /dev/null @@ -1 +0,0 @@ -export { withKeyedLock } from '@agent-device/kernel/keyed-lock'; diff --git a/src/utils/method-scope.ts b/src/utils/method-scope.ts deleted file mode 100644 index 1d56831def..0000000000 --- a/src/utils/method-scope.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Wraps every method call on `target` in `runScope`, so leaf code below the - * method resolves scope-injected state (AsyncLocalStorage transports) instead - * of the local default. Shared by the Android adb-provider and Apple - * runner-provider interactor seams. - */ -export function withMethodScope( - target: T, - runScope: (task: () => Promise) => Promise, -): T { - return new Proxy(target, { - get(proxyTarget, property, receiver) { - const value = Reflect.get(proxyTarget, property, receiver); - if (typeof value !== 'function') return value; - return (...args: unknown[]) => runScope(async () => await value.apply(proxyTarget, args)); - }, - }); -} diff --git a/src/utils/output.ts b/src/utils/output.ts index 0bea051959..c43e874a2e 100644 --- a/src/utils/output.ts +++ b/src/utils/output.ts @@ -18,7 +18,7 @@ import { import type { ScreenshotDiffResult } from '../screenshot-diff/screenshot-diff.ts'; import type { ScreenshotDiffRegion } from '../screenshot-diff/screenshot-diff-regions.ts'; import { styleText } from 'node:util'; -import { buildMobileSnapshotPresentation } from '../snapshot/mobile-snapshot-semantics.ts'; +import { buildMobileSnapshotPresentation } from '@agent-device/capture-kit/mobile-snapshot-semantics'; type JsonResult = | { success: true; data?: unknown } diff --git a/src/utils/process-exit.ts b/src/utils/process-exit.ts index 97f47b971b..c364c370b7 100644 --- a/src/utils/process-exit.ts +++ b/src/utils/process-exit.ts @@ -1,4 +1,4 @@ -import { sleep } from './timeouts.ts'; +import { sleep } from '@agent-device/host-kit/retry'; const FLUSH_TIMEOUT_MS = 2000; diff --git a/src/utils/result-serialization.ts b/src/utils/result-serialization.ts index 575827c396..829b412ab7 100644 --- a/src/utils/result-serialization.ts +++ b/src/utils/result-serialization.ts @@ -15,7 +15,7 @@ import type { SessionCloseResult, } from '@agent-device/contracts/client'; import { isSerialAddressablePlatform, type PublicPlatform } from '@agent-device/kernel/device'; -import { successText, withSuccessText } from './success-text.ts'; +import { successText, withSuccessText } from '@agent-device/kernel/success-text'; export function buildAppIdentifiers(params: { session?: string; diff --git a/src/utils/screenshot-result.ts b/src/utils/screenshot-result.ts index 491dfa0169..816bcc501c 100644 --- a/src/utils/screenshot-result.ts +++ b/src/utils/screenshot-result.ts @@ -1,6 +1,6 @@ import type { ScreenshotResultData } from '@agent-device/contracts/capture'; import type { ScreenshotOverlayRef } from '@agent-device/kernel/snapshot'; -import { isRecord, parsePoint, parseRect } from './parsing.ts'; +import { isRecord, parsePoint, parseRect } from '@agent-device/kernel/record'; export function pickScreenshotResultData(value: ScreenshotResultData): ScreenshotResultData { return { diff --git a/src/utils/scroll-edge-state.ts b/src/utils/scroll-edge-state.ts index b5deaaae09..1695eea582 100644 --- a/src/utils/scroll-edge-state.ts +++ b/src/utils/scroll-edge-state.ts @@ -1,4 +1,4 @@ -import { deriveMobileSnapshotHiddenContentHints } from '../snapshot/mobile-snapshot-semantics.ts'; +import { deriveMobileSnapshotHiddenContentHints } from '@agent-device/capture-kit/mobile-snapshot-semantics'; import { isNodeVisibleInEffectiveViewport, isScrollableNodeLike, diff --git a/src/utils/source-value.test.ts b/src/utils/source-value.test.ts index 5f4d5cb4e6..74b5e76cd9 100644 --- a/src/utils/source-value.test.ts +++ b/src/utils/source-value.test.ts @@ -1,6 +1,9 @@ import { describe, expect, test } from 'vitest'; -import { buildPrimaryEnvVarName, parseSourceValue } from './source-value.ts'; -import type { SourceValueDefinition } from './source-value.ts'; +import { + buildPrimaryEnvVarName, + parseSourceValue, + type SourceValueDefinition, +} from '@agent-device/kernel/source-value'; const LABEL = 'config file'; diff --git a/src/utils/swift-cache.ts b/src/utils/swift-cache.ts index 0b89e321c7..ad382f5a8b 100644 --- a/src/utils/swift-cache.ts +++ b/src/utils/swift-cache.ts @@ -2,10 +2,11 @@ import { createHash } from 'node:crypto'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; +import { trimEdgeDashes } from '@agent-device/kernel/collections'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from './exec.ts'; -import { acquireProcessLock } from './process-lock.ts'; -import { readProcessStartTime } from './host-process.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { readProcessStartTime } from '@agent-device/host-kit/process'; +import { acquireProcessLock } from '@agent-device/host-kit/file'; const SWIFT_CACHE_VERSION = '2'; const LOCK_RETRY_DELAY_MS = 25; @@ -181,19 +182,6 @@ function sanitizeCacheName(value: string): string { return trimEdgeDashes(value.replaceAll(/[^A-Za-z0-9._-]/g, '-')) || 'swift-helper'; } -/** - * Linear-time edge trim. The regex form (`/^-+|-+$/g`) backtracks - * polynomially on long dash runs (CodeQL js/polynomial-redos), and cache - * names are derived from caller-supplied strings. - */ -function trimEdgeDashes(value: string): string { - let start = 0; - let end = value.length; - while (start < end && value[start] === '-') start += 1; - while (end > start && value[end - 1] === '-') end -= 1; - return value.slice(start, end); -} - function hashParts(parts: Array): string { const hash = createHash('sha256'); for (const part of parts) { diff --git a/src/utils/update-check.ts b/src/utils/update-check.ts index e4b828f092..e430e336a2 100644 --- a/src/utils/update-check.ts +++ b/src/utils/update-check.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { runCmdDetached } from './exec.ts'; +import { runCmdDetached } from '@agent-device/host-kit/command'; const PACKAGE_NAME = 'agent-device'; const UPDATE_CHECK_INTERVAL_MS = 14 * 24 * 60 * 60 * 1000; diff --git a/src/utils/version.ts b/src/utils/version.ts deleted file mode 100644 index 27c0cefed5..0000000000 --- a/src/utils/version.ts +++ /dev/null @@ -1,48 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { createTtlMemo } from './ttl-memo.ts'; - -// A running process cannot observe its own package version or its own project -// root changing, and both are read several times per CLI invocation (daemon -// reuse check, transport client version, help header, runner cache key), so -// each is resolved once and cleared between tests by the process-memo reset. -const versionMemo = createTtlMemo(); -const projectRootMemo = createTtlMemo<'self', string>(); - -export function readVersion(root: string = findProjectRoot()): string { - const memoized = versionMemo.get(root); - if (memoized !== undefined) return memoized; - - let pkg: { version?: unknown }; - try { - pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as { - version?: unknown; - }; - } catch { - // An absent or unreadable package.json is not memoized: the caller may be - // asking about a root that is still being materialized (a runner or helper - // package), and '0.0.0' must not stick to it for the rest of the process. - return '0.0.0'; - } - const version = typeof pkg.version === 'string' ? pkg.version : '0.0.0'; - versionMemo.set(root, version); - return version; -} - -export function findProjectRoot(): string { - const memoized = projectRootMemo.get('self'); - if (memoized !== undefined) return memoized; - - const start = path.dirname(fileURLToPath(import.meta.url)); - let current = start; - for (let i = 0; i < 6; i += 1) { - const pkgPath = path.join(current, 'package.json'); - if (fs.existsSync(pkgPath)) { - projectRootMemo.set('self', current); - return current; - } - current = path.dirname(current); - } - return start; -} diff --git a/src/utils/video.ts b/src/utils/video.ts index afd7c76344..c3634b2bcd 100644 --- a/src/utils/video.ts +++ b/src/utils/video.ts @@ -1,8 +1,9 @@ import fs from 'node:fs'; import { AppError } from '@agent-device/kernel/errors'; -import { runCmd } from './exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; +import { sleep } from '@agent-device/host-kit/retry'; import { buildSwiftToolEnv, compileSwiftSourceText } from './swift-cache.ts'; -import { sleep } from './timeouts.ts'; + import { hasPlayableWebmStructure } from './video-webm.ts'; // Duration zero must pass: a recording of a fully static screen legitimately contains a single diff --git a/stryker.config.json b/stryker.config.json index 1ab0498448..35a3443c6a 100644 --- a/stryker.config.json +++ b/stryker.config.json @@ -22,6 +22,7 @@ "ignoreStatic": true, "tempDirName": ".tmp/stryker", "cleanTempDir": true, + "dryRunTimeoutMinutes": 15, "timeoutMS": 20000, "timeoutFactor": 2, "thresholds": { @@ -38,6 +39,6 @@ "!packages/selectors/src/**/*.test.ts", "!packages/selectors/src/internal/__tests__/**", "packages/ad-script/src/internal/target-annotation-serde.ts", - "src/snapshot/snapshot-occlusion.ts" + "packages/capture-kit/src/snapshot-occlusion.ts" ] } diff --git a/test/integration/android-emulator-e2e/live-form-scenario.ts b/test/integration/android-emulator-e2e/live-form-scenario.ts index 267160fc11..e273a54717 100644 --- a/test/integration/android-emulator-e2e/live-form-scenario.ts +++ b/test/integration/android-emulator-e2e/live-form-scenario.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import path from 'node:path'; import { PUBLIC_COMMANDS } from '../../../src/command-catalog.ts'; -import { parseRect } from '../../../src/utils/parsing.ts'; +import { parseRect } from '@agent-device/kernel/record'; import type { CliJsonResult } from '../cli-json.ts'; import { assertElementText, diff --git a/test/integration/cli-exit-flush.test.ts b/test/integration/cli-exit-flush.test.ts index fdc9fa3452..6d3be13c68 100644 --- a/test/integration/cli-exit-flush.test.ts +++ b/test/integration/cli-exit-flush.test.ts @@ -2,7 +2,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { runCmd } from '../../src/utils/exec.ts'; +import { runCmd } from '@agent-device/host-kit/command'; import { PAYLOAD_MARKER } from './support/exit-payload.ts'; const SUPPORT_DIR = path.join(import.meta.dirname, 'support'); diff --git a/test/integration/cli-json.ts b/test/integration/cli-json.ts index 707469f166..9b41042c5f 100644 --- a/test/integration/cli-json.ts +++ b/test/integration/cli-json.ts @@ -1,4 +1,4 @@ -import { runCmd, runCmdSync, type ExecResult } from '../../src/utils/exec.ts'; +import { runCmd, runCmdSync, type ExecResult } from '@agent-device/host-kit/command'; const CLI_TIMEOUT_MS = 120_000; diff --git a/test/integration/client-api-doc-snippets.test.ts b/test/integration/client-api-doc-snippets.test.ts index 1fd046c4a9..62657a3023 100644 --- a/test/integration/client-api-doc-snippets.test.ts +++ b/test/integration/client-api-doc-snippets.test.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import assert from 'node:assert/strict'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; // Compiles every fenced ```ts snippet in website/docs/docs/client-api.md against // agent-device's real `agent-device/*` subpath sources, so a doc snippet that no diff --git a/test/integration/daemon-leak-oracle-cli.test.ts b/test/integration/daemon-leak-oracle-cli.test.ts index b150925f0f..3c7645c7ef 100644 --- a/test/integration/daemon-leak-oracle-cli.test.ts +++ b/test/integration/daemon-leak-oracle-cli.test.ts @@ -3,8 +3,8 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { runCmdSync } from '../../src/utils/exec.ts'; -import { readProcessCommand, readProcessStartTime } from '../../src/utils/host-process.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; +import { readProcessCommand, readProcessStartTime } from '@agent-device/host-kit/process'; // #1781 B1: the oracle's `after-close` checkpoint is only meaningful when it can // name the sessions that closed — without them it would accept every unfinalized diff --git a/test/integration/daemon-replace-exit-flush.test.ts b/test/integration/daemon-replace-exit-flush.test.ts index b3009a4085..59e02e7120 100644 --- a/test/integration/daemon-replace-exit-flush.test.ts +++ b/test/integration/daemon-replace-exit-flush.test.ts @@ -5,7 +5,7 @@ import os from 'node:os'; import path from 'node:path'; import { skipWhenLoopbackUnavailable } from '../../src/__tests__/test-utils/loopback.ts'; import { stopProcessForTakeover } from '../../src/daemon/daemon-process.ts'; -import { isProcessAlive } from '../../src/utils/host-process.ts'; +import { isProcessAlive } from '@agent-device/host-kit/process'; import { assertNoDaemonLeaks } from './support/daemon-leak-oracle.ts'; import { runCliJson } from './test-helpers.ts'; diff --git a/test/integration/installed-package-metro.test.ts b/test/integration/installed-package-metro.test.ts index 38b7eca89d..ce663509f0 100644 --- a/test/integration/installed-package-metro.test.ts +++ b/test/integration/installed-package-metro.test.ts @@ -8,7 +8,7 @@ import path from 'node:path'; import type { Duplex } from 'node:stream'; import { fileURLToPath } from 'node:url'; import { closeLoopbackServer, listenOnLoopback } from '../../src/__tests__/test-utils/loopback.ts'; -import { runCmd, runCmdSync } from '../../src/utils/exec.ts'; +import { runCmd, runCmdSync } from '@agent-device/host-kit/command'; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..'); const SUBPROCESS_TIMEOUT_MS = 120_000; diff --git a/test/integration/ios-simulator-e2e/snapshot-backend-conformance.ts b/test/integration/ios-simulator-e2e/snapshot-backend-conformance.ts index 57234dc68d..964145a20f 100644 --- a/test/integration/ios-simulator-e2e/snapshot-backend-conformance.ts +++ b/test/integration/ios-simulator-e2e/snapshot-backend-conformance.ts @@ -11,7 +11,7 @@ import { type SnapshotCaptureBackend, type SnapshotPreferredBackend, } from '@agent-device/kernel/snapshot'; -import { SNAPSHOT_BACKEND_CAPABILITIES } from '../../../src/snapshot-quality/backend-capabilities.ts'; +import { SNAPSHOT_BACKEND_CAPABILITIES } from '@agent-device/capture-kit/snapshot-quality-backend-capabilities'; import { isSemanticTouchTarget } from '../../../src/core/touch-semantics.ts'; export type SnapshotBackendConformanceFixture = { diff --git a/test/integration/limrun-public-types.test.ts b/test/integration/limrun-public-types.test.ts index 58de67d275..6accf62a49 100644 --- a/test/integration/limrun-public-types.test.ts +++ b/test/integration/limrun-public-types.test.ts @@ -3,7 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..'); const TSC_BIN = path.join(repoRoot, 'node_modules/.bin/tsc'); diff --git a/test/integration/provider-scenarios/active-session-script-publication.test.ts b/test/integration/provider-scenarios/active-session-script-publication.test.ts index 412de427b4..927bb060d8 100644 --- a/test/integration/provider-scenarios/active-session-script-publication.test.ts +++ b/test/integration/provider-scenarios/active-session-script-publication.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { emitDiagnostic } from '../../../src/utils/diagnostics.ts'; +import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { INTERNAL_COMMANDS } from '../../../src/command-catalog.ts'; import { test } from 'vitest'; import { assertRpcError, assertRpcOk } from './assertions.ts'; diff --git a/test/integration/provider-scenarios/android-world.ts b/test/integration/provider-scenarios/android-world.ts index fe14a0347a..90ee1e4716 100644 --- a/test/integration/provider-scenarios/android-world.ts +++ b/test/integration/provider-scenarios/android-world.ts @@ -14,7 +14,7 @@ import { ANDROID_SNAPSHOT_HELPER_FIXTURE_ARTIFACT, androidSnapshotHelperOutput, } from '../../../src/__tests__/test-utils/android-snapshot-helper.ts'; -import { runCmd, runCmdBackground } from '../../../src/utils/exec.ts'; +import { runCmd, runCmdBackground } from '@agent-device/host-kit/command'; import { validPng } from './assertions.ts'; import { PROVIDER_SCENARIO_ANDROID } from './fixtures.ts'; import { @@ -274,7 +274,7 @@ function argsStartWith(args: string[], prefix: string[]): boolean { // The real device shell unwraps a single-quoted argument (and collapses the // `'\''` escape back to `'`) before `cmd` ever sees it, so this harness has // to mirror that unwrap to keep modelling what the device actually receives -// — the inverse of the quoting in src/utils/shell-quote.ts. +// — the inverse of the quoting in @agent-device/host-kit/command. function unquoteAndroidShellArg(value: string): string { if (!value.startsWith("'") || !value.endsWith("'") || value.length < 2) return value; return value.slice(1, -1).replaceAll("'\\''", "'"); diff --git a/test/integration/provider-scenarios/assertions.ts b/test/integration/provider-scenarios/assertions.ts index 0557d16983..a1f023a1b3 100644 --- a/test/integration/provider-scenarios/assertions.ts +++ b/test/integration/provider-scenarios/assertions.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; -import { PNG } from '../../../src/utils/png.ts'; +import { PNG } from '@agent-device/capture-kit/png'; import type { ProviderScenarioRpcResult } from './harness.ts'; export function assertCommandCall(calls: readonly string[][], expected: readonly string[]): void { diff --git a/test/integration/provider-scenarios/daemon-http-boundary-cancellation.test.ts b/test/integration/provider-scenarios/daemon-http-boundary-cancellation.test.ts index b579981fac..5f1a600156 100644 --- a/test/integration/provider-scenarios/daemon-http-boundary-cancellation.test.ts +++ b/test/integration/provider-scenarios/daemon-http-boundary-cancellation.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import http from 'node:http'; import { test } from 'vitest'; -import { getRequestSignal } from '../../../src/request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { createDaemonHttpServer } from '../../../src/daemon/server/http-server.ts'; import type { DaemonRequest, DaemonResponse } from '../../../src/daemon/types.ts'; import { diff --git a/test/integration/provider-scenarios/daemon-http-disconnect.test.ts b/test/integration/provider-scenarios/daemon-http-disconnect.test.ts index 1809626df2..75ac7346bd 100644 --- a/test/integration/provider-scenarios/daemon-http-disconnect.test.ts +++ b/test/integration/provider-scenarios/daemon-http-disconnect.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import http from 'node:http'; import { test } from 'vitest'; -import { getRequestSignal } from '../../../src/request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { createDaemonHttpServer } from '../../../src/daemon/server/http-server.ts'; import type { DaemonRequest, DaemonResponse } from '../../../src/daemon/types.ts'; import { diff --git a/test/integration/provider-scenarios/daemon-http-server.test.ts b/test/integration/provider-scenarios/daemon-http-server.test.ts index 572e7c3762..576432d81f 100644 --- a/test/integration/provider-scenarios/daemon-http-server.test.ts +++ b/test/integration/provider-scenarios/daemon-http-server.test.ts @@ -13,8 +13,12 @@ import { } from '../../../src/daemon/artifact-tracking.ts'; import { DAEMON_RPC_PROTOCOL_VERSION } from '../../../src/daemon/http-health.ts'; import { createDaemonHttpServer } from '../../../src/daemon/server/http-server.ts'; -import { emitRequestProgress } from '../../../src/request/progress.ts'; -import { getRequestSignal, isRequestCanceled } from '../../../src/request/cancel.ts'; +import { + emitRequestProgress, + getRequestSignal, + isRequestCanceled, +} from '@agent-device/host-kit/request'; + import type { DaemonRequest, DaemonResponse } from '../../../src/daemon/types.ts'; import { closeLoopbackServer, diff --git a/test/integration/provider-scenarios/daemon-transport.test.ts b/test/integration/provider-scenarios/daemon-transport.test.ts index 76609be106..2520764e56 100644 --- a/test/integration/provider-scenarios/daemon-transport.test.ts +++ b/test/integration/provider-scenarios/daemon-transport.test.ts @@ -8,7 +8,7 @@ import { listenHttpServer, listenNetServer, } from '../../../src/daemon/server/transport.ts'; -import { getRequestSignal } from '../../../src/request/cancel.ts'; +import { getRequestSignal } from '@agent-device/host-kit/request'; import { closeLoopbackServer, skipWhenLoopbackUnavailable, diff --git a/test/integration/provider-scenarios/harness.ts b/test/integration/provider-scenarios/harness.ts index 4365446f3f..60d2bbacc8 100644 --- a/test/integration/provider-scenarios/harness.ts +++ b/test/integration/provider-scenarios/harness.ts @@ -22,7 +22,8 @@ import { trackDownloadableArtifact } from '../../../src/daemon/artifact-tracking import { LeaseRegistry } from '../../../src/daemon/lease-registry.ts'; import { SessionStore } from '../../../src/daemon/session-store.ts'; import type { DaemonRequest, DaemonResponse, SessionState } from '../../../src/daemon/types.ts'; -import { runCmdBackground } from '../../../src/utils/exec.ts'; +import { runCmdBackground } from '@agent-device/host-kit/command'; +import { createOwnedProcessRecordStore } from '@agent-device/host-kit/process'; import { withClientReplayScriptSources } from '../../../src/__tests__/test-utils/replay-script-source.ts'; import type { DeviceInventoryProvider, @@ -37,7 +38,7 @@ import { createHostDiagnostics } from '../../../src/platform-runtime-host-diagno import type { PlatformRuntimeProviderRegistration } from '../../../src/platform-runtime-gateway.ts'; import { createProviderPlatformRuntimeRegistrations } from '../../../src/provider-device-runtimes.ts'; import { unavailableDeviceRuntimeGateway } from '../../../src/daemon/__tests__/test-device-runtime-gateway.ts'; -import { createOwnedProcessRecordStore } from '../../../src/utils/owned-process-record.ts'; + import { openWebSessionNames } from '../../../src/daemon/web-session-names.ts'; const PROVIDER_SCENARIO_TOKEN = 'provider-scenario-token'; diff --git a/test/integration/provider-scenarios/providers.ts b/test/integration/provider-scenarios/providers.ts index ddac165f6a..1da7e848f9 100644 --- a/test/integration/provider-scenarios/providers.ts +++ b/test/integration/provider-scenarios/providers.ts @@ -6,7 +6,7 @@ import type { AppleToolProvider, AppleToolSubcommandExecutor, } from '../../../src/platforms/apple/core/tool-provider.ts'; -import type { ExecResult } from '../../../src/utils/exec.ts'; +import { type ExecResult } from '@agent-device/host-kit/command'; import type { ProviderScenarioTranscript } from './transcript.ts'; export type FlatToolCall = [string, ...string[]]; diff --git a/test/integration/smoke-clean-xcuitest-derived.test.ts b/test/integration/smoke-clean-xcuitest-derived.test.ts index d989d601d7..14299151a6 100644 --- a/test/integration/smoke-clean-xcuitest-derived.test.ts +++ b/test/integration/smoke-clean-xcuitest-derived.test.ts @@ -4,7 +4,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..'); const scriptPath = path.join(repoRoot, 'scripts', 'clean-xcuitest-derived.mjs'); diff --git a/test/integration/smoke-cli.test.ts b/test/integration/smoke-cli.test.ts index 2ab1a7d188..8d98b19bc2 100644 --- a/test/integration/smoke-cli.test.ts +++ b/test/integration/smoke-cli.test.ts @@ -1,6 +1,6 @@ import test from 'node:test'; import assert from 'node:assert/strict'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; function runCli(args: string[]): { status: number; stdout: string; stderr: string } { const result = runCmdSync( diff --git a/test/integration/smoke-daemon-clean.test.ts b/test/integration/smoke-daemon-clean.test.ts index 2d39ff29b1..8a814cce3b 100644 --- a/test/integration/smoke-daemon-clean.test.ts +++ b/test/integration/smoke-daemon-clean.test.ts @@ -4,9 +4,10 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { skipWhenLoopbackUnavailable } from '../../src/__tests__/test-utils/loopback.ts'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; +import { isProcessAlive } from '@agent-device/host-kit/process'; import { stopProcessForTakeover } from '../../src/daemon/daemon-process.ts'; -import { isProcessAlive } from '../../src/utils/host-process.ts'; + import { assertNoDaemonLeaks } from './support/daemon-leak-oracle.ts'; import { runCliJson } from './test-helpers.ts'; diff --git a/test/integration/smoke-web-platform.test.ts b/test/integration/smoke-web-platform.test.ts index 29a054f239..d6f2b4cee2 100644 --- a/test/integration/smoke-web-platform.test.ts +++ b/test/integration/smoke-web-platform.test.ts @@ -28,7 +28,7 @@ import { listHostProcesses, readProcessStartTime, stopPidsWithEscalation, -} from '../../src/utils/host-process.ts'; +} from '@agent-device/host-kit/process'; const TEST_NAME = 'live web platform e2e smoke'; const SHUTDOWN_TEST_NAME = 'live web platform e2e daemon-shutdown browser cleanup'; diff --git a/test/integration/support/daemon-leak-model.ts b/test/integration/support/daemon-leak-model.ts index 14684e2e93..a3f9cc4a8f 100644 --- a/test/integration/support/daemon-leak-model.ts +++ b/test/integration/support/daemon-leak-model.ts @@ -27,7 +27,7 @@ // still live, or no longer matches and therefore cannot be // proved dead. The latter fails closed rather than allowing // pid reuse to turn unknown evidence into a green checkpoint. -import { uniquePositivePids } from '../../../src/utils/host-process.ts'; +import { uniquePositivePids } from '@agent-device/host-kit/process'; import type { OwnedProcessRecord } from '@agent-device/contracts/platform-runtime-host'; export type DaemonLeakPhase = 'after-close' | 'after-shutdown'; diff --git a/test/integration/support/daemon-leak-oracle.ts b/test/integration/support/daemon-leak-oracle.ts index baf6084b38..cb1f7b245a 100644 --- a/test/integration/support/daemon-leak-oracle.ts +++ b/test/integration/support/daemon-leak-oracle.ts @@ -8,8 +8,9 @@ import { isProcessAlive, readHostProcessIdentityObservations, readProcessCommand, -} from '../../../src/utils/host-process.ts'; -import { readOwnedProcessRecordFile } from '../../../src/utils/owned-process-record.ts'; + readOwnedProcessRecordFile, +} from '@agent-device/host-kit/process'; + import { AppError } from '@agent-device/kernel/errors'; import type { OwnedProcessRecord } from '@agent-device/contracts/platform-runtime-host'; import { diff --git a/test/integration/test-helpers.ts b/test/integration/test-helpers.ts index 73418354e9..1356e55889 100644 --- a/test/integration/test-helpers.ts +++ b/test/integration/test-helpers.ts @@ -1,8 +1,8 @@ import assert from 'node:assert/strict'; import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; -import { PNG } from '../../src/utils/png.ts'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { PNG } from '@agent-device/capture-kit/png'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { type CliJsonResult, formatResultDebug, runSourceCliJsonSync } from './cli-json.ts'; const RECORDING_INSPECT_TIMEOUT_MS = 60_000; diff --git a/test/output-economy/output-economy.baseline.test.ts b/test/output-economy/output-economy.baseline.test.ts index a798739a93..53460c793f 100644 --- a/test/output-economy/output-economy.baseline.test.ts +++ b/test/output-economy/output-economy.baseline.test.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { describe, expect, test } from 'vitest'; import { defaultHintForCode, retriableForErrorCode } from '@agent-device/kernel/errors'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { findEconomyBudgetIncreases, formatEconomyBudgetIncreases, diff --git a/test/output-economy/owner-files-no-leak.test.ts b/test/output-economy/owner-files-no-leak.test.ts index a87e47208b..e7bf23ac46 100644 --- a/test/output-economy/owner-files-no-leak.test.ts +++ b/test/output-economy/owner-files-no-leak.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'vitest'; import fs from 'node:fs'; import path from 'node:path'; -import { runCmdSync } from '../../src/utils/exec.ts'; +import { runCmdSync } from '@agent-device/host-kit/command'; import { COMMAND_OWNER_FILES } from '../../src/core/command-descriptor/owner-files.ts'; import { getDaemonRouteOwnerFiles } from '../../src/daemon/route-owner-files.ts'; diff --git a/tsdown.config.ts b/tsdown.config.ts index a94ac7dc19..1277f297d8 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -71,7 +71,7 @@ export default defineConfig({ 'internal/bin': 'src/bin.ts', 'internal/companion-tunnel': 'src/client/companion-tunnel.ts', 'internal/daemon': 'src/daemon.ts', - 'internal/png-worker': 'src/utils/png-worker.ts', + 'internal/png-worker': 'packages/capture-kit/src/png-worker.ts', 'internal/update-check-entry': 'src/utils/update-check-entry.ts', }, deps: { diff --git a/vitest.mutation.config.ts b/vitest.mutation.config.ts index 9b73834fa7..9e31dff3dc 100644 --- a/vitest.mutation.config.ts +++ b/vitest.mutation.config.ts @@ -7,27 +7,12 @@ import { SERIALIZED_TESTS, SETUP_FILES } from './vitest.config.ts'; const repoRoot = path.dirname(fileURLToPath(import.meta.url)); -// Workspace-package aliases for the Stryker sandbox (#1490 W0). Stryker copies -// the tree into .stryker-tmp and symlinks node_modules back to the real repo, -// so a `@agent-device/*` specifier resolved through pnpm's link escapes the -// sandbox: mutants written into the sandbox copy never load, and -// `vitest related` finds no tests for a mutated package file. Aliasing each -// EXPORTED specifier to its source file — resolved relative to this config, -// which Stryker copies into the sandbox — keeps resolution inside the mutated -// tree. Entries come from the shared exports-map reader, never a wildcard, so -// this cannot resolve package internals the boundary forbids (R11). -const workspaceAliases = workspaceSourceAliases(repoRoot); +const inStrykerSandbox = repoRoot.includes(path.join('.tmp', 'stryker')); +const workspaceAliases = workspaceSourceAliases( + repoRoot, + inStrykerSandbox ? 'disk-manifests' : 'tracked-manifests', +); -// Test scope for the decision-kernel mutation lane (issue #1415). -// -// `scripts/mutation/run.ts` derives the per-run scope from Vitest's module graph -// (`vitest related` over the mutated files) and hands it over through -// AGENT_DEVICE_MUTATION_TEST_FILES; the fallback is the deterministic unit suite, -// which keeps `pnpm exec stryker run` usable by hand. Excluded either way: the -// real-subprocess-spawn tests (SERIALIZED_TESTS — #1823, only `fuzz-worker` still -// runs them in a serialized Vitest project) and the CLI-capture tests — see -// scripts/mutation/test-scope.ts for why, and why excluding them cannot hide a -// surviving mutant. const scope = readTestScope(); export default defineConfig({