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
9 changes: 2 additions & 7 deletions .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,9 @@
"exports": ["applyRuntimeHintValues", "clearRuntimeHintValues"]
},
{
// createAgentDevice and localCommandPolicy are consumed by the lazy imports in src/cli/commands/{recording,screenshot}.ts; Fallow cannot connect the dynamic member reads.
// createAgentDevice is consumed by the lazy imports in src/cli/commands/{recording,screenshot}.ts; Fallow cannot connect the dynamic member reads.
"file": "src/runtime.ts",
"exports": ["createAgentDevice", "localCommandPolicy"]
"exports": ["createAgentDevice"]
},
{
// The contracts snapshot façade must re-export every symbol of ../snapshot-scope.ts (scripts/layering/package-boundaries.test.ts, façade exhaustiveness); callers import it from @agent-device/contracts/snapshot-scope, so the façade copy has no consumer.
Expand Down Expand Up @@ -576,11 +576,6 @@
"summarizeAgentBrowserProcesses",
"summarizeManagedAgentBrowserProcesses"
]
},
{
// src/runtime.ts keeps its published type surface while its implementation moves to src/command-runtime/runtime-factory.ts (#2540, #3294). These two re-exports have no in-repo consumer; they stay so the module's exported shape is unchanged.
"file": "src/runtime.ts",
"exports": ["CommandPolicy", "CommandSessionRecord"]
}
],
"usedClassMembers": [
Expand Down
4 changes: 2 additions & 2 deletions docs/adr/0019-request-bound-platform-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -982,8 +982,8 @@ anchor, not as a proposed commitment.
`packages/provider-limrun/src/`; these root files are the composition-time wiring, comparable in
role to `src/platform-runtime.ts` itself.
- `src/core` (R13-governed consumer seams), `src/commands`, `src/cli`, `src/cli-schema`,
`src/mcp`, `src/client`, `src/sdk`, `src/ai-sdk`, `src/remote`, `src/request`, `src/metro`,
`src/backend*.ts`. Not migration targets: these are command-surface, protocol-projection, and
`src/mcp`, `src/client`, `src/sdk`, `src/ai-sdk`, `src/remote`, `src/request`, `src/metro`.
Not migration targets: these are command-surface, protocol-projection, and
daemon-owned zones this ADR keeps in `src/` by design, not residue awaiting a package.

Neither number has an owning issue, gate, or accepted budget yet. Treat both as inputs to a
Expand Down
20 changes: 20 additions & 0 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@
"types": "./src/application-lifecycle-runtime-plan.ts",
"default": "./src/application-lifecycle-runtime-plan.ts"
},
"./artifact-adapter": {
"types": "./src/artifact-adapter.ts",
"default": "./src/artifact-adapter.ts"
},
"./async-lifecycle": {
"types": "./src/async-lifecycle.ts",
"default": "./src/async-lifecycle.ts"
Expand Down Expand Up @@ -116,10 +120,18 @@
"types": "./src/back-runtime.ts",
"default": "./src/back-runtime.ts"
},
"./backend": {
"types": "./src/backend.ts",
"default": "./src/backend.ts"
},
"./backend-diagnostics": {
"types": "./src/backend-diagnostics.ts",
"default": "./src/backend-diagnostics.ts"
},
"./backend-snapshot-options": {
"types": "./src/backend-snapshot-options.ts",
"default": "./src/backend-snapshot-options.ts"
},
"./boot-failure": {
"types": "./src/boot-failure.ts",
"default": "./src/boot-failure.ts"
Expand Down Expand Up @@ -152,6 +164,10 @@
"types": "./src/command-platform-execution.ts",
"default": "./src/command-platform-execution.ts"
},
"./command-policy": {
"types": "./src/command-policy.ts",
"default": "./src/command-policy.ts"
},
"./daemon-http": {
"types": "./src/daemon-http.ts",
"default": "./src/daemon-http.ts"
Expand Down Expand Up @@ -360,6 +376,10 @@
"types": "./src/platform-runtime-unavailable.ts",
"default": "./src/platform-runtime-unavailable.ts"
},
"./runtime-contract": {
"types": "./src/runtime-contract.ts",
"default": "./src/runtime-contract.ts"
},
"./runtime-contract-error": {
"types": "./src/runtime-contract-error.ts",
"default": "./src/runtime-contract-error.ts"
Expand Down
95 changes: 95 additions & 0 deletions packages/contracts/src/artifact-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { DaemonArtifactType } from '@agent-device/kernel/contracts';

export type FileInputRef =
| {
kind: 'path';
path: string;
}
| {
kind: 'uploadedArtifact';
id: string;
};

export type FileOutputRef =
| {
kind: 'path';
path: string;
}
| {
kind: 'downloadableArtifact';
clientPath?: string;
fileName?: string;
};

export type ArtifactDescriptor =
| {
kind: 'localPath';
field: string;
artifactType: DaemonArtifactType | undefined;
path: string;
fileName?: string;
metadata?: Record<string, unknown>;
}
| {
kind: 'artifact';
field: string;
artifactType: DaemonArtifactType | undefined;
artifactId: string;
fileName?: string;
url?: string;
clientPath?: string;
metadata?: Record<string, unknown>;
};

export type OutputVisibility = 'client-visible' | 'internal';

export type ResolvedInputFile = {
path: string;
cleanup?: () => Promise<void>;
};

export type ReservedOutputFile = {
path: string;
visibility: OutputVisibility;
publish: () => Promise<ArtifactDescriptor | undefined>;
cleanup?: () => Promise<void>;
};

export type TemporaryFile = {
path: string;
visibility: 'internal';
cleanup: () => Promise<void>;
};

export type ResolveInputOptions = {
usage: string;
field?: string;
};

export type ReserveOutputOptions = {
field: string;
ext: string;
artifactType: DaemonArtifactType | undefined;
requestedClientPath?: string;
visibility?: OutputVisibility;
};

export type CreateTempFileOptions = {
prefix: string;
ext: string;
};

export type ArtifactAdapter = {
resolveInput(ref: FileInputRef, options: ResolveInputOptions): Promise<ResolvedInputFile>;
reserveOutput(
ref: FileOutputRef | undefined,
options: ReserveOutputOptions,
): Promise<ReservedOutputFile>;
createTempFile(options: CreateTempFileOptions): Promise<TemporaryFile>;
};

export type LocalArtifactAdapterOptions = {
cwd?: string;
tempDir?: string;
rootDir?: string;
};
35 changes: 17 additions & 18 deletions src/backend.ts → packages/contracts/src/backend.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import type {
ScreenshotResultData,
SnapshotCaptureAnnotations,
SnapshotDiagnosticsSummary,
} from '@agent-device/contracts/capture';
import type { JsonObject } from '@agent-device/contracts/client';
import type { AppsFilter, DeviceRotation } from '@agent-device/contracts/device';
import type { AlertAction, AlertInfo } from '@agent-device/contracts/alert-contract';
import type { BackMode } from '@agent-device/contracts/back-mode';
import type { ClickButton } from '@agent-device/contracts/click-button';
import type { GesturePlan } from '@agent-device/contracts/gesture-plan-types';
import type { RepeatedInput } from '@agent-device/contracts/interaction';
import type { ScrollDirection } from '@agent-device/contracts/scroll-gesture';
import type { ResolvedScrollExecutionOptions } from '@agent-device/contracts/scroll-command';
import type { TvRemoteButton } from '@agent-device/contracts/tv-remote';
import type { RecordingExportQuality } from '@agent-device/contracts/recording';
import type { SessionSurface } from '@agent-device/contracts/session';
import type { ScreenshotResultData } from './snapshot-types.ts';
import type { SnapshotCaptureAnnotations } from './snapshot-capture-annotations.ts';
import type { SnapshotDiagnosticsSummary } from './snapshot-diagnostics.ts';
import type { JsonObject } from './json.ts';
import type { AppsFilter } from './app-inventory.ts';
import type { DeviceRotation } from './device-rotation.ts';
import type { AlertAction, AlertInfo } from './alert-contract.ts';
import type { BackMode } from './back-mode.ts';
import type { ClickButton } from './click-button.ts';
import type { GesturePlan } from './gesture-plan-types.ts';
import type { RepeatedInput } from './interaction.ts';
import type { ScrollDirection } from './scroll-gesture.ts';
import type { ResolvedScrollExecutionOptions } from './scroll-command.ts';
import type { TvRemoteButton } from './tv-remote.ts';
import type { RecordingExportQuality } from './recording-export-quality.ts';
import type { SessionSurface } from './session-surface.ts';
import type {
DeviceTarget,
Platform,
Expand Down Expand Up @@ -327,7 +326,7 @@ import type {
BackendDumpNetworkResult,
BackendNetworkEntry,
BackendNetworkIncludeMode,
} from '@agent-device/contracts/backend-diagnostics';
} from './backend-diagnostics.ts';

export type {
BackendDiagnosticsPageOptions,
Expand Down
10 changes: 10 additions & 0 deletions packages/contracts/src/command-policy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect, test } from 'vitest';
import { localCommandPolicy, restrictedCommandPolicy } from './command-policy.ts';

test('command policy presets expose local and restricted defaults', () => {
expect(localCommandPolicy().allowLocalInputPaths).toBe(true);
expect(localCommandPolicy().allowLocalOutputPaths).toBe(true);
expect(restrictedCommandPolicy().allowLocalInputPaths).toBe(false);
Comment thread
thymikee marked this conversation as resolved.
expect(restrictedCommandPolicy().allowLocalOutputPaths).toBe(false);
expect(restrictedCommandPolicy({ allowLocalInputPaths: true }).allowLocalInputPaths).toBe(true);
});
23 changes: 23 additions & 0 deletions packages/contracts/src/command-policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { CommandPolicy } from './runtime-contract.ts';

/**
* The command policy presets. Pure literal factories: the caller decides which
* local-path permissions a runtime gets, and these name the two default stances.
*/
export function localCommandPolicy(overrides: Partial<CommandPolicy> = {}): CommandPolicy {
return {
allowLocalInputPaths: true,
allowLocalOutputPaths: true,
maxImagePixels: 20_000_000,
...overrides,
};
}

export function restrictedCommandPolicy(overrides: Partial<CommandPolicy> = {}): CommandPolicy {
return {
allowLocalInputPaths: false,
allowLocalOutputPaths: false,
maxImagePixels: 20_000_000,
...overrides,
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AgentDeviceBackend } from './backend.ts';
import type { ArtifactAdapter } from './io.ts';
import type { ArtifactAdapter } from './artifact-adapter.ts';
import type { SnapshotState } from '@agent-device/kernel/snapshot';

export type CommandPolicy = {
Expand Down
5 changes: 5 additions & 0 deletions scripts/layering/contracts-exports.snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@agent-device/contracts/application-lifecycle-interaction",
"@agent-device/contracts/application-lifecycle-runtime",
"@agent-device/contracts/application-lifecycle-runtime-plan",
"@agent-device/contracts/artifact-adapter",
"@agent-device/contracts/async-lifecycle",
"@agent-device/contracts/audio-probe-result",
"@agent-device/contracts/audio-probe-runtime",
Expand All @@ -26,7 +27,9 @@
"@agent-device/contracts/audio-runtime-plan",
"@agent-device/contracts/back-mode",
"@agent-device/contracts/back-runtime",
"@agent-device/contracts/backend",
"@agent-device/contracts/backend-diagnostics",
"@agent-device/contracts/backend-snapshot-options",
"@agent-device/contracts/boot-failure",
"@agent-device/contracts/capture",
"@agent-device/contracts/click-button",
Expand All @@ -35,6 +38,7 @@
"@agent-device/contracts/clipboard-runtime",
"@agent-device/contracts/command",
"@agent-device/contracts/command-platform-execution",
"@agent-device/contracts/command-policy",
"@agent-device/contracts/daemon-http",
"@agent-device/contracts/deploy-result-target",
"@agent-device/contracts/device",
Expand Down Expand Up @@ -98,6 +102,7 @@
"@agent-device/contracts/remote",
"@agent-device/contracts/replay",
"@agent-device/contracts/runner-lease-context",
"@agent-device/contracts/runtime-contract",
"@agent-device/contracts/runtime-contract-error",
"@agent-device/contracts/runtime-operation-names",
"@agent-device/contracts/screen-capture-contract",
Expand Down
2 changes: 1 addition & 1 deletion scripts/layering/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ test('every production zone is deliberately classified as ranked or unranked', (

test('listSourceFiles includes root-level src/*.ts production files', () => {
const files = new Set(listSourceFiles());
for (const rootFile of ['src/cli.ts', 'src/runtime.ts', 'src/backend.ts']) {
for (const rootFile of ['src/cli.ts', 'src/runtime.ts', 'src/io.ts']) {
assert.ok(files.has(rootFile), `expected ${rootFile} in analyzed source files`);
}
assert.ok(![...files].some((file) => file.endsWith('.test.ts')));
Expand Down
13 changes: 4 additions & 9 deletions scripts/layering/root-module-zones.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,10 @@ export const ROOT_MODULE_ZONES: Readonly<Record<string, readonly string[]>> = {
'(root)': ['src/bin.ts', 'src/cli.ts', 'src/daemon.ts'],
// Imported by core's interactor resolution, so they sit at core's rank.
core: ['src/platform-runtime-android-adb-host.ts', 'src/provider-device-runtime.ts'],
// The in-process command runtime: the backend and artifact contracts it drives and the
// assemblies that bind command families onto it. The daemon executes commands through it.
'command-runtime': [
'src/backend-snapshot-options.ts',
'src/backend.ts',
'src/io.ts',
'src/runtime-contract.ts',
'src/runtime.ts',
],
// The in-process command runtime: the local artifact-adapter implementation and the assembly
// that binds command families onto it. The backend and runtime contracts it drives moved to
// @agent-device/contracts, so the daemon reads that vocabulary from below the daemon.
'command-runtime': ['src/io.ts', 'src/runtime.ts'],
// The platform composition the daemon, CLI and SDK import eagerly. It shares the zone with its
// private `src/platform-runtime/` submodule.
'platform-runtime': [
Expand Down
2 changes: 1 addition & 1 deletion scripts/layering/tracked-sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { execFileSync } from 'node:child_process';

// `src/**/*.ts` only matches NESTED files, so root-level `src/*.ts` (src/cli.ts,
// src/backend.ts) needs its own pathspec or it silently drops out of every scan.
// src/io.ts) needs its own pathspec or it silently drops out of every scan.
// Workspace package sources are production files too (#1490 W0).
const TRACKED_SOURCE_PATHSPECS = [
'src/*.ts',
Expand Down
32 changes: 11 additions & 21 deletions src/__tests__/runtime-public.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,19 @@ import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { test } from 'vitest';
import {
createAgentDevice,
localCommandPolicy,
type AgentDevice,
type CommandSessionStore,
} from '../runtime.ts';
import type { AgentDeviceBackend } from '../backend.ts';
import { createAgentDevice, type AgentDevice } from '../runtime.ts';
import { localCommandPolicy } from '@agent-device/contracts/command-policy';
import type { CommandSessionStore } from '@agent-device/contracts/runtime-contract';
import type { AgentDeviceBackend } from '@agent-device/contracts/backend';
import type { ScreenshotCommandOptions } from '../commands/index.ts';
import {
createLocalArtifactAdapter,
type ArtifactAdapter,
type FileInputRef,
type FileOutputRef,
} from '../io.ts';
import { createLocalArtifactAdapter } from '../io.ts';
import type {
ArtifactAdapter,
FileInputRef,
FileOutputRef,
} from '@agent-device/contracts/artifact-adapter';
import { mkdtempForTestSync } from './test-utils/tmp-dir.ts';
import {
createMemorySessionStore,
restrictedCommandPolicy,
} from '../command-runtime/runtime-factory.ts';
import { createMemorySessionStore } from '../command-runtime/runtime-factory.ts';

const backend = {
platform: 'ios',
Expand Down Expand Up @@ -104,10 +98,6 @@ test('runtime screenshot command cleans reserved output when publish fails', asy

test('runtime policy helpers expose local and restricted defaults', async () => {
assert.equal(typeof createLocalArtifactAdapter, 'function');
assert.equal(localCommandPolicy().allowLocalInputPaths, true);
assert.equal(localCommandPolicy().allowLocalOutputPaths, true);
assert.equal(restrictedCommandPolicy().allowLocalInputPaths, false);
assert.equal(restrictedCommandPolicy({ allowLocalInputPaths: true }).allowLocalInputPaths, true);
const store = createMemorySessionStore([{ name: 'default' }]);
assert.equal((await store.get('default'))?.name, 'default');
});
Expand Down
Loading
Loading