callstack / callstack/agent-device
Add bounded crash discovery and retrieval as first-class debug evidence
- Dominant language
- TypeScript
- Stars
- 4.6k
- Forks
- 299
- Avg merge
- 10h 17m
- Merged PRs (30d)
- 515
Description
## Problem
`debug symbols` can symbolicate an Apple crash when the caller already has a crash artifact, but
agent-device does not provide a first-class way to discover and retrieve recent app crash reports from
the selected target. Agents must leave the command surface, use platform-specific tools, and often
paste too much crash data into context.
We need a bounded evidence workflow that discovers recent crashes, retrieves one artifact, and hands
it cleanly to the existing symbolication path.
## Desired command model
Explore a narrow crash-evidence family, for example:
```text
agent-device debug crashes list [--since ...] [--limit ...]
agent-device debug crashes get [--out ...]
agent-device debug symbols --artifact --dsym ...
```
Names are provisional. Prefer extending the existing `debug` family only if it stays one coherent
question; otherwise introduce a focused command family rather than growing a mixed debug namespace.
`list` should return compact metadata only: stable ID, app/bundle/package identity, timestamp,
platform, crash kind/signal/exception when cheaply available, process, target identity, and artifact
availability. `get` should copy/download one report into the session artifact store and return path,
size, checksum when available, provenance, and a bounded summary. Raw reports must never be dumped by
default.
## Platform research
Document capabilities and limitations before finalizing a cross-platform contract:
- iOS simulator: inventory diagnostic reports through supported simulator/host facilities; establish
whether IDs remain stable after reboot and how app identity is filtered.
- iOS physical device: evaluate `devicectl`/device diagnostics availability, pairing/developer-mode
requirements, permissions, OS/Xcode version gates, and whether retrieval can be app-scoped.
- Android emulator/physical: distinguish Java/Kotlin tombstones or logcat fatal exceptions from native
tombstones/ANRs; determine what is retrievable without root and what only exists as bounded log
evidence.
- macOS: assess user diagnostic reports and sandbox/privacy boundaries without scanning unrelated
applications by default.
- Remote/cloud providers: map provider-owned artifacts into the existing artifacts model where their
APIs expose crash reports; return typed unsupported capability otherwise.
- TV and other targets: add only evidence-backed cells. Do not infer support from a similar platform
name.
## Architecture constraints
- Classify every dispatch path/platform cell in
`packages/contracts/src/interaction-guarantees.ts` only if this is an interaction guarantee; otherwise
use the command descriptor/capability registries that own command support. Do not manufacture parity
through command-name lists.
- All host process execution goes through `src/utils/exec.ts` helpers.
- Artifacts and diagnostics use `src/daemon/session-store.ts` and the existing artifact response model;
do not hand-build session paths or write ad-hoc logs.
- Errors use `normalizeError` fields and typed details (`reason`, permission/tool/version requirements,
attempted source). No message sniffing.
- Preserve app/session scoping. Listing all host crash reports should require an explicit scope and
must not leak unrelated app names or user data in default output.
- Keep response arrays and summaries bounded. Full artifacts stay on disk/provider storage and are
referenced by path/handle.
- Existing `debug symbols` remains the symbolication owner. Retrieval should compose with it rather
than duplicate UUID matching, frame parsing, or dSYM search.
- Declare timeout, batching, MCP exposure, result typing, and platform capability on the command
descriptor surface. Add an MCP `outputSchema` for closed result shapes.
## Proposed result sketches
List result:
```json
{
"crashes": [
{
"id": "provider-stable-id",
"occurredAt": "2026-08-16T12:00:00Z",
"app": "com.example.app",
"process": "Example",
"platform": "ios",
"kind": "exception",
"summary": "EXC_BAD_ACCESS"
}
],
"truncated": false,
"source": "platform-diagnostics"
}
```
Get result:
```json
{
"crash": { "id": "provider-stable-id", "occurredAt": "...", "app": "..." },
"artifact": {
"artifactType": "crash-report",
"path": ".../crash.ips",
"sizeBytes": 12345
},
"summary": { "exception": "EXC_BAD_ACCESS", "topFrames": [] }
}
```
Treat these as design prompts, not wire compatibility commitments.
## Verification plan
- Build a fixture corpus for Apple `.ips`/`.crash`, Android fatal exception/ANR/native samples, and
malformed or partially written reports. Shared fixtures are named exports, not repeated literals.
- Unit-test filtering, ordering, stable ID generation, bounds, redaction, timestamp parsing, and typed
unsupported/permission failures.
- Prove list does not read or return report bodies and get retrieves only the selected ID.
- Prove the retrieved Apple artifact can be passed unchanged to `debug symbols`, including matching
and mismatched dSYM cases.
- Integration-test supported simulator/emulator paths with a controlled crashing fixture app. See a
new test fail before implementation, then pass after the owning interface exists.
- Test app/session scoping with crashes from two apps and verify the default result cannot expose the
unrelated app.
- Test pagination/limit behavior and very large reports; command output remains bounded while the
artifact size is preserved accurately.
- Capability tests cover every claimed platform/kind cell and pin explicit unsupported cells with
recovery hints.
- Verify CLI text, `--json`, Node result types, MCP structured content/output schema, artifact listing,
and docs/help remain aligned.
## Acceptance criteria
- Agents can list recent crashes for the selected app/target without raw platform commands.
- Agents can retrieve exactly one crash as a session artifact and receive bounded structured metadata.
- Apple retrieval composes directly with `debug symbols`.
- Unsupported and permission-limited targets fail with typed actionable details.
- Default output is token-bounded and privacy-scoped.
- Support claims are backed by platform fixtures/integration evidence, not nominal parity.
Contributor guide
Assessment
This issue has not been assessed yet.