Task Definition and Execution API
Task descriptors, target constraints, invocation data, capability requirements, composition, and named assertions.
Use this reference when defining a task and its success criteria. Start with the Task API overview for a complete minimal module and the distinction between platform, device kind, execution mode, and framework.
TaskDefinition
| Field | Type | Required | Contract |
|---|---|---|---|
schemaVersion | 1 | 2 | No | Defaults to version 1, which requires an SDK app-tool provider. Use version 2 for capability-aware execution. |
requires | ExecutionRequirements | No | Version 2 only: required capabilities and executable appTools IDs, checked before execution. |
enabled | boolean | No | Defaults to true. Disabled tasks remain discoverable and reject execution. |
appId | string | No | Exact optional app scope. |
title | string | Yes | Short discovery label. |
description | string | Yes | Complete workflow and authoritative result. |
startingState | string | No | Advisory prerequisites, 1–1000 characters. The task must check them. |
successState | string | No | Advisory state established or preserved on success, 1–1000 characters. Back it with named assertions. |
platforms | TaskPlatform[] | No | ios, android, or both. Omit when platform-independent. |
deviceKinds | TaskDeviceKind[] | No | virtual, physical, or both. Omit when either kind works. |
frameworks | TaskFramework[] | No | Compatible app frameworks. Omit when framework-independent. |
feature | string | No | Focused discovery domain such as map. |
keywords | string[] | No | At most 32 discovery terms. |
inputSchema | JsonSchema | No | Validates the object supplied as input and applies defaults. |
outputSchema | JsonSchema | No | Describes returned output; version 1 does not enforce it. |
hostTools | string[] | No | Extra host tools callable through ansight.callTool; maximum 32. Standard typed APIs such as ansight.keyboard need no declaration. |
timeoutSeconds | number | No | Whole run: 1–300 seconds; default 120. |
maximumActions | number | No | Host, app, discovery, and child-task calls: 1–100; default 64. |
The descriptor must be a static object literal. Do not use functions, variables, spreads, or computed values inside it.
TaskInvocation<TInput>
| Property | Type | Meaning |
|---|---|---|
run | Readonly<TaskRun> | Host-owned run identity and effective limits. |
input | Readonly<TInput> | Validated input after schema defaults are applied. |
secrets | get(alias), require(alias) | Resolve only values granted to this run. get returns undefined for an ungranted alias; require throws. |
ansight | AnsightHost | Session-pinned host evidence and control methods. |
app | TaskAppContext | Standard app-tool suites and custom-tool bridge. |
expect | TaskExpect | Playwright-style named assertions that establish pass or failure. |
TaskRun
| Property | Type | Meaning |
|---|---|---|
runId | string | Unique run ID. |
executionMode | "sdk" | "device" | The selected session’s capture and execution mode. |
taskId | string | Repository-relative task ID. |
appId | string | Exact app scope enforced by the host. |
sessionId | string | Live session enforced for every call. |
repositoryRootPath | string | Absolute connected repository root. |
timeoutSeconds | number | Effective whole-task timeout. |
maximumActions | number | Effective action limit. |
Declare supported targets
Target metadata describes the task’s intended compatibility; it is not task
input and does not select or launch a device. A workspace test that references
the task with taskId validates these declarations against its selected target
and connected session. Keep runtime prerequisites in requires and task code.
export const task = {
schemaVersion: 2,
title: "Verify a MAUI page on Android",
description: "Check the selected MAUI page on an Android device.",
platforms: ["android"],
deviceKinds: ["virtual", "physical"],
frameworks: ["dotnet-maui"],
startingState: "The app is signed in and the home page is visible.",
successState: "The home page remains visible with its expected content.",
requires: { appTools: ["maui.get_current_page"] }
} satisfies TaskDefinition;
This descriptor fragment uses TaskDefinition from ./ansight-task.d.ts.
Its state hints help discovery and planning; they neither navigate the app nor
establish a passing result. The task function still needs named assertions.
TaskPlatform is "ios" | "android". TaskDeviceKind is
"virtual" | "physical": virtual means iOS Simulator or Android Emulator.
TaskFramework accepts dotnet-maui, dotnet-ios, dotnet-android,
react-native, flutter, capacitor, ios-uikit, ios-swiftui,
android-views, android-compose, and native-unknown.
Omitting a target constraint does not make an unavailable API work. See the compatibility model and device support matrix.
Editor hovers carry @supportedPlatforms, @unsupportedPlatforms,
@supportedDeviceKinds, @unsupportedDeviceKinds, @supportedFrameworks, and
@unsupportedFrameworks tags on platform-sensitive declarations. These describe
compatibility; they do not grant a capability or replace the selected provider’s
runtime checks. Method-specific notes identify current declaration differences.
Execution capabilities
Use descriptor version 2 for tasks that support
external sessions. Version 1 tasks
retain their SDK requirement. Declare essential capabilities in requires so
the host can refuse an incompatible session before running task code:
import type { TaskDefinition, TaskInvocation } from "./ansight-task.d.ts";
export const task = {
schemaVersion: 2,
title: "Capture notes database",
description: "Save the simulator app's notes database to the session.",
platforms: ["ios"],
deviceKinds: ["virtual"],
requires: { capabilities: ["files.capture"] }
} satisfies TaskDefinition;
export default async function run({ ansight, expect }: TaskInvocation) {
const captured = await ansight.files.capture({
root: "data",
path: "Documents/notes.sqlite"
});
expect(captured.snapshotId, { id: "database-copy-retained" }).toBeTruthy();
return captured;
}
That example uses an iOS data-container path. An Android app might instead
store the file at files/notes.sqlite or databases/notes.sqlite.
await ansight.capabilities.snapshot() returns sessionId, executionMode,
live, appAvailable, executable appTools, and a capabilities map. Each
capability contains available, provider, and an unavailable reason.
Use await ansight.capabilities.require({ capabilities: ["files.read"] })
for an optional branch that needs extra support. Available keys include
ui.semantic, ui.screenshot, lifecycle, host.spans, evidence.read,
evidence.logs, evidence.telemetry, evidence.artifacts, logs.native,
files.read, files.capture, and app.tools; actual availability is session-specific.
app.available is false without a connected SDK provider. Calling any SDK
app.* operation then rejects with code: "capability_unavailable", the
missing capability, executionMode, and retryable: false. SDK-required
operations include setup guidance. For a task that needs a particular app
tool, declare requires: { appTools: ["files.list_directory"] } rather than
assuming the suite exists. Missing declared requirements refuse the task;
they do not skip the operation and report success.
Each requirements array accepts at most 64 non-empty identifiers, each at most
160 characters without control characters. Unknown or unavailable required
capabilities reject the run; use an exact executable tool ID for appTools.
A capability snapshot is not a complete per-method compatibility table. For
example, lifecycle does not promise physical-iPhone backgrounding, and
evidence.read does not promise every evidence API in device mode. Check the
host method index and each method’s support
restrictions. An available evidence reader also does not prove that its data
was captured.
Task composition
ansight.tasks.list(args?) searches the same repository and App ID as the
calling task. It accepts query, feature, and maxResults; focused queries
use the same metadata, input-schema, synonym, and typo matching as
ansight task list.
Use the exact discovered ID to invoke another task against the same live session:
type ChildOutput = { selectedArea: string };
const catalog = await ansight.tasks.list({
query: "select map area",
feature: "map"
});
const selected = catalog.tasks.find(candidate => candidate.taskId === "map.select-area");
if (!selected) throw new Error("The map selection task is unavailable.");
const child = await ansight.tasks.run<ChildOutput>({
taskId: selected.taskId,
input: { area: "Secret Garden" }
});
expect(child.output?.selectedArea, { id: "area-selected" }).toBe("Secret Garden");
run resolves only when the child task is Passed; every other child status
rejects the call and therefore fails the caller unless its code deliberately
handles the error. The child keeps its own run ID, assertions, timeout, action
budget, tool-call audit, and persisted result. The parent records
ansight.tasks.run as one action, and its remaining deadline caps the complete
child call. Parent and child assertions are not merged, so the parent must still
record at least one named assertion for its own outcome.
The host reloads the caller’s exact workspace for discovery and execution, pins every child to the caller’s App ID and session, rejects self-calls and recursive cycles, and permits at most eight task execution levels in one call chain. A child cannot select another repository, app, session, or device.
expect: named assertions
| Matcher | Behavior |
|---|---|
expect(actual, { id, message? }).toBe(expected) | Uses Object.is, matching Playwright’s strict identity matcher. |
.toEqual(expected) | Uses strict recursive deep equality. |
.toBeTruthy() / .toBeFalsy() | Checks JavaScript truthiness. |
.toBeDefined() / .toBeUndefined() / .toBeNull() | Checks nullish states explicitly. |
.toContain(expected) | Checks string inclusion or direct array membership. |
.toContainEqual(expected) | Checks deep-equal array membership. |
.not.<matcher> | Negates the following matcher. |
expect.soft(actual, metadata).<matcher> | Records a failure and continues the task function. |
Assertion IDs must be stable and unique. At least one assertion must pass and
none may fail for the task status to be Passed. Hard failures stop the task
function; soft failures continue. A recorded failure remains authoritative even
if task code catches the hard assertion error. Returning normally without an
assertion produces Inconclusive.
Execution rules
- Await host and app calls serially; overlapping calls are rejected.
- Standard host APIs need no
hostToolsdeclaration. ansight.tasks.listandansight.tasks.runneed nohostToolsdeclaration.ansight.callToolaccepts only names declared intask.hostTools.app.callTooltargets app-defined tools and needs no descriptor declaration.- Every host, app, discovery, and child-task call counts toward the caller’s
maximumActions. - Tasks can access only the enforced live session through Ansight APIs.
- Repository TypeScript is trusted local code, not a filesystem or network sandbox.