Repository Triggers
Match app and session events in the host, run bounded repository handlers, return one app-tool action, and inspect durable attempt history.
CLI v0.23.1
A repository trigger reacts automatically to one normalized app or session event. Its main purpose is to enrich session replay and the timeline with an app-owned state snapshot at the moment that state is most useful. The C# host matches the App ID, exact event kind, and declarative conditions before it starts the TypeScript module. The trigger function can return no action or one app-tool action.
Use a trigger for bounded capture work such as requesting diagnostics when an error log arrives, snapshotting relevant database state after a write or migration, or preserving map annotations, route state, and viewport after a map event. Those artifacts sit beside screenshots, touches, logs, telemetry, and visual trees, allowing a human or agent to reconstruct how hidden app state changed throughout a session during post-mortem inspection. Use a task when a user or agent should explicitly start the workflow. Triggers are not schedulers, background listeners, or arbitrary TypeScript predicates.
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 the support files and a starter trigger with the CLI:
ansight workspace init .
ansight workspace add trigger . diagnostics.capture-errors \
--app-id com.example.app \
--event-kind session.log.received
You can also create and edit triggers in Studio’s Test workspace.
Complete trigger example
Every trigger exports a static trigger descriptor and one default function:
import type {
TriggerContext,
TriggerDefinition
} from "./ansight-automation.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. Studio 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 | Contract version. Omitted descriptors use version 1. |
appId | No | Optional exact App ID. Required when an embedded host supplies repository paths without a Studio workspace. |
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
Contract version 1 publishes these normalized event kinds:
- App lifecycle and application events:
app.lifecycle.changed,app.event - Pairing events:
app.pairing.discoveryReceived,app.pairing.accepted,app.pairing.rejected - Capture lifecycle:
session.capture.started,session.capture.updated,session.capture.stopped,session.capture.finalized - Session transfers:
session.transfer.telemetry,session.transfer.log,session.transfer.appEvent,session.transfer.appProfile,session.transfer.screenshot,session.transfer.visualTree,session.transfer.touchInput,session.transfer.annotatedFeedback - Captured log entries:
session.log.received - Quality controls:
quality.check.failed,quality.trend.regressed,quality.trend.recovered
Raw SDK application events use app.event, even when Studio also projects them
into a session timeline or log view. Match app.event when the trigger needs the
original application-event payload.
Conditions
Conditions are evaluated in C# before Node starts. 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 many short-lived processes and consume the bounded trigger queue.
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
Studio-owned host 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 a fresh process while retaining the same run,
event, and correlation identity.
Connect triggers in Studio
Trigger code is trusted local automation and is never enabled merely because it exists in the repository.
- Open Workspace → Apps.
- Select the registered App ID.
- Link Agent workspace to the repository root.
- Review the discovered trigger IDs, event kinds, actions, and limits.
- Choose Connect and accept the local-code trust warning.
The connection is scoped to the exact App ID. Studio remembers the opt-in and restores the connection while that app remains linked to the same workspace. Choose Disconnect to stop future matches.
A connected device cannot nominate or change the repository path. The selected Studio 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 with repository automation enabled:
ansight host run --enable-repository-automations
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
CLI connections last for the resident host’s lifetime. To restore them at host startup, pass both options:
ansight host run \
--enable-repository-automations \
--automation-repository /path/to/repository
An embedded host can configure repository paths directly. In that mode, every
trigger descriptor must declare appId because no selected Studio workspace
supplies the scope.
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 Workspace → 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 Studio 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: install Node or configure an embedded host with an absolute JavaScript executable path.
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 Node 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.