agentprobe-testing 0.5.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.
Files changed (92) hide show
  1. agentprobe_testing-0.5.0/LICENSE +109 -0
  2. agentprobe_testing-0.5.0/PKG-INFO +127 -0
  3. agentprobe_testing-0.5.0/README.md +275 -0
  4. agentprobe_testing-0.5.0/agentprobe/__init__.py +104 -0
  5. agentprobe_testing-0.5.0/agentprobe/agents/__init__.py +0 -0
  6. agentprobe_testing-0.5.0/agentprobe/agents/base.py +32 -0
  7. agentprobe_testing-0.5.0/agentprobe/agents/rule_based.py +336 -0
  8. agentprobe_testing-0.5.0/agentprobe/agents/scripted.py +30 -0
  9. agentprobe_testing-0.5.0/agentprobe/agents/target_agent.py +106 -0
  10. agentprobe_testing-0.5.0/agentprobe/agreement.py +80 -0
  11. agentprobe_testing-0.5.0/agentprobe/classifier.py +159 -0
  12. agentprobe_testing-0.5.0/agentprobe/cli.py +684 -0
  13. agentprobe_testing-0.5.0/agentprobe/diff.py +150 -0
  14. agentprobe_testing-0.5.0/agentprobe/domain.py +121 -0
  15. agentprobe_testing-0.5.0/agentprobe/domains/__init__.py +0 -0
  16. agentprobe_testing-0.5.0/agentprobe/domains/access_control/__init__.py +0 -0
  17. agentprobe_testing-0.5.0/agentprobe/domains/access_control/agent.py +90 -0
  18. agentprobe_testing-0.5.0/agentprobe/domains/access_control/clean.py +154 -0
  19. agentprobe_testing-0.5.0/agentprobe/domains/access_control/complex_agent.py +123 -0
  20. agentprobe_testing-0.5.0/agentprobe/domains/access_control/decoy.py +124 -0
  21. agentprobe_testing-0.5.0/agentprobe/domains/access_control/domain.py +35 -0
  22. agentprobe_testing-0.5.0/agentprobe/domains/access_control/entities.py +43 -0
  23. agentprobe_testing-0.5.0/agentprobe/domains/access_control/injector_prompt.py +196 -0
  24. agentprobe_testing-0.5.0/agentprobe/domains/access_control/rule_based_agent.py +263 -0
  25. agentprobe_testing-0.5.0/agentprobe/domains/access_control/scenarios.py +17 -0
  26. agentprobe_testing-0.5.0/agentprobe/domains/access_control/split.py +96 -0
  27. agentprobe_testing-0.5.0/agentprobe/domains/access_control/tools.py +235 -0
  28. agentprobe_testing-0.5.0/agentprobe/domains/access_control/trap.py +100 -0
  29. agentprobe_testing-0.5.0/agentprobe/feedback.py +121 -0
  30. agentprobe_testing-0.5.0/agentprobe/generic_world.py +99 -0
  31. agentprobe_testing-0.5.0/agentprobe/injection.py +475 -0
  32. agentprobe_testing-0.5.0/agentprobe/injector.py +810 -0
  33. agentprobe_testing-0.5.0/agentprobe/llm.py +123 -0
  34. agentprobe_testing-0.5.0/agentprobe/playbook.py +211 -0
  35. agentprobe_testing-0.5.0/agentprobe/quickstart.py +295 -0
  36. agentprobe_testing-0.5.0/agentprobe/reachability.py +196 -0
  37. agentprobe_testing-0.5.0/agentprobe/registry.py +313 -0
  38. agentprobe_testing-0.5.0/agentprobe/report.py +666 -0
  39. agentprobe_testing-0.5.0/agentprobe/runner.py +317 -0
  40. agentprobe_testing-0.5.0/agentprobe/scenario.py +75 -0
  41. agentprobe_testing-0.5.0/agentprobe/scenarios/__init__.py +0 -0
  42. agentprobe_testing-0.5.0/agentprobe/scenarios/clean.py +194 -0
  43. agentprobe_testing-0.5.0/agentprobe/scenarios/decoy.py +272 -0
  44. agentprobe_testing-0.5.0/agentprobe/scenarios/registry.py +16 -0
  45. agentprobe_testing-0.5.0/agentprobe/scenarios/split.py +203 -0
  46. agentprobe_testing-0.5.0/agentprobe/scenarios/trap.py +215 -0
  47. agentprobe_testing-0.5.0/agentprobe/termui.py +154 -0
  48. agentprobe_testing-0.5.0/agentprobe/tools.py +275 -0
  49. agentprobe_testing-0.5.0/agentprobe/trajectory.py +107 -0
  50. agentprobe_testing-0.5.0/agentprobe/triage.py +153 -0
  51. agentprobe_testing-0.5.0/agentprobe/validate_scenarios.py +489 -0
  52. agentprobe_testing-0.5.0/agentprobe/world.py +189 -0
  53. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/PKG-INFO +127 -0
  54. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/SOURCES.txt +90 -0
  55. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/dependency_links.txt +1 -0
  56. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/entry_points.txt +4 -0
  57. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/requires.txt +6 -0
  58. agentprobe_testing-0.5.0/agentprobe_testing.egg-info/top_level.txt +1 -0
  59. agentprobe_testing-0.5.0/pyproject.toml +39 -0
  60. agentprobe_testing-0.5.0/setup.cfg +4 -0
  61. agentprobe_testing-0.5.0/tests/test_access_control_agent.py +142 -0
  62. agentprobe_testing-0.5.0/tests/test_access_control_domain.py +270 -0
  63. agentprobe_testing-0.5.0/tests/test_access_control_rule_based_agent.py +331 -0
  64. agentprobe_testing-0.5.0/tests/test_access_control_scenarios.py +267 -0
  65. agentprobe_testing-0.5.0/tests/test_access_control_tools.py +232 -0
  66. agentprobe_testing-0.5.0/tests/test_agreement.py +88 -0
  67. agentprobe_testing-0.5.0/tests/test_classifier.py +133 -0
  68. agentprobe_testing-0.5.0/tests/test_cli.py +720 -0
  69. agentprobe_testing-0.5.0/tests/test_complex_access_control_agent.py +84 -0
  70. agentprobe_testing-0.5.0/tests/test_diff.py +135 -0
  71. agentprobe_testing-0.5.0/tests/test_domain.py +93 -0
  72. agentprobe_testing-0.5.0/tests/test_feedback.py +133 -0
  73. agentprobe_testing-0.5.0/tests/test_generic_world.py +88 -0
  74. agentprobe_testing-0.5.0/tests/test_injection.py +544 -0
  75. agentprobe_testing-0.5.0/tests/test_injector.py +1128 -0
  76. agentprobe_testing-0.5.0/tests/test_llm.py +163 -0
  77. agentprobe_testing-0.5.0/tests/test_package_api.py +47 -0
  78. agentprobe_testing-0.5.0/tests/test_playbook.py +291 -0
  79. agentprobe_testing-0.5.0/tests/test_quickstart.py +260 -0
  80. agentprobe_testing-0.5.0/tests/test_reachability.py +210 -0
  81. agentprobe_testing-0.5.0/tests/test_registry.py +419 -0
  82. agentprobe_testing-0.5.0/tests/test_report.py +604 -0
  83. agentprobe_testing-0.5.0/tests/test_rule_based_agent.py +329 -0
  84. agentprobe_testing-0.5.0/tests/test_runner.py +699 -0
  85. agentprobe_testing-0.5.0/tests/test_scenarios.py +69 -0
  86. agentprobe_testing-0.5.0/tests/test_target_agent.py +136 -0
  87. agentprobe_testing-0.5.0/tests/test_termui.py +74 -0
  88. agentprobe_testing-0.5.0/tests/test_tools.py +194 -0
  89. agentprobe_testing-0.5.0/tests/test_trajectory.py +28 -0
  90. agentprobe_testing-0.5.0/tests/test_triage.py +238 -0
  91. agentprobe_testing-0.5.0/tests/test_validate_scenarios.py +540 -0
  92. agentprobe_testing-0.5.0/tests/test_world.py +136 -0
@@ -0,0 +1,109 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: Agent-Probe
6
+ Licensed Work: AgentProbe
7
+ The Licensed Work is (c) 2026 Agent-Probe
8
+ Additional Use Grant: You may use, copy, modify, and make production use of
9
+ the Licensed Work for any purpose -- including testing,
10
+ developing, and operating your own software agents --
11
+ free of charge, provided that you do not offer the
12
+ Licensed Work, or a modified or derivative version of
13
+ it, to third parties as part of a hosted, managed, or
14
+ embedded service that competes with a paid product or
15
+ service offered by the Licensor (including, without
16
+ limitation, hosted domain generation, hosted
17
+ dashboards, or hosted test-run storage substantially
18
+ similar to those offered at
19
+ https://agentprobe-api.agentprobe.workers.dev).
20
+ Change Date: 2030-01-01
21
+ Change License: Apache License, Version 2.0
22
+
23
+ For information about alternative licensing arrangements for the Licensed
24
+ Work, please open an issue at https://github.com/Agent-Probe/agentprobe.
25
+
26
+ Notice
27
+
28
+ The Business Source License (this document, or the "License") is not an
29
+ Open Source license. However, the Licensed Work will eventually be made
30
+ available under an Open Source License, as stated in this License.
31
+
32
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights
33
+ Reserved. "Business Source License" is a trademark of MariaDB Corporation
34
+ Ab.
35
+
36
+ -----------------------------------------------------------------------------
37
+
38
+ Business Source License 1.1
39
+
40
+ Terms
41
+
42
+ The Licensor hereby grants you the right to copy, modify, create
43
+ derivative works, redistribute, and make non-production use of the
44
+ Licensed Work. The Licensor may make an Additional Use Grant, above,
45
+ permitting limited production use.
46
+
47
+ Effective on the Change Date, or the fourth anniversary of the first
48
+ publicly available distribution of a specific version of the Licensed
49
+ Work under this License, whichever comes first, the Licensor hereby
50
+ grants you rights under the terms of the Change License, and the rights
51
+ granted in the paragraph above terminate.
52
+
53
+ If your use of the Licensed Work does not comply with the requirements
54
+ currently in effect as described in this License, you must purchase a
55
+ commercial license from the Licensor, its affiliated entities, or
56
+ authorized resellers, or you must refrain from using the Licensed Work.
57
+
58
+ All copies of the original and modified Licensed Work, and derivative
59
+ works of the Licensed Work, are subject to this License. This License
60
+ applies separately for each version of the Licensed Work and the Change
61
+ Date may vary for each version of the Licensed Work released by
62
+ Licensor.
63
+
64
+ You must conspicuously display this License on each original or
65
+ modified copy of the Licensed Work. If you receive the Licensed Work in
66
+ original or modified form from a third party, the terms and conditions
67
+ set forth in this License apply to your use of that work.
68
+
69
+ Any use of the Licensed Work in violation of this License will
70
+ automatically terminate your rights under this License for the current
71
+ and all other versions of the Licensed Work.
72
+
73
+ This License does not grant you any right in any trademark or logo of
74
+ Licensor or its affiliates (provided that you may use a trademark or
75
+ logo of Licensor as expressly required by this License).
76
+
77
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS
78
+ PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES
79
+ AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
80
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
81
+ NON-INFRINGEMENT, AND TITLE.
82
+
83
+ MariaDB hereby grants you permission to use this License's text to
84
+ license your works, and to refer to it using the trademark "Business
85
+ Source License", as long as you comply with the Covenants of Licensor
86
+ below.
87
+
88
+ Covenants of Licensor
89
+
90
+ In consideration of the right to use this License's text and the
91
+ "Business Source License" name and trademark, Licensor covenants to
92
+ MariaDB, and to all other recipients of the licensed work to be
93
+ provided by Licensor:
94
+
95
+ 1. To specify as the Change License the GPL Version 2.0 or any later
96
+ version, or a license that is compatible with GPL Version 2.0 or a
97
+ later version, where "compatible" means that software provided
98
+ under the Change License can be included in a program with software
99
+ provided under GPL Version 2.0 or a later version. Licensor may
100
+ specify additional Change Licenses without limitation.
101
+
102
+ 2. To either: (a) specify an additional grant of rights to use that
103
+ does not impose any additional restriction on the right granted in
104
+ this License, as the Additional Use Grant, or (b) insert the text
105
+ "None".
106
+
107
+ 3. To specify a Change Date.
108
+
109
+ 4. Not to modify this License in any other way.
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentprobe-testing
3
+ Version: 0.5.0
4
+ Summary: Adaptive chaos-testing for LLM agents: a live model-driven Injector that reads an agent's real trajectory and decides where to break something, instead of scripting perturbations in advance.
5
+ License: Business Source License 1.1
6
+
7
+ Parameters
8
+
9
+ Licensor: Agent-Probe
10
+ Licensed Work: AgentProbe
11
+ The Licensed Work is (c) 2026 Agent-Probe
12
+ Additional Use Grant: You may use, copy, modify, and make production use of
13
+ the Licensed Work for any purpose -- including testing,
14
+ developing, and operating your own software agents --
15
+ free of charge, provided that you do not offer the
16
+ Licensed Work, or a modified or derivative version of
17
+ it, to third parties as part of a hosted, managed, or
18
+ embedded service that competes with a paid product or
19
+ service offered by the Licensor (including, without
20
+ limitation, hosted domain generation, hosted
21
+ dashboards, or hosted test-run storage substantially
22
+ similar to those offered at
23
+ https://agentprobe-api.agentprobe.workers.dev).
24
+ Change Date: 2030-01-01
25
+ Change License: Apache License, Version 2.0
26
+
27
+ For information about alternative licensing arrangements for the Licensed
28
+ Work, please open an issue at https://github.com/Agent-Probe/agentprobe.
29
+
30
+ Notice
31
+
32
+ The Business Source License (this document, or the "License") is not an
33
+ Open Source license. However, the Licensed Work will eventually be made
34
+ available under an Open Source License, as stated in this License.
35
+
36
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights
37
+ Reserved. "Business Source License" is a trademark of MariaDB Corporation
38
+ Ab.
39
+
40
+ -----------------------------------------------------------------------------
41
+
42
+ Business Source License 1.1
43
+
44
+ Terms
45
+
46
+ The Licensor hereby grants you the right to copy, modify, create
47
+ derivative works, redistribute, and make non-production use of the
48
+ Licensed Work. The Licensor may make an Additional Use Grant, above,
49
+ permitting limited production use.
50
+
51
+ Effective on the Change Date, or the fourth anniversary of the first
52
+ publicly available distribution of a specific version of the Licensed
53
+ Work under this License, whichever comes first, the Licensor hereby
54
+ grants you rights under the terms of the Change License, and the rights
55
+ granted in the paragraph above terminate.
56
+
57
+ If your use of the Licensed Work does not comply with the requirements
58
+ currently in effect as described in this License, you must purchase a
59
+ commercial license from the Licensor, its affiliated entities, or
60
+ authorized resellers, or you must refrain from using the Licensed Work.
61
+
62
+ All copies of the original and modified Licensed Work, and derivative
63
+ works of the Licensed Work, are subject to this License. This License
64
+ applies separately for each version of the Licensed Work and the Change
65
+ Date may vary for each version of the Licensed Work released by
66
+ Licensor.
67
+
68
+ You must conspicuously display this License on each original or
69
+ modified copy of the Licensed Work. If you receive the Licensed Work in
70
+ original or modified form from a third party, the terms and conditions
71
+ set forth in this License apply to your use of that work.
72
+
73
+ Any use of the Licensed Work in violation of this License will
74
+ automatically terminate your rights under this License for the current
75
+ and all other versions of the Licensed Work.
76
+
77
+ This License does not grant you any right in any trademark or logo of
78
+ Licensor or its affiliates (provided that you may use a trademark or
79
+ logo of Licensor as expressly required by this License).
80
+
81
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS
82
+ PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES
83
+ AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
84
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
85
+ NON-INFRINGEMENT, AND TITLE.
86
+
87
+ MariaDB hereby grants you permission to use this License's text to
88
+ license your works, and to refer to it using the trademark "Business
89
+ Source License", as long as you comply with the Covenants of Licensor
90
+ below.
91
+
92
+ Covenants of Licensor
93
+
94
+ In consideration of the right to use this License's text and the
95
+ "Business Source License" name and trademark, Licensor covenants to
96
+ MariaDB, and to all other recipients of the licensed work to be
97
+ provided by Licensor:
98
+
99
+ 1. To specify as the Change License the GPL Version 2.0 or any later
100
+ version, or a license that is compatible with GPL Version 2.0 or a
101
+ later version, where "compatible" means that software provided
102
+ under the Change License can be included in a program with software
103
+ provided under GPL Version 2.0 or a later version. Licensor may
104
+ specify additional Change Licenses without limitation.
105
+
106
+ 2. To either: (a) specify an additional grant of rights to use that
107
+ does not impose any additional restriction on the right granted in
108
+ this License, as the Additional Use Grant, or (b) insert the text
109
+ "None".
110
+
111
+ 3. To specify a Change Date.
112
+
113
+ 4. Not to modify this License in any other way.
114
+
115
+ Project-URL: Repository, https://github.com/Agent-Probe/agentprobe
116
+ Project-URL: Changelog, https://github.com/Agent-Probe/agentprobe/blob/main/CHANGELOG.md
117
+ Classifier: Development Status :: 4 - Beta
118
+ Classifier: Intended Audience :: Developers
119
+ Classifier: Programming Language :: Python :: 3
120
+ Requires-Python: >=3.11
121
+ License-File: LICENSE
122
+ Requires-Dist: anthropic>=1.0.0
123
+ Requires-Dist: python-dotenv>=1.0.0
124
+ Provides-Extra: dev
125
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
126
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
127
+ Dynamic: license-file
@@ -0,0 +1,275 @@
1
+ # AgentProbe
2
+
3
+ [![CI](https://github.com/Agent-Probe/agentprobe/actions/workflows/ci.yml/badge.svg)](https://github.com/Agent-Probe/agentprobe/actions/workflows/ci.yml)
4
+
5
+ Chaos-testing for LLM agents: a second agent (the **Injector**) watches
6
+ your agent's live trajectory — its actual tool calls, its actual
7
+ commitments — and adaptively decides where to break something, instead of
8
+ firing perturbations on a fixed schedule authored in advance. See
9
+ [PRIOR_ART.md](PRIOR_ART.md) for how this differs from τ²-bench, Gaia2,
10
+ and STAGE-Claw, all of which script their perturbations offline.
11
+
12
+ Six injection kinds: a failed tool call (`TOOL_ERROR`), a record that
13
+ silently changes after your agent already read it (`STALE_READ`), a new
14
+ fact that contradicts an earlier one (`CONTRADICTION`), information that
15
+ only matters after an irreversible action already happened (`LATE_INFO`),
16
+ a second equally-valid candidate record appearing (`AMBIGUITY`), and
17
+ adversarial content disguised as ordinary data (`PROMPT_INJECTION`).
18
+
19
+ ## Quickstart
20
+
21
+ ```bash
22
+ pip install -e .
23
+ python examples/free_zero_cost_demo.py # no API key needed, no network calls at all
24
+ ```
25
+
26
+ That runs a real (deterministic, not scripted) decision-tree agent
27
+ against a hardcoded chaos injector across both domains this repo ships
28
+ with, prints a pass/fail report, and saves a step-by-step HTML report
29
+ next to itself. Nothing here costs money.
30
+
31
+ To see it with real Claude agents on both sides (Target reasoning live,
32
+ Injector deciding live), across both domains this repo ships with, which
33
+ does cost a small amount of real API usage:
34
+
35
+ ```bash
36
+ python examples/live_llm_test.py # needs ANTHROPIC_API_KEY
37
+ ```
38
+
39
+ For a harder test subject than the baseline `AccessControlAgent` --
40
+ `ComplexAccessControlAgent` follows an explicit multi-factor decision
41
+ framework (sensitivity vs. clearance vs. justification quality, required
42
+ policy lookups before granting anything high-sensitivity, a mandatory
43
+ re-check right before committing, and explicit prompt-injection-defense
44
+ instructions) instead of a short generic prompt. Same cost profile as
45
+ `live_llm_test.py`, scoped to one scenario per class (clean/split/decoy/
46
+ trap) so it's cheap to run and still exercises everything that framework
47
+ claims:
48
+
49
+ ```bash
50
+ python examples/test_complex_agent.py # needs ANTHROPIC_API_KEY
51
+ ```
52
+
53
+ ## As a library
54
+
55
+ The fastest way to test your own agent — write one function, get a report:
56
+
57
+ ```python
58
+ import agentprobe as ap
59
+
60
+ def my_agent(task: str, history: list[dict]) -> ap.AgentAction:
61
+ # call your real agent here; history is everything observed so far
62
+ # this run, as {"tool_name", "tool_args", "result", "ok"} dicts
63
+ ...
64
+ return ap.AgentAction(kind="tool_call", tool_name="...", tool_args={...})
65
+ # or: return ap.AgentAction(kind="final_answer", text="...")
66
+
67
+ report = ap.quick_test(
68
+ ap.TICKET_SCENARIOS_BY_ID["clean-1"],
69
+ my_agent,
70
+ injector=ap.HardcodedToolErrorInjector(offsets=[0], tool_name="close_ticket"),
71
+ )
72
+ print(report.render())
73
+ ```
74
+
75
+ No class to write, no manual state tracking — `quick_test` calls `my_agent`
76
+ fresh for both the clean and chaos runs, so there's nothing to reset
77
+ between them either. This only tests *ticket-support* scenarios though
78
+ (`ap.TICKET_SCENARIOS_BY_ID`) — meaningfully testing a different business's
79
+ agent means authoring a new [`Domain`](agentprobe/domain.py) for it first
80
+ (see "Domains" below); `quick_test` removes the harness boilerplate,
81
+ not the need to describe what your agent's tools and correct answers are.
82
+
83
+ For full control (multi-scenario runs, custom Report fields, manual state
84
+ in your agent), the same thing without the convenience wrapper:
85
+
86
+ ```python
87
+ import agentprobe as ap
88
+
89
+ injector = ap.HardcodedToolErrorInjector(offsets=[0], tool_name="close_ticket")
90
+ clean, chaos = ap.run_robustness_pair(
91
+ ap.TICKET_SCENARIOS_BY_ID["clean-1"],
92
+ target_factory=lambda: ap.RuleBasedAgent(), # zero API cost
93
+ injector=injector,
94
+ )
95
+ print(ap.Report(
96
+ mode="robustness", injector_model="hardcoded", target_model="rule_based",
97
+ clean_trajectories=[clean], chaos_trajectories=[chaos],
98
+ ).render())
99
+ ```
100
+
101
+ `import agentprobe` re-exports the pieces most programs need directly —
102
+ see [`agentprobe/__init__.py`](agentprobe/__init__.py) for the full
103
+ surface. The `access_control` domain lives at
104
+ `agentprobe.domains.access_control` rather than being re-exported at the
105
+ top level, to keep this surface to the one domain most people reach for
106
+ first.
107
+
108
+ Beyond one-scenario `quick_test()`, see [USAGE.md](USAGE.md) for: running
109
+ a whole domain at once (`quick_test_all`), a one-line pytest integration
110
+ (`assert_passes`), catching regressions between uploaded runs
111
+ (`check_regression`), and replaying a specific past run's exact chaos
112
+ sequence against a new agent version (`replay_run`) — plus a worked
113
+ end-to-end CI regression-gate example combining all four.
114
+
115
+ ## CLI
116
+
117
+ ```bash
118
+ agentprobe run [options]
119
+ ```
120
+
121
+ Key options (`agentprobe run --help` for the full list):
122
+
123
+ | Flag | What it controls |
124
+ |---|---|
125
+ | `--domain {ticket,access_control}` | Which business domain's world/tools/scenarios to run against |
126
+ | `--target {claude,scripted,rule_based}` | `claude`: real Claude tool-calling loop. `scripted`: no-op stub for exercising the harness. `rule_based`: a real deterministic decision tree, zero API cost |
127
+ | `--injector {none,hardcoded,model,replay}` | `hardcoded`: fires one kind on a named tool at a fixed offset, zero cost. `model`: a live Claude Injector reasoning about the trajectory. `replay`: re-fires a previously recorded sequence for free |
128
+ | `--mode {robustness,recovery}` | `robustness`: a clean control run + a chaos run from the same seed. `recovery`: one run, chaos mid-trajectory, no control |
129
+ | `--scenario ID` | Run a single scenario instead of the whole registry |
130
+ | `--list-scenarios` | Print every scenario in `--domain` as JSON and exit without running anything -- for building a CI test matrix |
131
+ | `--classify` | Run the response classifier (HANDLED/IGNORED/MISREAD/OVERREACTED/LOOPED) on every valid injection |
132
+ | `--max-total-cost-usd N` | Stop starting new scenarios once combined Target + Injector spend for this invocation reaches `N` -- a whole-run safety net, separate from each scenario's own much smaller `max_cost_usd` |
133
+ | `--detailed` | Print the full scenario-by-scenario, step-by-step breakdown to the terminal |
134
+ | `--html-out PATH` | Save a self-contained HTML report (always includes the step-by-step detail, collapsed per scenario) |
135
+ | `--json-out PATH` | Save the full trajectory data, for `agentprobe diff` later |
136
+ | `--use-playbook` | Learn from and contribute to a local cross-run history of which triggers actually fire (see "Data moat" below) |
137
+
138
+ Other subcommands: `agentprobe diff before.json after.json
139
+ --fail-on-regression` (CI gate), `agentprobe score-agreement
140
+ labeled.json` (classifier hand-labeling agreement), `agentprobe
141
+ apply-feedback feedback.json` (fold human feedback into the playbook).
142
+
143
+ ## Domains
144
+
145
+ A **domain** is a business world (entities + tools + business rules) the
146
+ Injector and Target both operate on. This repo ships two:
147
+
148
+ - **ticket** (default): customer support tickets — refund/close/reply/
149
+ escalate over tickets, orders, customers.
150
+ - **access_control**: employees requesting access to internal systems —
151
+ grant/close/reply/escalate over requests, resources, employees.
152
+
153
+ The actual chaos-injection engine (trigger timing, the six injection
154
+ kinds, the reachability checker, the Playbook) doesn't know or care which
155
+ domain it's running — see [`agentprobe/domain.py`](agentprobe/domain.py)
156
+ for the `Domain` bundle that's the seam between them, and
157
+ [`agentprobe/domains/access_control/`](agentprobe/domains/access_control/)
158
+ for a complete worked second example (entities, toolkit, scenarios, a
159
+ live Claude agent, a free rule-based agent, and its own Injector system
160
+ prompt). Adding a third domain means writing those same pieces for your
161
+ own world — the engine underneath is already generic.
162
+
163
+ ## Generating a domain instead of writing one
164
+
165
+ Writing `agentprobe/domain.py`'s `Domain` bundle by hand (entities, tools,
166
+ business rules) is real, unavoidable work — see "Domains" above. If you'd
167
+ rather not write it yourself, [agentprobe-api.agentprobe.workers.dev](https://agentprobe-api.agentprobe.workers.dev)
168
+ can generate one from your existing tool schemas plus a plain-English
169
+ description of your business rules, and hand you back a key:
170
+
171
+ ```python
172
+ import agentprobe as ap
173
+
174
+ fetched = ap.fetch_domain("joeblow-ai-agents_bd0725d86767c7481581fc41")
175
+ report = ap.quick_test(
176
+ fetched.scenarios["wrong-order-among-several"],
177
+ lambda: ap.TargetAgent(system_prompt="You are JoeBlow's order support agent."),
178
+ domain=fetched.domain,
179
+ )
180
+ print(report.render())
181
+ ```
182
+
183
+ **Trust disclaimer — read this before using `fetch_domain`:** the domain
184
+ behind a key is Python code an LLM generated from your description, not
185
+ code AgentProbe's authors reviewed. `fetch_domain` downloads and *executes*
186
+ that code locally, and prints a warning with a `review_url` every time it
187
+ runs — open that link and skim the generated business logic (what counts
188
+ as "already refunded," what's irreversible, what a precondition checks)
189
+ before trusting any report it produces. A subtly wrong rule doesn't error;
190
+ it just makes your test pass without proving anything real about your
191
+ agent. Only fetch a key you generated for your own business, the same
192
+ rule as never installing a package from someone you don't trust.
193
+
194
+ ## The website: accounts, personal API keys, and a dashboard
195
+
196
+ [agentprobe-api.agentprobe.workers.dev](https://agentprobe-api.agentprobe.workers.dev)
197
+ is the real site, not just the API — sign in with GitHub and you get:
198
+
199
+ - **Your Keys** — every domain you've generated (via `/v1/generate` with
200
+ your API key attached), click-to-copy, so you don't have to keep your
201
+ own notes of which key was which business.
202
+ - **Your Runs** — a history of test runs you've uploaded, each with its
203
+ full report viewable in the browser.
204
+ - **API access** (on the Dashboard) — create a personal key
205
+ (`ap_live_...`, shown once at creation) to authenticate the two things
206
+ above from your own scripts.
207
+
208
+ Attribute a generated domain to your account by passing the key as a
209
+ bearer token:
210
+
211
+ ```bash
212
+ curl -X POST https://agentprobe-api.agentprobe.workers.dev/v1/generate \
213
+ -H "Authorization: Bearer ap_live_..." \
214
+ -H "content-type: application/json" \
215
+ -d '{"name": "...", "tool_schemas": [...], "business_description": "..."}'
216
+ ```
217
+
218
+ And upload a finished run so it shows up under Your Runs by passing the
219
+ same key to `quick_test()`:
220
+
221
+ ```python
222
+ report = ap.quick_test(
223
+ scenario, my_agent, domain=fetched.domain,
224
+ upload_api_key="ap_live_...", # from Your Dashboard
225
+ domain_key=fetched_key, # optional -- links the run back to its domain
226
+ )
227
+ ```
228
+
229
+ Both are entirely optional — omit `upload_api_key` (the default) and
230
+ `quick_test()` stays fully local, same as always. See
231
+ [`server/README.md`](server/README.md) for how the site itself is built
232
+ and deployed (Cloudflare Worker + D1 + GitHub OAuth).
233
+
234
+ ## Cost
235
+
236
+ Nothing about this harness requires API spend to develop or test against:
237
+ `--target scripted` / `--target rule_based` and `--injector none` /
238
+ `--injector hardcoded` make zero network calls between them. Live testing
239
+ (`--target claude`, `--injector model`, `--classify`) uses the real
240
+ Anthropic API and costs real money — check `agentprobe/llm.py`'s pricing
241
+ table for current per-model rates, and a run's own report always prints
242
+ its actual cost breakdown (target/injector/classifier/total).
243
+
244
+ ## Data moat
245
+
246
+ `--use-playbook` accumulates cross-run outcome data (which trigger
247
+ actually fires for a given injection kind and scenario shape) into a
248
+ local JSON file, surfaced back into the Injector's prompt as an advisory
249
+ hint on future runs — see [`agentprobe/playbook.py`](agentprobe/playbook.py).
250
+ `--export-for-feedback` / `agentprobe apply-feedback` let a human add
251
+ free-text judgment (what was good, what should change, what was missing)
252
+ about a run's injections into the same file — see
253
+ [`agentprobe/feedback.py`](agentprobe/feedback.py). Both are keyed by
254
+ structural scenario shape, not by scenario/customer content, so they
255
+ don't touch a BYOK customer's actual data isolation.
256
+
257
+ ## Testing
258
+
259
+ ```bash
260
+ pytest tests/ -q
261
+ ```
262
+
263
+ 600+ tests, all free — no test in this suite makes a live API call
264
+ (anything that would is mocked). Contributions should keep it that way.
265
+
266
+ The website/API server (`server/`) has its own separate suite, same rule:
267
+
268
+ ```bash
269
+ cd server && node --test test/
270
+ ```
271
+
272
+ 90+ tests against fake D1/KV/fetch — no real Cloudflare account, network
273
+ access, or secrets needed for either suite. Both run in CI on every push.
274
+
275
+ See [CHANGELOG.md](CHANGELOG.md) for what's new in each version.
@@ -0,0 +1,104 @@
1
+ """AgentProbe: chaos-testing for LLM agents, via a second agent (the
2
+ Injector) that watches a Target's live trajectory and adaptively decides
3
+ where to break something -- see README.md for the full pitch.
4
+
5
+ This re-exports the pieces most programs actually need so a simple case
6
+ doesn't require knowing the submodule layout:
7
+
8
+ import agentprobe as ap
9
+
10
+ injector = ap.HardcodedToolErrorInjector(offsets=[0], tool_name="close_ticket")
11
+ clean, chaos = ap.run_robustness_pair(
12
+ ap.TICKET_SCENARIOS_BY_ID["clean-1"],
13
+ target_factory=lambda: ap.RuleBasedAgent(), # zero API cost
14
+ injector=injector,
15
+ )
16
+ print(ap.Report(
17
+ mode="robustness", injector_model="hardcoded", target_model="rule_based",
18
+ clean_trajectories=[clean], chaos_trajectories=[chaos],
19
+ ).render())
20
+
21
+ Everything here is free (RuleBasedAgent + HardcodedToolErrorInjector make
22
+ no network calls at all). Swap in TargetAgent/ModelInjector for the real,
23
+ billed thing.
24
+
25
+ A second domain (access-control) ships in agentprobe.domains.access_control
26
+ rather than being re-exported here, to keep this top-level surface to the
27
+ one domain most people reach for first -- see domain.py for the seam that
28
+ lets you plug in a third.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from agentprobe.agents.base import Agent, AgentAction
34
+ from agentprobe.agents.rule_based import RuleBasedAgent
35
+ from agentprobe.agents.scripted import ScriptedAgent
36
+ from agentprobe.agents.target_agent import TargetAgent
37
+ from agentprobe.domain import Domain, TICKET_DOMAIN
38
+ from agentprobe.injection import AppliedInjection, Injection, InjectionKind, injection_was_triggered
39
+ from agentprobe.injector import (
40
+ HardcodedToolErrorInjector,
41
+ Injector,
42
+ ModelInjector,
43
+ NullInjector,
44
+ RecordingInjector,
45
+ ReplayInjector,
46
+ )
47
+ from agentprobe.quickstart import assert_passes, quick_test, quick_test_all, wrap_agent
48
+ from agentprobe.registry import (
49
+ FetchedDomain,
50
+ RegressionResult,
51
+ check_regression,
52
+ fetch_domain,
53
+ get_latest_run,
54
+ replay_run,
55
+ upload_run,
56
+ )
57
+ from agentprobe.report import InjectionRecord, Report
58
+ from agentprobe.runner import run_recovery, run_robustness_pair
59
+ from agentprobe.scenario import CommitPattern, FactPattern, GoalSpec, Scenario
60
+ from agentprobe.scenarios.registry import ALL_SCENARIOS as TICKET_SCENARIOS
61
+ from agentprobe.scenarios.registry import BY_ID as TICKET_SCENARIOS_BY_ID
62
+
63
+ __version__ = "0.5.0"
64
+
65
+ __all__ = [
66
+ "Agent",
67
+ "AgentAction",
68
+ "AppliedInjection",
69
+ "CommitPattern",
70
+ "Domain",
71
+ "FactPattern",
72
+ "FetchedDomain",
73
+ "GoalSpec",
74
+ "HardcodedToolErrorInjector",
75
+ "Injection",
76
+ "InjectionKind",
77
+ "InjectionRecord",
78
+ "Injector",
79
+ "ModelInjector",
80
+ "NullInjector",
81
+ "RecordingInjector",
82
+ "RegressionResult",
83
+ "ReplayInjector",
84
+ "Report",
85
+ "RuleBasedAgent",
86
+ "Scenario",
87
+ "ScriptedAgent",
88
+ "TICKET_DOMAIN",
89
+ "TICKET_SCENARIOS",
90
+ "TICKET_SCENARIOS_BY_ID",
91
+ "TargetAgent",
92
+ "assert_passes",
93
+ "check_regression",
94
+ "fetch_domain",
95
+ "get_latest_run",
96
+ "injection_was_triggered",
97
+ "quick_test",
98
+ "quick_test_all",
99
+ "replay_run",
100
+ "run_recovery",
101
+ "run_robustness_pair",
102
+ "upload_run",
103
+ "wrap_agent",
104
+ ]
File without changes
@@ -0,0 +1,32 @@
1
+ """Agent interface the runner drives. An agent is a small state machine:
2
+ start a task, propose an action, observe the result, repeat."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass
8
+ from typing import Any, Literal, Optional
9
+
10
+
11
+ @dataclass
12
+ class AgentAction:
13
+ kind: Literal["tool_call", "final_answer"]
14
+ tool_name: Optional[str] = None
15
+ tool_args: Optional[dict[str, Any]] = None
16
+ text: Optional[str] = None
17
+ latency_s: float = 0.0
18
+ cost_usd: float = 0.0
19
+
20
+
21
+ class Agent(ABC):
22
+ @abstractmethod
23
+ def start(self, task: str, tool_schemas: list[dict]) -> None:
24
+ """Reset internal state for a new scenario."""
25
+
26
+ @abstractmethod
27
+ def next_action(self) -> AgentAction:
28
+ """Propose the next step: a tool call or a final answer."""
29
+
30
+ @abstractmethod
31
+ def observe(self, tool_name: str, tool_args: dict, result: Any, ok: bool) -> None:
32
+ """Feed back the result of the tool call just executed."""