techrevati-runtime 0.2.0__tar.gz → 0.3.0.dev1__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 (125) hide show
  1. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CHANGELOG.md +129 -0
  2. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/PKG-INFO +5 -5
  3. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/README.md +4 -4
  4. techrevati_runtime-0.3.0.dev1/docs/api/governance.md +3 -0
  5. techrevati_runtime-0.3.0.dev1/docs/api/hooks.md +3 -0
  6. techrevati_runtime-0.3.0.dev1/docs/api/streaming.md +3 -0
  7. techrevati_runtime-0.3.0.dev1/docs/patterns/governance.md +194 -0
  8. techrevati_runtime-0.3.0.dev1/docs/patterns/hooks.md +262 -0
  9. techrevati_runtime-0.3.0.dev1/docs/patterns/streaming.md +177 -0
  10. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/mkdocs.yml +6 -0
  11. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/pyproject.toml +1 -1
  12. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/__init__.py +58 -0
  13. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/agent_events.py +54 -0
  14. techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/governance.py +259 -0
  15. techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/guardrails.py +433 -0
  16. techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/hooks.py +515 -0
  17. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/orchestrator.py +341 -16
  18. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/otel.py +53 -2
  19. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/retry_policy.py +49 -10
  20. techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/streaming.py +118 -0
  21. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/usage_tracking.py +37 -3
  22. techrevati_runtime-0.3.0.dev1/tests/test_async_guardrails.py +122 -0
  23. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_async_orchestrator.py +13 -13
  24. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_checkpoint.py +12 -12
  25. techrevati_runtime-0.3.0.dev1/tests/test_deprecation_warnings.py +66 -0
  26. techrevati_runtime-0.3.0.dev1/tests/test_governance.py +152 -0
  27. techrevati_runtime-0.3.0.dev1/tests/test_governance_events.py +101 -0
  28. techrevati_runtime-0.3.0.dev1/tests/test_governance_integration.py +200 -0
  29. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_guardrails.py +48 -11
  30. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_handoffs.py +5 -5
  31. techrevati_runtime-0.3.0.dev1/tests/test_hooks.py +422 -0
  32. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_max_iterations.py +7 -8
  33. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_orchestrator.py +18 -18
  34. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_otel.py +4 -4
  35. techrevati_runtime-0.3.0.dev1/tests/test_otel_atexit_cleanup.py +108 -0
  36. techrevati_runtime-0.3.0.dev1/tests/test_pattern_and_prompt_injection_guardrails.py +153 -0
  37. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_rate_limit.py +4 -4
  38. techrevati_runtime-0.3.0.dev1/tests/test_register_pricing_on_conflict.py +69 -0
  39. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s0_regressions.py +3 -3
  40. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s5_usage_limits_and_caching.py +1 -1
  41. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_sinks.py +6 -6
  42. techrevati_runtime-0.3.0.dev1/tests/test_step_retries.py +115 -0
  43. techrevati_runtime-0.3.0.dev1/tests/test_streaming.py +263 -0
  44. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_taskgroup_parallel_tools.py +5 -5
  45. techrevati_runtime-0.2.0/src/techrevati/runtime/guardrails.py +0 -138
  46. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  47. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  48. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/dependabot.yml +0 -0
  49. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/ci.yml +0 -0
  50. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/codeql.yml +0 -0
  51. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/docs.yml +0 -0
  52. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/release.yml +0 -0
  53. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.gitignore +0 -0
  54. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.pre-commit-config.yaml +0 -0
  55. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CODEOWNERS +0 -0
  56. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CONTRIBUTING.md +0 -0
  57. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/LICENSE +0 -0
  58. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/SECURITY.md +0 -0
  59. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/checkpoint.md +0 -0
  60. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/circuit_breaker.md +0 -0
  61. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/guardrails.md +0 -0
  62. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/handoffs.md +0 -0
  63. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/orchestrator.md +0 -0
  64. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/otel.md +0 -0
  65. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/persistence.md +0 -0
  66. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/rate_limit.md +0 -0
  67. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/retry_policy.md +0 -0
  68. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/routing.md +0 -0
  69. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/scheduler.md +0 -0
  70. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/sinks.md +0 -0
  71. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/usage_tracking.md +0 -0
  72. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/changelog.md +0 -0
  73. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/getting-started.md +0 -0
  74. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/index.md +0 -0
  75. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/migrating-from-0.0.x.md +0 -0
  76. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/migrating-from-0.1.x.md +0 -0
  77. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/agent-events.md +0 -0
  78. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/circuit-breaker.md +0 -0
  79. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/durability.md +0 -0
  80. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/lifecycle.md +0 -0
  81. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/orchestrator.md +0 -0
  82. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/permissions.md +0 -0
  83. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/policy.md +0 -0
  84. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/quality-gate.md +0 -0
  85. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/rate-limiting.md +0 -0
  86. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/retry.md +0 -0
  87. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/routing.md +0 -0
  88. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/usage-tracking.md +0 -0
  89. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/tutorials/end-to-end.md +0 -0
  90. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/durable_agent.py +0 -0
  91. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/parallel_tools.py +0 -0
  92. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/pricing.json +0 -0
  93. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/tiny_agent.py +0 -0
  94. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/scripts/check_module_coverage.py +0 -0
  95. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/__init__.py +0 -0
  96. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/agent_lifecycle.py +0 -0
  97. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/checkpoint.py +0 -0
  98. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/circuit_breaker.py +0 -0
  99. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/data/pricing.json +0 -0
  100. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/handoffs.py +0 -0
  101. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/permissions.py +0 -0
  102. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/persistence.py +0 -0
  103. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/policy_engine.py +0 -0
  104. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/py.typed +0 -0
  105. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/quality_gate.py +0 -0
  106. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/rate_limit.py +0 -0
  107. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/routing.py +0 -0
  108. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/scheduler.py +0 -0
  109. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/sinks.py +0 -0
  110. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/__init__.py +0 -0
  111. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/conftest.py +0 -0
  112. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_agent_events.py +0 -0
  113. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_agent_lifecycle.py +0 -0
  114. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_async_circuit_breaker.py +0 -0
  115. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_circuit_breaker.py +0 -0
  116. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_otel_nesting.py +0 -0
  117. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_permissions.py +0 -0
  118. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_policy_engine.py +0 -0
  119. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_property_circuit_breaker.py +0 -0
  120. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_property_retry_policy.py +0 -0
  121. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_quality_gate.py +0 -0
  122. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_retry_policy.py +0 -0
  123. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_routing.py +0 -0
  124. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s5_scheduler_persistence_async_policy.py +0 -0
  125. {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_usage_tracking.py +0 -0
@@ -5,6 +5,135 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project
5
5
  follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html), with the
6
6
  caveat that 0.x APIs are explicitly unstable.
7
7
 
8
+ ## [0.3.0.dev1] — 2026-05-22
9
+
10
+ First preview of the 0.3.0 milestone (EU AI Act compliance release).
11
+ Bundles Sprint 2 (governance plane + async guardrails) and Sprint 3
12
+ (streaming + mutating hook chain) plus all the 0.2.1 sharp-edges
13
+ fixes — 0.2.1 itself was committed but never tagged for PyPI, so its
14
+ fixes ride into 0.3.0 instead. Install with
15
+ `pip install --pre techrevati-runtime` to try the preview; the stable
16
+ channel still resolves to 0.2.0.
17
+
18
+ This is a **dev release** (PEP 440 `.dev1`). API surface is
19
+ forward-compatible with planned 0.3.0 features but may shift before the
20
+ final cut; pin to `==0.3.0.dev1` if you depend on the exact surface.
21
+
22
+ ### Added — Sprint 3 (streaming + lifecycle hooks)
23
+
24
+ - **`AsyncOrchestrationSession.arun_turn_stream`** — structured
25
+ `StreamEvent` generator that reemits caller-produced text chunks and
26
+ terminates with a `final` event carrying the resolved usage snapshot.
27
+ Participates in the full session bookkeeping (iteration cap,
28
+ governance plane, rate limiter, usage tracker) exactly like
29
+ `arun_turn`. Cancellation is consumer-driven: wrap with
30
+ `contextlib.aclosing` to get deterministic cleanup of the upstream
31
+ generator; `session._last_stream_cancelled` flips to `True` on the
32
+ cancelled path.
33
+ - **`StreamEvent`** frozen dataclass + classmethod constructors
34
+ (`text`, `tool_call`, `tool_result`, `handoff`, `final`, `error`),
35
+ `to_dict()` / `to_json()` for wire serialization. `StreamEventType`
36
+ and `StreamFinalStatus` type aliases exported for consumer typing.
37
+ - **`Hook` + `AsyncHook` Protocols** — interceptor chain that *mutates*
38
+ data, in contrast to the observe-only `EventSink`. Methods are
39
+ optional via `hasattr` dispatch; implement only what you need.
40
+ - **`HookContext`** — mutable dataclass shared across the hook chain.
41
+ Fields: `role`, `phase`, `model`, `prompt`, `tool`, `args`, `extra`.
42
+ Pass via `hook_ctx=` on `run_turn` / `arun_turn` / `run_tool` /
43
+ `arun_tool` / `arun_turn_stream`.
44
+ - **`AgentSession(hooks=[...])`** — hook chain wired through every
45
+ sync + async session. Chain runs left-to-right; later hooks see
46
+ earlier mutations.
47
+ - **Built-in hooks:** `RedactPIIHook` (best-effort PII scrubbing for
48
+ strings, dicts, OpenAI-style message lists; runs `before_model` and
49
+ `after_model`), `LogModelIOHook` (stdlib logger emits model input +
50
+ output with truncation), `TokenBudgetCheckHook` (pre-flight token
51
+ budget guard that raises `HookBudgetExceededError`).
52
+ - **Docs:** `docs/patterns/streaming.md`, `docs/patterns/hooks.md`,
53
+ plus API reference stubs `docs/api/streaming.md`,
54
+ `docs/api/hooks.md`; mkdocs nav updated.
55
+
56
+ ### Added — Sprint 2 (governance plane + async guardrails)
57
+
58
+ - **`GovernancePlane`** primitive — hard-stop limit enforcement
59
+ *outside* agent code, terminal on breach (does NOT route through
60
+ recovery). Built-in limits: `MaxIterationsLimit`, `MaxBudgetLimit`,
61
+ `MaxConsecutiveFailuresLimit`, `MaxToolCallsLimit`. Each carries a
62
+ `value`, `scope` (`session` for now), and `on_breach` mode
63
+ (`terminate` raises `GovernanceBreachError`; `alert` emits
64
+ `governance.alert` events and continues).
65
+ - **`AgentSession(governance=...)`** wired through both sync and async
66
+ sessions. The plane ticks turn counter pre-turn, tool counter
67
+ pre-tool, and cost / success-streak post-turn.
68
+ - **`AsyncGuardrail`** Protocol (`acheck_pre` / `acheck_post`) for
69
+ guardrails that need I/O. Mixed sync + async guardrail lists
70
+ supported on async sessions.
71
+ - **`PatternGuardrail`** (regex deny-list, one compiled alternation
72
+ per instance) and **`PromptInjectionGuardrail`** (subclass with 11
73
+ canonical injection signatures: instruction override, role hijack,
74
+ delimiter abuse, base64 blob, system-prompt extraction).
75
+ - **`governance.breach`** + **`governance.alert`** event names in
76
+ `AgentEventName` — emitted via `EventSink` before the breach
77
+ exception raises so audit logs catch them even when the exception
78
+ propagates past handlers.
79
+ - **Docs:** `docs/patterns/governance.md` + `docs/api/governance.md`.
80
+
81
+ ## [0.2.1] — 2026-05-20
82
+
83
+ Sharp-edges patch landed the same day as 0.2.0 to close silent footguns
84
+ identified in the 0.3.0 migration audit. No new primitives; one
85
+ intentional soft-breaking semantic change to `GuardrailViolatedError`
86
+ (callers reading the legacy single-violation fields still work — see
87
+ "Changed" below).
88
+
89
+ ### Fixed
90
+
91
+ - **`RecoveryRecipe.step_retries` is now honored** by `attempt_recovery`
92
+ and `aattempt_recovery`. Previously the field existed on the dataclass
93
+ but the recovery executors did not consume it — a recipe that set
94
+ `step_retries={RecoveryStep.RETRY_WITH_BACKOFF: 3}` silently ran the
95
+ step exactly once. Now the executor retries the step up to the
96
+ budgeted count before moving to the next step. Missing keys default
97
+ to a single attempt, preserving 0.2.0 behavior.
98
+ - **`OpenTelemetrySink` cleans up orphan parent spans on interpreter
99
+ exit.** If a process died between `AGENT_STARTED` / `PHASE_STARTED`
100
+ and the matching `AGENT_COMPLETED` / `AGENT_FAILED` / `PHASE_COMPLETED`,
101
+ the parent span previously stayed open in the exporter buffer and
102
+ corrupted the APM trace tree. An `atexit` hook now marks every
103
+ still-open parent with `error.type=abrupt_termination` and an `ERROR`
104
+ status before ending it. The hook is no-op on the clean-exit path.
105
+
106
+ ### Added
107
+
108
+ - **`register_pricing(model, pricing, *, on_conflict="overwrite")`** — explicit
109
+ merge semantics. `"overwrite"` (default) preserves 0.2.0 behavior;
110
+ `"error"` raises `PricingAlreadyRegisteredError` on re-registration;
111
+ `"keep"` retains the existing entry and drops the new pricing silently.
112
+ Useful for "register defaults if not present" startup patterns.
113
+ - **`PricingAlreadyRegisteredError`** — exported from
114
+ `techrevati.runtime`. Subclass of `ValueError`, carries `.model`.
115
+ - **`GuardrailViolation`** dataclass — one entry in the new
116
+ `GuardrailViolatedError.violations` tuple. Carries `outcome`,
117
+ `guardrail` (name), `stage` (`"pre"` / `"post"`). Has `to_dict()` for
118
+ audit-log serialization.
119
+ - **`DeprecationWarning` on `Orchestrator(...)` instantiation** — emitted
120
+ once per process. `AgentSession` has been the canonical class name
121
+ since 0.2.0; the alias remains for a deprecation window and will be
122
+ removed in 0.3.0. Silent in import — only the first construction
123
+ warns.
124
+
125
+ ### Changed
126
+
127
+ - **`GuardrailViolatedError.violations`** — every guardrail that fires
128
+ at the same stage is now collected and surfaced as a tuple on the
129
+ raised error, instead of short-circuiting on the first violation.
130
+ Required for EU AI Act Article 12 record-keeping (audit logs must
131
+ reflect the full set of guardrails that fired). Legacy callers that
132
+ read `error.outcome` / `error.guardrail` / `error.stage` still work —
133
+ those attributes mirror the first violation. The orchestrator now
134
+ runs every pre-check and post-check before raising; tests that
135
+ asserted short-circuit behavior have been updated.
136
+
8
137
  ## [0.2.0] — 2026-05-20
9
138
 
10
139
  Durable execution, token-aware rate limiting, OTel agent-level span
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: techrevati-runtime
3
- Version: 0.2.0
3
+ Version: 0.3.0.dev1
4
4
  Summary: Async-aware runtime primitives for multi-step LLM agent loops.
5
5
  Project-URL: Homepage, https://github.com/Techrevati/runtime
6
6
  Project-URL: Documentation, https://Techrevati.github.io/runtime
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
47
47
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-green.svg)](#design-goals)
48
48
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
49
 
50
- Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.1.x; minor breaking changes possible until 0.2.0.**
50
+ Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, durable checkpointing, token-aware rate limiting, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.2.x; 0.x APIs remain explicitly unstable.**
51
51
 
52
52
  ```bash
53
53
  pip install techrevati-runtime
@@ -190,20 +190,20 @@ print(tracker.format_cost())
190
190
  - **OpenAI Agents SDK** is a *cohesive runtime* tied to OpenAI's models, with default tracing through their dashboards. Use it when you're committed to OpenAI and want the smoothest path.
191
191
  - **`techrevati-runtime`** is a *zero-dep primitive set*. Sync + async. Vendor-neutral. Emits OpenTelemetry GenAI semantic conventions so the same APM dashboards that consume OpenAI Agents SDK telemetry will pick us up too. Bring your own model client and your own persistence — the runtime stays opinion-free.
192
192
 
193
- The runtime is **not** a durable workflow engine. Sessions are in-memory; a pluggable checkpointer is on the 0.2.0 roadmap. If you need restart-resumable workflows today, pair this with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer.
193
+ The runtime ships a pluggable `CheckpointSaver` protocol with `InMemorySaver` and `SqliteSaver` implementations (0.2.0) enough for resume-from-checkpoint replay across restarts. It is still not a full durable workflow engine in the Temporal sense; pair with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer if you need cross-host scheduling, retries-as-history, or a durable timer service.
194
194
 
195
195
  ## Limitations (be honest with yourself before adopting)
196
196
 
197
197
  - **Pricing must be registered.** The bundled `pricing.json` is intentionally empty. Without `register_pricing()` or `load_pricing_from_file()`, every cost calculation returns $0.00 (you will see a one-time warning per model).
198
198
  - **Budget enforcement is opt-in.** Set `Orchestrator(enforce_budget=True)` to raise `BudgetExceededError`; the default merely records an event and continues.
199
199
  - **Permissions are advisory.** `OrchestrationSession.run_tool()` enforces; `run_turn()` does not gate model calls. There is no sandbox — pair with OS-level isolation if needed.
200
- - **No durable execution.** Sessions are in-memory and ephemeral. Pair with Temporal/dbos for restart-resumable workflows.
200
+ - **Durable execution is opt-in.** Default sessions are in-memory; pass a `CheckpointSaver` (e.g. `SqliteSaver`) plus a stable `thread_id` to get resume-from-checkpoint replay. Pair with Temporal/dbos if you need cross-host scheduling or durable timers.
201
201
  - **Default sinks are in-memory ring buffers.** Long-running sessions need a durable `EventSink` and `UsageSink` (e.g. `OpenTelemetrySink`, or your own).
202
202
  - **`CircuitBreaker` state is per-process.** Each replica counts its own failures. Add a shared coordinator if you need fleet-wide breaker state.
203
203
 
204
204
  ## Status
205
205
 
206
- `techrevati-runtime` is at version **0.1.0** (beta). This release ships async-first execution, the four standard primitives (Sessions, Tools, Handoffs, Guardrails), `max_iterations` cap, and OpenTelemetry GenAI semantic conventions. Minor breaking changes are possible between 0.1.x and 0.2.0 they will be documented in [docs/migrating-from-0.0.x.md](docs/migrating-from-0.0.x.md) and gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
206
+ `techrevati-runtime` is at version **0.2.0** (beta). This release ships durable execution (`CheckpointSaver` + `SqliteSaver`), token-aware rate limiting (`RateLimiter` / `AsyncRateLimiter`), provider routing, per-session `UsageLimits`, nested OTel agent spans, persistent SQLite sinks, and supply-chain hardening (CycloneDX SBOM + CodeQL + zero-deps smoke). The `AgentSession` rename and OTel wire-format change are the two soft-breaking items — see [docs/migrating-from-0.1.x.md](docs/migrating-from-0.1.x.md). 0.x APIs remain unstable; breaking changes will continue to be gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
207
207
 
208
208
  See [CHANGELOG.md](CHANGELOG.md) for the per-sprint release notes and [docs/tutorials/end-to-end.md](docs/tutorials/end-to-end.md) for a guided tour of every primitive.
209
209
 
@@ -6,7 +6,7 @@
6
6
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-green.svg)](#design-goals)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
8
 
9
- Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.1.x; minor breaking changes possible until 0.2.0.**
9
+ Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, durable checkpointing, token-aware rate limiting, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.2.x; 0.x APIs remain explicitly unstable.**
10
10
 
11
11
  ```bash
12
12
  pip install techrevati-runtime
@@ -149,20 +149,20 @@ print(tracker.format_cost())
149
149
  - **OpenAI Agents SDK** is a *cohesive runtime* tied to OpenAI's models, with default tracing through their dashboards. Use it when you're committed to OpenAI and want the smoothest path.
150
150
  - **`techrevati-runtime`** is a *zero-dep primitive set*. Sync + async. Vendor-neutral. Emits OpenTelemetry GenAI semantic conventions so the same APM dashboards that consume OpenAI Agents SDK telemetry will pick us up too. Bring your own model client and your own persistence — the runtime stays opinion-free.
151
151
 
152
- The runtime is **not** a durable workflow engine. Sessions are in-memory; a pluggable checkpointer is on the 0.2.0 roadmap. If you need restart-resumable workflows today, pair this with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer.
152
+ The runtime ships a pluggable `CheckpointSaver` protocol with `InMemorySaver` and `SqliteSaver` implementations (0.2.0) enough for resume-from-checkpoint replay across restarts. It is still not a full durable workflow engine in the Temporal sense; pair with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer if you need cross-host scheduling, retries-as-history, or a durable timer service.
153
153
 
154
154
  ## Limitations (be honest with yourself before adopting)
155
155
 
156
156
  - **Pricing must be registered.** The bundled `pricing.json` is intentionally empty. Without `register_pricing()` or `load_pricing_from_file()`, every cost calculation returns $0.00 (you will see a one-time warning per model).
157
157
  - **Budget enforcement is opt-in.** Set `Orchestrator(enforce_budget=True)` to raise `BudgetExceededError`; the default merely records an event and continues.
158
158
  - **Permissions are advisory.** `OrchestrationSession.run_tool()` enforces; `run_turn()` does not gate model calls. There is no sandbox — pair with OS-level isolation if needed.
159
- - **No durable execution.** Sessions are in-memory and ephemeral. Pair with Temporal/dbos for restart-resumable workflows.
159
+ - **Durable execution is opt-in.** Default sessions are in-memory; pass a `CheckpointSaver` (e.g. `SqliteSaver`) plus a stable `thread_id` to get resume-from-checkpoint replay. Pair with Temporal/dbos if you need cross-host scheduling or durable timers.
160
160
  - **Default sinks are in-memory ring buffers.** Long-running sessions need a durable `EventSink` and `UsageSink` (e.g. `OpenTelemetrySink`, or your own).
161
161
  - **`CircuitBreaker` state is per-process.** Each replica counts its own failures. Add a shared coordinator if you need fleet-wide breaker state.
162
162
 
163
163
  ## Status
164
164
 
165
- `techrevati-runtime` is at version **0.1.0** (beta). This release ships async-first execution, the four standard primitives (Sessions, Tools, Handoffs, Guardrails), `max_iterations` cap, and OpenTelemetry GenAI semantic conventions. Minor breaking changes are possible between 0.1.x and 0.2.0 they will be documented in [docs/migrating-from-0.0.x.md](docs/migrating-from-0.0.x.md) and gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
165
+ `techrevati-runtime` is at version **0.2.0** (beta). This release ships durable execution (`CheckpointSaver` + `SqliteSaver`), token-aware rate limiting (`RateLimiter` / `AsyncRateLimiter`), provider routing, per-session `UsageLimits`, nested OTel agent spans, persistent SQLite sinks, and supply-chain hardening (CycloneDX SBOM + CodeQL + zero-deps smoke). The `AgentSession` rename and OTel wire-format change are the two soft-breaking items — see [docs/migrating-from-0.1.x.md](docs/migrating-from-0.1.x.md). 0.x APIs remain unstable; breaking changes will continue to be gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
166
166
 
167
167
  See [CHANGELOG.md](CHANGELOG.md) for the per-sprint release notes and [docs/tutorials/end-to-end.md](docs/tutorials/end-to-end.md) for a guided tour of every primitive.
168
168
 
@@ -0,0 +1,3 @@
1
+ # API: `techrevati.runtime.governance`
2
+
3
+ ::: techrevati.runtime.governance
@@ -0,0 +1,3 @@
1
+ # Hooks
2
+
3
+ ::: techrevati.runtime.hooks
@@ -0,0 +1,3 @@
1
+ # Streaming
2
+
3
+ ::: techrevati.runtime.streaming
@@ -0,0 +1,194 @@
1
+ # Governance Plane
2
+
3
+ `GovernancePlane` is the runtime's last line of defense: hard-stop
4
+ limits enforced *outside* agent code so the agent cannot bypass them
5
+ via recovery. When a limit configured with `on_breach="terminate"` is
6
+ exceeded, the orchestrator raises `GovernanceBreachError`, which the
7
+ session marks as `FAILED` and re-raises **without** going through the
8
+ failure classifier or the recovery loop.
9
+
10
+ This is the technical primitive auditors expect for EU AI Act
11
+ deployments — Article 14 (human oversight via stopping conditions),
12
+ Article 15 (robustness / fail-safes), and Article 26 (deployer
13
+ monitoring + reporting). The full article-by-article compliance mapping
14
+ ships in 0.3.0 Sprint 6 (`docs/compliance/`).
15
+
16
+ ## When to use this
17
+
18
+ - **You ship to EU customers** and any user-deployed system would meet
19
+ the Annex III "high-risk" definition. Article 9 risk management,
20
+ Article 12 record-keeping, and Article 26 deployer-side monitoring
21
+ all want a runtime kill-switch.
22
+ - **Cost is dollars per token, not milliseconds per request.** A
23
+ budget cap that the agent could in principle catch + recover from is
24
+ not a hard cap. `MaxBudgetLimit(on_breach="terminate")` is.
25
+ - **The agent runs unattended and must stop on its own.** Production
26
+ multi-step agent loops can drift; a 25-turn iteration cap + a
27
+ consecutive-failure cap rules out the canonical runaway-loop
28
+ failure mode.
29
+ - **You need a rollout signal before flipping a knob to hard-stop.**
30
+ Use `on_breach="alert"` first; measure breach rates from the
31
+ `governance.alert` events; flip to `"terminate"` when you trust the
32
+ threshold.
33
+
34
+ ## When NOT to use this
35
+
36
+ - For *recoverable* token / cost ceilings inside one session that the
37
+ agent is allowed to react to. Use [`UsageLimits`](usage-tracking.md)
38
+ instead — its `UsageLimitExceededError` is catchable and recovery
39
+ flows can respond.
40
+ - For per-tool authorization. Use [`PermissionEnforcer`](permissions.md).
41
+ - For pattern blocking on tool inputs or outputs. Use the
42
+ built-in `PatternGuardrail` / `PromptInjectionGuardrail` (or a
43
+ custom `Guardrail`) — see [Permissions](permissions.md) and
44
+ [API: Guardrails](../api/guardrails.md).
45
+
46
+ ## Quickstart
47
+
48
+ ```python
49
+ from techrevati.runtime import (
50
+ AgentSession,
51
+ GovernancePlane,
52
+ MaxBudgetLimit,
53
+ MaxConsecutiveFailuresLimit,
54
+ MaxIterationsLimit,
55
+ MaxToolCallsLimit,
56
+ )
57
+
58
+ plane = GovernancePlane(
59
+ limits=(
60
+ MaxIterationsLimit(value=25, on_breach="terminate"),
61
+ MaxBudgetLimit(value=5.00, on_breach="terminate"),
62
+ MaxConsecutiveFailuresLimit(value=3, on_breach="terminate"),
63
+ MaxToolCallsLimit(value=100, on_breach="alert"),
64
+ ),
65
+ )
66
+
67
+ session = AgentSession(role="writer", phase="draft", governance=plane)
68
+ ```
69
+
70
+ The orchestrator ticks the plane's counters at three points:
71
+
72
+ - **Pre-turn** — `record_turn_start()` and `enforce()`. The iteration
73
+ cap fires here.
74
+ - **Pre-tool** — `record_tool_call()` and `enforce()`. The tool-call
75
+ cap fires here.
76
+ - **Post-turn** — `record_success()` or `record_failure()`,
77
+ `record_cost(cost_delta)`, and `enforce()`. The budget and
78
+ consecutive-failures caps fire here.
79
+
80
+ ## The four built-in limits
81
+
82
+ ### `MaxIterationsLimit`
83
+
84
+ Caps total turns in the session. Distinct from
85
+ `AgentSession.max_iterations` — that one raises a recoverable
86
+ `MaxIterationsExceededError`; this one is terminal.
87
+
88
+ ```python
89
+ MaxIterationsLimit(value=25, on_breach="terminate")
90
+ ```
91
+
92
+ ### `MaxBudgetLimit`
93
+
94
+ Caps cumulative cost in USD. Distinct from `UsageLimits.cost_usd_max`
95
+ — that one is recoverable; this one is terminal.
96
+
97
+ ```python
98
+ MaxBudgetLimit(value=5.00, on_breach="terminate")
99
+ ```
100
+
101
+ ### `MaxConsecutiveFailuresLimit`
102
+
103
+ Counts consecutive failures. A single successful turn resets the
104
+ counter to zero. Catches "the agent retries the same broken thing
105
+ forever" failure modes that per-step retry budgets alone do not.
106
+
107
+ ```python
108
+ MaxConsecutiveFailuresLimit(value=3, on_breach="terminate")
109
+ ```
110
+
111
+ ### `MaxToolCallsLimit`
112
+
113
+ Caps total tool invocations in the session. Distinct from
114
+ `UsageLimits.tool_calls_max` only in being terminal.
115
+
116
+ ```python
117
+ MaxToolCallsLimit(value=100, on_breach="alert")
118
+ ```
119
+
120
+ ## `on_breach` modes
121
+
122
+ | Mode | Behavior |
123
+ |---|---|
124
+ | `"terminate"` (default) | Raises `GovernanceBreachError`. Worker → `FAILED`. Recovery loop is **NOT** invoked. |
125
+ | `"alert"` | Emits a `governance.alert` event on every breached evaluation. Session continues. |
126
+
127
+ Rolling out a new limit safely: deploy with `"alert"` for 1–2 weeks,
128
+ observe the `governance.alert` event rate, then flip to `"terminate"`.
129
+
130
+ ## Event surface
131
+
132
+ Two new `AgentEventName` values surface in 0.3.0:
133
+
134
+ - `governance.breach` — emitted **before** `GovernanceBreachError`
135
+ raises so downstream sinks see the breach in the audit log even when
136
+ the exception propagates past them.
137
+ - `governance.alert` — emitted once per evaluation per breached
138
+ alert-mode limit.
139
+
140
+ Both carry `data = {limit_name, observed, ceiling, scope}` for sink
141
+ serialization.
142
+
143
+ ## Composing with `UsageLimits`
144
+
145
+ These two primitives are not redundant — they sit at different layers.
146
+
147
+ ```python
148
+ sess = AgentSession(
149
+ role="writer",
150
+ phase="draft",
151
+ # Soft cap: agent code can catch UsageLimitExceededError and react.
152
+ usage_limits=UsageLimits(total_tokens_max=200_000),
153
+ # Hard cap: governance breach terminates the session regardless.
154
+ governance=GovernancePlane(
155
+ limits=(MaxBudgetLimit(value=10.00, on_breach="terminate"),),
156
+ ),
157
+ )
158
+ ```
159
+
160
+ A common pattern is: `usage_limits` cap at 80% of the budget, `governance`
161
+ hard-stop at 100%. The agent gets a recoverable warning before the
162
+ session dies.
163
+
164
+ ## Tuning the knobs
165
+
166
+ | Knob | Reasonable range | Notes |
167
+ |---|---|---|
168
+ | `MaxIterationsLimit.value` | 10–50 for production loops | Same default as OpenAI Agents SDK. |
169
+ | `MaxBudgetLimit.value` | per-customer / per-session limit | Pair with `UsageLimits.cost_usd_max` at 80%. |
170
+ | `MaxConsecutiveFailuresLimit.value` | 2–5 | Below 2 is twitchy; above 5 hides real reliability bugs. |
171
+ | `MaxToolCallsLimit.value` | 5×–10× expected | Useful as an alert before flipping to terminate. |
172
+ | `on_breach="alert"` | Always start here for new limits | Measure first, terminate second. |
173
+
174
+ ## Anti-patterns
175
+
176
+ - **Catching `GovernanceBreachError` and retrying.** Don't. The point
177
+ of the plane is that the agent cannot bypass it. If you want
178
+ recoverable behavior, use `UsageLimits` instead.
179
+ - **Putting business logic in `GovernanceState.record_*`.** The state
180
+ object is a counter; do not subclass it to fire side effects on
181
+ every tick. Add a custom `EventSink` for that.
182
+ - **One plane per turn.** Construct the plane once and pass it to
183
+ `AgentSession`; do not create a new plane on each turn — counters
184
+ reset.
185
+ - **Mixing `"alert"` and `"terminate"` randomly across limits.** Pick a
186
+ rollout phase per limit, document it, and don't half-migrate.
187
+
188
+ ## Sources
189
+
190
+ - Waxell — *AI Agent Circuit Breakers: The Reliability Pattern Production
191
+ Teams Are Missing* — [https://dev.to/waxell/ai-agent-circuit-breakers-...](https://dev.to/waxell/ai-agent-circuit-breakers-the-reliability-pattern-production-teams-are-missing-5bpg)
192
+ - DZone — *Engineering Hard-Stop Safety Into Autonomous Agent
193
+ Workflows* — [https://dzone.com/articles/algorithmic-circuit-breakers-agent-safety](https://dzone.com/articles/algorithmic-circuit-breakers-agent-safety)
194
+ - EU AI Act Articles 9, 12, 14, 15, 26 — [artificialintelligenceact.eu](https://artificialintelligenceact.eu/section/3-2/)