Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9a7b576
refactor(move): relocate concurrency torture lane into daemon
thymikee Oct 16, 2026
d690aa0
refactor(move): colocate daemon HTTP server integration test
thymikee Oct 16, 2026
a91d56f
refactor(tests): route daemon scenarios through public runtime
thymikee Oct 16, 2026
f74e212
chore(gates): remove daemon test exports from root tests
thymikee Oct 16, 2026
dcc4640
test(provider): allow recording retry scenario time
thymikee Oct 16, 2026
96fb9bc
chore(exports): retain string-only context fixture path
thymikee Oct 16, 2026
b290643
refactor(move): colocate remote artifact directory test
thymikee Oct 16, 2026
0302aa7
test(remote): colocate directory artifact download coverage
thymikee Oct 16, 2026
ca4fdf0
test(provider): scope fake Apple shutdown and recording
thymikee Oct 16, 2026
d33f24e
test: restore production coverage for daemon export sweep
thymikee Oct 16, 2026
b0622d5
fix: route output gates through daemon API
thymikee Oct 16, 2026
0fda560
test: assert proxy route diagnostics cleanly
thymikee Oct 16, 2026
14d96a8
test: keep proxy diagnostics and cleanup covered
thymikee Oct 16, 2026
0458724
test: assert proxied diagnostics materialize
thymikee Oct 16, 2026
87b0b29
test: restore provider and settle assertions
thymikee Oct 16, 2026
d9432c2
fix(test): redact proxy upstream errors
thymikee Oct 16, 2026
f2592cb
fix(test): pin parity proxy requests to loopback
thymikee Oct 16, 2026
63df0ae
test: assert daemon artifact proxy sequence
thymikee Oct 16, 2026
125a5c2
test: avoid recording proxy error stacks
thymikee Oct 16, 2026
070be35
test: close daemon on proxy startup failure
thymikee Oct 16, 2026
1de8fe0
test: align help recovery cases with samples
thymikee Oct 16, 2026
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 .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ jobs:
with: { gate: integration-node }

- name: Run seeded concurrency torture lane (fast PR sweep)
# #1416's nightly torture lane lives under test/integration/nightly/, out
# #1416's nightly torture lane lives under packages/daemon/test/nightly/, out
# of the test:integration:node glob, so this is a *deliberate* fast PR
# sweep (TORTURE_RUNS default 128 seeds, ~sub-second) — not an accidental
# glob inclusion. The Concurrency Torture Nightly workflow sweeps a much
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
"test:integration:node": "node --experimental-strip-types scripts/node-test-tmpdir.ts --test --experimental-test-isolation=none test/integration/*.test.ts",
"test:integration:macos-coverage": "node --experimental-strip-types scripts/node-test-tmpdir.ts --test test/integration/smoke-macos-coverage.test.ts",
"test:integration": "pnpm test:integration:node && pnpm test:integration:provider",
"test:concurrency-torture": "node --experimental-strip-types scripts/node-test-tmpdir.ts --test test/integration/nightly/concurrency-torture.test.ts",
"test:concurrency-torture": "node --experimental-strip-types scripts/node-test-tmpdir.ts --test packages/daemon/test/nightly/concurrency-torture.test.ts",
"test:replay:ios": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator",
"test:replay:ios-device": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device",
"test:replay:android": "node --experimental-strip-types src/bin.ts test test/integration/replays/android/emulator",
Expand Down
56 changes: 0 additions & 56 deletions packages/daemon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,10 @@
"types": "./src/core/context.ts",
"default": "./src/core/context.ts"
},
"./testing/daemon-command-registry": {
"types": "./src/core/daemon-command-registry.ts",
"default": "./src/core/daemon-command-registry.ts"
},
"./testing/deferred-interaction-outcome": {
"types": "./src/core/deferred-interaction-outcome.ts",
"default": "./src/core/deferred-interaction-outcome.ts"
},
"./testing/device/device-claim-conflict": {
"types": "./src/resources/device/device-claim-conflict.ts",
"default": "./src/resources/device/device-claim-conflict.ts"
},
"./testing/device/device-ready": {
"types": "./src/core/device/device-ready.ts",
"default": "./src/core/device/device-ready.ts"
Expand All @@ -148,10 +140,6 @@
"types": "./src/core/gesture-runtime.ts",
"default": "./src/core/gesture-runtime.ts"
},
"./testing/handlers/session-app-deployment": {
"types": "./src/sessions/handlers/session-app-deployment.ts",
"default": "./src/sessions/handlers/session-app-deployment.ts"
},
"./testing/handlers/snapshot": {
"types": "./src/sessions/handlers/snapshot.ts",
"default": "./src/sessions/handlers/snapshot.ts"
Expand All @@ -164,18 +152,6 @@
"types": "./src/execution/interaction/internal/__tests__/*.ts",
"default": "./src/execution/interaction/internal/__tests__/*.ts"
},
"./testing/interaction/internal/interaction-touch-response": {
"types": "./src/execution/interaction/internal/interaction-touch-response.ts",
"default": "./src/execution/interaction/internal/interaction-touch-response.ts"
},
"./testing/interactor-resolution": {
"types": "./src/core/interactor-resolution.ts",
"default": "./src/core/interactor-resolution.ts"
},
"./testing/ios-app-session-hint": {
"types": "./src/core/ios-app-session-hint.ts",
"default": "./src/core/ios-app-session-hint.ts"
},
"./testing/lease-registry": {
"types": "./src/resources/lease-registry.ts",
"default": "./src/resources/lease-registry.ts"
Expand All @@ -192,10 +168,6 @@
"types": "./src/core/ref-frame.ts",
"default": "./src/core/ref-frame.ts"
},
"./testing/request-binding": {
"types": "./src/server/request-binding.ts",
"default": "./src/server/request-binding.ts"
},
"./testing/request-dispatch-disclosure": {
"types": "./src/execution/request-dispatch-disclosure.ts",
"default": "./src/execution/request-dispatch-disclosure.ts"
Expand All @@ -204,10 +176,6 @@
"types": "./src/core/request-dispatch-ledger.ts",
"default": "./src/core/request-dispatch-ledger.ts"
},
"./testing/request-execution-scope": {
"types": "./src/server/request-execution-scope.ts",
"default": "./src/server/request-execution-scope.ts"
},
"./testing/request-finalization": {
"types": "./src/server/request-finalization.ts",
"default": "./src/server/request-finalization.ts"
Expand All @@ -224,34 +192,18 @@
"types": "./src/server/request-platform-provider-context.ts",
"default": "./src/server/request-platform-provider-context.ts"
},
"./testing/request-router": {
"types": "./src/server/request-router.ts",
"default": "./src/server/request-router.ts"
},
"./testing/request-runtime-binding": {
"types": "./src/core/request-runtime-binding.ts",
"default": "./src/core/request-runtime-binding.ts"
},
"./testing/response-views": {
"types": "./src/server/response-views.ts",
"default": "./src/server/response-views.ts"
},
"./testing/screenshot-runtime": {
"types": "./src/execution/screenshot-runtime.ts",
"default": "./src/execution/screenshot-runtime.ts"
},
"./testing/scroll-until": {
"types": "./src/execution/scroll-until.ts",
"default": "./src/execution/scroll-until.ts"
},
"./testing/selector-capture-binding": {
"types": "./src/execution/selector-capture-binding.ts",
"default": "./src/execution/selector-capture-binding.ts"
},
"./testing/selector-match-errors": {
"types": "./src/execution/selector-match-errors.ts",
"default": "./src/execution/selector-match-errors.ts"
},
"./testing/selector-runtime": {
"types": "./src/execution/selector-runtime.ts",
"default": "./src/execution/selector-runtime.ts"
Expand All @@ -276,10 +228,6 @@
"types": "./src/sessions/session-observability/__tests__/*.ts",
"default": "./src/sessions/session-observability/__tests__/*.ts"
},
"./testing/session-recovery-hints": {
"types": "./src/core/session-recovery-hints.ts",
"default": "./src/core/session-recovery-hints.ts"
},
"./testing/session-snapshot": {
"types": "./src/execution/session-snapshot.ts",
"default": "./src/execution/session-snapshot.ts"
Expand Down Expand Up @@ -311,10 +259,6 @@
"./testing/wait-runtime": {
"types": "./src/execution/wait-runtime.ts",
"default": "./src/execution/wait-runtime.ts"
},
"./testing/web-session-names": {
"types": "./src/sessions/web-session-names.ts",
"default": "./src/sessions/web-session-names.ts"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { isSessionRecording } from '../session-script-publication-capability.ts'
* (`selector-recording.test.ts` covers which commands set `interactiveObservation`).
*/
import { test, expect } from 'vitest';
import { recordActionEntry } from '../session-action-recorder.ts';
import { recordActionEntry, type RecordActionEntry } from '../session-action-recorder.ts';
import { authoringPublication, makeIosSession } from '../../__tests__/support/session-factories.ts';

test('an observation-only action is excluded while repair-armed and no --record is given', () => {
Expand Down Expand Up @@ -64,6 +64,38 @@ test('an observation-only action records normally outside a repair-armed session
expect(session.actions.map((a) => a.command)).toEqual(['is']);
});

test('ordinary action history preserves authored commands, positionals, and replay flags', () => {
const session = makeIosSession('default');
const entries = [
{ command: 'hover', positionals: ['@e4'], flags: {}, result: {} },
{ command: 'click', positionals: ['@e4'], flags: {}, result: {} },
{
command: 'fill',
positionals: ['@e3', 'qa@example.test'],
flags: { delayMs: 1 },
result: {},
},
{ command: 'type', positionals: [' ok'], flags: {}, result: {} },
{ command: 'pan', positionals: ['195', '320'], flags: { pointerCount: 2 }, result: {} },
{ command: 'tv-remote', positionals: ['select'], flags: {}, result: {} },
{ command: 'fold', positionals: [], flags: { keyframes: 'fold.json' }, result: {} },
{
command: 'trigger-app-event',
positionals: ['screenshot_taken'],
flags: {},
result: {},
},
{ command: 'back', positionals: [], flags: {}, result: {} },
{ command: 'home', positionals: [], flags: {}, result: {} },
] satisfies RecordActionEntry[];

for (const entry of entries) recordActionEntry(session, entry);

expect(
session.actions.map(({ command, positionals, flags }) => ({ command, positionals, flags })),
).toEqual(entries.map(({ command, positionals, flags }) => ({ command, positionals, flags })));
});

test('a mutating action is never excluded, repair-armed or not', () => {
const session = makeIosSession('default', {
scriptPublication: {
Expand Down Expand Up @@ -123,6 +155,19 @@ test('--no-record still takes precedence over an observation-only action, repair
expect(session.actions).toHaveLength(0);
});

test('--no-record suppresses a mutating coordinate press from action history', () => {
const session = makeIosSession('default');
const action = recordActionEntry(session, {
command: 'press',
positionals: ['50', '60'],
flags: { noRecord: true },
result: { x: 50, y: 60 },
});

expect(action).toBeUndefined();
expect(session.actions).toHaveLength(0);
});

test('parameterized fills keep literals out of recording state with deterministic placeholders', () => {
const session = makeIosSession('default');
const password = 'literal-password-1348';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect, test } from 'vitest';
import type { PressCommandResult } from '@agent-device/contracts/interaction';
import { buildInteractionResponseData } from './interaction-touch-response.ts';

test('the response builder keeps capture evidence out of serialized touch payloads', () => {
const node = { ref: 'e1', index: 0, type: 'Button', label: 'Continue' };
const result = {
kind: 'selector',
point: { x: 100, y: 200 },
target: { kind: 'selector', selector: 'label=Continue' },
node,
selectorChain: ['label=Continue'],
preAction: { nodes: [node] },
backendResult: { ref: 'e1' },
targetEvidence: { id: 'target-evidence-private' },
} as PressCommandResult & { targetEvidence: { id: string } };

const payloads = buildInteractionResponseData({
source: { kind: 'runtime', result },
referenceFrame: undefined,
});

expect(payloads.recordedTarget).toEqual({ node, preActionNodes: [node] });
for (const payload of [payloads.result, payloads.responseData]) {
expect(payload).not.toHaveProperty('node');
expect(payload).not.toHaveProperty('preAction');
expect(payload).not.toHaveProperty('targetEvidence');
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
mockHoverRef,
mockLongPressPoint,
mockTapPoint,
mockTypeText,
resetGetRuntimeFixture,
runtimeBindingSpies,
} from '../../__tests__/interaction-get-runtime-fixture.ts';
Expand Down Expand Up @@ -237,7 +238,69 @@ test('hover @ref on web dispatches through the provider hoverRef route, not coor
});
expect(mockHoverRef).toHaveBeenCalledWith(expect.objectContaining({ ref: '@e1' }));
expect(mockHoverPoint).not.toHaveBeenCalled();
expect(sessionStore.get(sessionName)?.actions[0]?.command).toBe('hover');
const hoverAction = sessionStore.get(sessionName)?.actions[0];
expect(hoverAction?.command).toBe('hover');
expect(hoverAction?.result).toMatchObject({ ref: 'e1', gesture: 'hover' });
expect(hoverAction?.result).not.toHaveProperty('x');
expect(hoverAction?.result).not.toHaveProperty('y');

const typeResponse = await handleInteractionCommands({
createCommandSurface: touchCreateCommandSurface,
req: {
token: 't',
session: sessionName,
command: 'type',
positionals: [' ok'],
flags: {},
},
sessionName,
sessionStore,
contextFromFlags,
...getRuntimeBindings(),
});

expect(typeResponse?.ok).toBe(true);
expect(mockTypeText).toHaveBeenCalledOnce();
expect(sessionStore.get(sessionName)?.actions.at(-1)).toMatchObject({
command: 'type',
positionals: [' ok'],
});
});

test('click @ref on web records the ref result without fabricated coordinates', async () => {
const sessionStore = makeSessionStore();
const sessionName = 'click-ref';
const session = makeSession(sessionName);
session.device = WEB_DESKTOP_DEVICE;
session.snapshot = {
nodes: attachRefs([{ index: 0, role: 'link', label: 'Second message', enabled: true }]),
createdAt: Date.now(),
backend: 'web',
};
sessionStore.publish(sessionName, session);

const response = await handleInteractionCommands({
createCommandSurface: touchCreateCommandSurface,
req: {
token: 't',
session: sessionName,
command: 'click',
positionals: ['@e1'],
flags: {},
},
sessionName,
sessionStore,
contextFromFlags,
...getRuntimeBindings(),
});

expect(response).toMatchObject({ ok: true, data: { ref: 'e1' } });
const action = sessionStore.get(sessionName)?.actions[0];
expect(action?.command).toBe('click');
expect(action?.positionals).toEqual(['@e1']);
expect(action?.result).toMatchObject({ ref: 'e1' });
expect(action?.result).not.toHaveProperty('x');
expect(action?.result).not.toHaveProperty('y');
});

test('hover selector on web resolves the target and dispatches coordinate hover', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ test('find get-text digest keeps ref + text, drops the verbose node', () => {
expect('node' in digest).toBe(false);
});

test('a text read keeps every OTHER cheap field (e.g. warning) while dropping the node', () => {
test('selector text digest preserves a recovery warning while dropping the verbose node', () => {
const digest = findView!(
{
ref: '@e7',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import assert from 'node:assert/strict';
import crypto from 'node:crypto';
import fs from 'node:fs';
import http from 'node:http';
import os from 'node:os';
import path from 'node:path';
import { test } from 'vitest';
import { AppError } from '@agent-device/kernel/errors';
import {
cleanupUploadedArtifact,
prepareUploadedArtifact,
trackDownloadableArtifact,
} from '@agent-device/daemon/testing/artifact-tracking';
} from '../../resources/artifact-tracking.ts';
import { DAEMON_RPC_PROTOCOL_VERSION } from '@agent-device/contracts/daemon-http';
import { createDaemonHttpServer } from '@agent-device/daemon/testing/server/http-server';
import { createDaemonHttpServer } from './http-server.ts';
import {
emitRequestProgress,
getRequestSignal,
Expand All @@ -24,9 +23,9 @@ import {
import {
closeLoopbackServer,
listenOnLoopback,
mkdtempForTestSync,
skipWhenLoopbackUnavailable,
} from '@agent-device/test-kit';
import { restoreEnv } from './harness.ts';

type RpcResponse = {
status: number;
Expand Down Expand Up @@ -476,7 +475,7 @@ test('Provider-backed integration daemon HTTP server accepts uploads and streams
return;
}

const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-device-http-server-'));
const root = mkdtempForTestSync('agent-device-http-server-');
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
const downloadablePath = path.join(root, 'screen.png');
fs.writeFileSync(downloadablePath, 'png-binary');
const artifactId = trackDownloadableArtifact({
Expand Down Expand Up @@ -526,7 +525,6 @@ test('Provider-backed integration daemon HTTP server accepts uploads and streams
assert.equal(rejectedUpload.status, 401);
} finally {
await closeLoopbackServer(server);
fs.rmSync(root, { recursive: true, force: true });
}
});

Expand Down Expand Up @@ -759,7 +757,7 @@ test('Provider-backed integration daemon HTTP auth hook can scope tenants and re
return;
}

const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-device-http-auth-hook-'));
const root = mkdtempForTestSync('agent-device-http-auth-hook-');
const hookPath = path.join(root, 'auth-hook.mjs');
fs.writeFileSync(
hookPath,
Expand Down Expand Up @@ -817,8 +815,8 @@ test('Provider-backed integration daemon HTTP auth hook can scope tenants and re
if (server) {
await closeLoopbackServer(server);
}
restoreEnv('AGENT_DEVICE_HTTP_AUTH_HOOK', previousHook);
fs.rmSync(root, { recursive: true, force: true });
if (previousHook === undefined) delete process.env.AGENT_DEVICE_HTTP_AUTH_HOOK;
else process.env.AGENT_DEVICE_HTTP_AUTH_HOOK = previousHook;
}
});

Expand Down
Loading
Loading