Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@
"test:fuzz-worker": "vitest run --project fuzz-worker",
"test:integration:provider": "vitest run --project provider-integration",
"test:integration:progress": "node --experimental-strip-types scripts/integration-progress.ts",
"test:integration:progress:check": "node --experimental-strip-types scripts/integration-progress.ts --check",
"test:integration:progress:check": "node --experimental-strip-types scripts/node-test-tmpdir.ts --experimental-strip-types --test scripts/integration-progress-model.test.ts && node --experimental-strip-types scripts/integration-progress.ts --check",
"test:fixture-cache": "node --experimental-strip-types scripts/node-test-tmpdir.ts --test test/ci/trusted-fixture-artifact.test.mjs",
"test:fixture-fallback": "sh ./test/scripts/setup-fixture-app-fallback-smoke.sh",
"test:output-economy": "vitest run --project output-economy",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
ANDROID_FRESHNESS_WINDOW_MS,
androidFreshnessReason,
buildSnapshotSignatures,
isLikelyStaleSnapshotDrop,
isNavigationSensitiveAction,
} from './android.ts';
export {
Expand Down
2 changes: 0 additions & 2 deletions packages/contracts/src/gesture-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ export type GesturePayload =
| TransformGesturePayload
| DragGesturePayload;

export type CoordinateGesturePayload = Exclude<GesturePayload, DragGesturePayload>;

export function readGesturePayload(input: unknown): GesturePayload {
const record = readRecord(input);
const kind = readEnum(record, 'kind', GESTURE_KINDS);
Expand Down
37 changes: 0 additions & 37 deletions packages/contracts/src/interactor-operation-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,6 @@ const device = {
const available = { available: true } as const;
const unavailable = { available: false, reason: 'unsupported-device-kind' } as const;

test('binds every operation the facts admitted, driving the resolved interactor', async () => {
const back = vi.fn(async () => undefined);
const home = vi.fn(async () => undefined);
const resolveInteractor = vi.fn(async () => ({ back, home }) as unknown as Interactor);
const signal = new AbortController().signal;

const operations = bindAdmittedLocalInteractorOperations({
device,
signal,
resolveInteractor,
facts: { back: available, home: available },
});

expect(operations.back).toBeTypeOf('function');
expect(operations.home).toBeTypeOf('function');
await operations.back?.({});
await operations.home?.({});
expect(back).toHaveBeenCalledOnce();
expect(home).toHaveBeenCalledOnce();
});

test('skips an operation the facts refused', () => {
const operations = bindAdmittedLocalInteractorOperations({
device,
Expand All @@ -51,22 +30,6 @@ test('skips an operation the facts refused', () => {

// The facts a caller passes are the ONLY source of truth for what binds — there is no separate,
// caller-maintained operation list that could drift from them.
test('binds every admitted operation the facts name, not just the ones a caller had in mind', () => {
const operations = bindAdmittedLocalInteractorOperations({
device,
signal: new AbortController().signal,
resolveInteractor: vi.fn(),
facts: { back: available, home: available, tvRemote: available },
});

expect(operations.back).toBeTypeOf('function');
expect(operations.home).toBeTypeOf('function');
expect(operations.tvRemote).toBeTypeOf('function');
});

// The walk list and the per-operation binder records are derived from one canonical tuple, so
// they can't drift at the type level; this pins the runtime behavior — every one of the seven
// operations binds when its fact admits it, not just the two or three most tests exercise.
test('binds every one of the seven navigation operations when every fact admits it', async () => {
const back = vi.fn(async () => undefined);
const home = vi.fn(async () => undefined);
Expand Down
4 changes: 0 additions & 4 deletions packages/contracts/src/platform-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,6 @@ export type DeviceBindingRequest = Readonly<{
export type DeviceRuntimeGateway<Operations extends object> = Readonly<{
inspectFacts(device: DeviceInfo): Promise<RuntimeFacts<Operations>>;
bind(request: DeviceBindingRequest): Promise<DeviceBinding<Operations>>;
/** Complete close compatibility capability; unlike bind, this never admits a runtime facet. */
getCloseShutdown?: () => Promise<
import('./device-shutdown-runtime.ts').DeviceShutdownCloseCapability
>;
shutdown(): Promise<void>;
/** Package-coordinated durable lifecycle phases used for startup recovery and daemon shutdown. */
applicationLifecycle?: ApplicationLifecycleResourceLifecycle;
Expand Down
2 changes: 1 addition & 1 deletion packages/host-kit/src/internal/exec-kill-settle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type ExecBackgroundOptions,
} from './exec.ts';
import { shellQuote } from '@agent-device/kernel/device-shell';
import { sleep } from './timeouts.ts';
import { sleep } from './retry.ts';
import { mkdtempForTestSync } from './tmp-dir.fixtures.ts';

// A direct child can hand our stdout/stderr pipes to a descendant, and `close` waits
Expand Down
38 changes: 15 additions & 23 deletions packages/host-kit/src/internal/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,28 +313,28 @@ export async function resolveExecutableOverridePath(
rawPath: string | undefined,
envName: string,
): Promise<string | undefined> {
const candidate = normalizeOverridePath(rawPath, envName, 'executable');
if (!candidate) return undefined;
if (!(await isExecutablePath(candidate))) {
throw new AppError(
'TOOL_MISSING',
`${envName} points to a missing or non-executable file: ${candidate}`,
{ envName, path: candidate },
);
}
return candidate;
return await resolveOverridePath(rawPath, envName, 'executable', isExecutablePath);
}

export async function resolveFileOverridePath(
rawPath: string | undefined,
envName: string,
): Promise<string | undefined> {
const candidate = normalizeOverridePath(rawPath, envName, 'file');
return await resolveOverridePath(rawPath, envName, 'file', isFilePath);
}

async function resolveOverridePath(
rawPath: string | undefined,
envName: string,
kind: 'executable' | 'file',
isUsable: (candidate: string) => Promise<boolean>,
): Promise<string | undefined> {
const candidate = normalizeOverridePath(rawPath, envName, kind);
if (!candidate) return undefined;
if (!(await isFilePath(candidate))) {
if (!(await isUsable(candidate))) {
throw new AppError(
'TOOL_MISSING',
`${envName} points to a missing or non-file path: ${candidate}`,
`${envName} points to a missing or non-${kind} ${kind === 'file' ? 'path' : 'file'}: ${candidate}`,
{ envName, path: candidate },
);
}
Expand Down Expand Up @@ -647,14 +647,6 @@ function createStdinError(
);
}

function createCommandCanceledError(
executable: string,
cmd: string,
args: readonly string[],
): AppError {
return createRequestCanceledError({ cmd, args, executable });
}

function createTimeoutError(
executable: string,
cmd: string,
Expand Down Expand Up @@ -768,7 +760,7 @@ function spawnRejectionError(
err: Error,
): AppError {
return abort.didAbort
? createCommandCanceledError(executable, cmd, args)
? createRequestCanceledError({ cmd, args, executable })
: createSpawnError(executable, cmd, args, err);
}

Expand All @@ -782,7 +774,7 @@ function commandCloseFailure(
stdout: string,
stderr: string,
): AppError | null {
if (abort.didAbort) return createCommandCanceledError(executable, cmd, args);
if (abort.didAbort) return createRequestCanceledError({ cmd, args, executable });
if (exitCode !== 0 && !allowFailure) {
return createExitError(executable, cmd, args, exitCode, stdout, stderr);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/host-kit/src/internal/host-process.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ExecOptions, ExecResult } from './exec.ts';
import { runCmd, runCmdSync } from './exec.ts';
import { sleep } from './timeouts.ts';
import { sleep } from './retry.ts';

const PS_TIMEOUT_MS = 1_000;
const HOST_PS_COMMAND = process.platform === 'win32' ? 'ps' : '/bin/ps';
Expand Down
2 changes: 1 addition & 1 deletion packages/host-kit/src/internal/process-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ownerIdentityMatches,
type OwnerLiveness,
} from './owner-identity.ts';
import { sleep } from './timeouts.ts';
import { sleep } from './retry.ts';

const OWNER_FILE_NAME = 'owner.json';
const DEFAULT_LOCK_TIMEOUT_MS = 30_000;
Expand Down
2 changes: 1 addition & 1 deletion packages/host-kit/src/internal/retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ function computeDelay(base: number, max: number, jitter: number, attempt: number
return Math.max(0, exp + (Math.random() * 2 - 1) * jitterAmount);
}

function sleep(ms: number, signal?: AbortSignal): Promise<void> {
export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
return new Promise((resolve) => {
if (signal?.aborted) {
resolve();
Expand Down
3 changes: 0 additions & 3 deletions packages/host-kit/src/internal/timeouts.ts

This file was deleted.

9 changes: 7 additions & 2 deletions packages/host-kit/src/retry.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { Deadline, type DeadlineClock, isEnvTruthy, retryWithPolicy } from './internal/retry.ts';
export { sleep } from './internal/timeouts.ts';
export {
Deadline,
type DeadlineClock,
isEnvTruthy,
retryWithPolicy,
sleep,
} from './internal/retry.ts';
22 changes: 1 addition & 21 deletions packages/kernel/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,27 +222,7 @@ function readDeviceList(value: unknown): Array<{ id: string; name: string }> {
* error representation because clients immediately rehydrate this wire shape
* into `AppError` before rendering or handling it.
*/
export type DaemonError = {
code: string;
message: string;
cause?: ErrorCause;
hint?: string;
diagnosticId?: string;
/** Path on the DAEMON host. Meaningful to a local caller only (#1801). */
logPath?: string;
/** Why no readable path is named; set by the client, never by the daemon. */
logPathUnavailable?: string;
/**
* Additive locator (#1801) for the request diagnostics record `logPath`
* names, so a remote caller can fetch it over the daemon API instead of
* being handed a path on a filesystem it cannot read.
*/
diagnosticsRecord?: DiagnosticsRecordRef;
details?: ErrorWireDetails;
/** Additive retry and platform-support signals; absent when not derivable. */
retriable?: boolean;
supportedOn?: string;
};
export type DaemonError = NormalizedError;

/** Rehydrate a daemon transport error into the error type used by local callers. */
export function throwDaemonError(error: DaemonError): never {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { expect, test } from 'vitest';
import { attachSnapshotClickabilityEvidence } from '@agent-device/contracts/capture';
import {
rankMaestroCandidates,
selectMaestroSnapshotMatch,
selectMaestroSnapshotMatches,
} from '../runtime-target-ranking.ts';
import { rankMaestroCandidates, selectMaestroSnapshotMatch } from '../runtime-target-ranking.ts';
import { makeSnapshot } from './runtime-target-fixtures.ts';

test('typed target matching preserves snapshot read order', () => {
Expand Down Expand Up @@ -226,22 +222,6 @@ test('authored index sorts by y then x with missing bounds last', () => {
expect(selectMaestroSnapshotMatch(snapshot.nodes, 2)).toBeNull();
});

test('nested childOf applies its own index before scoping the candidate', () => {
const snapshot = makeSnapshot([
{ index: 0, identifier: 'row', rect: { x: 0, y: 200, width: 200, height: 80 } },
{ index: 1, identifier: 'row', rect: { x: 0, y: 20, width: 200, height: 80 } },
{ index: 2, parentIndex: 0, label: 'Delete', rect: { x: 120, y: 220, width: 60, height: 40 } },
{ index: 3, parentIndex: 1, label: 'Delete', rect: { x: 120, y: 40, width: 60, height: 40 } },
]);

expect(
selectMaestroSnapshotMatches(snapshot, {
text: 'Delete',
childOf: { id: 'row', index: 1 },
}).map((node) => node.index),
).toEqual([2]);
});

test('target selection never fabricates a rectangle or promotes to an ancestor', () => {
const snapshot = makeSnapshot([
{
Expand Down
9 changes: 0 additions & 9 deletions packages/maestro/src/internal/runtime-target-ranking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@ export function matchMaestroCandidatesWithResolver(
return { matches, parentMatched };
}

export function selectMaestroSnapshotMatches(
snapshot: SnapshotState,
selector: MaestroSelector,
platform?: MaestroPlatform,
): SnapshotNode[] {
const clickability = platform ? resolveMaestroClickability(snapshot, platform) : undefined;
return createMaestroResolver(snapshot, clickability).resolve(selector).indexed;
}

export function selectMaestroPositionMatches(
snapshot: SnapshotState,
relation: MaestroPositionRelation,
Expand Down
Loading
Loading