openbox-temporal-sdk-python 1.1.2__tar.gz → 1.2.1__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.
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/.gitignore +1 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/CHANGELOG.md +122 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/PKG-INFO +121 -109
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/README.md +119 -108
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/changelog-hook-level-governance.md +9 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/code-standards.md +29 -48
- openbox_temporal_sdk_python-1.2.1/docs/codebase-summary.md +632 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/configuration.md +45 -3
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/project-overview-pdr.md +42 -30
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/system-architecture.md +218 -511
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/docs/temporal-plugin-integration-guide.md +15 -11
- openbox_temporal_sdk_python-1.2.1/openbox/__init__.py +125 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/activities.py +44 -16
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/activity_interceptor.py +123 -199
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/client.py +32 -8
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/config.py +166 -49
- openbox_temporal_sdk_python-1.2.1/openbox/core_adapter.py +279 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/errors.py +63 -33
- openbox_temporal_sdk_python-1.2.1/openbox/governance_state.py +101 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/hitl.py +8 -12
- openbox_temporal_sdk_python-1.2.1/openbox/multi_agent.py +136 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/plugin.py +34 -36
- openbox_temporal_sdk_python-1.2.1/openbox/request_signing.py +89 -0
- openbox_temporal_sdk_python-1.2.1/openbox/tracing.py +59 -0
- openbox_temporal_sdk_python-1.2.1/openbox/types.py +79 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/verdict_handler.py +0 -1
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/worker.py +35 -36
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/workflow_interceptor.py +68 -61
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/pyproject.toml +6 -1
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/sonar-project.properties +1 -4
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/__init__.py +0 -1
- openbox_temporal_sdk_python-1.2.1/tests/conftest.py +56 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_activities.py +7 -30
- openbox_temporal_sdk_python-1.2.1/tests/test_activity_interceptor.py +1869 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_approval_action_precedence.py +102 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_base_sdk_signing_parity.py +102 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_config.py +0 -31
- openbox_temporal_sdk_python-1.2.1/tests/test_core_conformance_suite.py +261 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_multi_agent.py +129 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_no_import_machinery_in_governed_open_paths.py +73 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_plugin.py +70 -46
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_plugin_integration.py +12 -18
- openbox_temporal_sdk_python-1.2.1/tests/test_public_api_compatibility.py +154 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_request_signing.py +280 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_signing_config.py +242 -0
- openbox_temporal_sdk_python-1.2.1/tests/test_temporal_hook_parity.py +129 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_types.py +0 -98
- openbox_temporal_sdk_python-1.2.1/tests/test_worker.py +1099 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/tests/test_workflow_interceptor.py +219 -79
- openbox_temporal_sdk_python-1.2.1/tests/test_workflow_sandbox_import_safety.py +67 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/uv.lock +268 -1
- openbox_temporal_sdk_python-1.1.2/docs/codebase-summary.md +0 -786
- openbox_temporal_sdk_python-1.1.2/openbox/__init__.py +0 -181
- openbox_temporal_sdk_python-1.1.2/openbox/context_propagation.py +0 -56
- openbox_temporal_sdk_python-1.1.2/openbox/db_governance_hooks.py +0 -776
- openbox_temporal_sdk_python-1.1.2/openbox/file_governance_hooks.py +0 -396
- openbox_temporal_sdk_python-1.1.2/openbox/hook_governance.py +0 -437
- openbox_temporal_sdk_python-1.1.2/openbox/http_governance_hooks.py +0 -859
- openbox_temporal_sdk_python-1.1.2/openbox/otel_setup.py +0 -474
- openbox_temporal_sdk_python-1.1.2/openbox/span_processor.py +0 -240
- openbox_temporal_sdk_python-1.1.2/openbox/tracing.py +0 -297
- openbox_temporal_sdk_python-1.1.2/openbox/types.py +0 -188
- openbox_temporal_sdk_python-1.1.2/tests/conftest.py +0 -96
- openbox_temporal_sdk_python-1.1.2/tests/test_activity_interceptor.py +0 -2572
- openbox_temporal_sdk_python-1.1.2/tests/test_db_governance_hooks.py +0 -886
- openbox_temporal_sdk_python-1.1.2/tests/test_file_governance_hooks.py +0 -784
- openbox_temporal_sdk_python-1.1.2/tests/test_http_body_truncation.py +0 -75
- openbox_temporal_sdk_python-1.1.2/tests/test_otel_setup.py +0 -2255
- openbox_temporal_sdk_python-1.1.2/tests/test_psycopg2_hooks_verify.py +0 -60
- openbox_temporal_sdk_python-1.1.2/tests/test_span_processor.py +0 -706
- openbox_temporal_sdk_python-1.1.2/tests/test_tracing.py +0 -1288
- openbox_temporal_sdk_python-1.1.2/tests/test_worker.py +0 -1919
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/.github/workflows/publish.yml +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/.github/workflows/sonarqube.yaml +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/.python-version +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/.repomixignore +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/LICENSE +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/openbox/py.typed +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/release-manifest.json +0 -0
- {openbox_temporal_sdk_python-1.1.2 → openbox_temporal_sdk_python-1.2.1}/repomix-output.xml +0 -0
|
@@ -2,6 +2,128 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to OpenBox SDK for Temporal Workflows.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.2.1] - 2026-07-21
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Resolve `openbox-sdk-python` from PyPI.** The dependency now installs from the
|
|
12
|
+
published package (floor raised to `>=1.0.1`) instead of a local editable checkout,
|
|
13
|
+
so `pip install openbox-temporal-sdk-python` works without a sibling source tree.
|
|
14
|
+
|
|
15
|
+
## [1.2.0] - 2026-07-20
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Depends on `openbox-sdk-python>=0.2.0`** (import package `openbox_core`) — the shared
|
|
20
|
+
base SDK owning contracts, the always-strict gate, identity/signing, the evaluate
|
|
21
|
+
client, context runtime, Core `SpanData` wire serialization, generic instrumentation,
|
|
22
|
+
and the conformance kit. The `0.2.0` floor adds Redis/MongoDB/urllib3/urllib
|
|
23
|
+
instrumentation in the base runtime so no hook coverage is lost in the flip.
|
|
24
|
+
- **Hook governance is now owned entirely by the base runtime.** The worker/plugin
|
|
25
|
+
build and own an `openbox_core` runtime and call `install_instrumentation()`;
|
|
26
|
+
HTTP/DB/file/function hooks, payload shape, evaluation, and enforcement all live in
|
|
27
|
+
`openbox_core`. LangGraph and Temporal now emit one identical flat hook interface.
|
|
28
|
+
- `request_signing` now shims over `openbox_core.identity`. Signed request bytes are
|
|
29
|
+
UNCHANGED — gated byte-for-byte by a golden fixture generated from the pre-migration
|
|
30
|
+
signer (`tests/test_base_sdk_signing_parity.py`).
|
|
31
|
+
- `Verdict`, `GuardrailsCheckResult`, and `WorkflowEventType` re-export the shared
|
|
32
|
+
contracts; `GovernanceVerdictResponse` subclasses the shared `EvaluationResult`
|
|
33
|
+
(same public surface, plus `fallback_used`/`diagnostics`/`raw`/`approval_expiration_time`).
|
|
34
|
+
- Workflow lifecycle events route through shared `EventEnvelope` factories
|
|
35
|
+
(sandbox-safe pure contracts; wire payloads unchanged).
|
|
36
|
+
- Activity execution binds the shared `ActivityContext` with a guaranteed
|
|
37
|
+
try/finally reset — governance context can no longer leak when an activity raises.
|
|
38
|
+
|
|
39
|
+
### Behavior changes (regression-gated)
|
|
40
|
+
|
|
41
|
+
- **Approval decision precedence:** approval poll responses parse via the shared
|
|
42
|
+
`ApprovalResult`; `action` now outranks `verdict` when both are present
|
|
43
|
+
(previously verdict-first), and a response with NEITHER field stays PENDING
|
|
44
|
+
(previously implicit ALLOW). Pinned by `tests/test_approval_action_precedence.py`.
|
|
45
|
+
|
|
46
|
+
### Known behavior differences in the core runtime
|
|
47
|
+
|
|
48
|
+
- **Completed-hook semantics:** core completed hooks never raise to the caller —
|
|
49
|
+
stop verdicts mark FUTURE execution blocked (abort/halt flags). Some legacy
|
|
50
|
+
completed hooks could raise after the operation ran (HTTP response hook, file
|
|
51
|
+
close); raising post-hoc cannot undo the operation, so the core model drops it
|
|
52
|
+
by design.
|
|
53
|
+
- **Fail-closed shaping:** core maps started-hook evaluation failures under
|
|
54
|
+
`on_api_error="fail_closed"` (and started-hook contract errors, always) to a
|
|
55
|
+
non-retryable `GovernanceHalt` via the adapter — same terminal effect as
|
|
56
|
+
legacy's HALT-shaped `GovernanceBlockedError`, different exception chain.
|
|
57
|
+
- **Redaction point for `activity_input`:** the core `ActivityContext` receives
|
|
58
|
+
the post-redaction input (what actually ran); the legacy buffer stored the
|
|
59
|
+
pre-redaction value. Core hook payloads therefore never leak redacted fields.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- `openbox.governance_state.TemporalGovernanceState` — run-scoped state carrying the
|
|
64
|
+
Temporal effects that must survive past a base hook callback: signal BLOCK/HALT
|
|
65
|
+
verdicts that fail the next activity, HITL pending-approval retry markers, and the
|
|
66
|
+
completed-hook stop bridge (completed BLOCK skips the duplicate completed event;
|
|
67
|
+
completed HALT reaches the terminate path). Consumed on every activity exit path.
|
|
68
|
+
- `openbox.core_adapter` — `TemporalFrameworkAdapter` (maps base-SDK verdicts to
|
|
69
|
+
native `ApplicationError` types and the retry-based HITL loop; REQUIRE_APPROVAL marks
|
|
70
|
+
the pending-approval retry marker and degrades to a non-retryable block when HITL is
|
|
71
|
+
disabled/skipped; completed BLOCK/HALT records run-scoped stop state with the resolved
|
|
72
|
+
`ActivityContext`), and `create_core_runtime(...)` which builds and owns the base
|
|
73
|
+
runtime the worker/plugin install. `core_activity_scope` is the sole hook-context
|
|
74
|
+
bridge (guaranteed try/finally reset + trace registration).
|
|
75
|
+
- Redis, MongoDB, urllib3, and urllib governance now flow through the base runtime.
|
|
76
|
+
- Gates: workflow-sandbox import-safety, base-SDK conformance suite driven by the
|
|
77
|
+
Temporal adapter, public-API compatibility suite, and a deterministic
|
|
78
|
+
`llm_completion` hook-parity test (flat interface via the Temporal runtime, no live
|
|
79
|
+
OpenAI credentials).
|
|
80
|
+
|
|
81
|
+
### Removed
|
|
82
|
+
|
|
83
|
+
- `WorkflowSpanProcessor`, `WorkflowSpanBuffer`, and the `setup_opentelemetry_for_governance`
|
|
84
|
+
entry point (public exports). Governance instrumentation is installed by the base
|
|
85
|
+
runtime; there is no Temporal-local OpenTelemetry setup to call.
|
|
86
|
+
- Temporal-local hook modules `otel_setup`, `hook_governance`, `http_governance_hooks`,
|
|
87
|
+
`db_governance_hooks`, `file_governance_hooks`, `span_processor`, and the dead
|
|
88
|
+
`context_propagation` helper (its ContextVar-to-executor propagation lives in the base
|
|
89
|
+
runtime). Their payload-shape/instrumentation coverage moved to the base SDK suite.
|
|
90
|
+
- `openbox.tracing.traced` now wraps `openbox_core.instrumentation.function.governed`;
|
|
91
|
+
`create_span` remains a plain span helper.
|
|
92
|
+
|
|
93
|
+
### Fixed (legacy file instrumentation — pre-existing upstream)
|
|
94
|
+
|
|
95
|
+
- **Runtime RecursionError under file instrumentation:** governance evaluation
|
|
96
|
+
itself opens files (httpx/ssl, package-metadata scans via importlib_metadata/
|
|
97
|
+
zipp) — governing those opens re-evaluated recursively until RecursionError.
|
|
98
|
+
traced_open now (1) passes through any open performed on a thread already
|
|
99
|
+
inside file-governance work (re-entrancy guard) and (2) bypasses
|
|
100
|
+
interpreter-owned trees (venv, stdlib, site-packages — sys.prefix/base_prefix
|
|
101
|
+
+ sysconfig paths): those reads are Python machinery, not application data
|
|
102
|
+
access. Application paths (./.env, data files, temp files) remain governed.
|
|
103
|
+
Same guard mirrored in the base SDK's file instrumentation.
|
|
104
|
+
|
|
105
|
+
### Fixed (HTTP hook payloads — pre-existing upstream)
|
|
106
|
+
|
|
107
|
+
- **Mangled method on httpx spans:** OTel's httpx instrumentation passes the
|
|
108
|
+
method as BYTES; `str(b"POST")` shipped `http_method: "b'POST'"` on every
|
|
109
|
+
httpx started span. Methods now decode bytes-safely at all client paths.
|
|
110
|
+
- **Credential leak in governance payloads:** raw request/response headers
|
|
111
|
+
(`authorization`, `cookie`, `set-cookie`, `x-api-key`, …) were sent to Core
|
|
112
|
+
verbatim — live API keys landed in Core logs. All header dicts are now
|
|
113
|
+
redacted at the span-data builder choke point. Same redaction added to the
|
|
114
|
+
base SDK instrumentation. **Rotate any API keys that appeared in payloads.**
|
|
115
|
+
|
|
116
|
+
### Migration notes
|
|
117
|
+
|
|
118
|
+
- Base-runtime instrumentation is the sole hook governance path
|
|
119
|
+
and the legacy in-repo hook stack is removed. Approval-retry, signal BLOCK/HALT, and
|
|
120
|
+
completed-hook BLOCK/HALT behavior are preserved (run-scoped, cleaned after use).
|
|
121
|
+
- Dropped coverage vs the legacy stack: direct raw-driver DB queries (psycopg2/mysql/
|
|
122
|
+
pymysql/sqlite3 used WITHOUT SQLAlchemy). The base runtime governs SQLAlchemy (all
|
|
123
|
+
backends), asyncpg, Redis, and MongoDB; activating raw dbapi driver instrumentors
|
|
124
|
+
interferes with SQLAlchemy's own dialect queries, so raw-driver-only governance is a
|
|
125
|
+
base-SDK follow-up.
|
|
126
|
+
|
|
5
127
|
## [1.1.2] - 2026-04-22
|
|
6
128
|
|
|
7
129
|
### Security
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openbox-temporal-sdk-python
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
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>=1.0.1
|
|
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
|
-
-
|
|
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
|
|
@@ -199,8 +201,6 @@ worker = create_openbox_worker(
|
|
|
199
201
|
|
|
200
202
|
# Database instrumentation
|
|
201
203
|
instrument_databases=True,
|
|
202
|
-
db_libraries={"psycopg2", "sqlalchemy"}, # None = all available
|
|
203
|
-
sqlalchemy_engine=engine, # pass pre-existing engine for query capture
|
|
204
204
|
|
|
205
205
|
# File I/O instrumentation
|
|
206
206
|
instrument_file_io=False, # disabled by default
|
|
@@ -247,6 +247,48 @@ OpenBox Core returns a verdict indicating what action the SDK should take.
|
|
|
247
247
|
| SignalReceived | Signal received | workflow_id, signal_name, signal_args |
|
|
248
248
|
| ActivityStarted | Activity begins | activity_id, activity_type, activity_input |
|
|
249
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.
|
|
250
292
|
|
|
251
293
|
---
|
|
252
294
|
|
|
@@ -279,7 +321,7 @@ OpenBox Core can validate and redact sensitive data before/after activity execut
|
|
|
279
321
|
|
|
280
322
|
## Error Handling
|
|
281
323
|
|
|
282
|
-
Configure error policy via `
|
|
324
|
+
Configure error policy via the `governance_policy` parameter (`"fail_open"` / `"fail_closed"`):
|
|
283
325
|
|
|
284
326
|
| Policy | Behavior |
|
|
285
327
|
|--------|----------|
|
|
@@ -307,15 +349,12 @@ Configure error policy via `on_api_error` (constants available as `hook_governan
|
|
|
307
349
|
- `redis` — native OTel `request_hook`/`response_hook`
|
|
308
350
|
- `sqlalchemy` — `before/after_cursor_execute` event listeners
|
|
309
351
|
|
|
310
|
-
**SQLAlchemy Note:**
|
|
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.
|
|
311
353
|
|
|
312
354
|
```python
|
|
313
|
-
from db.engine import engine
|
|
314
|
-
|
|
315
355
|
worker = create_openbox_worker(
|
|
316
356
|
...,
|
|
317
|
-
|
|
318
|
-
sqlalchemy_engine=engine,
|
|
357
|
+
instrument_databases=True, # default; installs all available DB instrumentors
|
|
319
358
|
)
|
|
320
359
|
```
|
|
321
360
|
|
|
@@ -411,7 +450,7 @@ Every database operation is evaluated at `started` (pre-query, can block) and `c
|
|
|
411
450
|
|
|
412
451
|
### Function Tracing
|
|
413
452
|
|
|
414
|
-
Functions decorated with `@traced` are automatically governed when `
|
|
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:
|
|
415
454
|
|
|
416
455
|
| Stage | Trigger | Data Available |
|
|
417
456
|
|-------|---------|----------------|
|
|
@@ -420,15 +459,13 @@ Functions decorated with `@traced` are automatically governed when `hook_governa
|
|
|
420
459
|
|
|
421
460
|
**How it works (Function Tracing):**
|
|
422
461
|
|
|
423
|
-
1. `@traced`
|
|
424
|
-
2. →
|
|
425
|
-
3. If verdict is BLOCK/HALT →
|
|
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
|
|
426
465
|
4. Function executes normally
|
|
427
|
-
5. →
|
|
428
|
-
6. If verdict is BLOCK/HALT →
|
|
429
|
-
7. When
|
|
430
|
-
|
|
431
|
-
**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 installed → transparent passthrough, zero governance calls
|
|
432
469
|
|
|
433
470
|
---
|
|
434
471
|
|
|
@@ -439,116 +476,87 @@ See [System Architecture](./docs/system-architecture.md) for detailed component
|
|
|
439
476
|
**High-Level Flow:**
|
|
440
477
|
|
|
441
478
|
```
|
|
442
|
-
Workflow/Activity →
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
Hook-Level (per HTTP request):
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
Hook-Level (per file operation):
|
|
453
|
-
Activity open() → hook_governance → API (started) → Allow/Block
|
|
454
|
-
Activity read/write() → hook_governance → API (started) → Allow/Block
|
|
455
|
-
→ hook_governance → API (completed) → Allow/Block
|
|
456
|
-
Activity close() → hook_governance → API (completed) → Allow/Block
|
|
457
|
-
|
|
458
|
-
Hook-Level (per DB query):
|
|
459
|
-
Activity DB Call → db_governance_hooks → hook_governance → API (started) → Allow/Block
|
|
460
|
-
→ Query executes → hook_governance → API (completed)
|
|
461
|
-
|
|
462
|
-
Hook-Level (per @traced function):
|
|
463
|
-
@traced call → hook_governance → API (started) → Allow/Block
|
|
464
|
-
→ 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
|
+
Operation → base-SDK instrumentation → OpenBox Core API (started) → Allow/Block
|
|
487
|
+
→ Operation runs → OpenBox Core API (completed) → Allow/Block
|
|
465
488
|
```
|
|
466
489
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
-
|
|
471
|
-
- `span_processor.py` — Span buffering, activity context tracking
|
|
472
|
-
- `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.
|
|
473
494
|
|
|
474
|
-
|
|
495
|
+
**Responsibility split:**
|
|
475
496
|
|
|
476
|
-
|
|
497
|
+
Base SDK (`openbox_core`):
|
|
498
|
+
- HTTP / DB / file / function hook instrumentation
|
|
499
|
+
- Hook payload shape, evaluation, and enforcement
|
|
477
500
|
|
|
478
|
-
|
|
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`)
|
|
479
505
|
|
|
480
|
-
|
|
481
|
-
from openbox import (
|
|
482
|
-
initialize,
|
|
483
|
-
WorkflowSpanProcessor,
|
|
484
|
-
GovernanceInterceptor,
|
|
485
|
-
GovernanceConfig,
|
|
486
|
-
)
|
|
487
|
-
from openbox.otel_setup import setup_opentelemetry_for_governance
|
|
488
|
-
from openbox.activity_interceptor import ActivityGovernanceInterceptor
|
|
489
|
-
from openbox.activities import build_governance_activities
|
|
490
|
-
from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
|
|
506
|
+
---
|
|
491
507
|
|
|
492
|
-
|
|
493
|
-
initialize(api_url="http://localhost:8086", api_key="obx_test_key_1")
|
|
508
|
+
## Advanced Usage
|
|
494
509
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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.
|
|
499
515
|
|
|
500
|
-
|
|
501
|
-
setup_opentelemetry_for_governance(
|
|
502
|
-
span_processor,
|
|
503
|
-
api_url="http://localhost:8086",
|
|
504
|
-
api_key="obx_test_key_1",
|
|
505
|
-
sqlalchemy_engine=engine, # optional: instrument pre-existing engine
|
|
506
|
-
)
|
|
516
|
+
Worker factory:
|
|
507
517
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
api_timeout=30.0,
|
|
512
|
-
)
|
|
518
|
+
```python
|
|
519
|
+
import os
|
|
520
|
+
from openbox import create_openbox_worker
|
|
513
521
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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,
|
|
520
533
|
)
|
|
521
534
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
api_key="obx_test_key_1",
|
|
525
|
-
span_processor=span_processor,
|
|
526
|
-
config=config,
|
|
527
|
-
)
|
|
535
|
+
await worker.run()
|
|
536
|
+
```
|
|
528
537
|
|
|
529
|
-
|
|
530
|
-
# so they never flow through workflow history).
|
|
531
|
-
governance_activities = build_governance_activities(
|
|
532
|
-
api_url="http://localhost:8086",
|
|
533
|
-
api_key="obx_test_key_1",
|
|
534
|
-
)
|
|
538
|
+
Plugin (Temporal >= 1.23.0) — attach to a plain `Worker`:
|
|
535
539
|
|
|
536
|
-
|
|
540
|
+
```python
|
|
537
541
|
from temporalio.worker import Worker
|
|
538
|
-
from
|
|
542
|
+
from openbox.plugin import OpenBoxPlugin
|
|
543
|
+
|
|
539
544
|
worker = Worker(
|
|
540
|
-
client
|
|
545
|
+
client,
|
|
541
546
|
task_queue="my-task-queue",
|
|
542
547
|
workflows=[MyWorkflow],
|
|
543
|
-
activities=[my_activity
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
],
|
|
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
|
+
)],
|
|
549
554
|
)
|
|
550
555
|
```
|
|
551
556
|
|
|
557
|
+
Both entry points accept the same governance, HITL, signing, and instrumentation
|
|
558
|
+
options (see [Configuration](./docs/configuration.md)).
|
|
559
|
+
|
|
552
560
|
---
|
|
553
561
|
|
|
554
562
|
## Documentation
|
|
@@ -563,13 +571,17 @@ worker = Worker(
|
|
|
563
571
|
|
|
564
572
|
## Testing
|
|
565
573
|
|
|
566
|
-
The SDK includes comprehensive test coverage
|
|
574
|
+
The SDK includes comprehensive test coverage under `tests/`:
|
|
567
575
|
|
|
568
576
|
```bash
|
|
569
577
|
pytest tests/
|
|
570
578
|
```
|
|
571
579
|
|
|
572
|
-
|
|
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.
|
|
573
585
|
|
|
574
586
|
---
|
|
575
587
|
|