stacktrace-cli 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl
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/__init__.py +1 -1
- stacktrace_cli/daemon/cli.py +2 -4
- stacktrace_cli/daemon/presentation.py +68 -0
- stacktrace_cli/daemon/store.py +15 -3
- {stacktrace_cli-0.4.1.dist-info → stacktrace_cli-0.5.0.dist-info}/METADATA +2 -2
- {stacktrace_cli-0.4.1.dist-info → stacktrace_cli-0.5.0.dist-info}/RECORD +8 -7
- {stacktrace_cli-0.4.1.dist-info → stacktrace_cli-0.5.0.dist-info}/WHEEL +0 -0
- {stacktrace_cli-0.4.1.dist-info → stacktrace_cli-0.5.0.dist-info}/entry_points.txt +0 -0
stacktrace_cli/__init__.py
CHANGED
stacktrace_cli/daemon/cli.py
CHANGED
|
@@ -9,6 +9,7 @@ import sys
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
11
|
from .paths import RuntimePaths
|
|
12
|
+
from .presentation import render_finding
|
|
12
13
|
from .store import FindingStore, SessionKey
|
|
13
14
|
|
|
14
15
|
|
|
@@ -100,7 +101,4 @@ def findings(agent_kind: str, session_id: str, output_format: str) -> None:
|
|
|
100
101
|
click.echo("No Stacktrace findings for this session.")
|
|
101
102
|
return
|
|
102
103
|
for finding in stored:
|
|
103
|
-
click.echo(
|
|
104
|
-
f"{str(finding.get('severity', 'unknown')).upper()} "
|
|
105
|
-
f"{finding.get('rule_id', 'unknown-rule')}: {finding.get('title', 'Finding')}"
|
|
106
|
-
)
|
|
104
|
+
click.echo(render_finding(finding))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Explain durable findings without reintroducing discarded transcript prose."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from stacktrace_cli.detector.blocked import REASON_CODES, reason_for
|
|
8
|
+
|
|
9
|
+
# Notification prose is selected by rule ID only (ADR-0031). In particular,
|
|
10
|
+
# finding titles, component names and evidence never supply notification text.
|
|
11
|
+
_GUIDANCE = {
|
|
12
|
+
"stacktrace-credential-egress": (
|
|
13
|
+
"Credential-shaped material appeared in an outbound tool call",
|
|
14
|
+
(
|
|
15
|
+
"Treat the credential as potentially exposed: revoke or rotate it, "
|
|
16
|
+
"then inspect why the outbound tool received it."
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
"stacktrace-deceptive-completion": (
|
|
20
|
+
"A success claim conflicts with verification evidence",
|
|
21
|
+
"Inspect the cited verification results before relying on the completion claim.",
|
|
22
|
+
),
|
|
23
|
+
"stacktrace-agent-blocked": (
|
|
24
|
+
"The agent encountered a block while working",
|
|
25
|
+
"Inspect the recorded block reason before retrying the task.",
|
|
26
|
+
),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def rule_guidance(rule_id: str) -> tuple[str, str]:
|
|
31
|
+
return _GUIDANCE.get(
|
|
32
|
+
rule_id,
|
|
33
|
+
("A finding was recorded", "Inspect the retained evidence in the local session."),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def render_finding(finding: dict[str, object]) -> str:
|
|
38
|
+
"""Render the old, title-less ledger as well as newly recorded findings."""
|
|
39
|
+
rule_id = str(finding.get("rule_id", "unknown-rule"))
|
|
40
|
+
title, remedy = rule_guidance(rule_id)
|
|
41
|
+
evidence = finding.get("evidence", [])
|
|
42
|
+
items = (
|
|
43
|
+
[item for item in evidence if isinstance(item, dict)] if isinstance(evidence, list) else []
|
|
44
|
+
)
|
|
45
|
+
if rule_id == "stacktrace-agent-blocked" and items:
|
|
46
|
+
code = items[0].get("kind")
|
|
47
|
+
if isinstance(code, str) and code in REASON_CODES:
|
|
48
|
+
reason = reason_for(code)
|
|
49
|
+
title, remedy = reason.title, reason.remedy
|
|
50
|
+
|
|
51
|
+
lines = [
|
|
52
|
+
f"{str(finding.get('severity', 'unknown')).upper()} {rule_id}: {title}",
|
|
53
|
+
f" Confidence: {finding.get('confidence', 'unknown')}",
|
|
54
|
+
]
|
|
55
|
+
for item in items:
|
|
56
|
+
# Quote stored descriptors as data, including escaped terminal controls.
|
|
57
|
+
kind = json.dumps(item.get("kind"), ensure_ascii=True)
|
|
58
|
+
detail = json.dumps(item.get("detail"), ensure_ascii=True)
|
|
59
|
+
span = json.dumps(item.get("span"), ensure_ascii=True)
|
|
60
|
+
lines.extend((f" Evidence ({kind}): {detail}", f" Location: {span}"))
|
|
61
|
+
if not items:
|
|
62
|
+
lines.append(" Evidence: no descriptors were retained.")
|
|
63
|
+
lines.append(f" Next step: {remedy}")
|
|
64
|
+
lines.append(
|
|
65
|
+
" Tool/component names and raw call content are not retained in this ledger; "
|
|
66
|
+
"inspect the cited locations in the local session for attribution."
|
|
67
|
+
)
|
|
68
|
+
return "\n".join(lines)
|
stacktrace_cli/daemon/store.py
CHANGED
|
@@ -17,6 +17,8 @@ from stacktrace_cli.detector.rules import scope_of
|
|
|
17
17
|
from stacktrace_cli.private_state import ensure_private_directory
|
|
18
18
|
from stacktrace_cli.telemetry import emit_derived
|
|
19
19
|
|
|
20
|
+
from .presentation import rule_guidance
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
@dataclass(frozen=True)
|
|
22
24
|
class SessionKey:
|
|
@@ -266,10 +268,16 @@ class FindingStore:
|
|
|
266
268
|
identity = f"{session.agent_kind}\0{session.session_id}\0{rule_id}\0{anchor}".encode()
|
|
267
269
|
return f"stn_{hashlib.sha256(identity).hexdigest()[:24]}"
|
|
268
270
|
|
|
271
|
+
#: Severities that earn a notification. `Severity` is `low | medium | high`;
|
|
272
|
+
#: `critical` is carried because the gate has always tested for it, though
|
|
273
|
+
#: no rule emits it. `low` stays silent.
|
|
274
|
+
_NOTIFIABLE_SEVERITIES = frozenset({"medium", "high", "critical"})
|
|
275
|
+
|
|
269
276
|
@staticmethod
|
|
270
277
|
def _eligible(finding: dict[str, object]) -> bool:
|
|
271
278
|
return (
|
|
272
|
-
finding.get("severity") in
|
|
279
|
+
finding.get("severity") in FindingStore._NOTIFIABLE_SEVERITIES
|
|
280
|
+
and finding.get("confidence") == "high"
|
|
273
281
|
)
|
|
274
282
|
|
|
275
283
|
@staticmethod
|
|
@@ -302,13 +310,17 @@ class FindingStore:
|
|
|
302
310
|
severity = finding["severity"]
|
|
303
311
|
assert isinstance(rule_id, str)
|
|
304
312
|
assert isinstance(severity, str)
|
|
313
|
+
explanation, _ = rule_guidance(rule_id)
|
|
305
314
|
return Notification(
|
|
306
315
|
event_id=event_id,
|
|
307
316
|
session=session,
|
|
308
317
|
rule_id=rule_id,
|
|
309
318
|
severity=severity,
|
|
310
|
-
title=f"Stacktrace
|
|
311
|
-
body=
|
|
319
|
+
title=f"Stacktrace: {explanation}",
|
|
320
|
+
body=(
|
|
321
|
+
f"Severity: {severity}. Rule: {rule_id}. "
|
|
322
|
+
"Run /stacktrace:findings for evidence and next steps."
|
|
323
|
+
),
|
|
312
324
|
)
|
|
313
325
|
|
|
314
326
|
@staticmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stacktrace-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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>
|
|
@@ -51,7 +51,7 @@ Requires Python 3.11 or newer.
|
|
|
51
51
|
|
|
52
52
|
```console
|
|
53
53
|
$ stacktrace --version
|
|
54
|
-
stacktrace 0.
|
|
54
|
+
stacktrace 0.5.0 (openaca 0.7.0)
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Quick start
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
stacktrace_cli/__init__.py,sha256=
|
|
1
|
+
stacktrace_cli/__init__.py,sha256=zHZ09_BkTtuYNvOWuyPl2wfwi89SQMreN3vwkdmlloU,111
|
|
2
2
|
stacktrace_cli/__main__.py,sha256=F_tqj3PRzeBtY-uvBs923H8oc-s9bebhIh6UnAUwNYc,448
|
|
3
3
|
stacktrace_cli/analysis.py,sha256=cNsFO0Awr2gxzQWfEC1befhQ-6XOZDcv0VMmR5QYFug,23766
|
|
4
4
|
stacktrace_cli/build.py,sha256=lXMXBMoQtXC5qVMAWBsJavcVfXPmJiAZgV3cRBqUvyk,4846
|
|
@@ -15,16 +15,17 @@ stacktrace_cli/correlate/project_map.py,sha256=R37SktEsBzVPufQZmQeYWm9PoO0YGg98Y
|
|
|
15
15
|
stacktrace_cli/correlate/record.py,sha256=ggpEJaQNyuUqjNcE2FuskivTgNhIBmqnxWncpigRT50,12057
|
|
16
16
|
stacktrace_cli/correlate/render.py,sha256=hJZhZcqE4bKAeXIegddY7uViCyp7NAPTQl1cj9YfM68,35387
|
|
17
17
|
stacktrace_cli/daemon/__init__.py,sha256=YP0eKpNcRl1GzKO1GXh9gOoigViU2duV-PEOw0lLLwQ,59
|
|
18
|
-
stacktrace_cli/daemon/cli.py,sha256=
|
|
18
|
+
stacktrace_cli/daemon/cli.py,sha256=vDUF93touwMFKEH1lEv8J5cHCermBGu8hCmqJJqyJ58,2932
|
|
19
19
|
stacktrace_cli/daemon/client.py,sha256=DRiyuhlPn4S4XFzzegCBZJmhkcMjL6N8xu1ETDPZq6c,4751
|
|
20
20
|
stacktrace_cli/daemon/composition.py,sha256=3SiDqtfKpW5lhpN3H9SYVGQzhSotRvrt-o_3kd41D54,4286
|
|
21
21
|
stacktrace_cli/daemon/observer.py,sha256=uc8AriC4xPvSENg4bdWSI7vtF1vjA4GmcWANZZvcziQ,6761
|
|
22
22
|
stacktrace_cli/daemon/paths.py,sha256=fDmGavMvSQg_UbJTmamwmOUBwTm18NUeI_Lo2JFMtGI,1380
|
|
23
|
+
stacktrace_cli/daemon/presentation.py,sha256=LPm9ibm4Kw42RtB3H4PNT3iJgHUyWn-rlhgvymmlHrk,2751
|
|
23
24
|
stacktrace_cli/daemon/protocol.py,sha256=s6VGko72dWOW0V2iDq0WTpYQmWV2l4Q-97bU_NR5Xo4,762
|
|
24
25
|
stacktrace_cli/daemon/runtime.py,sha256=cJusGYyWKo-4hp1bKYgDPCgvBFKHptX6DEv2GqeNcXg,8115
|
|
25
26
|
stacktrace_cli/daemon/server.py,sha256=pMNoJ8BOTNAhyJigM_WHwDOczjJtyN_YYaNUs5M6qic,5986
|
|
26
27
|
stacktrace_cli/daemon/service.py,sha256=ULUWfS0hvjQ2j9Lk0u_tofpFbfLDMUcXrPYOircSld0,6128
|
|
27
|
-
stacktrace_cli/daemon/store.py,sha256=
|
|
28
|
+
stacktrace_cli/daemon/store.py,sha256=uS3QcLUN48RiVje3lnvHEs_zrtpRYg7ly1VQph8cr9M,14788
|
|
28
29
|
stacktrace_cli/detector/__init__.py,sha256=uUwte-wGAjsXDlTgYClDtYtmZSj3-X52U_bRHIZzICw,69
|
|
29
30
|
stacktrace_cli/detector/analyzer.py,sha256=HGBJmXI8_9Z8iDink1YXTMMuIoyUYWOSVc0qeNzWR00,8437
|
|
30
31
|
stacktrace_cli/detector/blocked.py,sha256=Bd88-ADzTlWV-Bd5JH7DPRrlJHxh6KwoAe2tlDd-drA,24377
|
|
@@ -84,7 +85,7 @@ stacktrace_cli/telemetry/cli.py,sha256=YgNFWxa24BFuq20uW7VdNRBaSJO7fKsvmMQYR8yo9
|
|
|
84
85
|
stacktrace_cli/telemetry/events.py,sha256=VF_My0Nu9wLT9qk2FQfMgPjJob9uVbDtIHjBJ4ju5pw,10690
|
|
85
86
|
stacktrace_cli/telemetry/posthog.py,sha256=8Bu5AwhVUcvV-4pqTdybWUfOGqPR-7W7apmSjkX3_94,4705
|
|
86
87
|
stacktrace_cli/telemetry/state.py,sha256=-7UmK8bBFj3Zh3bzX0kJwxn7-CvreMar_0x1NiZghkE,7242
|
|
87
|
-
stacktrace_cli-0.
|
|
88
|
-
stacktrace_cli-0.
|
|
89
|
-
stacktrace_cli-0.
|
|
90
|
-
stacktrace_cli-0.
|
|
88
|
+
stacktrace_cli-0.5.0.dist-info/METADATA,sha256=YO4ib9vOki1qJ2mTuUkhjYv15tORfuIGX3whJy9R0YY,7763
|
|
89
|
+
stacktrace_cli-0.5.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
90
|
+
stacktrace_cli-0.5.0.dist-info/entry_points.txt,sha256=OYDmb2CtEjd8TV78zxiGB1XVrLrC6vvayAPXa79_tJ0,60
|
|
91
|
+
stacktrace_cli-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|