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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{"type":"replay_test_attempt_start","ts":"2026-09-19T14:27:51.710Z","replayPath":"/tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-faJ9EM/01-flow.yaml","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1","requestId":"req-maestro-test-wire-true:test:1:01-flow.yaml:attempt:1"}
{"type":"replay_test_attempt_stop","ts":"2026-09-19T14:27:51.712Z","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1","ok":true,"timedOut":false,"durationMs":2}
{"type":"replay_test_finalize_start","ts":"2026-09-19T14:27:51.712Z","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1"}
{"type":"replay_test_finalize_stop","ts":"2026-09-19T14:27:51.712Z","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1","ok":true,"durationMs":0}
{"type":"replay_test_cleanup_start","ts":"2026-09-19T14:27:51.712Z","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1"}
{"type":"replay_test_cleanup_stop","ts":"2026-09-19T14:27:51.712Z","session":"default:test:req-maestro-test-wire-true:1-01-flow:attempt-1","ok":true,"durationMs":0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
file: /tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-faJ9EM/01-flow.yaml
session: default:test:req-maestro-test-wire-true:1-01-flow:attempt-1
attempt: 1/1
status: passed
replayed: 1
healed: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{"type":"replay_test_attempt_start","ts":"2026-09-19T14:27:51.713Z","replayPath":"/tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-faJ9EM/02-flow.yaml","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1","requestId":"req-maestro-test-wire-true:test:2:02-flow.yaml:attempt:1"}
{"type":"replay_test_attempt_stop","ts":"2026-09-19T14:27:51.714Z","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1","ok":true,"timedOut":false,"durationMs":1}
{"type":"replay_test_finalize_start","ts":"2026-09-19T14:27:51.714Z","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1"}
{"type":"replay_test_finalize_stop","ts":"2026-09-19T14:27:51.714Z","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1","ok":true,"durationMs":0}
{"type":"replay_test_cleanup_start","ts":"2026-09-19T14:27:51.714Z","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1"}
{"type":"replay_test_cleanup_stop","ts":"2026-09-19T14:27:51.714Z","session":"default:test:req-maestro-test-wire-true:2-02-flow:attempt-1","ok":true,"durationMs":0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
file: /tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-faJ9EM/02-flow.yaml
session: default:test:req-maestro-test-wire-true:2-02-flow:attempt-1
attempt: 1/1
status: passed
replayed: 1
healed: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{"type":"replay_test_attempt_start","ts":"2026-09-19T14:27:51.718Z","replayPath":"/tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-QE9WSU/01-flow.yaml","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1","requestId":"req-maestro-test-wire-undefined:test:1:01-flow.yaml:attempt:1"}
{"type":"replay_test_attempt_stop","ts":"2026-09-19T14:27:51.718Z","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1","ok":true,"timedOut":false,"durationMs":0}
{"type":"replay_test_finalize_start","ts":"2026-09-19T14:27:51.718Z","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1"}
{"type":"replay_test_finalize_stop","ts":"2026-09-19T14:27:51.719Z","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1","ok":true,"durationMs":1}
{"type":"replay_test_cleanup_start","ts":"2026-09-19T14:27:51.719Z","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1"}
{"type":"replay_test_cleanup_stop","ts":"2026-09-19T14:27:51.719Z","session":"default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1","ok":true,"durationMs":0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
file: /tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-QE9WSU/01-flow.yaml
session: default:test:req-maestro-test-wire-undefined:1-01-flow:attempt-1
attempt: 1/1
status: passed
replayed: 1
healed: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{"type":"replay_test_attempt_start","ts":"2026-09-19T14:27:51.720Z","replayPath":"/tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-QE9WSU/02-flow.yaml","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1","requestId":"req-maestro-test-wire-undefined:test:2:02-flow.yaml:attempt:1"}
{"type":"replay_test_attempt_stop","ts":"2026-09-19T14:27:51.720Z","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1","ok":true,"timedOut":false,"durationMs":0}
{"type":"replay_test_finalize_start","ts":"2026-09-19T14:27:51.720Z","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1"}
{"type":"replay_test_finalize_stop","ts":"2026-09-19T14:27:51.720Z","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1","ok":true,"durationMs":0}
{"type":"replay_test_cleanup_start","ts":"2026-09-19T14:27:51.721Z","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1"}
{"type":"replay_test_cleanup_stop","ts":"2026-09-19T14:27:51.721Z","session":"default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1","ok":true,"durationMs":0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appId: com.example.app
---
- evalScript: ${output.sum = 1 + 2}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
file: /tmp/agent-device-test-run-23838-sCm2rx/agent-device-maestro-remote-test-QE9WSU/02-flow.yaml
session: default:test:req-maestro-test-wire-undefined:2-02-flow:attempt-1
attempt: 1/1
status: passed
replayed: 1
healed: 0
1 change: 1 addition & 0 deletions packages/ad-script/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export {
} from './internal/format.ts';

export {
inferFillText,
readRecordedInputVariableName,
recordedInputPlaceholder,
validateRecordedInputVariableName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ test('matchesLocalIdentity: with no recorded id, role+label must both match, abs
// ---------------------------------------------------------------------------
// Shared id demotion. `idMatchCountInTree` / `demoteNonUniqueLocalIdentity`
// are the ONE shared uniqueness predicate behind both id-demotion sites (the
// `target-v1` identity tuple `session-target-evidence.ts` writes at record
// `target-v1` identity tuple `@agent-device/selectors/target-evidence` writes at record
// time, and the selector chain `buildSelectorChainForNode` builds — see
// `src/__tests__/selectors-build.test.ts` for that consumer's own coverage). Neither
// consumer test exercises the pair directly; these tests pin the shared
Expand Down
22 changes: 22 additions & 0 deletions packages/ad-script/src/internal/recorded-input.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { SessionAction } from '@agent-device/contracts/session';
import { AppError } from '@agent-device/kernel/errors';
import { REPLAY_VAR_KEY_RE } from './script.ts';

Expand Down Expand Up @@ -29,3 +30,24 @@ export function readRecordedInputVariableName(value: string): string | undefined
if (!match?.[1] || match[1].startsWith('AD_')) return undefined;
return match[1];
}

/** The text a recorded `fill` typed: its result text when recorded, else its positionals past the target. */
export function inferFillText(action: SessionAction): string {
const resultText = action.result?.text;
if (typeof resultText === 'string') return resultText;
const positionals = action.positionals ?? [];
if (positionals.length === 0) return '';
const first = positionals[0];
if (first?.startsWith('@')) {
if (positionals.length >= 3) return positionals.slice(2).join(' ');
return positionals.slice(1).join(' ');
}
if (
positionals.length >= 3 &&
!Number.isNaN(Number(positionals[0])) &&
!Number.isNaN(Number(positionals[1]))
) {
return positionals.slice(2).join(' ');
}
return positionals.slice(1).join(' ');
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* deviations"): this used to live in `@agent-device/ad-replay`'s
* `target-identity.ts`, reasoning that it was engine-owned policy rather
* than script vocabulary. In practice its only real consumers were the
* daemon's RECORD-time self-check (`src/daemon/session-target-evidence.ts`)
* daemon's RECORD-time self-check (`@agent-device/selectors/target-evidence`)
* and its REPLAY-time classification wrapper
* (`src/daemon/replay/internal/session-replay-target-classification.ts`) — both
* daemon files, neither reachable through `inspectAdReplay`/`runAdReplay`.
Expand Down
4 changes: 2 additions & 2 deletions packages/ad-script/src/internal/target-annotation-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* ADR 0012 decision 3: the record/replay-shared local-identity + ancestry-
* prefix matching over versioned `.ad` target-binding evidence, plus the
* bounded diagnostic diffs built on top of it. Both the writer (over
* `SnapshotNode`-derived values, `src/daemon/session-target-evidence.ts`) and
* `SnapshotNode`-derived values, `@agent-device/selectors/target-evidence`) and
* replay-time verification (`src/daemon/replay/internal/session-replay-target-classification.ts` and
* `src/commands/interaction/runtime/selector-wait.ts`) share this verbatim so both
* sides compute the SAME identity/ancestry match by construction (#1478 P5
Expand Down Expand Up @@ -49,7 +49,7 @@ type IdentityTreeNode = Pick<RawSnapshotNode, 'type' | 'identifier' | 'label'>;
* ADR 0012 decision 3: the ONE snapshot-node local-identity reader —
* normalized (NFC, label whitespace collapse, `normalizeType` role) AND
* 256-byte field-capped, on every path. Shared by the record-time writer
* (`src/daemon/session-target-evidence.ts`), replay-time verification
* (`@agent-device/selectors/target-evidence`), replay-time verification
* (`src/daemon/replay/internal/session-replay-target-verification.ts`), and the
* dispatch-side post-resolution guard
* (`src/commands/interaction/runtime/resolution.ts`), so all three compute
Expand Down
2 changes: 1 addition & 1 deletion packages/ad-script/src/internal/target-annotation-serde.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* ADR 0012 decision 3: versioned `.ad` target-binding evidence — the
* comment-line SERDE half shared by the writer
* (`src/daemon/session-target-evidence.ts`) and the parser
* (`@agent-device/selectors/target-evidence`) and the parser
* (`packages/ad-script/src/internal/script.ts`). Owns the wire type,
* canonical field order, normalization, size caps, and payload
* parsing/validation.
Expand Down
4 changes: 4 additions & 0 deletions packages/capture-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
"types": "./src/ios-snapshot-engine/index.ts",
"default": "./src/ios-snapshot-engine/index.ts"
},
"./ios-snapshot-tree": {
"types": "./src/ios-snapshot-engine/tree.ts",
"default": "./src/ios-snapshot-engine/tree.ts"
},
"./ios-snapshot-planning": {
"types": "./src/ios-snapshot-planning.ts",
"default": "./src/ios-snapshot-planning.ts"
Expand Down
2 changes: 1 addition & 1 deletion packages/capture-kit/src/ios-snapshot-engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export {
presentIosInteractiveSnapshot,
} from './semantic-index.ts';
export { collectIosStructuralIdentifierSuppression } from './noise-structural.ts';
export { findNearestScrollableContainer, mergeReplacement, updateReplacement } from './tree.ts';
export { mergeReplacement, updateReplacement } from './tree.ts';
export { IosSnapshotEngineError, toIosSnapshotEngineErrorDetails } from './types.ts';
export type { SnapshotTreeRuleContext } from './tree.ts';
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import type { CommandFlags } from '@agent-device/contracts/command';
import type { CommandFlags } from './command-flags.ts';
import type {
DaemonInstallSource,
LeaseBackend,
DaemonInstallSource as PublicDaemonInstallSource,
DaemonRequestMeta as PublicDaemonRequestMeta,
DaemonRequest as WireRequest,
} from '@agent-device/kernel/contracts';
import type { PlatformSelector } from '@agent-device/kernel/device';

export type DaemonInstallSource = PublicDaemonInstallSource;

/**
* Request metadata as a client may send it, with the two fields the daemon narrows past what the
* wire can express and the lease coordinates the router reads. Public throughout: nothing here
* names live session state.
*/
type DaemonRequestMeta = Omit<PublicDaemonRequestMeta, 'installSource' | 'lockPlatform'> & {
export type DaemonWireRequestMeta = Omit<
PublicDaemonRequestMeta,
'installSource' | 'lockPlatform'
> & {
installSource?: DaemonInstallSource;
lockPlatform?: PlatformSelector;
leaseBackend?: LeaseBackend;
Expand All @@ -24,15 +25,15 @@ type DaemonRequestMeta = Omit<PublicDaemonRequestMeta, 'installSource' | 'lockPl
/**
* A dispatched request with nothing daemon-private in it: `token` and `session` are required as
* they are by dispatch time, and `flags` is narrowed to the `CommandFlags` vocabulary the wire
* cannot enforce. There is no `internal` key and no property path to `SessionState` or
* `DeviceLease`, which is what lets a consumer read a request — its command, flags and public
* metadata — without depending on the daemon's live session record. `daemon-request.ts` adds the
* daemon-only half on top of this shape; a zone that only needs to classify a command takes
* `contracts/dispatched-command.ts` instead.
* cannot enforce. There is no `internal` key and no property path to the daemon's live session
* record or its admitted lease, which is what lets a package read a request — its command, flags
* and public metadata — and build the next one to dispatch. The daemon's `daemon-request.ts`
* adds the daemon-only half on top of this shape; a zone that only needs to classify a command
* takes `DispatchedCommand` instead.
*/
export type DaemonWireRequest = Omit<WireRequest, 'token' | 'session' | 'flags' | 'meta'> & {
token: string;
session: string;
flags?: CommandFlags;
meta?: DaemonRequestMeta;
meta?: DaemonWireRequestMeta;
};
19 changes: 19 additions & 0 deletions packages/contracts/src/device-inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,22 @@ function deviceInventoryGroupForDevice(device: DeviceInfo): DeviceInventoryGroup
if (isIosFamily(device) || isMacOs(device)) return 'apple';
return device.platform;
}

/**
* The refusal reasons a host-local device claim can answer with when another owner holds the
* device. Replay reads them to tell an infrastructure conflict from a script failure.
*/
export type DeviceClaimConflictReason =
| 'DEVICE_CLAIM_LIVE_OWNER'
| 'DEVICE_CLAIM_RECOVERY_PENDING'
| 'DEVICE_CLAIM_OWNER_UNCERTAIN';

const DEVICE_CLAIM_CONFLICT_REASONS = new Set<DeviceClaimConflictReason>([
'DEVICE_CLAIM_LIVE_OWNER',
'DEVICE_CLAIM_RECOVERY_PENDING',
'DEVICE_CLAIM_OWNER_UNCERTAIN',
]);

export function isDeviceClaimConflictReason(value: unknown): value is DeviceClaimConflictReason {
return DEVICE_CLAIM_CONFLICT_REASONS.has(value as DeviceClaimConflictReason);
}
1 change: 1 addition & 0 deletions packages/contracts/src/facades/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export {
export type { DaemonBatchStep } from '../batch-step.ts';
export type { CliFlags, DaemonExcludedCliFlag } from '../cli-flags.ts';
export type { CommandFlags, MaestroRuntimeFlags } from '../command-flags.ts';
export type { DaemonWireRequest, DaemonWireRequestMeta } from '../daemon-wire-request.ts';
export type { DispatchedCommand } from '../dispatched-command.ts';
export { readOptionalInteger, readOptionalNumber } from '../input-validation.ts';
export {
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/src/facades/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export {
WEB_DESKTOP_DEVICE,
countDeviceInventoryByGroup,
filterDeviceInventoryProjection,
isDeviceClaimConflictReason,
projectProviderDeviceInventoryRequest,
} from '../device-inventory.ts';
export type {
DeviceClaimConflictReason,
DeviceInventoryGroup,
DeviceInventoryGroupCounts,
DeviceInventoryRequest,
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/src/facades/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export type { RefFrameEffect } from '../ref-frame-effect.ts';
export { REPLAY_TARGET_GUARD_MISMATCH_REASON, WAIT_LANDMARK_MISMATCH_REASON } from '../replay.ts';
export type {
ReplayCommandResult,
ReplayDispatchOptions,
ReplayOpenLifecycle,
ReplayObservationAuthority,
ReplayObservationAuthorityBinder,
ReplayObservationCapture,
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/facades/session.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type { SessionAction } from '../session-action.ts';
export type { SessionScope } from '../session-scope.ts';
export { SESSION_SURFACES, parseSessionSurface } from '../session-surface.ts';
export type { SessionSurface } from '../session-surface.ts';
Loading
Loading