Explain bugs with app state
Preserve Android app state alongside screenshots and logs to investigate the internal conditions behind a visible problem.
When a screen shows a generic error, the cause may be hidden in a request, a ViewModel, or a sync queue. Artifacts preserve that app-owned state so people and agents can compare it with the screenshots, logs, and touches from the same session.
The app exposes named files, ZIPs, JSON snapshots, or binary/text payloads through artifacts.query and artifacts.request.
Good artifact candidates include:
| Artifact | Useful contents | Why use an artifact |
|---|---|---|
network-request.json | OkHttp or Retrofit method, route, sanitized URL, status, timing, retry count, cache policy, request/response body snippets, correlation ids, redacted headers. | Connects a failed screen or toast to the exact request that caused it. |
map-state.json | Google Maps or Mapbox camera, viewport, zoom/bearing/pitch, style URI, loaded source ids, visible layer ids, selected markers/features, tile/cache counters, last map error. | Lets an agent compare the map’s internal state with screenshots and user gestures. |
view-model-state.json | ViewModel, Compose state holder, selected ids, validation errors, loading flags, paging cursor, dirty fields. | Captures state that is too nested for logs and too sensitive for broad reflection. |
navigation-state.json | Navigation graph route, back stack, selected tab, sheet/dialog state, deep-link parameters, last failed navigation. | Makes a replayable checkpoint for UI-flow bugs. |
support-bundle.zip | Recent logs, sanitized config, Room or SQLite diagnostics, WorkManager queue summaries, cache summaries, domain snapshots. | Keeps related evidence together as one downloadable session artifact. |
Use a custom tool instead when the host needs a small JSON answer or a narrow action. Use an artifact when the evidence should be durable, downloadable, and correlated with screenshots, logs, telemetry, visual trees, touches, and annotations.
Project runtime objects into safe DTOs, redact tokens and secrets, trim large bodies, and register providers only for approved local development or QA workflows.
Register Providers
val options = AnsightOptions.createBuilder(Ansight.developerOptions(clientName = "Android App"))
.addArtifactProvider(CurrentReportArtifactProvider())
.withReadOnlyToolAccess()
.build()
Ansight.initializeAndActivate(application, options)
When artifactProviders is non-empty, the runtime registers the artifact tools during initialization.
| Tool id | Policy | Purpose |
|---|---|---|
artifacts.query | Read | Discover providers and artifact definitions. |
artifacts.request | Read | Ask a provider to create an artifact and stream the bytes. |
Artifact requests use ToolPolicy.Read, but payloads can still export sensitive app data. Treat every provider as an explicit export surface with stable ids, clear descriptions, and narrowly shaped payloads.
Provider Example
import ai.ansight.runtime.AndroidArtifactDefinition
import ai.ansight.runtime.AndroidArtifactMetadata
import ai.ansight.runtime.AndroidArtifactProvider
import ai.ansight.runtime.AndroidArtifactProviderDescriptor
import ai.ansight.runtime.AndroidArtifactQueryContext
import ai.ansight.runtime.AndroidArtifactRequest
import ai.ansight.runtime.AndroidArtifactResult
class CurrentReportArtifactProvider : AndroidArtifactProvider {
override val descriptor = AndroidArtifactProviderDescriptor(
id = "app.reports",
name = "Reports",
description = "Exports app report artifacts.",
category = "reports",
)
override fun query(context: AndroidArtifactQueryContext): List<AndroidArtifactDefinition> =
listOf(
AndroidArtifactDefinition(
id = "current-report",
name = "Current Report",
description = "Exports the current report as CSV.",
kind = "report",
category = "reports",
mimeType = "text/csv",
fileName = "current-report.csv",
tags = listOf("csv", "current"),
),
)
override fun create(request: AndroidArtifactRequest): AndroidArtifactResult {
require(request.artifactId == "current-report") { "Unknown artifact." }
val bytes = "id,total\n1,42\n".toByteArray(Charsets.UTF_8)
return AndroidArtifactResult(
metadata = AndroidArtifactMetadata(
providerId = request.providerId,
artifactId = request.artifactId,
name = "Current Report",
kind = "report",
mimeType = "text/csv",
fileName = "current-report.csv",
sizeBytes = bytes.size.toLong(),
tags = listOf("csv", "current"),
),
bytes = bytes,
)
}
}
Choosing Artifacts
Choose artifacts for outputs that should leave the app as files or durable session evidence:
- attach a sanitized network request/response bundle to a failed checkout, login, sync, or search flow
- export a map-state snapshot when a map is blank, centered incorrectly, or missing expected markers
- export ViewModel, Compose, navigation, Room, or WorkManager state that is too nested for a short tool result
- package a support bundle that should stay attached to the captured session
Use Custom Tools for small reads or controlled actions that do not need file materialization.