Enrich your timeline with custom app data
Capture app state automatically at meaningful events so you can explain what happened with more than screenshots and logs.
Preserve what your app knew when an error or important event occurred. Repository triggers request custom app data in response to app or session events, placing the captured evidence beside screenshots, touches, logs, telemetry, and visual trees in the Ansight timeline.
This gives humans and agents the missing context they need to understand how hidden app state changed throughout a session. Use a trigger to request diagnostics when an error log arrives, snapshot relevant database state after a write or migration, or preserve map annotations, route state, and viewport after a map event.
The CLI matches the App ID, exact event kind, and declarative conditions before running the trigger, which can return no action or one app-tool action. Version 2 also supports a host screenshot, sandbox file capture, or annotation action without the SDK. Use a task when a user or agent should explicitly start the workflow. Triggers are not schedulers, background listeners, or arbitrary TypeScript predicates.
See Loading and Session Lifecycle for the
startup flow chart, when descriptors load, and what session.capture.started
does and does not guarantee.
Create a trigger
Trigger modules live beneath ansight/triggers. A module’s relative path
becomes its ID: ansight/triggers/diagnostics/capture-errors.ts becomes
diagnostics.capture-errors.
Create a starter trigger with the CLI:
ansight workspace add trigger . diagnostics.capture-errors \
--app-id com.example.app \
--event-kind session.log.received
This also initializes the workspace support files if they do not exist yet. The example below adds conditions and an action to that starter trigger.
You can also review discovered triggers from the local player’s Tests area.
Review the complete TypeScript declarations and trigger descriptor schema online, or download them from the source viewer.
Complete trigger example
Every trigger exports a static trigger descriptor and one default function:
import type {
TriggerContext,
TriggerDefinition
} from "./ansight-trigger.d.ts";
type LogPayload = {
priority: string;
sourceEventId?: string;
message?: string;
};
export const trigger = {
"schemaVersion": 1,
"eventKind": "session.log.received",
"eventSchema": {
"type": "object",
"properties": {
"priority": { "type": "string" },
"sourceEventId": { "type": "string" },
"message": { "type": "string" }
},
"required": ["priority"],
"additionalProperties": true
},
"conditions": [
{
"field": "payload.priority",
"operator": "equals",
"value": "Error"
}
],
"functionTimeoutMs": 100,
"actionTimeoutSeconds": 20,
"retry": {
"maxAttempts": 3,
"initialDelayMs": 250,
"backoffMultiplier": 2,
"maxDelayMs": 2000
}
} satisfies TriggerDefinition;
export default async function captureError(
{ event, app }: TriggerContext<LogPayload>
) {
return app.artifacts.request({
providerId: "example.diagnostics",
artifactId: "state",
arguments: {
sourceEventId: event.payload.sourceEventId ?? "",
message: event.payload.message ?? ""
}
});
}
The descriptor must remain a JSON-compatible object literal. The host extracts it without importing or executing the module, so do not use variables, spreads, functions, or computed values inside the descriptor.
Descriptor fields
| Field | Required | Meaning |
|---|---|---|
schemaVersion | No | 1 (default) requires an SDK provider; 2 supports capability requirements and host evidence actions. |
requires | No | Version 2: required capabilities and executable appTools IDs, checked before the handler runs. |
enabled | No | Whether the trigger may match events and execute. Defaults to true; disabled triggers remain visible in inspection and connected catalogs. |
appId | No | Optional exact App ID when the repository has a registered app scope; otherwise required. |
eventKind | Yes | Exact normalized host event kind. |
eventSchema | No | Descriptive schema for event.payload. It does not control matching. |
conditions | No | Up to 16 declarative conditions, all of which must match. |
functionTimeoutMs | No | Trigger function bound from 10–1,000 ms. Default: 100 ms. |
actionTimeoutSeconds | No | Separate returned-action bound from 1–300 seconds. Default: 30 seconds. |
retry | No | Host-owned retry policy with at most five total attempts. |
eventSchema helps editors, agents, and module inspection understand the
payload. Contract version 1 does not reject events that differ from this schema.
Use conditions for authoritative matching.
Event kinds
Choose one exact eventKind for each trigger, then narrow it with
conditions. Each event includes its App ID, optional session ID,
timestamp, and a payload specific to the event family.
| Event family | Use it for |
|---|---|
| Application events | App-defined milestones and foreground/background changes |
| Pairing | Enrollment progress and connection failures |
| Capture lifecycle | Session connection, disconnection, and finalization |
| Session transfers | Notifications that evidence arrived |
| Captured logs | Matching an individual log message or severity |
| Trends | Failed checks, regressions, and recovery |
| Unknown events | Diagnostic fallback kinds |
Application events
app.event
Fires for each application event reported by the SDK, such as a completed login
or a map-load milestone. Use it to capture app state at a meaningful point in a
workflow. Filter payload.label, payload.eventType, payload.details, or
payload.channelId; payload.clientName identifies the app.
This event carries the original application-event fields even when the player
also shows that event in its timeline or log view. For example, match
payload.label against your app’s map.loaded event.
app.lifecycle.changed
Fires when the reported app lifecycle state changes, such as entering the
foreground or background. Use it for diagnostics around app activation or
suspension. The payload contains previousState, currentState,
changedAtUtc, and clientName; for example, filter payload.currentState
to Foreground.
A foreground transition does not guarantee that a screen has finished loading. Use an app-defined readiness event when that distinction matters.
Pairing events
Pairing payloads contain clientName, remoteAddress, configId, reasonCode,
and reasonMessage. Some values can be absent, and a rejected request may not
have a session. Use these events for enrollment diagnostics; a live app-tool
action requires an established connection.
app.pairing.discoveryReceived
Fires after an enrollment connection request passes authorization and receives a reserved session ID. Use it to track connection setup. It does not indicate that every discovery packet was accepted or that the app is connected yet.
app.pairing.accepted
Fires when enrollment is accepted and a live-session endpoint has been issued.
Use it to confirm pairing progress. Wait for session.capture.started before
assuming the app has opened that connection.
app.pairing.rejected
Fires when authorization is rejected or a live-session endpoint cannot be
provided. Inspect payload.reasonCode and payload.reasonMessage to diagnose
the failure. Avoid returning an app-tool action when no live session exists.
Capture lifecycle
These events describe the capture connection. Their payload contains
clientName, status, and message. See the
session lifecycle flow chart for their
place in the overall startup and capture sequence.
session.capture.started
Fires when the app opens its session connection. Use it for connection-start diagnostics. It can fire again on reconnect and does not guarantee that app tools, the first screenshot, or the initial screen are ready.
session.capture.updated
This name is reserved in the current contract for capture-status updates. The current CLI does not emit it during normal capture. Use a specific transfer, log, or lifecycle event for a trigger that must run today.
session.capture.stopped
Fires when the session connection closes or is replaced by a newer connection. Use it to observe disconnection. Final capture work may still be running, and the disconnected app can no longer supply a final live snapshot.
session.capture.finalized
Fires after capture shutdown, including native-log and external-screenshot capture, has finished. Use it for work that depends on the completed capture. Automatic Trends analysis is queued at this point; this event does not mean Trends results are already available.
Session transfers
Transfer events announce arriving evidence. They carry clientName, itemCount,
message, and an optional capturedAtUtc, rather than the complete evidence
content. Use them when arrival itself matters. Use the dedicated app-event or
log event when conditions need fields from an individual item.
session.transfer.telemetry
Fires when metric channels are registered or telemetry samples arrive.
itemCount reports the channel or sample count. Use Trends definitions
for metric budgets; this payload does not expose individual metric values.
session.transfer.log
Fires when client log evidence arrives. Use it to observe log transfer activity.
Choose session.log.received to match severity, source, or message text.
session.transfer.appEvent
Fires when a batch of application events arrives; network-request arrivals
also use this transfer kind. Use it for transfer diagnostics. Choose app.event
to match an original SDK event’s label or details.
session.transfer.appProfile
Fires when device/app profile evidence arrives. Use it to observe profile availability. The notification summarizes the transfer; it does not include the complete profile or guarantee every app tool is ready.
session.transfer.screenshot
Fires when a screenshot frame is received from the app. Use it to correlate app state with incoming visual evidence. The notification contains no image bytes and should not be treated as a notification for every capture source.
session.transfer.visualTree
Fires when an incoming visual-tree snapshot is saved. Use it to observe new UI
structure evidence. itemCount counts the snapshot, not its nodes; the payload
does not contain the tree itself.
session.transfer.touchInput
Fires when touch input records arrive. Use it to correlate app state with input
activity. itemCount counts records, which may represent parts of a gesture;
the notification does not describe a completed semantic tap or swipe.
session.transfer.annotatedFeedback
Fires after annotated feedback from the app has been successfully imported. Use it to collect related app diagnostics for that feedback. The notification identifies the transfer rather than embedding the annotation bundle.
Captured logs
session.log.received
Fires for an individual captured log entry. Use it to request diagnostics when
a particular error or warning appears. The payload contains streamId,
message, priority, source, tag, sourceEventId, processId, and threadId.
For example, combine payload.priority equal to Error with
payload.message containing a specific failure. Narrow the conditions so
routine logs do not repeatedly trigger expensive capture work.
This event includes native system logs captured for the app: android-logcat
on Android and apple-unified-log on iOS simulators, as well as the sdk stream.
Filter payload.streamId to choose a stream. Native log capture must be enabled;
the trigger sees only entries retained for the selected session.
For an externally monitored app, use a version 2 trigger and declare the host capability its action needs. For example, capture a screenshot on a native error:
export const trigger = {
"schemaVersion": 2,
"appId": "com.example.notes",
"eventKind": "session.log.received",
"requires": { "capabilities": ["ui.screenshot"] },
"conditions": [
{ "field": "payload.streamId", "operator": "equals", "value": "android-logcat" },
{ "field": "payload.priority", "operator": "equals", "value": "Error" },
{ "field": "payload.tag", "operator": "equals", "value": "Notes" }
]
};
export default ({ ansight }) => ansight.screenshots.capture({});
Use apple-unified-log and the tag shown in your captured logs for iOS. Host file
and screenshot actions work without the SDK; app-internal tools still require
the SDK and are refused when unavailable. Linking the trusted workspace to the
App ID enables its triggers for future matching log entries.
Trends events
These events report Trends analysis outcomes. Payload fields include
definitionId, spanGroup, message, trendsStatus,
historyStatus, and historyComparison; fields vary with the outcome.
They can arrive after the app disconnects, so a live app-tool action may no
longer be available.
trends.check.failed
Fires when a required Trends check fails or errors. A report-persistence
error can also produce this event with definitionId set to
trends.persistence. Use the definition ID and status to focus follow-up
on the failed check.
trends.regression.detected
Fires when Trends history reports a regression against the configured
baseline. Use it to follow up on deteriorating behavior across comparable
runs. Inspect definitionId, spanGroup, and historyComparison to identify
the affected comparison.
trends.regression.recovered
Fires when Trends history reports recovery from a regression. Use it to record that the affected comparison has recovered; it does not mean every Trends check for the app passed.
Unknown events
These are fallback names for an unrecognized event subtype in each family. They are not wildcards and do not subscribe to every event in that family.
app.pairing.unknown
An unrecognized pairing subtype, carrying the pairing diagnostic fields.
session.capture.unknown
An unrecognized capture subtype, carrying capture status and message fields.
session.transfer.unknown
An unrecognized transfer subtype, carrying transfer counts and timing fields.
trends.unknown
An unrecognized Trends subtype, carrying the available outcome fields.
Conditions
The CLI evaluates conditions before running the trigger. They can inspect these event envelope fields:
eventIdkindoccurredAtUtcappIdsessionIdcorrelationIdcausationId- nested payload paths beginning with
payload.
Supported operators are equals, notEquals, contains, startsWith,
endsWith, exists, and notExists. String comparisons can set
ignoreCase: true. Every condition is ANDed; there is no executable predicate.
value is required except for exists and notExists:
"conditions": [
{
"field": "payload.label",
"operator": "equals",
"value": "MapPage"
},
{
"field": "payload.details",
"operator": "contains",
"value": "navigation settled",
"ignoreCase": true
},
{
"field": "sessionId",
"operator": "exists"
}
]
Keep conditions as narrow as possible. A broad high-frequency match can start too many trigger runs and exhaust available execution capacity.
Function and returned action
The default function receives { run, event, app }:
runidentifies the trigger run, attempt, repository, queue time, and effective limits.eventis the immutable normalized event envelope that matched.- Standardized suite methods such as
app.artifacts.request(arguments)create actions for core app tools. app.callTool(toolId, arguments)creates an action for a user-defined tool.
These methods do not call the app from inside the module. Return their result so
the host can validate the action, preserve the matched event’s session and
correlation identity, apply app grants, audit the request, and execute it under
actionTimeoutSeconds.
A trigger can return null, return nothing, or return one action. It cannot call
host-owned UI tools or execute multiple app actions. The connected app
validates custom tool IDs and arguments through its normal authorization policy.
For replay enrichment, prefer an artifact provider whose snapshot is small, bounded, and understandable without live app context. Give the artifact a stable provider and artifact ID, include the source event or domain revision in its arguments or metadata, and capture only the state needed to explain the event. This makes successive database, map, navigation, cache, or domain-state snapshots comparable on the session timeline without turning the trigger into a continuous polling mechanism.
Diagnostic result retention
A successful file_descriptors.list_open action also creates a durable
open-file-descriptors.json artifact snapshot on the matched session timeline.
Count and usage actions remain compact trigger-run results. Continuous
open-handle and JNI-reference counts remain telemetry; JNI reference graphs are
not currently exposed as an app tool.
Timeouts and retry
The function and returned action have separate bounds. A slow app tool should
use actionTimeoutSeconds; do not expand the tight TypeScript function bound to
cover action execution. Module loading has its own five-second safety limit.
Retry configuration is host-owned:
| Field | Range | Default when retries are enabled |
|---|---|---|
maxAttempts | 1–5, including the first attempt | 1 |
initialDelayMs | 10–60,000 ms | 250 ms |
backoffMultiplier | 1–10 | 2 |
maxDelayMs | At least initialDelayMs, at most 60,000 ms | Eight times the initial delay, capped at 60,000 ms |
Retry delays are invalid when maxAttempts is 1. Only Failed and TimedOut
attempts retry. Succeeded, Rejected, Cancelled, and exhausted attempts are
terminal. Every attempt starts with fresh module state while retaining the same run,
event, and correlation identity.
Connect triggers through the local host
Trigger code is trusted local automation and is never enabled merely because it exists in the repository.
- Review the repository’s trigger code, event kinds, actions, and limits.
- Open Apps in the local player and select the registered App ID.
- Link Agent workspace to the repository root. Linking enables repository automation and the host attempts to connect its trigger catalog.
- Inspect the connection and any validation warnings. Use Connect to reconnect after fixing or updating definitions.
The connection is scoped to the exact App ID. The host remembers the opt-in and restores the connection while that app remains linked to the same workspace. Choose Disconnect to stop future matches.
Registering a codebase through ansight app register --codebase or
ansight workspace init --app-id also enables this workspace link. A separate
manual Connect step is not required for a valid enabled link.
A connected device cannot nominate or change the repository path. The selected registered host app owns that association.
Inspect and connect from the CLI
Inspecting validates the repository without executing or connecting triggers:
ansight repo automation inspect com.example.app .
CLI trigger connections require a resident host. Repository automation is enabled by default:
ansight host run
From another terminal, connect and inspect the active catalog:
ansight repo automation connect com.example.app .
ansight repo automation list
Disconnect or inspect recent runs with:
ansight repo automation disconnect com.example.app
ansight repo automation runs com.example.app --limit 100
Linking an Agent workspace restores its enabled trigger connection at host startup. For a one-off repository that is not linked to an app, pass the repository option:
ansight host run \
--automation-repository /path/to/repository
When a repository is configured directly without a registered app scope,
every trigger descriptor must declare its appId.
Inspect a trigger through the CLI
Validate the repository catalog without connecting it:
ansight repo automation inspect com.example.app /path/to/repository --json
Use the returned trigger IDs, descriptors, module paths, and validation messages as the authoritative discovery result.
Run history and diagnosis
In Apps, select the App ID and inspect Triggers → Recent runs. Each attempt records:
- trigger ID, run ID, attempt number, and retry state
- matched event, event ID, correlation ID, and session ID
- start time, completion time, and duration
- status, message, and process exit code
- returned action and app-tool result
- standard error
Select a run for its complete trace. History survives host restart and is stored as bounded local JSONL. The CLI can return up to 500 recent app-scoped attempts.
No run trace means the module never started. Check the App ID connection, exact event kind, every condition, and bounded queue admission. Other common states:
Rejected: the action returned an invalid shape or required a live session the event did not have.Failed: inspect the execution message, standard error, and tool result.TimedOut: identify whether the function bound or separate action bound expired.- Runtime unavailable: run
ansight doctorand follow the runtime setup guide.
Security and limits
App-tool actions retain the connected app’s grants, critical-tool policy, current availability, cancellation, audit, and artifact persistence. A trigger cannot grant itself more access.
Capability declarations restrict calls back into Ansight; they do not sandbox filesystem, process, or network access available to trusted local workspace code. Review every module before connecting the repository.
A repository can expose up to 256 triggers, and each executable module is limited to 1 MiB. An invalid trigger definition rejects the repository trigger catalog, so run inspection after every change and reconnect to load updates.