stacktrace-cli 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.agents/skills/release-stacktrace/SKILL.md +32 -16
- stacktrace_cli-0.4.0/.claude/skills/release-stacktrace/SKILL.md +1 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.github/workflows/claude.yml +10 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/PKG-INFO +56 -24
- stacktrace_cli-0.4.0/README.md +160 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0004-three-stage-detector.md +1 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0006-trust-boundary-and-detection-upload.md +1 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0011-verdict-cache.md +1 -0
- stacktrace_cli-0.4.0/docs/adrs/0030-a-detection-is-dated-by-the-turns-it-cites.md +104 -0
- stacktrace_cli-0.4.0/docs/adrs/0031-one-host-daemon-observes-native-session-logs.md +115 -0
- stacktrace_cli-0.4.0/docs/adrs/0032-refresh-daemon-compositions-outside-detection.md +89 -0
- stacktrace_cli-0.4.0/docs/adrs/0033-a-third-party-analyzer-only-by-a-flag-that-names-it.md +119 -0
- stacktrace_cli-0.4.0/docs/adrs/0034-the-drift-grade-is-read-off-a-run-not-a-call.md +138 -0
- stacktrace_cli-0.4.0/docs/adrs/0035-naming-an-analyzer-selects-stored-answers.md +88 -0
- stacktrace_cli-0.4.0/docs/adrs/0036-deliver-eligible-findings-to-slack-from-the-daemon.md +495 -0
- stacktrace_cli-0.4.0/docs/adrs/0037-reuse-openaidr-append-cursors-in-the-daemon.md +94 -0
- stacktrace_cli-0.4.0/docs/adrs/0038-poll-normalized-sessions-for-supplemental-changes.md +82 -0
- stacktrace_cli-0.4.0/docs/adrs/0039-send-usage-telemetry-by-default-with-an-opt-out.md +231 -0
- stacktrace_cli-0.4.0/docs/adrs/0041-defer-native-windows-support.md +71 -0
- stacktrace_cli-0.4.0/docs/adrs/0042-read-the-build-commit-from-the-install-record.md +72 -0
- stacktrace_cli-0.4.0/docs/adrs/0043-name-the-session-agent-and-host-and-count-errors-by-class.md +144 -0
- stacktrace_cli-0.4.0/docs/adrs/0044-send-the-build-as-version-prints-it.md +62 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/INDEX.md +19 -1
- stacktrace_cli-0.4.0/docs/plans/009-reasoning-jev.md +3439 -0
- stacktrace_cli-0.4.0/docs/plans/010-session-daemon.md +16 -0
- stacktrace_cli-0.4.0/docs/releases/v0.3.1.md +38 -0
- stacktrace_cli-0.4.0/docs/releases/v0.4.0.md +204 -0
- stacktrace_cli-0.4.0/docs/research/001-intent-drift-metric.md +313 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/aidr.md +32 -16
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/detection-upload.md +18 -18
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/detector.md +131 -170
- stacktrace_cli-0.4.0/docs/specs/event-time.md +174 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/monitor.md +160 -47
- stacktrace_cli-0.4.0/docs/specs/notification-delivery.md +235 -0
- stacktrace_cli-0.4.0/docs/specs/reasoning-jev.md +445 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/session-input.md +5 -3
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/pyproject.toml +13 -5
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/__init__.py +1 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/analysis.py +202 -20
- stacktrace_cli-0.4.0/src/stacktrace_cli/build.py +130 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/cli.py +137 -55
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/acquire.py +23 -2
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/orchestrate.py +73 -9
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/render.py +7 -5
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/__init__.py +1 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/cli.py +106 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/client.py +134 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/composition.py +133 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/observer.py +173 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/paths.py +35 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/protocol.py +29 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/runtime.py +222 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/server.py +164 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/service.py +154 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/daemon/store.py +368 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/blocked.py +450 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/cache.py +245 -9
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/deterministic.py +502 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/finding.py +163 -15
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/history.py +210 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/jev.py +213 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/priors.py +270 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/prompts/__init__.py +20 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/prompts/jev-v1/stacktrace-deceptive-completion.json +16 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/prompts/jev-v2/stacktrace-deceptive-completion.json +16 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/reasoning.py +1741 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/render.py +88 -7
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/rules.py +69 -56
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/run.py +423 -77
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/secrets.py +8 -6
- stacktrace_cli-0.4.0/src/stacktrace_cli/detector/windows.py +293 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/monitor/reasoning.py +304 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/monitor/render.py +667 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/server.py +242 -21
- stacktrace_cli-0.4.0/src/stacktrace_cli/monitor/site/app.js +2023 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/index.html +16 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/styles.css +89 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/state.py +27 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/watch.py +227 -47
- stacktrace_cli-0.4.0/src/stacktrace_cli/options.py +107 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/private_state.py +38 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/cli.py +20 -10
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/detect_payload.py +37 -5
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/redact.py +3 -3
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/spool.py +3 -2
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/sync_detect.py +15 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/upload_contract.py +7 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/sessions/access.py +24 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/sessions/index.py +30 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/sessions/protocols.py +13 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/sessions/render.py +15 -9
- stacktrace_cli-0.4.0/src/stacktrace_cli/telemetry/__init__.py +11 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/telemetry/cli.py +67 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/telemetry/events.py +266 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/telemetry/posthog.py +126 -0
- stacktrace_cli-0.4.0/src/stacktrace_cli/telemetry/state.py +190 -0
- stacktrace_cli-0.4.0/tests/conftest.py +87 -0
- stacktrace_cli-0.4.0/tests/daemon/__init__.py +1 -0
- stacktrace_cli-0.4.0/tests/daemon/test_cli.py +124 -0
- stacktrace_cli-0.4.0/tests/daemon/test_client.py +136 -0
- stacktrace_cli-0.4.0/tests/daemon/test_composition.py +158 -0
- stacktrace_cli-0.4.0/tests/daemon/test_observer.py +407 -0
- stacktrace_cli-0.4.0/tests/daemon/test_runtime.py +411 -0
- stacktrace_cli-0.4.0/tests/daemon/test_service.py +227 -0
- stacktrace_cli-0.4.0/tests/daemon/test_socket.py +183 -0
- stacktrace_cli-0.4.0/tests/daemon/test_store.py +188 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/detector_session_fixture.py +17 -0
- stacktrace_cli-0.4.0/tests/monitor/feed_harness.mjs +1894 -0
- stacktrace_cli-0.4.0/tests/monitor/test_feed_model.py +1192 -0
- stacktrace_cli-0.4.0/tests/monitor/test_reasoning_gates.py +686 -0
- stacktrace_cli-0.4.0/tests/monitor/test_render.py +1015 -0
- stacktrace_cli-0.4.0/tests/monitor/test_server.py +796 -0
- stacktrace_cli-0.4.0/tests/monitor/test_site_assets.py +372 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/monitor/test_verdicts.py +14 -15
- stacktrace_cli-0.4.0/tests/monitor/test_watch.py +1124 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_activity.py +56 -4
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_contract_is_exhaustive.py +4 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_layers_compose.py +4 -41
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_seam.py +11 -3
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_spool.py +1 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_sync_detect.py +19 -18
- stacktrace_cli-0.4.0/tests/telemetry/__init__.py +0 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_cli.py +148 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_events.py +344 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_hooks.py +414 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_release_gate.py +14 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_state.py +150 -0
- stacktrace_cli-0.4.0/tests/telemetry/test_uploader.py +173 -0
- stacktrace_cli-0.4.0/tests/test_adr_cross_references_are_reciprocal.py +104 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_analysis.py +116 -8
- stacktrace_cli-0.4.0/tests/test_build_commit.py +244 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_call_status_conventions.py +64 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_cli.py +290 -40
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_correlate_orchestrate.py +20 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_correlate_render.py +1 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detection_components.py +30 -30
- stacktrace_cli-0.4.0/tests/test_detection_event_time.py +245 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detection_subject.py +3 -93
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_analyzer_live.py +20 -0
- stacktrace_cli-0.4.0/tests/test_detector_blocked.py +717 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_cache.py +482 -6
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_deterministic.py +35 -87
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_end_to_end.py +47 -40
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_finding.py +109 -13
- stacktrace_cli-0.4.0/tests/test_detector_history.py +291 -0
- stacktrace_cli-0.4.0/tests/test_detector_jev.py +306 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_priors.py +103 -312
- stacktrace_cli-0.4.0/tests/test_detector_prompts.py +42 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_reasoning.py +714 -507
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_render.py +117 -8
- stacktrace_cli-0.4.0/tests/test_detector_run.py +1358 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_verdict.py +17 -17
- stacktrace_cli-0.4.0/tests/test_detector_why_it_stopped.py +322 -0
- stacktrace_cli-0.4.0/tests/test_detector_windows.py +547 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +37 -55
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_monitor_cli.py +68 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_readme_examples_are_real_output.py +37 -0
- stacktrace_cli-0.4.0/tests/test_release_gate_coverage.py +58 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_release_readiness.py +18 -0
- stacktrace_cli-0.4.0/tests/test_session_identity_is_the_pair.py +340 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_sessions_access.py +18 -1
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_sessions_render.py +65 -1
- stacktrace_cli-0.4.0/tests/test_the_detector_spec_lists_the_shipped_catalogue.py +72 -0
- stacktrace_cli-0.4.0/tests/test_the_jev_spec_states_the_shipped_thresholds.py +96 -0
- stacktrace_cli-0.4.0/tests/test_the_locality_claim_is_qualified_everywhere.py +115 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_untrusted_content_never_travels.py +164 -29
- stacktrace_cli-0.4.0/tests/test_what_jev_is_told_is_what_was_disclosed.py +322 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/uv.lock +215 -9
- stacktrace_cli-0.3.0/.claude/skills/release-stacktrace/SKILL.md +0 -194
- stacktrace_cli-0.3.0/README.md +0 -130
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/deterministic.py +0 -654
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/markers.py +0 -158
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/priors.py +0 -502
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/prompts/v1/stacktrace-injected-instruction-followed.md +0 -14
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/prompts/v1/stacktrace-intent-drift.md +0 -11
- stacktrace_cli-0.3.0/src/stacktrace_cli/detector/reasoning.py +0 -1191
- stacktrace_cli-0.3.0/src/stacktrace_cli/monitor/reasoning.py +0 -172
- stacktrace_cli-0.3.0/src/stacktrace_cli/monitor/render.py +0 -256
- stacktrace_cli-0.3.0/src/stacktrace_cli/monitor/site/app.js +0 -1047
- stacktrace_cli-0.3.0/tests/monitor/feed_harness.mjs +0 -853
- stacktrace_cli-0.3.0/tests/monitor/test_feed_model.py +0 -508
- stacktrace_cli-0.3.0/tests/monitor/test_reasoning_gates.py +0 -400
- stacktrace_cli-0.3.0/tests/monitor/test_render.py +0 -363
- stacktrace_cli-0.3.0/tests/monitor/test_server.py +0 -413
- stacktrace_cli-0.3.0/tests/monitor/test_site_assets.py +0 -208
- stacktrace_cli-0.3.0/tests/monitor/test_watch.py +0 -607
- stacktrace_cli-0.3.0/tests/test_detector_liveness.py +0 -698
- stacktrace_cli-0.3.0/tests/test_detector_markers.py +0 -150
- stacktrace_cli-0.3.0/tests/test_detector_run.py +0 -854
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.claude/settings.json +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.codex/hooks.json +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.github/workflows/autofix.yml +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.github/workflows/ci.yml +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.github/workflows/publish-pypi.yml +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/.gitignore +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/AGENTS.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/CLAUDE.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0003-runtime-edges.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0008-console-in-separate-demo-package.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0014-declared-project-mapping.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0021-two-command-kinds.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0026-the-console-becomes-a-product-surface.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0027-a-finding-may-name-what-it-could-not-place.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0028-the-console-shows-what-it-placed.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/0029-stage-three-returns-to-opt-in.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/HOOK-PROMPT.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/adrs/TEMPLATE.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/cutover-openaca-remote.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/002-session-input.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/003-correlation.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/004-detector.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/005-remote-sync.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/006-cli-composition.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/007-detection-upload.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/plans/008-monitor.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.0.1.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.1.0.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.2.0.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.2.1.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.2.2.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.2.3.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/releases/v0.3.0.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/cli-composition.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/correlation.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/docs/specs/remote-sync.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/scripts/git-hooks/pre-push +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/scripts/install-hooks.sh +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/__main__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/composition.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/join.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/observed.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/project_map.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/correlate/record.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/analyzer.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/detector/verdict.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/fonts/OFL.txt +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/monitor/verdicts.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/client.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/config.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/payload.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/policy.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/remote/sync.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/sessions/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/src/stacktrace_cli/sessions/outcome.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/agent_bom_fixture.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/monitor/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/monitor/test_state.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/__init__.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/helpers.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_cli.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_client.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_config.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_cli.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_client.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_payload_reaches_the_cloud_model.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_redaction.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_spool.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_upload_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_detect_wire_payload.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_facade_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_payload.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_policy.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_redact_payload.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_sync.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/remote/test_upload_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_acquire.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_agent_instance_id.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_bom_shape.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_composition.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_correlated_session.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_analyzer_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_detector_secrets.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_help_sections.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_join.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_no_live_document_states_the_old_stage_three_default.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_observed.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_openaca_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_openaidr_contract.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_option_arity_matches_the_binary.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_project_map.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_seam_boundary.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_sessions_end_to_end.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_sessions_protocol_typing.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_sessions_protocols.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_shell_separators_match_the_shell.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_stored_verdicts_are_never_gated_on_reasoning.py +0 -0
- {stacktrace_cli-0.3.0 → stacktrace_cli-0.4.0}/tests/test_verification_subcommands_match_the_binary.py +0 -0
|
@@ -42,11 +42,12 @@ git rev-parse --verify "v<version>" 2>/dev/null # MUST FAIL — tag must not
|
|
|
42
42
|
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
`
|
|
46
|
-
requires `openaca`/`openaidr` pinned exactly, which is not true of
|
|
47
|
-
checkout (both float on `>=` between releases) and is only made true
|
|
48
|
-
Step 4. Running it against an unpinned tree would fail on every release,
|
|
49
|
-
just a broken one. It runs at the end of Step 4, once its precondition
|
|
45
|
+
The `release_gate` marker is **not** run here: `test_release_readiness.py`'s
|
|
46
|
+
share of it requires `openaca`/`openaidr` pinned exactly, which is not true of
|
|
47
|
+
a normal checkout (both float on `>=` between releases) and is only made true
|
|
48
|
+
by Step 4. Running it against an unpinned tree would fail on every release,
|
|
49
|
+
not just a broken one. It runs at the end of Step 4, once its precondition
|
|
50
|
+
holds.
|
|
50
51
|
|
|
51
52
|
PyPI versions are immutable, including yanked ones. If `<version>` is
|
|
52
53
|
already on PyPI, the only path forward is a new version number:
|
|
@@ -102,9 +103,10 @@ shipped.
|
|
|
102
103
|
|
|
103
104
|
### Step 4. Bump the version, pin workspace dependencies, and gate
|
|
104
105
|
|
|
105
|
-
`pyproject.toml`'s `version
|
|
106
|
-
`__version__`
|
|
107
|
-
|
|
106
|
+
`pyproject.toml`'s `version`, `src/stacktrace_cli/__init__.py`'s
|
|
107
|
+
`__version__` and the `--version` console block in `README.md` are three
|
|
108
|
+
separate copies of the version string. The workflow fails the build if the
|
|
109
|
+
first two disagree with the tag. Move them together:
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
sed -i.bak 's/^version = ".*"/version = "<version>"/' pyproject.toml && rm pyproject.toml.bak
|
|
@@ -125,6 +127,16 @@ uv lock # refresh uv.lock, now free t
|
|
|
125
127
|
uv run stacktrace --version # sanity: prints <version>
|
|
126
128
|
```
|
|
127
129
|
|
|
130
|
+
Paste that exact output over the console block under `## Installation` in
|
|
131
|
+
`README.md`. `readme = "README.md"` makes that file the PyPI long description,
|
|
132
|
+
and a description is baked into the artifact at upload — releases being
|
|
133
|
+
immutable, a stale line there is only ever corrected by burning the next
|
|
134
|
+
version number, which is what 0.3.1 was for. The block sat at `0.2.1` through
|
|
135
|
+
three releases before anything checked it;
|
|
136
|
+
`tests/test_readme_examples_are_real_output.py` now fails the build when it
|
|
137
|
+
drifts, so this is a step you are reminded of rather than one you must
|
|
138
|
+
remember.
|
|
139
|
+
|
|
128
140
|
Now pin both to whatever `uv lock` just resolved — the one point in the
|
|
129
141
|
procedure where the lockfile has just picked the newest available versions
|
|
130
142
|
and a release is actually being cut:
|
|
@@ -135,16 +147,20 @@ openaidr_version=$(grep -A1 '^name = "openaidr"' uv.lock | grep version | cut -d
|
|
|
135
147
|
sed -i.bak -E "s/^ \"openaca(>=|==)[^\"]*\"/ \"openaca==${openaca_version}\"/" pyproject.toml && rm pyproject.toml.bak
|
|
136
148
|
sed -i.bak -E "s/^ \"openaidr(>=|==)[^\"]*\"/ \"openaidr==${openaidr_version}\"/" pyproject.toml && rm pyproject.toml.bak
|
|
137
149
|
uv lock # refresh uv.lock for the exact pin
|
|
138
|
-
uv run pytest
|
|
150
|
+
uv run pytest -m release_gate
|
|
139
151
|
```
|
|
140
152
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
By marker, not by file: a second `release_gate` test can be added anywhere
|
|
154
|
+
under `tests/` (as `tests/telemetry/test_release_gate.py` was, checking that
|
|
155
|
+
PostHog's project key is set) and this command picks it up without the skill
|
|
156
|
+
needing to name it. The gate is not covered by `.github/workflows/publish-pypi.yml`,
|
|
157
|
+
which runs the default `uv run pytest -q` — and `pyproject.toml`'s `addopts`
|
|
158
|
+
excludes `release_gate` from that default run on purpose (it fails throughout
|
|
159
|
+
normal development, before this step pins the floors and, for the PostHog key,
|
|
160
|
+
before Micheal fills it in). This skill is the only place any `release_gate`
|
|
161
|
+
test runs before a tag is pushed, so skipping it here, or naming only one file
|
|
162
|
+
here, is the one way an unpinned `openaidr`/`openaca` requirement or an unset
|
|
163
|
+
PostHog key reaches PyPI. Stop and ask if it fails.
|
|
148
164
|
|
|
149
165
|
### Step 5. Release-prep commit + PR
|
|
150
166
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../../../.agents/skills/release-stacktrace/SKILL.md
|
|
@@ -99,6 +99,16 @@ jobs:
|
|
|
99
99
|
id: claude
|
|
100
100
|
uses: anthropics/claude-code-action@v1
|
|
101
101
|
with:
|
|
102
|
+
# Keep pre-push checks attached to the session until they finish.
|
|
103
|
+
# Background pushes can be abandoned when the model ends its turn.
|
|
104
|
+
settings: |
|
|
105
|
+
{
|
|
106
|
+
"env": {
|
|
107
|
+
"CLAUDE_CODE_DISABLE_BACKGROUND_TASKS": "1",
|
|
108
|
+
"BASH_DEFAULT_TIMEOUT_MS": "600000"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
102
112
|
# Subscription-tied OAuth path. Provision via `claude setup-token`
|
|
103
113
|
# CLI and store as the CLAUDE_CODE_OAUTH_TOKEN repo secret.
|
|
104
114
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stacktrace-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: CLI for Stacktrace — Detection and Response platform for AI Agents.
|
|
5
5
|
Project-URL: Homepage, https://stacktrace.ai
|
|
6
6
|
Author-email: "Stacktrace AI, Inc" <founders@stacktrace.ai>
|
|
@@ -10,7 +10,8 @@ Classifier: Development Status :: 4 - Beta
|
|
|
10
10
|
Classifier: Environment :: Console
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: Other/Proprietary License
|
|
13
|
-
Classifier: Operating System ::
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
15
|
Classifier: Programming Language :: Python :: 3
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -19,8 +20,9 @@ Classifier: Topic :: Security
|
|
|
19
20
|
Requires-Python: >=3.11
|
|
20
21
|
Requires-Dist: click>=8.1
|
|
21
22
|
Requires-Dist: httpx<1.0.dev0,>=0.28.1
|
|
22
|
-
Requires-Dist: openaca==0.
|
|
23
|
-
Requires-Dist: openaidr==0.
|
|
23
|
+
Requires-Dist: openaca==0.7.0
|
|
24
|
+
Requires-Dist: openaidr==0.3.0
|
|
25
|
+
Requires-Dist: posthog<8,>=7.0
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
25
27
|
|
|
26
28
|
# stacktrace-cli
|
|
@@ -49,7 +51,7 @@ Requires Python 3.11 or newer.
|
|
|
49
51
|
|
|
50
52
|
```console
|
|
51
53
|
$ stacktrace --version
|
|
52
|
-
stacktrace 0.
|
|
54
|
+
stacktrace 0.4.0 (openaca 0.7.0)
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
## Quick start
|
|
@@ -81,10 +83,10 @@ name.
|
|
|
81
83
|
```console
|
|
82
84
|
$ stacktrace sessions --since 2d --include-content
|
|
83
85
|
claude-code:s1 [claude-code] 2026-08-27T09:00:00+00:00 2 turns 2 calls
|
|
84
|
-
assistant: Reading the changelog before drafting the release notes.
|
|
86
|
+
2026-08-29T12:00:00+00:00 assistant: Reading the changelog before drafting the release notes.
|
|
85
87
|
ok 28c Read
|
|
86
88
|
result: ## 0.4.0 - correlate, detect
|
|
87
|
-
assistant: Filing the release-notes follow-up.
|
|
89
|
+
2026-08-29T12:00:00+00:00 assistant: Filing the release-notes follow-up.
|
|
88
90
|
- github/create_issue
|
|
89
91
|
|
|
90
92
|
Summary — 1 sessions, 2 turns, 2 tool calls
|
|
@@ -110,31 +112,61 @@ counts those rather than filling one in.
|
|
|
110
112
|
|
|
111
113
|
## What `detect` finds
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
ladders — a
|
|
115
|
+
Three kinds of finding, under two families that carry separate severity
|
|
116
|
+
ladders — a blocked agent and a leaked credential do not belong on one scale.
|
|
115
117
|
|
|
116
|
-
**Security** — a credential reaching an outbound call;
|
|
117
|
-
the
|
|
118
|
-
vulnerability behind it.
|
|
118
|
+
**Security** — a credential reaching an outbound call; success reported against
|
|
119
|
+
the session's own evidence of failure.
|
|
119
120
|
|
|
120
|
-
**Reliability** —
|
|
121
|
+
**Reliability** — why the agent stopped, in words, with what to do about it: a
|
|
122
|
+
spend limit reached, a safety check blocking, a service refusing a call.
|
|
121
123
|
|
|
122
|
-
Findings are correlated against an Agent BOM before they are
|
|
123
|
-
|
|
124
|
-
because it is installed.
|
|
124
|
+
Findings are correlated against an Agent BOM before they are reported, so one
|
|
125
|
+
that names a component names the one a call actually reached.
|
|
125
126
|
|
|
126
127
|
## What leaves your machine
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
**On a default run**, `detect` reads the record on this machine. It needs no
|
|
130
|
+
model, no credential and no account, and nothing derived from a session — no
|
|
131
|
+
prompt, no tool argument, no result — is sent anywhere.
|
|
132
|
+
|
|
133
|
+
One network call is made even then, and it is worth stating because *nothing
|
|
134
|
+
leaves* is not what happens: correlation matches advisories by sending the
|
|
135
|
+
package coordinates of the components a session invoked to osv.dev.
|
|
136
|
+
Coordinates only.
|
|
137
|
+
|
|
138
|
+
**One flag pair changes that, and it has to be typed in full.** `detect
|
|
139
|
+
--reasoning --analyzer jev` adds a third stage that sends two small states to
|
|
140
|
+
TypeSafe's Jev: a *gate* carrying the closing lines of each hand-back, capped,
|
|
141
|
+
which ends most sessions there — and, for a session that clears it, one
|
|
142
|
+
per-call *window* each carrying a completion claim, one verification command
|
|
143
|
+
and the tail of its output. Neither flag has a default and neither implies the other,
|
|
144
|
+
so naming the vendor is your act rather than ours (ADR-0033). Without both,
|
|
145
|
+
stage three is not asked. `detect --analyzer jev` alone reads back what such a
|
|
146
|
+
run already paid for and commissions nothing (ADR-0035).
|
|
133
147
|
|
|
134
148
|
`sessions` omits prompts, tool arguments and results unless you pass
|
|
135
|
-
`--include-content`. `monitor` binds to loopback only, refuses a
|
|
136
|
-
address rather than warning about it
|
|
137
|
-
|
|
149
|
+
`--include-content`. `monitor` binds to loopback only, and refuses a
|
|
150
|
+
non-loopback address rather than warning about it.
|
|
151
|
+
|
|
152
|
+
**Usage telemetry is enabled by default.** Disable it before normal use with
|
|
153
|
+
`stacktrace telemetry off`. An existing opt-out stays off; unreadable or invalid
|
|
154
|
+
settings also disable telemetry. The first enabled usage event creates a random
|
|
155
|
+
identifier. The daemon sends four event types to PostHog's US region: an enabled
|
|
156
|
+
identity first becomes active, a session starts, a finding is delivered, or a
|
|
157
|
+
product error occurs. Event fields describe the rule, severity, destination,
|
|
158
|
+
reason code or error category, the session id and agent kind for session and finding events, plus CLI version, OS, environment, whether it ran in a Claude Code cloud session (`remote`) and timestamp.
|
|
159
|
+
`environment` is `production` unless `STACKTRACE_TELEMETRY_ENV` is set to
|
|
160
|
+
`staging`, `development` or `test`. They do not
|
|
161
|
+
include prompts, tool arguments, results, full findings, paths or repository
|
|
162
|
+
names. The persistent identifier and timing mean this is not anonymous data.
|
|
163
|
+
|
|
164
|
+
`stacktrace telemetry show` prints the state, the destination, the environment,
|
|
165
|
+
the identifier and every event with every property it may carry. `off` deletes
|
|
166
|
+
the local identifier; it does not delete previously uploaded records, and an
|
|
167
|
+
event already handed to the client may still be sent. `stacktrace telemetry on`
|
|
168
|
+
enables telemetry again under a new identity (ADR-0039).
|
|
169
|
+
`STACKTRACE_TELEMETRY_DISABLED=1` sends nothing, for CI and test runs.
|
|
138
170
|
|
|
139
171
|
## Status
|
|
140
172
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# stacktrace-cli
|
|
2
|
+
|
|
3
|
+
Detection and response for AI coding agents, from the command line.
|
|
4
|
+
|
|
5
|
+
Coding agents read files, run shell commands and call MCP servers on their own
|
|
6
|
+
initiative, and they write a transcript of every bit of it to disk.
|
|
7
|
+
`stacktrace` reads those transcripts, correlates what ran against the
|
|
8
|
+
components the agent is built from, and reports the security and reliability
|
|
9
|
+
findings in it — locally, on the machine the agent worked on.
|
|
10
|
+
|
|
11
|
+
The PyPI distribution is `stacktrace-cli`; the command it installs is
|
|
12
|
+
`stacktrace`. The two names differ because the bare `stacktrace` name on PyPI
|
|
13
|
+
belongs to an unrelated project.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install stacktrace-cli # isolated; recommended
|
|
19
|
+
# or
|
|
20
|
+
pip install stacktrace-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Requires Python 3.11 or newer.
|
|
24
|
+
|
|
25
|
+
```console
|
|
26
|
+
$ stacktrace --version
|
|
27
|
+
stacktrace 0.4.0 (openaca 0.7.0)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
stacktrace sessions # what the agents on this machine did
|
|
34
|
+
stacktrace detect # what is wrong with it
|
|
35
|
+
stacktrace monitor # the same, live in a browser
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
| Command | |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `sessions` | Print what the agents on this machine actually did. |
|
|
43
|
+
| `detect` | Find security and reliability findings in what agents did. |
|
|
44
|
+
| `monitor` | Watch this machine's agents in a browser, live. |
|
|
45
|
+
| `remote` | Configure remote endpoint services and upload to Stacktrace Cloud. |
|
|
46
|
+
| `scan` | Scan a repository or endpoint for agent-composition findings. |
|
|
47
|
+
| `bom` | Generate an Agent BOM for a repository or endpoint. |
|
|
48
|
+
| `policy` | Validate and compile restrictive endpoint policies. |
|
|
49
|
+
|
|
50
|
+
The last three are composition analysis, supplied by
|
|
51
|
+
[`openaca`](https://pypi.org/project/openaca/) and available under either
|
|
52
|
+
name.
|
|
53
|
+
|
|
54
|
+
## What it looks like
|
|
55
|
+
|
|
56
|
+
```console
|
|
57
|
+
$ stacktrace sessions --since 2d --include-content
|
|
58
|
+
claude-code:s1 [claude-code] 2026-08-27T09:00:00+00:00 2 turns 2 calls
|
|
59
|
+
2026-08-29T12:00:00+00:00 assistant: Reading the changelog before drafting the release notes.
|
|
60
|
+
ok 28c Read
|
|
61
|
+
result: ## 0.4.0 - correlate, detect
|
|
62
|
+
2026-08-29T12:00:00+00:00 assistant: Filing the release-notes follow-up.
|
|
63
|
+
- github/create_issue
|
|
64
|
+
|
|
65
|
+
Summary — 1 sessions, 2 turns, 2 tool calls
|
|
66
|
+
|
|
67
|
+
agent kinds
|
|
68
|
+
1 claude-code
|
|
69
|
+
|
|
70
|
+
tools called (2 distinct)
|
|
71
|
+
1 Read
|
|
72
|
+
1 github/create_issue
|
|
73
|
+
|
|
74
|
+
MCP servers reached (1 distinct)
|
|
75
|
+
1 github
|
|
76
|
+
|
|
77
|
+
0 subagent turns · 0 results abridged upstream · 1 ok
|
|
78
|
+
|
|
79
|
+
1 of 2 calls returned with no outcome the collector could establish; the agent's parser supplies no success signal.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A blank status column is the collector's `unknown`, not a pending call: the
|
|
83
|
+
client recorded no outcome that could be established, and the closing line
|
|
84
|
+
counts those rather than filling one in.
|
|
85
|
+
|
|
86
|
+
## What `detect` finds
|
|
87
|
+
|
|
88
|
+
Three kinds of finding, under two families that carry separate severity
|
|
89
|
+
ladders — a blocked agent and a leaked credential do not belong on one scale.
|
|
90
|
+
|
|
91
|
+
**Security** — a credential reaching an outbound call; success reported against
|
|
92
|
+
the session's own evidence of failure.
|
|
93
|
+
|
|
94
|
+
**Reliability** — why the agent stopped, in words, with what to do about it: a
|
|
95
|
+
spend limit reached, a safety check blocking, a service refusing a call.
|
|
96
|
+
|
|
97
|
+
Findings are correlated against an Agent BOM before they are reported, so one
|
|
98
|
+
that names a component names the one a call actually reached.
|
|
99
|
+
|
|
100
|
+
## What leaves your machine
|
|
101
|
+
|
|
102
|
+
**On a default run**, `detect` reads the record on this machine. It needs no
|
|
103
|
+
model, no credential and no account, and nothing derived from a session — no
|
|
104
|
+
prompt, no tool argument, no result — is sent anywhere.
|
|
105
|
+
|
|
106
|
+
One network call is made even then, and it is worth stating because *nothing
|
|
107
|
+
leaves* is not what happens: correlation matches advisories by sending the
|
|
108
|
+
package coordinates of the components a session invoked to osv.dev.
|
|
109
|
+
Coordinates only.
|
|
110
|
+
|
|
111
|
+
**One flag pair changes that, and it has to be typed in full.** `detect
|
|
112
|
+
--reasoning --analyzer jev` adds a third stage that sends two small states to
|
|
113
|
+
TypeSafe's Jev: a *gate* carrying the closing lines of each hand-back, capped,
|
|
114
|
+
which ends most sessions there — and, for a session that clears it, one
|
|
115
|
+
per-call *window* each carrying a completion claim, one verification command
|
|
116
|
+
and the tail of its output. Neither flag has a default and neither implies the other,
|
|
117
|
+
so naming the vendor is your act rather than ours (ADR-0033). Without both,
|
|
118
|
+
stage three is not asked. `detect --analyzer jev` alone reads back what such a
|
|
119
|
+
run already paid for and commissions nothing (ADR-0035).
|
|
120
|
+
|
|
121
|
+
`sessions` omits prompts, tool arguments and results unless you pass
|
|
122
|
+
`--include-content`. `monitor` binds to loopback only, and refuses a
|
|
123
|
+
non-loopback address rather than warning about it.
|
|
124
|
+
|
|
125
|
+
**Usage telemetry is enabled by default.** Disable it before normal use with
|
|
126
|
+
`stacktrace telemetry off`. An existing opt-out stays off; unreadable or invalid
|
|
127
|
+
settings also disable telemetry. The first enabled usage event creates a random
|
|
128
|
+
identifier. The daemon sends four event types to PostHog's US region: an enabled
|
|
129
|
+
identity first becomes active, a session starts, a finding is delivered, or a
|
|
130
|
+
product error occurs. Event fields describe the rule, severity, destination,
|
|
131
|
+
reason code or error category, the session id and agent kind for session and finding events, plus CLI version, OS, environment, whether it ran in a Claude Code cloud session (`remote`) and timestamp.
|
|
132
|
+
`environment` is `production` unless `STACKTRACE_TELEMETRY_ENV` is set to
|
|
133
|
+
`staging`, `development` or `test`. They do not
|
|
134
|
+
include prompts, tool arguments, results, full findings, paths or repository
|
|
135
|
+
names. The persistent identifier and timing mean this is not anonymous data.
|
|
136
|
+
|
|
137
|
+
`stacktrace telemetry show` prints the state, the destination, the environment,
|
|
138
|
+
the identifier and every event with every property it may carry. `off` deletes
|
|
139
|
+
the local identifier; it does not delete previously uploaded records, and an
|
|
140
|
+
event already handed to the client may still be sent. `stacktrace telemetry on`
|
|
141
|
+
enables telemetry again under a new identity (ADR-0039).
|
|
142
|
+
`STACKTRACE_TELEMETRY_DISABLED=1` sends nothing, for CI and test runs.
|
|
143
|
+
|
|
144
|
+
## Status
|
|
145
|
+
|
|
146
|
+
Beta, and under active development.
|
|
147
|
+
|
|
148
|
+
`sessions`, `detect` and `monitor` work end to end today. Session collection
|
|
149
|
+
currently reads Claude Code transcripts; further agent kinds are in progress
|
|
150
|
+
upstream in [OpenAIDR](https://github.com/open-agent-security/openaidr).
|
|
151
|
+
|
|
152
|
+
## Built on
|
|
153
|
+
|
|
154
|
+
Two Apache-2.0 packages, neither of which depends on this one:
|
|
155
|
+
[`openaca`](https://pypi.org/project/openaca/) for agent composition analysis,
|
|
156
|
+
and [`openaidr`](https://pypi.org/project/openaidr/) for session collection.
|
|
157
|
+
|
|
158
|
+
## Licence
|
|
159
|
+
|
|
160
|
+
Proprietary. © Stacktrace AI, Inc. — [stacktrace.ai](https://stacktrace.ai)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0030
|
|
3
|
+
title: A detection is dated by the turns it cites
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-15
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
A finding carried no time of its own. `Detection` had no timestamp, `Evidence`
|
|
13
|
+
was `(span, kind, detail)`, and `SessionRef` carried the session's `started_at`
|
|
14
|
+
— so every surface answered "when did this happen" with the session's start.
|
|
15
|
+
|
|
16
|
+
That is wrong by however long the session ran. OpenAIDR takes `started_at` from
|
|
17
|
+
the transcript's earliest record, so a session left open for a day dates its last
|
|
18
|
+
finding by its first minute, and a session resumed across a fortnight dates
|
|
19
|
+
today's finding two weeks ago. The local console worked around it with the
|
|
20
|
+
transcript file's modification time; the receiving service works around it by
|
|
21
|
+
admitting a finding on the later of the session start and its own receipt time.
|
|
22
|
+
Both are compensating for a value this package never produced.
|
|
23
|
+
|
|
24
|
+
OpenAIDR now carries `Turn.occurred_at` (its ADR-0010). The question this ADR
|
|
25
|
+
settles is which turns date a finding, and what shape that date takes.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
A detection is dated by **the turns its evidence cites**: `occurred_start` is the
|
|
30
|
+
MIN and `occurred_end` the MAX over its citations' turn times, and each
|
|
31
|
+
`Evidence` carries the time of its own turn beside it. A finding citing one turn
|
|
32
|
+
has equal bounds, which is the correct answer rather than a degenerate one.
|
|
33
|
+
|
|
34
|
+
Citations that resolve to no turn — `ContextItem` spans, which take the literal
|
|
35
|
+
turn key `context` — contribute nothing and do not undate a finding its other
|
|
36
|
+
citations can date. A finding with no dated citation carries no range, and the
|
|
37
|
+
consuming surface falls back to the clocks it used before.
|
|
38
|
+
|
|
39
|
+
Dating is applied **once**, at the seam where a detector run is assembled, not at
|
|
40
|
+
each of the five sites that extend the finding list.
|
|
41
|
+
|
|
42
|
+
## Alternatives considered
|
|
43
|
+
|
|
44
|
+
- **Date a finding by its session's start.** The status quo, and the thing being
|
|
45
|
+
fixed. Rejected: wrong by the session's whole duration, and worst exactly where
|
|
46
|
+
it matters — the long, busy sessions that produce the most findings.
|
|
47
|
+
- **Date a finding by the anchor citation alone** (`evidence[0]`). Rejected: the
|
|
48
|
+
anchor is *what the finding is about*, not *when it happened*. A session-scoped
|
|
49
|
+
rule accumulates citations as the session grows, so the anchor would freeze a
|
|
50
|
+
live finding at its oldest evidence.
|
|
51
|
+
- **A single instant per detection — the newest cited turn.** Specified first,
|
|
52
|
+
then cut. It answers "how recent" but destroys "over what span", and the span
|
|
53
|
+
is the question for a multi-turn episode: a finding whose citations run from
|
|
54
|
+
turn 12 to turn 47 is telling a story about an interval, and collapsing it to
|
|
55
|
+
turn 47 discards the part a responder reconstructs the incident from.
|
|
56
|
+
- **Per-turn start *and* end, carried on every citation.** Also specified, then
|
|
57
|
+
cut. It requires OpenAIDR to model a turn end, which needs the ambiguous
|
|
58
|
+
call-id join and a rule for a turn holding a still-running call — and it
|
|
59
|
+
answers a question nobody asked. A consumer wanting the span of an episode
|
|
60
|
+
gets it from the first and last turn it cites, which is exactly this decision.
|
|
61
|
+
- **Put the range on `SessionRef`.** Rejected twice. `SessionRef`'s four fields
|
|
62
|
+
mirror OpenAIDR's outward-carriage descriptor, and widening one without the
|
|
63
|
+
other breaks the mirror. More decisively, a time on `SessionRef` would be
|
|
64
|
+
shared by every detection from that session — which is the bug restated, not
|
|
65
|
+
fixed.
|
|
66
|
+
- **Let the receiving service derive the range from the evidence rows it
|
|
67
|
+
stores.** Rejected: it forces a join to answer the commonest filter, and the
|
|
68
|
+
producer already holds the values. The range is denormalised onto the
|
|
69
|
+
detection for exactly that reason.
|
|
70
|
+
- **Re-derive the turn from the span string.** The span embeds its turn key, so
|
|
71
|
+
parsing it would work. Rejected: span construction is OpenAIDR's (its
|
|
72
|
+
ADR-0001), and a consumer that parses it takes a dependency on a format it does
|
|
73
|
+
not own and cannot be told has changed. The lookup walks the turns instead.
|
|
74
|
+
|
|
75
|
+
## Consequences
|
|
76
|
+
|
|
77
|
+
A responder can see when a finding happened, and for a multi-turn finding, over
|
|
78
|
+
what interval. The monitor and the receiving service both get a device-side clock
|
|
79
|
+
that is a property of the session rather than of the file holding it.
|
|
80
|
+
|
|
81
|
+
Every consumer must treat equal bounds as ordinary and print one time rather than
|
|
82
|
+
a range — most findings cite one turn, so this is the common path, not an edge.
|
|
83
|
+
|
|
84
|
+
Nulls propagate honestly and therefore have to be handled honestly: a finding
|
|
85
|
+
citing only context spans is undated, and a surface that renders that as "just
|
|
86
|
+
now" or as the session's start has reintroduced the defect this fixes.
|
|
87
|
+
|
|
88
|
+
The wire shape grows three nullable keys. Because the upload contract is an
|
|
89
|
+
exhaustive allowlist, they must be added to it in the same commit as the
|
|
90
|
+
producer, or every upload fails locally before it is sent.
|
|
91
|
+
|
|
92
|
+
## When to revisit
|
|
93
|
+
|
|
94
|
+
If a rule family appears whose citations are not tool calls — a finding about a
|
|
95
|
+
session's shape rather than its actions — the span-to-turn lookup would return
|
|
96
|
+
nothing for it, and dating would need a second source.
|
|
97
|
+
|
|
98
|
+
If undated findings turn out to be common rather than rare in practice, which
|
|
99
|
+
would mean context-only citations matter more than measured, and would justify
|
|
100
|
+
dating a `ContextItem` upstream.
|
|
101
|
+
|
|
102
|
+
If the range is ever used to *admit* a finding to a window rather than to
|
|
103
|
+
display it, the null-together property and the fallback chain need restating for
|
|
104
|
+
that use before it is relied on.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0031
|
|
3
|
+
title: Let one host daemon observe native session logs and deliver findings
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-17
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0006
|
|
9
|
+
amended-by: 0032, 0036, 0037, 0038, 0039
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
Automatic detection needs to work while an agent session is active without
|
|
15
|
+
making every agent integration implement the detector. Claude exposes lifecycle
|
|
16
|
+
hooks and an experimental session monitor, but other hosts expose different
|
|
17
|
+
surfaces. Hooks also describe lifecycle boundaries rather than the completion of
|
|
18
|
+
an asynchronous detection pass: starting detached work on every turn creates
|
|
19
|
+
overlapping workers, while waiting in the hook delays the agent.
|
|
20
|
+
|
|
21
|
+
The native session log is already the complete local event ledger consumed
|
|
22
|
+
through OpenAIDR. Reading it requires no proxy, SDK wrapper, provider
|
|
23
|
+
instrumentation or change to the user's agent configuration beyond installing
|
|
24
|
+
an adapter. The missing piece is a process that notices changes, runs the
|
|
25
|
+
existing pipeline incrementally, and routes the result back to the one session
|
|
26
|
+
that caused it.
|
|
27
|
+
|
|
28
|
+
That process needs durable delivery state. ADR-0006 permits findings to travel
|
|
29
|
+
but says session models and correlation records do not persist; ADR-0011 makes a
|
|
30
|
+
narrow exception for verdicts and explicitly says another kind of
|
|
31
|
+
session-derived state needs its own case. A content-free finding and its delivery
|
|
32
|
+
receipt are that second case.
|
|
33
|
+
|
|
34
|
+
## Decision
|
|
35
|
+
|
|
36
|
+
One per-user, host-level Stacktrace daemon observes native session logs, runs the
|
|
37
|
+
existing in-process analysis pipeline, persists content-free findings, and
|
|
38
|
+
delivers eligible notification events over a local Unix socket.
|
|
39
|
+
|
|
40
|
+
1. Agent adapters identify a native session and subscribe to the daemon. They do
|
|
41
|
+
not parse transcripts or run detection. The daemon routes only by exact
|
|
42
|
+
`(host, native session id)`; current directory and newest-session heuristics
|
|
43
|
+
are never routing keys.
|
|
44
|
+
2. OpenAIDR remains the only session-log parsing boundary. The daemon narrows
|
|
45
|
+
analysis to subscribed sessions and re-runs it only when that session's file
|
|
46
|
+
size or modification time changes.
|
|
47
|
+
3. The socket protocol is versioned newline-delimited JSON. Notification
|
|
48
|
+
delivery is at least once: the subscriber acknowledges only after flushing
|
|
49
|
+
the tagged event to its host, and reconnecting subscribers receive unacknowledged
|
|
50
|
+
events with the same stable event id.
|
|
51
|
+
4. This amends ADR-0006 to permit one durable local ledger containing the
|
|
52
|
+
already-published, content-free detection shape, stable event ids, native
|
|
53
|
+
session ids, transcript size and modification time, and delivery status. It
|
|
54
|
+
never stores turns, prompts, tool arguments, tool results, correlation
|
|
55
|
+
records, compositions, evidence excerpts or analyzer explanations.
|
|
56
|
+
5. Notification prose is generated from Stacktrace-owned rule identifiers and
|
|
57
|
+
grades. Transcript text and finding-provided titles are not copied into the
|
|
58
|
+
host-facing notification channel.
|
|
59
|
+
6. The daemon exists while at least one adapter is subscribed, plus a short
|
|
60
|
+
final-flush grace after disconnect. A lock admits one daemon per user, and an
|
|
61
|
+
adapter reconnects after daemon replacement.
|
|
62
|
+
|
|
63
|
+
## Alternatives considered
|
|
64
|
+
|
|
65
|
+
- **Run detection from Stop and SessionEnd hooks.** Rejected because a turn
|
|
66
|
+
boundary is not a detection boundary. Detached hooks create overlapping
|
|
67
|
+
workers and lose their results; attached hooks delay the agent. Hosts without
|
|
68
|
+
equivalent hooks would also need another architecture.
|
|
69
|
+
- **Put a session watcher and detector inside every plugin.** Rejected because
|
|
70
|
+
every active session would own duplicate parsing, policy and durable state,
|
|
71
|
+
and each new host adapter would copy the same engine.
|
|
72
|
+
- **Require OTEL, OpenInference, an MCP proxy or an SDK wrapper.** Rejected for
|
|
73
|
+
the default path because each requires instrumentation or traffic-path changes
|
|
74
|
+
before Stacktrace can observe an existing agent. They may become additional
|
|
75
|
+
inputs, but not an installation prerequisite.
|
|
76
|
+
- **Persist transcripts or normalized session events and process only appended
|
|
77
|
+
bytes.** Rejected because it widens the local data boundary and duplicates the
|
|
78
|
+
parser's state model. The first increment is the session selector plus the
|
|
79
|
+
file fingerprint: only a changed subscribed session is reparsed, and stable
|
|
80
|
+
finding identity prevents duplicate work at the delivery boundary.
|
|
81
|
+
- **Send findings to whichever subscriber shares a working directory.**
|
|
82
|
+
Rejected because parallel sessions commonly share a project. A plausible
|
|
83
|
+
notification delivered to the wrong conversation is a security failure, not
|
|
84
|
+
a cosmetic race.
|
|
85
|
+
|
|
86
|
+
## Consequences
|
|
87
|
+
|
|
88
|
+
Agent plugins become small adapters: identify the native session, maintain one
|
|
89
|
+
local subscription, and teach the host how to render a tagged Stacktrace event.
|
|
90
|
+
The analysis engine, policy, persistence and delivery semantics remain in one
|
|
91
|
+
place and can serve later Codex or Cursor adapters without moving them.
|
|
92
|
+
|
|
93
|
+
The persisted ledger weakens ADR-0006's former absolute local-persistence rule,
|
|
94
|
+
but only with the same content-free finding object already allowed to travel.
|
|
95
|
+
Tests must continue to prove notification text cannot contain attacker-controlled
|
|
96
|
+
finding titles and that sessions cannot receive one another's events.
|
|
97
|
+
|
|
98
|
+
The first increment reparses the changed session rather than parsing an append
|
|
99
|
+
stream. That keeps OpenAIDR as the format owner and is simpler to recover after
|
|
100
|
+
partial writes, at the cost of work proportional to one growing session. If
|
|
101
|
+
that cost threatens the notification objective, the incremental cursor belongs
|
|
102
|
+
in OpenAIDR's per-kind reader contract rather than in the daemon.
|
|
103
|
+
|
|
104
|
+
The model-mediated host notification is best effort: a host that is unavailable
|
|
105
|
+
cannot render it. Findings remain durable, so an independent notification
|
|
106
|
+
adapter can be added without changing observation or detection.
|
|
107
|
+
|
|
108
|
+
## When to revisit
|
|
109
|
+
|
|
110
|
+
Revisit the file-fingerprint increment when measured changed-session analysis
|
|
111
|
+
cannot meet the notification objective. Revisit native-log observation when a
|
|
112
|
+
host offers a stable, complete event stream requiring no user instrumentation.
|
|
113
|
+
Revisit Unix sockets if supported hosts require Windows. Do not revisit merely
|
|
114
|
+
because one host adds another lifecycle hook; hooks do not change the ownership
|
|
115
|
+
or asynchronous-boundary arguments.
|