openbox-temporal-sdk-python 1.1.1__tar.gz → 1.2.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 (82) hide show
  1. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.gitignore +1 -0
  2. openbox_temporal_sdk_python-1.2.0/CHANGELOG.md +238 -0
  3. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/PKG-INFO +140 -101
  4. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/README.md +138 -100
  5. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/changelog-hook-level-governance.md +9 -0
  6. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/code-standards.md +58 -63
  7. openbox_temporal_sdk_python-1.2.0/docs/codebase-summary.md +632 -0
  8. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/configuration.md +45 -3
  9. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/project-overview-pdr.md +42 -30
  10. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/system-architecture.md +218 -511
  11. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/temporal-plugin-integration-guide.md +15 -11
  12. openbox_temporal_sdk_python-1.2.0/openbox/__init__.py +125 -0
  13. openbox_temporal_sdk_python-1.2.0/openbox/activities.py +278 -0
  14. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/activity_interceptor.py +123 -199
  15. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/client.py +32 -8
  16. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/config.py +166 -49
  17. openbox_temporal_sdk_python-1.2.0/openbox/core_adapter.py +279 -0
  18. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/errors.py +74 -30
  19. openbox_temporal_sdk_python-1.2.0/openbox/governance_state.py +101 -0
  20. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/hitl.py +8 -12
  21. openbox_temporal_sdk_python-1.2.0/openbox/multi_agent.py +136 -0
  22. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/plugin.py +57 -34
  23. openbox_temporal_sdk_python-1.2.0/openbox/request_signing.py +89 -0
  24. openbox_temporal_sdk_python-1.2.0/openbox/tracing.py +59 -0
  25. openbox_temporal_sdk_python-1.2.0/openbox/types.py +79 -0
  26. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/verdict_handler.py +0 -1
  27. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/worker.py +60 -48
  28. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/workflow_interceptor.py +126 -92
  29. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/pyproject.toml +9 -1
  30. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/sonar-project.properties +1 -4
  31. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/__init__.py +0 -1
  32. openbox_temporal_sdk_python-1.2.0/tests/conftest.py +56 -0
  33. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_activities.py +7 -30
  34. openbox_temporal_sdk_python-1.2.0/tests/test_activity_interceptor.py +1869 -0
  35. openbox_temporal_sdk_python-1.2.0/tests/test_approval_action_precedence.py +102 -0
  36. openbox_temporal_sdk_python-1.2.0/tests/test_base_sdk_signing_parity.py +102 -0
  37. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_config.py +0 -31
  38. openbox_temporal_sdk_python-1.2.0/tests/test_core_conformance_suite.py +261 -0
  39. openbox_temporal_sdk_python-1.2.0/tests/test_multi_agent.py +129 -0
  40. openbox_temporal_sdk_python-1.2.0/tests/test_no_import_machinery_in_governed_open_paths.py +70 -0
  41. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_plugin.py +73 -46
  42. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_plugin_integration.py +16 -21
  43. openbox_temporal_sdk_python-1.2.0/tests/test_public_api_compatibility.py +154 -0
  44. openbox_temporal_sdk_python-1.2.0/tests/test_request_signing.py +280 -0
  45. openbox_temporal_sdk_python-1.2.0/tests/test_signing_config.py +242 -0
  46. openbox_temporal_sdk_python-1.2.0/tests/test_temporal_hook_parity.py +129 -0
  47. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_types.py +0 -98
  48. openbox_temporal_sdk_python-1.2.0/tests/test_worker.py +1099 -0
  49. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_workflow_interceptor.py +267 -142
  50. openbox_temporal_sdk_python-1.2.0/tests/test_workflow_sandbox_import_safety.py +67 -0
  51. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/uv.lock +309 -12
  52. openbox_temporal_sdk_python-1.1.1/CHANGELOG.md +0 -68
  53. openbox_temporal_sdk_python-1.1.1/docs/codebase-summary.md +0 -768
  54. openbox_temporal_sdk_python-1.1.1/openbox/__init__.py +0 -181
  55. openbox_temporal_sdk_python-1.1.1/openbox/activities.py +0 -201
  56. openbox_temporal_sdk_python-1.1.1/openbox/context_propagation.py +0 -56
  57. openbox_temporal_sdk_python-1.1.1/openbox/db_governance_hooks.py +0 -771
  58. openbox_temporal_sdk_python-1.1.1/openbox/file_governance_hooks.py +0 -396
  59. openbox_temporal_sdk_python-1.1.1/openbox/hook_governance.py +0 -427
  60. openbox_temporal_sdk_python-1.1.1/openbox/http_governance_hooks.py +0 -859
  61. openbox_temporal_sdk_python-1.1.1/openbox/otel_setup.py +0 -474
  62. openbox_temporal_sdk_python-1.1.1/openbox/span_processor.py +0 -240
  63. openbox_temporal_sdk_python-1.1.1/openbox/tracing.py +0 -297
  64. openbox_temporal_sdk_python-1.1.1/openbox/types.py +0 -188
  65. openbox_temporal_sdk_python-1.1.1/tests/conftest.py +0 -96
  66. openbox_temporal_sdk_python-1.1.1/tests/test_activity_interceptor.py +0 -2572
  67. openbox_temporal_sdk_python-1.1.1/tests/test_db_governance_hooks.py +0 -886
  68. openbox_temporal_sdk_python-1.1.1/tests/test_file_governance_hooks.py +0 -784
  69. openbox_temporal_sdk_python-1.1.1/tests/test_http_body_truncation.py +0 -75
  70. openbox_temporal_sdk_python-1.1.1/tests/test_otel_setup.py +0 -2255
  71. openbox_temporal_sdk_python-1.1.1/tests/test_psycopg2_hooks_verify.py +0 -60
  72. openbox_temporal_sdk_python-1.1.1/tests/test_span_processor.py +0 -706
  73. openbox_temporal_sdk_python-1.1.1/tests/test_tracing.py +0 -1288
  74. openbox_temporal_sdk_python-1.1.1/tests/test_worker.py +0 -1896
  75. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.github/workflows/publish.yml +0 -0
  76. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.github/workflows/sonarqube.yaml +0 -0
  77. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.python-version +0 -0
  78. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.repomixignore +0 -0
  79. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/LICENSE +0 -0
  80. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/py.typed +0 -0
  81. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/release-manifest.json +0 -0
  82. {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/repomix-output.xml +0 -0
@@ -40,3 +40,4 @@ AGENTS.md
40
40
  .claude/
41
41
  CLAUDE.md
42
42
  plans/
43
+ _notes/
@@ -0,0 +1,238 @@
1
+ # Changelog
2
+
3
+ All notable changes to OpenBox SDK for Temporal Workflows.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.2.0] - 2026-07-20
8
+
9
+ ### Changed
10
+
11
+ - **Depends on `openbox-sdk-python>=0.2.0`** (import package `openbox_core`) — the shared
12
+ base SDK owning contracts, the always-strict gate, identity/signing, the evaluate
13
+ client, context runtime, Core `SpanData` wire serialization, generic instrumentation,
14
+ and the conformance kit. The `0.2.0` floor adds Redis/MongoDB/urllib3/urllib
15
+ instrumentation in the base runtime so no hook coverage is lost in the flip.
16
+ - **Hook governance is now owned entirely by the base runtime.** The worker/plugin
17
+ build and own an `openbox_core` runtime and call `install_instrumentation()`;
18
+ HTTP/DB/file/function hooks, payload shape, evaluation, and enforcement all live in
19
+ `openbox_core`. LangGraph and Temporal now emit one identical flat hook interface.
20
+ - `request_signing` now shims over `openbox_core.identity`. Signed request bytes are
21
+ UNCHANGED — gated byte-for-byte by a golden fixture generated from the pre-migration
22
+ signer (`tests/test_base_sdk_signing_parity.py`).
23
+ - `Verdict`, `GuardrailsCheckResult`, and `WorkflowEventType` re-export the shared
24
+ contracts; `GovernanceVerdictResponse` subclasses the shared `EvaluationResult`
25
+ (same public surface, plus `fallback_used`/`diagnostics`/`raw`/`approval_expiration_time`).
26
+ - Workflow lifecycle events route through shared `EventEnvelope` factories
27
+ (sandbox-safe pure contracts; wire payloads unchanged).
28
+ - Activity execution binds the shared `ActivityContext` with a guaranteed
29
+ try/finally reset — governance context can no longer leak when an activity raises.
30
+
31
+ ### Behavior changes (regression-gated)
32
+
33
+ - **Approval decision precedence:** approval poll responses parse via the shared
34
+ `ApprovalResult`; `action` now outranks `verdict` when both are present
35
+ (previously verdict-first), and a response with NEITHER field stays PENDING
36
+ (previously implicit ALLOW). Pinned by `tests/test_approval_action_precedence.py`.
37
+
38
+ ### Known behavior differences in the core runtime
39
+
40
+ - **Completed-hook semantics:** core completed hooks never raise to the caller —
41
+ stop verdicts mark FUTURE execution blocked (abort/halt flags). Some legacy
42
+ completed hooks could raise after the operation ran (HTTP response hook, file
43
+ close); raising post-hoc cannot undo the operation, so the core model drops it
44
+ by design.
45
+ - **Fail-closed shaping:** core maps started-hook evaluation failures under
46
+ `on_api_error="fail_closed"` (and started-hook contract errors, always) to a
47
+ non-retryable `GovernanceHalt` via the adapter — same terminal effect as
48
+ legacy's HALT-shaped `GovernanceBlockedError`, different exception chain.
49
+ - **Redaction point for `activity_input`:** the core `ActivityContext` receives
50
+ the post-redaction input (what actually ran); the legacy buffer stored the
51
+ pre-redaction value. Core hook payloads therefore never leak redacted fields.
52
+
53
+ ### Added
54
+
55
+ - `openbox.governance_state.TemporalGovernanceState` — run-scoped state carrying the
56
+ Temporal effects that must survive past a base hook callback: signal BLOCK/HALT
57
+ verdicts that fail the next activity, HITL pending-approval retry markers, and the
58
+ completed-hook stop bridge (completed BLOCK skips the duplicate completed event;
59
+ completed HALT reaches the terminate path). Consumed on every activity exit path.
60
+ - `openbox.core_adapter` — `TemporalFrameworkAdapter` (maps base-SDK verdicts to
61
+ native `ApplicationError` types and the retry-based HITL loop; REQUIRE_APPROVAL marks
62
+ the pending-approval retry marker and degrades to a non-retryable block when HITL is
63
+ disabled/skipped; completed BLOCK/HALT records run-scoped stop state with the resolved
64
+ `ActivityContext`), and `create_core_runtime(...)` which builds and owns the base
65
+ runtime the worker/plugin install. `core_activity_scope` is the sole hook-context
66
+ bridge (guaranteed try/finally reset + trace registration).
67
+ - Redis, MongoDB, urllib3, and urllib governance now flow through the base runtime.
68
+ - Gates: workflow-sandbox import-safety, base-SDK conformance suite driven by the
69
+ Temporal adapter, public-API compatibility suite, and a deterministic
70
+ `llm_completion` hook-parity test (flat interface via the Temporal runtime, no live
71
+ OpenAI credentials).
72
+
73
+ ### Removed
74
+
75
+ - `WorkflowSpanProcessor`, `WorkflowSpanBuffer`, and the `setup_opentelemetry_for_governance`
76
+ entry point (public exports). Governance instrumentation is installed by the base
77
+ runtime; there is no Temporal-local OpenTelemetry setup to call.
78
+ - Temporal-local hook modules `otel_setup`, `hook_governance`, `http_governance_hooks`,
79
+ `db_governance_hooks`, `file_governance_hooks`, `span_processor`, and the dead
80
+ `context_propagation` helper (its ContextVar-to-executor propagation lives in the base
81
+ runtime). Their payload-shape/instrumentation coverage moved to the base SDK suite.
82
+ - `openbox.tracing.traced` now wraps `openbox_core.instrumentation.function.governed`;
83
+ `create_span` remains a plain span helper.
84
+
85
+ ### Fixed (legacy file instrumentation — pre-existing upstream)
86
+
87
+ - **Runtime RecursionError under file instrumentation:** governance evaluation
88
+ itself opens files (httpx/ssl, package-metadata scans via importlib_metadata/
89
+ zipp) — governing those opens re-evaluated recursively until RecursionError.
90
+ traced_open now (1) passes through any open performed on a thread already
91
+ inside file-governance work (re-entrancy guard) and (2) bypasses
92
+ interpreter-owned trees (venv, stdlib, site-packages — sys.prefix/base_prefix
93
+ + sysconfig paths): those reads are Python machinery, not application data
94
+ access. Application paths (./.env, data files, temp files) remain governed.
95
+ Same guard mirrored in the base SDK's file instrumentation.
96
+
97
+ ### Fixed (HTTP hook payloads — pre-existing upstream)
98
+
99
+ - **Mangled method on httpx spans:** OTel's httpx instrumentation passes the
100
+ method as BYTES; `str(b"POST")` shipped `http_method: "b'POST'"` on every
101
+ httpx started span. Methods now decode bytes-safely at all client paths.
102
+ - **Credential leak in governance payloads:** raw request/response headers
103
+ (`authorization`, `cookie`, `set-cookie`, `x-api-key`, …) were sent to Core
104
+ verbatim — live API keys landed in Core logs. All header dicts are now
105
+ redacted at the span-data builder choke point. Same redaction added to the
106
+ base SDK instrumentation. **Rotate any API keys that appeared in payloads.**
107
+
108
+ ### Migration notes
109
+
110
+ - Base-runtime instrumentation is the sole hook governance path
111
+ and the legacy in-repo hook stack is removed. Approval-retry, signal BLOCK/HALT, and
112
+ completed-hook BLOCK/HALT behavior are preserved (run-scoped, cleaned after use).
113
+ - Dropped coverage vs the legacy stack: direct raw-driver DB queries (psycopg2/mysql/
114
+ pymysql/sqlite3 used WITHOUT SQLAlchemy). The base runtime governs SQLAlchemy (all
115
+ backends), asyncpg, Redis, and MongoDB; activating raw dbapi driver instrumentors
116
+ interferes with SQLAlchemy's own dialect queries, so raw-driver-only governance is a
117
+ base-SDK follow-up.
118
+
119
+ ## [1.1.2] - 2026-04-22
120
+
121
+ ### Security
122
+
123
+ - **API key no longer flows through workflow history.** `send_governance_event` refactored into a `GovernanceActivities` class that holds credentials on `self`; activity inputs carry only `payload`, `timeout`, and `on_api_error`. Before this change, anyone with Describe permissions on the namespace could read the API key from the recorded activity input.
124
+
125
+ ### Added
126
+
127
+ - W3C trace propagation through Temporal headers, on by default via `enable_trace_propagation=True` on `OpenBoxPlugin` and `create_openbox_worker()`. Uses `temporalio.contrib.opentelemetry.TracingInterceptor` so spans started by the caller stitch to workflow/activity spans on the worker side.
128
+ - `ApplicationError` type constants in `errors.py` (`GOVERNANCE_HALT_ERROR_TYPE`, `GOVERNANCE_BLOCK_ERROR_TYPE`, `GOVERNANCE_API_ERROR_TYPE`, `GOVERNANCE_STOP_ERROR_TYPE`) — single source of truth for governance error routing.
129
+ - `openbox.activities.GovernanceActivities` class + `build_governance_activities()` factory.
130
+
131
+ ### Fixed
132
+
133
+ - **Workflow exception shadowing** — `WorkflowFailed` event send is now wrapped in `try/except`, so a `GovernanceHaltError` from `fail_closed + API down` no longer replaces the real workflow error.
134
+ - **String-matching on exception types** — `workflow_interceptor` now inspects `ApplicationError.type` via the exception chain instead of `"GovernanceHalt" in str(e)`. Eliminates false positives when a user workflow happens to emit an error message containing a governance keyword.
135
+ - **Race creating governance HTTP client** — `hook_governance._get_sync_client` / `_get_async_client` now use double-checked locking. Previously two concurrent activities could each create a client, with the losing instance getting garbage-collected while its connection pool leaked.
136
+ - **Replayer plugin coverage** — `test_plugin_integration.py` now passes `plugins=[plugin]` to `Replayer`, so replay tests validate interceptor determinism, not just user workflow code.
137
+ - Version-pin mismatch — comments in `openbox/__init__.py` said `temporalio >= 1.24.0` but pyproject/README pin `1.23.0`. Corrected to `1.23.0`.
138
+
139
+ ### Changed
140
+
141
+ - `plugin.py` / `worker.py` now use `logging.getLogger(__name__)` for initialization status messages instead of `print()`.
142
+
143
+ ## [1.1.1] - 2026-04-07
144
+
145
+ ### Added
146
+
147
+ - **OpenBoxPlugin** — drop-in `SimplePlugin` integration for Temporal Workers. Single-line setup: `plugins=[OpenBoxPlugin(openbox_url=..., openbox_api_key=...)]`. Auto-registers interceptors, OTel instrumentation, sandbox passthrough, and `send_governance_event` activity
148
+ - Plugin integration guide for Temporal AI Partner Ecosystem (`docs/temporal-plugin-integration-guide.md`)
149
+ - HTTP body truncation tests (`tests/test_http_body_truncation.py`)
150
+ - Plugin unit tests (`tests/test_plugin.py`) and integration/replay tests (`tests/test_plugin_integration.py`)
151
+
152
+ ### Fixed
153
+
154
+ - **HTTP body truncation** — enforce `max_body_size` (default 64KB) on request/response bodies in governance spans
155
+ - **File I/O spans** — remove raw file content from governance payloads; only `bytes_read`/`bytes_written` metadata sent
156
+ - **error_type sanitization** — prevent serialized error objects from being sent as `error.cause.error_type` string in WorkflowFailed payloads
157
+ - Remove useless f-strings, redundant `(ImportError, Exception)` clauses, merge nested if statements
158
+ - Prefix unused `span` param in urllib hook
159
+
160
+ ### Changed
161
+
162
+ - `temporalio>=1.23.0` (from 1.8.0) for SimplePlugin support
163
+ - `GovernanceConfig.max_body_size` default changed from `None` (unlimited) to `65536` (64KB)
164
+ - `\w` regex shorthand in API key pattern
165
+
166
+ ### Refactored
167
+
168
+ - Reduce cognitive complexity across 7 modules: `activity_interceptor.py` (126→split), `workflow_interceptor.py` (40→split), `activities.py` (20→split), `db_governance_hooks.py` (34→split), `otel_setup.py` (51→split), `tracing.py` (85→split), `verdict_handler.py` (16→15)
169
+ - Extract shared helpers: `_run_governed_query_sync/async`, `_build_error_dict`, `_extract_dbapi_context`, `_instrument_sqlalchemy`
170
+
171
+ ### Dependencies
172
+
173
+ - Bump Pygments 2.19.2 → 2.20.0 (ReDoS fix, CVSS 1.9)
174
+
175
+ ## [1.1.0] - 2026-03-09
176
+
177
+ ### Added
178
+
179
+ - **Hook-level governance** — real-time, per-operation governance evaluation during activity execution
180
+ - Every HTTP request, database query, file operation, and traced function call is evaluated at `started` (before, can block) and `completed` (after, informational) stages
181
+ - Same `POST /api/v1/governance/evaluate` endpoint with new `hook_trigger` field in payload
182
+ - Automatically enabled when using `create_openbox_worker()`
183
+ - **Database query governance** — per-query started/completed evaluations for psycopg2, pymysql, mysql-connector, asyncpg, pymongo, redis, sqlalchemy
184
+ - **File I/O governance** — per-operation evaluations for open, read, write, readline, readlines, writelines, close (opt-in via `instrument_file_io=True`)
185
+ - **`@traced` decorator** (`openbox.tracing`) — function-level governance with OTel spans; zero overhead when governance not configured
186
+ - **`GovernanceBlockedError`** — new exception type for hook-level blocking with verdict, reason, and resource identifier
187
+ - **Abort propagation** — once one hook blocks, all subsequent hooks for the same activity short-circuit immediately
188
+ - **HALT workflow termination** from hook-level governance via `client.terminate()`
189
+ - **REQUIRE_APPROVAL** from hook-level governance enters the same HITL approval polling flow as activity-level approvals
190
+ - **`duration_ns`** computed for all hook span types (HTTP, file, function — DB already had it)
191
+
192
+ ### Changed
193
+
194
+ - **`hook_trigger` simplified to boolean** — was a dict with type/stage/data, now just `true`. All data moved to span root fields
195
+ - **Span data consolidation** — all type-specific fields at span root (`hook_type`, `http_method`, `db_system`, `file_path`, `function`, etc.)
196
+ - **`attributes` is OTel-original only** — no custom `openbox.*`, `http.request.*`, `db.result.*` fields injected
197
+ - Hook governance payloads send only the current span per evaluation (not accumulated history)
198
+ - Event-level payloads (ActivityStarted/Completed, Workflow events) no longer include spans
199
+ - Simplified `WorkflowSpanProcessor` — removed span buffering, governed span tracking, body data merging; `on_end()` now only forwards to fallback exporters
200
+
201
+ ### Fixed
202
+
203
+ - HALT verdict from hooks now correctly terminates the workflow (previously only stopped the activity like BLOCK)
204
+ - REQUIRE_APPROVAL from hooks now enters the approval polling flow (previously raised unhandled error)
205
+ - Stale buffer/verdict from previous workflow run no longer carries over when workflow_id is reused
206
+ - Subsequent hooks no longer fire after the first hook blocks an activity
207
+
208
+ ## [1.0.21] - 2026-03-04
209
+
210
+ ### Added
211
+
212
+ - Human-in-the-loop approval with expiration handling
213
+ - Approval polling via `POST /api/v1/governance/approval`
214
+ - Guardrails: input/output validation and redaction
215
+ - `GovernanceVerdictResponse.from_dict()` with guardrails_result parsing
216
+ - Output redaction for activity results
217
+ - `_deep_update_dataclass()` for in-place dataclass field updates from redacted dicts
218
+
219
+ ### Fixed
220
+
221
+ - Temporal Payload objects no longer slip through as non-serializable in governance payloads
222
+ - Stale buffer detection via run_id comparison
223
+
224
+ ## [1.0.0] - 2026-02-15
225
+
226
+ ### Added
227
+
228
+ - Initial release
229
+ - 6 event types: WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted
230
+ - 5-tier verdict system: ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT
231
+ - HTTP instrumentation via OpenTelemetry (httpx, requests, urllib3, urllib)
232
+ - Database instrumentation (psycopg2, pymysql, asyncpg, pymongo, redis, sqlalchemy)
233
+ - File I/O instrumentation (opt-in)
234
+ - Zero-code setup via `create_openbox_worker()` factory
235
+ - Workflow and activity interceptors for governance
236
+ - Span buffering and activity context tracking
237
+ - `fail_open` / `fail_closed` error policies
238
+ - v1.0 backward compatibility for legacy verdict strings
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openbox-temporal-sdk-python
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: OpenBox SDK - Governance and observability for Temporal workflows
5
5
  Project-URL: Homepage, https://github.com/OpenBox-AI/temporal-sdk-python
6
6
  Project-URL: Documentation, https://github.com/OpenBox-AI/temporal-sdk-python#readme
@@ -24,6 +24,7 @@ Requires-Python: >=3.11
24
24
  Requires-Dist: asyncpg>=0.29.0
25
25
  Requires-Dist: httpx<1,>=0.28.0
26
26
  Requires-Dist: mysql-connector-python>=8.0.0
27
+ Requires-Dist: openbox-sdk-python>=0.2.0
27
28
  Requires-Dist: opentelemetry-api<1.40.0,>=1.38.0
28
29
  Requires-Dist: opentelemetry-instrumentation-asyncpg<0.61b0,>=0.59b0
29
30
  Requires-Dist: opentelemetry-instrumentation-httpx<0.61b0,>=0.59b0
@@ -51,7 +52,8 @@ Description-Content-Type: text/markdown
51
52
  OpenBox SDK provides **governance and observability** for Temporal workflows by capturing workflow/activity lifecycle events, HTTP telemetry, database queries, and file operations, then sending them to OpenBox Core for policy evaluation.
52
53
 
53
54
  **Key Features:**
54
- - 6 event types (WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted)
55
+ - 7 event types (WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted, Handoff)
56
+ - Multi-agent sessions — propagate a shared `multi_agent_session_id` across workflow + activity events
55
57
  - 5-tier verdict system (ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT)
56
58
  - **Hook-level governance** — per-operation evaluation (HTTP requests, file I/O, database queries, function tracing) with started/completed stages
57
59
  - HTTP/Database/File I/O instrumentation via OpenTelemetry
@@ -100,7 +102,15 @@ await worker.run()
100
102
  ```
101
103
 
102
104
  The plugin automatically configures governance interceptors, OTel instrumentation,
103
- sandbox passthrough, and the `send_governance_event` activity.
105
+ sandbox passthrough, W3C trace propagation through Temporal headers (via
106
+ `temporalio.contrib.opentelemetry.TracingInterceptor`), and the
107
+ `send_governance_event` activity.
108
+
109
+ **Credentials never leave the plugin.** `openbox_api_key` is captured on the
110
+ governance activity instance itself — it does **not** flow through activity
111
+ inputs, so it is never written to workflow history. To opt out of trace
112
+ propagation (e.g., if you already wire `OpenTelemetryPlugin`), pass
113
+ `enable_trace_propagation=False`.
104
114
 
105
115
  ### Composing with Other Plugins
106
116
 
@@ -148,8 +158,10 @@ The factory automatically:
148
158
  1. Validates the API key
149
159
  2. Creates span processor
150
160
  3. Sets up OpenTelemetry instrumentation
151
- 4. Creates governance interceptors
152
- 5. Adds `send_governance_event` activity
161
+ 4. Creates governance interceptors (incl. W3C trace propagation)
162
+ 5. Builds the `GovernanceActivities` instance with credentials captured on
163
+ `self` and registers its `send_governance_event` method — the API key is
164
+ never passed through activity inputs / workflow history
153
165
  6. Returns fully configured Worker
154
166
 
155
167
  ---
@@ -189,12 +201,15 @@ worker = create_openbox_worker(
189
201
 
190
202
  # Database instrumentation
191
203
  instrument_databases=True,
192
- db_libraries={"psycopg2", "sqlalchemy"}, # None = all available
193
- sqlalchemy_engine=engine, # pass pre-existing engine for query capture
194
204
 
195
205
  # File I/O instrumentation
196
206
  instrument_file_io=False, # disabled by default
197
207
 
208
+ # Header-based W3C trace propagation (client → workflow → activities).
209
+ # Default True. Set False if you already wire OpenTelemetryPlugin or a
210
+ # custom propagator.
211
+ enable_trace_propagation=True,
212
+
198
213
  # Standard Worker options (all supported)
199
214
  activity_executor=my_executor,
200
215
  max_concurrent_activities=10,
@@ -232,6 +247,48 @@ OpenBox Core returns a verdict indicating what action the SDK should take.
232
247
  | SignalReceived | Signal received | workflow_id, signal_name, signal_args |
233
248
  | ActivityStarted | Activity begins | activity_id, activity_type, activity_input |
234
249
  | ActivityCompleted | Activity ends | activity_id, activity_type, activity_input, activity_output, spans, status, duration |
250
+ | Handoff | Agent hands off to another agent | from_agent_did, multi_agent_session_id |
251
+
252
+ When a workflow runs as part of a multi-agent session (see below), every event
253
+ above also carries `multi_agent_session_id`. The field is omitted when no
254
+ session id is supplied.
255
+
256
+ ---
257
+
258
+ ## Multi-Agent Sessions
259
+
260
+ Group the work of several agents under one shared `multi_agent_session_id`. The
261
+ SDK only **propagates** the id you supply — it never invents one, and it owns no
262
+ routing, session minting, or agent registry (those stay in your application).
263
+
264
+ **Supply the id** via the Temporal workflow memo at start time:
265
+
266
+ ```python
267
+ await client.start_workflow(
268
+ MyWorkflow.run,
269
+ arg,
270
+ id="order-123",
271
+ task_queue="my-queue",
272
+ memo={"openbox_multi_agent_session_id": session_id},
273
+ )
274
+ ```
275
+
276
+ The SDK then tags every governance event (workflow, activity, and hook events)
277
+ with that id, propagating it from the workflow to its activities automatically.
278
+
279
+ **Emit an explicit handoff** from inside workflow code:
280
+
281
+ ```python
282
+ from openbox import emit_handoff
283
+
284
+ await emit_handoff(
285
+ multi_agent_session_id=session_id,
286
+ from_agent_did="did:aip:...", # the sending agent
287
+ )
288
+ ```
289
+
290
+ The receiving agent is derived server-side from the signed identity and is never
291
+ sent. `emit_handoff` validates its arguments locally before any network call.
235
292
 
236
293
  ---
237
294
 
@@ -264,7 +321,7 @@ OpenBox Core can validate and redact sensitive data before/after activity execut
264
321
 
265
322
  ## Error Handling
266
323
 
267
- Configure error policy via `on_api_error` (constants available as `hook_governance.FAIL_OPEN` / `FAIL_CLOSED`):
324
+ Configure error policy via the `governance_policy` parameter (`"fail_open"` / `"fail_closed"`):
268
325
 
269
326
  | Policy | Behavior |
270
327
  |--------|----------|
@@ -292,15 +349,12 @@ Configure error policy via `on_api_error` (constants available as `hook_governan
292
349
  - `redis` — native OTel `request_hook`/`response_hook`
293
350
  - `sqlalchemy` — `before/after_cursor_execute` event listeners
294
351
 
295
- **SQLAlchemy Note:** If your SQLAlchemy engine is created before `create_openbox_worker()` runs (e.g., at module import time), you must pass it via the `sqlalchemy_engine` parameter. Without this, `SQLAlchemyInstrumentor` only patches future `create_engine()` calls and won't capture queries on pre-existing engines.
352
+ **SQLAlchemy Note:** Query-level governance works on pre-existing engines automatically — even engines created before `create_openbox_worker()` runs (e.g., at module import time) because the base runtime governs SQLAlchemy via a global `Engine` event listener. No engine handle needs to be passed in. The `db_libraries` and `sqlalchemy_engine` parameters are still accepted for backward compatibility but are no longer required and have no effect: the base runtime installs every available database instrumentor best-effort.
296
353
 
297
354
  ```python
298
- from db.engine import engine
299
-
300
355
  worker = create_openbox_worker(
301
356
  ...,
302
- db_libraries={"psycopg2", "sqlalchemy"},
303
- sqlalchemy_engine=engine,
357
+ instrument_databases=True, # default; installs all available DB instrumentors
304
358
  )
305
359
  ```
306
360
 
@@ -396,7 +450,7 @@ Every database operation is evaluated at `started` (pre-query, can block) and `c
396
450
 
397
451
  ### Function Tracing
398
452
 
399
- Functions decorated with `@traced` are automatically governed when `hook_governance` is configured:
453
+ Functions decorated with `@traced` are automatically governed when a worker (or plugin) has installed the base runtime. `@traced` wraps the base SDK's `governed()` decorator:
400
454
 
401
455
  | Stage | Trigger | Data Available |
402
456
  |-------|---------|----------------|
@@ -405,15 +459,13 @@ Functions decorated with `@traced` are automatically governed when `hook_governa
405
459
 
406
460
  **How it works (Function Tracing):**
407
461
 
408
- 1. `@traced` decorator wrapper starts OTel span
409
- 2. → SDK sends `started` governance evaluation with function name and module
410
- 3. If verdict is BLOCK/HALT → `GovernanceBlockedError` raised, function never executes
462
+ 1. `@traced` delegates to the base SDK's `governed()` decorator
463
+ 2. → the base runtime sends a `started` FUNCTION_CALL evaluation with function name and module
464
+ 3. If verdict is BLOCK/HALT → the call is blocked and the function never executes
411
465
  4. Function executes normally
412
- 5. → SDK sends `completed` governance evaluation with result or error info
413
- 6. If verdict is BLOCK/HALT → `GovernanceBlockedError` raised after execution
414
- 7. When `hook_governance` is not configured zero overhead, no governance calls
415
-
416
- **Governed span tracking:** When hook-level governance is active, the SDK marks HTTP spans as "governed" so the OTel `on_end` processor skips buffering them — preventing duplicate spans.
466
+ 5. → the base runtime sends a `completed` FUNCTION_CALL evaluation with result or error info
467
+ 6. If verdict is BLOCK/HALT → the block is surfaced after execution
468
+ 7. When no base runtime is installedtransparent passthrough, zero governance calls
417
469
 
418
470
  ---
419
471
 
@@ -424,104 +476,87 @@ See [System Architecture](./docs/system-architecture.md) for detailed component
424
476
  **High-Level Flow:**
425
477
 
426
478
  ```
427
- Workflow/Activity → Interceptors Span Processor → OpenBox Core API
428
-
429
- Returns Verdict
430
-
431
- (ALLOW, BLOCK, HALT, etc.)
432
-
433
- Hook-Level (per HTTP request):
434
- Activity HTTP Call OTel Hookhook_governance API (started) → Allow/Block
435
- Response hook_governance API (completed) → Allow/Block
436
-
437
- Hook-Level (per file operation):
438
- Activity open() → hook_governance → API (started) → Allow/Block
439
- Activity read/write() → hook_governance → API (started) → Allow/Block
440
- → hook_governance → API (completed) → Allow/Block
441
- Activity close() → hook_governance → API (completed) → Allow/Block
442
-
443
- Hook-Level (per DB query):
444
- Activity DB Call → db_governance_hooks → hook_governance → API (started) → Allow/Block
445
- → Query executes → hook_governance → API (completed)
446
-
447
- Hook-Level (per @traced function):
448
- @traced call → hook_governance → API (started) → Allow/Block
449
- → Function runs → hook_governance → API (completed)
479
+ Workflow / Activity lifecycle Temporal SDK interceptors → OpenBox Core API
480
+
481
+ Returns Verdict
482
+
483
+ (ALLOW, BLOCK, HALT, REQUIRE_APPROVAL)
484
+
485
+ Hook-Level (per HTTP request / DB query / file op / @traced fn):
486
+ Operationbase-SDK instrumentationOpenBox Core API (started) → Allow/Block
487
+ Operation runs OpenBox Core API (completed) → Allow/Block
450
488
  ```
451
489
 
452
- **Module responsibilities:**
453
- - `otel_setup.py` OTel instrumentation, hooks, TracedFile wrapper
454
- - `hook_governance.py` Shared governance evaluator (payload building, API calls, verdict handling)
455
- - `db_governance_hooks.py` — Per-library DB governance wrappers (wrapt, OTel hooks, SQLAlchemy events)
456
- - `span_processor.py` — Span buffering, activity context tracking
457
- - `activity_interceptor.py` — Activity lifecycle governance events
490
+ The Temporal worker/plugin builds and owns an `openbox_core` runtime that installs
491
+ all hook instrumentation. Hook payload building, evaluation, and enforcement are
492
+ owned entirely by the base SDK; the Temporal SDK maps the resulting verdicts onto
493
+ Temporal-native effects.
458
494
 
459
- ---
495
+ **Responsibility split:**
460
496
 
461
- ## Advanced Usage
497
+ Base SDK (`openbox_core`):
498
+ - HTTP / DB / file / function hook instrumentation
499
+ - Hook payload shape, evaluation, and enforcement
462
500
 
463
- For manual control, import individual components:
501
+ Temporal SDK (this package):
502
+ - `workflow_interceptor.py` / `activity_interceptor.py` — workflow/activity/signal lifecycle governance events (via workflow-safe activities)
503
+ - `governance_state.py` — `TemporalGovernanceState`: signal-verdict bridging, HITL pending-approval markers, completed-hook stop/halt propagation
504
+ - `core_adapter.py` — builds the base runtime (`create_core_runtime`) and maps base verdicts to Temporal effects (`TemporalFrameworkAdapter`)
464
505
 
465
- ```python
466
- from openbox import (
467
- initialize,
468
- WorkflowSpanProcessor,
469
- GovernanceInterceptor,
470
- GovernanceConfig,
471
- )
472
- from openbox.otel_setup import setup_opentelemetry_for_governance
473
- from openbox.activity_interceptor import ActivityGovernanceInterceptor
474
- from openbox.activities import send_governance_event
475
- from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
506
+ ---
476
507
 
477
- # 1. Initialize SDK
478
- initialize(api_url="http://localhost:8086", api_key="obx_test_key_1")
508
+ ## Advanced Usage
479
509
 
480
- # 2. Create span processor
481
- span_processor = WorkflowSpanProcessor(
482
- ignored_url_prefixes=["http://localhost:8086"]
483
- )
510
+ The supported integration is a single call. Both `create_openbox_worker(...)` and
511
+ `OpenBoxPlugin(...)` build and own the base `openbox_core` runtime, install all
512
+ hook instrumentation, register the governance interceptors, and wire the
513
+ `send_governance_event` activity for you — there is no manual OpenTelemetry or
514
+ span-processor setup to perform.
484
515
 
485
- # 3. Setup OTel instrumentation (governance always enabled)
486
- setup_opentelemetry_for_governance(
487
- span_processor,
488
- api_url="http://localhost:8086",
489
- api_key="obx_test_key_1",
490
- sqlalchemy_engine=engine, # optional: instrument pre-existing engine
491
- )
516
+ Worker factory:
492
517
 
493
- # 4. Create governance config
494
- config = GovernanceConfig(
495
- on_api_error="fail_closed",
496
- api_timeout=30.0,
497
- )
518
+ ```python
519
+ import os
520
+ from openbox import create_openbox_worker
498
521
 
499
- # 5. Create interceptors
500
- workflow_interceptor = GovernanceInterceptor(
501
- api_url="http://localhost:8086",
502
- api_key="obx_test_key_1",
503
- span_processor=span_processor,
504
- config=config,
522
+ worker = create_openbox_worker(
523
+ client=client,
524
+ task_queue="my-task-queue",
525
+ workflows=[MyWorkflow],
526
+ activities=[my_activity],
527
+ openbox_url=os.getenv("OPENBOX_URL"),
528
+ openbox_api_key=os.getenv("OPENBOX_API_KEY"),
529
+ governance_policy="fail_closed",
530
+ governance_timeout=30.0,
531
+ instrument_databases=True,
532
+ instrument_file_io=True,
505
533
  )
506
534
 
507
- activity_interceptor = ActivityGovernanceInterceptor(
508
- api_url="http://localhost:8086",
509
- api_key="obx_test_key_1",
510
- span_processor=span_processor,
511
- config=config,
512
- )
535
+ await worker.run()
536
+ ```
537
+
538
+ Plugin (Temporal >= 1.23.0) — attach to a plain `Worker`:
513
539
 
514
- # 6. Create worker
540
+ ```python
515
541
  from temporalio.worker import Worker
542
+ from openbox.plugin import OpenBoxPlugin
543
+
516
544
  worker = Worker(
517
- client=client,
545
+ client,
518
546
  task_queue="my-task-queue",
519
547
  workflows=[MyWorkflow],
520
- activities=[my_activity, send_governance_event],
521
- interceptors=[workflow_interceptor, activity_interceptor],
548
+ activities=[my_activity],
549
+ plugins=[OpenBoxPlugin(
550
+ openbox_url=os.getenv("OPENBOX_URL"),
551
+ openbox_api_key=os.getenv("OPENBOX_API_KEY"),
552
+ governance_policy="fail_closed",
553
+ )],
522
554
  )
523
555
  ```
524
556
 
557
+ Both entry points accept the same governance, HITL, signing, and instrumentation
558
+ options (see [Configuration](./docs/configuration.md)).
559
+
525
560
  ---
526
561
 
527
562
  ## Documentation
@@ -536,13 +571,17 @@ worker = Worker(
536
571
 
537
572
  ## Testing
538
573
 
539
- The SDK includes comprehensive test coverage with 15 test files:
574
+ The SDK includes comprehensive test coverage under `tests/`:
540
575
 
541
576
  ```bash
542
577
  pytest tests/
543
578
  ```
544
579
 
545
- Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_plugin.py`, `test_plugin_integration.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
580
+ Coverage spans the worker factory and plugin, workflow/activity/signal
581
+ interceptors, HITL approval flow, request signing, public-API compatibility, and
582
+ parity with the base SDK's hook governance (`test_temporal_hook_parity.py`,
583
+ `test_core_conformance_suite.py`). Hook instrumentation internals (HTTP/DB/file
584
+ payload shape) are verified in the base SDK's own conformance suite.
546
585
 
547
586
  ---
548
587