agenthooksprotocol 0.5.0__tar.gz → 0.7.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.
- {agenthooksprotocol-0.5.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.7.0}/PKG-INFO +36 -13
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/README.md +35 -12
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/pyproject.toml +1 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_grants.py +250 -144
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_hooks.py +26 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_hooks.py +3 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/__init__.py +282 -49
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/event.py +2 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/tool.py +8 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/ahp-codegen.lock.json +11 -11
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/capability.py +17 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/catalogue.py +36 -22
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/event.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/generated.py +327 -295
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/interop.py +6 -2
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/observation_chain.py +2 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/schemas.json +150 -16
- agenthooksprotocol-0.7.0/src/agenthooksprotocol/server/__init__.py +10 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_handlers.py +46 -2
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/hooks.py +59 -11
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/stdio.py +2 -2
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/tool.py +4 -0
- agenthooksprotocol-0.7.0/src/agenthooksprotocol/transports/stdio.py +297 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0/src/agenthooksprotocol.egg-info}/PKG-INFO +36 -13
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/SOURCES.txt +5 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_catalogue.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_compaction_wire.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_consumer_outcomes.py +4 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content_receipts.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_event_types.py +2 -0
- agenthooksprotocol-0.7.0/tests/test_harness_identity.py +125 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_inline_messages.py +7 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_integration.py +4 -1
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_integration_edges.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_interop.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle.py +2 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_observation.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content_hooks.py +8 -3
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_content.py +3 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_hooks.py +44 -5
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_server.py +2 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_schema_corrections.py +1 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_sdk_unification.py +12 -1
- agenthooksprotocol-0.7.0/tests/test_server_catch_all.py +101 -0
- agenthooksprotocol-0.7.0/tests/test_server_error_codes.py +171 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_session_manifest.py +16 -5
- agenthooksprotocol-0.7.0/tests/test_stdio_env.py +155 -0
- agenthooksprotocol-0.7.0/tests/test_stdio_transport.py +132 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_subscribes.py +15 -3
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_typed_capabilities.py +65 -8
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_typed_contract_regressions.py +2 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_upload_planning.py +3 -0
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/server/__init__.py +0 -8
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/transports/stdio.py +0 -92
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/LICENSE +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/setup.cfg +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/__init__.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_boundaries.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_diagnostics.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_elicitation.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_host_metadata.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/capability.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/effect.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/registration.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/subscription.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/transport.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_outcomes.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/attachment.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/auth.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/candidate.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/codec.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/compaction.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/contract.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/diagnostics.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/effect.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/elicitation.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/__init__.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/pydantic.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_client.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_server.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lineage.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/path.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/permission.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/py.typed +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/registration.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/response.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/runtime.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_admission.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_framing.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_json.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_slots.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/asgi.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/attachments.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/state.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/subscription.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transport.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/__init__.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/http.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/wire.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_attachments.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_codec.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_compaction.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content_lifetime.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_correlated_core.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_declared_contracts.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_elicitation_adapter.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_fixture_public.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_handwritten_surface.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_host_metadata.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle_auth.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_observation_chain_transport.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_package.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_contract_surface.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_examples.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_registration_auth.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_safety.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_shared_materialization.py +0 -0
- {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_structural_facade.py +0 -0
{agenthooksprotocol-0.5.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.7.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenthooksprotocol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official non-normative Python SDK for the Agent Hooks Protocol
|
|
5
5
|
Author: Agent Hooks Protocol maintainers
|
|
6
6
|
License:
|
|
@@ -250,13 +250,18 @@ with open("hooks.json") as file:
|
|
|
250
250
|
config = json.load(file)
|
|
251
251
|
|
|
252
252
|
async def review(existing_arguments):
|
|
253
|
-
async with Hooks(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
async with Hooks(
|
|
254
|
+
config,
|
|
255
|
+
source="urn:example:harness",
|
|
256
|
+
harness=event.Harness(name="example", version="1"),
|
|
257
|
+
capabilities=capability.HostCapabilities(
|
|
258
|
+
tool_before=capability.ToolBeforeCapabilities(
|
|
259
|
+
intercept=capability.ToolBeforeIntercept(
|
|
260
|
+
allow=True, deny=True, modify_input=capability.Modify.REPLACE,
|
|
261
|
+
),
|
|
257
262
|
),
|
|
258
263
|
),
|
|
259
|
-
)
|
|
264
|
+
) as hooks:
|
|
260
265
|
result = await hooks.tool_before(event.ToolBeforeInput(
|
|
261
266
|
call_id="call-1", name="read_file", input=existing_arguments,
|
|
262
267
|
path="native", origin="native",
|
|
@@ -264,16 +269,32 @@ async def review(existing_arguments):
|
|
|
264
269
|
return result # Host validation and authorization still precede execution.
|
|
265
270
|
```
|
|
266
271
|
|
|
267
|
-
Generated `Input` objects are distinct from canonical wire events. They flatten only protocol-owned wrappers, never application arguments, and `to_wire()` performs the shared mapping. `path` and `origin` remain separate required facts. Optional host IDs, timestamps, parent IDs, and other facts are retained; `event_id=` overrides an input ID. The SDK supplies type/source and the configured session manifest. Low-level `dispatch` accepts canonical dictionaries and an `event_name: EventType`. `from agenthooksprotocol.event import EventType` exposes the catalogue-derived literal type shared by `dispatch`, `subscribes`, and contextual response parsing. Canonical name literals are accepted directly; static checking rejects dynamic strings, unknown names, and selectors in these event-name positions. Registration selectors remain separate and support their documented wildcard matching.
|
|
272
|
+
Generated `Input` objects are distinct from canonical wire events. They flatten only protocol-owned wrappers, never application arguments, and `to_wire()` performs the shared mapping. `path` and `origin` remain separate required facts. Optional host IDs, timestamps, parent IDs, and other facts are retained; `event_id=` overrides an input ID. The SDK supplies type/source, the configured harness identity, and the configured session manifest. Full canonical wire events must include a matching `harness` identity. Low-level `dispatch` accepts canonical dictionaries and an `event_name: EventType`. `from agenthooksprotocol.event import EventType` exposes the catalogue-derived literal type shared by `dispatch`, `subscribes`, and contextual response parsing. Canonical name literals are accepted directly; static checking rejects dynamic strings, unknown names, and selectors in these event-name positions. Registration selectors remain separate and support their documented wildcard matching.
|
|
268
273
|
|
|
269
|
-
|
|
274
|
+
Host capabilities are frozen, keyword-only dataclasses; there are no builder functions. Each event has its own `<Event>Capabilities` declaration. Events that support interception take `intercept=<Event>Intercept(...)` grants, and every declaration takes `observe`, which defaults to `False`, so to advertise both modes you must pass `observe=True` explicitly. Observation-only events such as `SessionEndCapabilities(observe=True)` accept only the flag. `modify_input=capability.Modify.REPLACE` supplies both the effect grant and its operation block, and form/URL elicitation grants are explicit (`elicitation=capability.Elicitation.FORM`, `URL`, or `FORM_AND_URL`). Interception without a control grant and declarations with neither mode are rejected at construction; boundary compatibility is validated before delivery. Omission never implies permission. Per-call `capabilities=` only narrows configured grants.
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from agenthooksprotocol import capability
|
|
278
|
+
|
|
279
|
+
capabilities = capability.HostCapabilities(
|
|
280
|
+
tool_before=capability.ToolBeforeCapabilities(
|
|
281
|
+
intercept=capability.ToolBeforeIntercept(
|
|
282
|
+
allow=True, deny=True, modify_input=capability.Modify.REPLACE
|
|
283
|
+
),
|
|
284
|
+
observe=True, # intercept and observe
|
|
285
|
+
),
|
|
286
|
+
session_end=capability.SessionEndCapabilities(observe=True), # observe only
|
|
287
|
+
)
|
|
288
|
+
assert capabilities.to_wire()["tool.before"]["modes"] == ["intercept", "observe"]
|
|
289
|
+
assert capabilities.to_wire()["session.end"] == {"modes": ["observe"]}
|
|
290
|
+
```
|
|
270
291
|
|
|
271
292
|
Runnable, statically checked consumer programs live in [`examples/`](examples):
|
|
272
293
|
|
|
273
294
|
- `value_codecs.py`: independent argument/result/provenance codecs, admission-time invariants, and a form schema derived from its answer type.
|
|
274
295
|
- `typed_tool.py`: JSON registration, generated tool input, typed grants, initial state, real in-process ASGI handler, borrowed HTTP client, and explicit application decoding.
|
|
275
296
|
- `common_cases.py`: rewrite/allow, deny, invalid application shape, host policy, occurrence narrowing, native initial state, and fail-open/fail-closed reports. The host operation is an in-memory execution recorder, not a shell.
|
|
276
|
-
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public
|
|
297
|
+
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public `hooks.Handlers`, denial, and child cleanup.
|
|
277
298
|
- `upload.py`: streamed binary upload, metadata no-read behavior, independent upload credentials, verified upload receipt, and caller-owned immutable storage.
|
|
278
299
|
|
|
279
300
|
- `lifecycle.py`: caller-owned task groups, operation-owned observations, interrupted requests, idempotent close, and proof that an owned subprocess was reaped.
|
|
@@ -322,19 +343,21 @@ The same remaining budget covers queue/readiness waits, provider waits, selected
|
|
|
322
343
|
|
|
323
344
|
The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optional `resolve_credential` tokenRef callbacks at delivery time. OAuth bindings require an explicit provider. Missing configured secrets and unsupported mechanisms fail closed. Anonymous success stays anonymous; an explicit provider can authorize challenge discovery under host trust policy. Interception may replay at most once after an eligible 401 Bearer challenge, retaining request identity and serialized body; observations/uploads are never automatically replayed. Upload bindings are independent: event credentials are never an upload fallback. Transport-owned TLS/workload integration is not replaced by this provider.
|
|
324
345
|
|
|
346
|
+
Registered stdio backends start with a minimal base environment rather than the full host environment. The base is an allowlist of host variables: `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `LANG`, `LC_*`, `TZ`, `TMPDIR`, `TEMP` and `TMP`, plus `SYSTEMROOT`, `WINDIR`, `COMSPEC`, `PATHEXT`, `USERPROFILE`, `APPDATA`, `LOCALAPPDATA` and `PROGRAMDATA` on Windows, where names match case-insensitively. The registration's `transport.env` entries are applied on top as literal strings, with no interpolation, and override base values. `tokenEnv` is still resolved in the host environment and credentials are never copied into the child. Env values are never logged. `Hooks(..., inherit_environment=True)` is an SDK-level opt-out that starts from the full host environment instead. `transports.StdioTransport(..., env=..., inherit_environment=...)` exposes the same controls.
|
|
347
|
+
|
|
325
348
|
## Static host manifest
|
|
326
349
|
|
|
327
350
|
`hooks.subscribes("tool.before")` is a pure synchronous static-interest lookup. Use it before expensive input preparation: `False` means no eligible intercept or observe subscription selects the configured event. Exact selectors, `*`, and prefix selectors such as `tool.*` use the same matching as dispatch. Unknown or unconfigured events return `False`. Payload filters are conservatively ignored, so `True` means potential interest, not permission or guaranteed delivery. The lookup does not prepare input, authenticate, upload content, initialize transports, or contact backends.
|
|
328
351
|
|
|
329
|
-
Configure `Hooks(config, *, source, capabilities=capability.HostCapabilities(...), manifest_metadata=HostManifestMetadata(...))`. Typed host capabilities are the sole source of event modes and effect grants. Optional metadata declares transport/authentication facilities, tool paths, content categories, limits, managed policy, gaps, correlation fields, and opaque extras without granting event authority. Metadata cannot contain events or override known fields through extras. `hooks.manifest` returns a detached canonical snapshot.
|
|
352
|
+
Configure `Hooks(config, *, source, harness=event.Harness(name="example", version="1"), capabilities=capability.HostCapabilities(...), manifest_metadata=HostManifestMetadata(...))`. The required `source`, `harness`, and `capabilities` configure one host. Harness name and version identify the emitter on every event; SDK-generated events use a detached snapshot of that identity. Typed host capabilities are the sole source of event modes and effect grants. Optional metadata declares transport/authentication facilities, tool paths, content categories, limits, managed policy, gaps, correlation fields, and opaque extras without granting event authority. Metadata cannot contain events or override known fields through extras. `hooks.manifest` returns a detached canonical snapshot.
|
|
330
353
|
|
|
331
|
-
The frozen, keyword-only host capability model advertises only explicitly declared events and modes. Each event
|
|
354
|
+
The frozen, keyword-only host capability model advertises only explicitly declared events and modes. Each event has its own declaration type: `ToolBeforeCapabilities(intercept=..., observe=True)` accepts typed interception options and an observation flag, while observation-only events such as `SessionEndCapabilities(observe=True)` accept only the flag. Empty declarations, empty interception, and invalid option values are rejected at construction, while an empty `HostCapabilities()` grants nothing and elicitation-only interception is a valid facility declaration. `Modify.REPLACE`, `Modify.MERGE`, and `Modify.REPLACE_AND_MERGE` declare complete modification grants. `HostCapabilities.to_wire()` returns fresh canonical declarations directly; it does not parse JSON. Runtime admission still validates registration, request-time grants, and responses. The derived manifest records gaps for omitted events and host-level facilities not supplied through metadata. Explicit empty metadata collections declare no such facility; they do not mean universal support.
|
|
332
355
|
|
|
333
|
-
`await hooks.session_start(event.SessionStartInput(session=..., trigger="startup",
|
|
356
|
+
`await hooks.session_start(event.SessionStartInput(session=..., trigger="startup", permission_mode="default", items=[]))` needs only occurrence-specific host facts. The SDK supplies `manifest`, `harness`, `type`, and `source`, and supplies `id`/`time` when absent; `event_id=` takes precedence over a supplied ID. Named host inputs do not expose harness identity; the constructor supplies it. Caller payload fields cannot override the configured type, source, or manifest. Configuration, manifest snapshots, and delivered occurrences are independent copies. See [`examples/session_manifest.py`](examples/session_manifest.py) for a runnable, typechecked receiver example.
|
|
334
357
|
|
|
335
358
|
## Backend servers
|
|
336
359
|
|
|
337
|
-
`from agenthooksprotocol.server import hooks` exposes event-correlated callback maps. Configure `hooks.Handlers(intercept=hooks.InterceptHandlers(tool_before=before), observe=hooks.ObserveHandlers(tool_before=observer))`. A tool interception callback accepts `hooks.ToolBeforeContext` and returns `hooks.ToolBeforeReply`; an observation callback accepts `hooks.ToolBeforeObserveContext` and returns `None`. Callbacks can be synchronous or asynchronous. Contexts expose the typed event, full params/envelope, and detached raw evidence; interception contexts also expose request-time capabilities and state. Reply envelopes and correlation IDs belong to the server.
|
|
360
|
+
`from agenthooksprotocol.server import hooks` exposes event-correlated callback maps. Configure `hooks.Handlers(intercept=hooks.InterceptHandlers(tool_before=before), observe=hooks.ObserveHandlers(tool_before=observer))`. A tool interception callback accepts `hooks.ToolBeforeContext` and returns `hooks.ToolBeforeReply`; an observation callback accepts `hooks.ToolBeforeObserveContext` and returns `None`. Callbacks can be synchronous or asynchronous. Contexts expose the typed event, full params/envelope, and detached raw evidence; interception contexts also expose request-time capabilities and state. Reply envelopes and correlation IDs belong to the server. `hooks.ObserveHandlers(any=observer)` adds an optional catch-all that receives a `hooks.AnyObserveContext` for observed events without their own observer field. `from agenthooksprotocol.server import serve` is the AnyIO stdio server (`server.stdio.serve`).
|
|
338
361
|
|
|
339
362
|
`hooks.ToolBeforeReply(effects=(hooks.ReplaceInput(arguments), effect.Allow()))` preserves effect order. `hooks.ToolBeforeReply()` is a valid no-op. Reply types constrain event-specific controls; runtime validation still checks canonical/contextual legality and dynamic request grants. Tool contexts provide `decode_input(codec)` and `decode_candidate(result_codec, provenance_codec)` for caller-owned application types without discarding original wire evidence. The same dispatcher serves stdio and the framework-neutral ASGI adapter; ASGI does not require Starlette or FastAPI.
|
|
340
363
|
|
|
@@ -26,13 +26,18 @@ with open("hooks.json") as file:
|
|
|
26
26
|
config = json.load(file)
|
|
27
27
|
|
|
28
28
|
async def review(existing_arguments):
|
|
29
|
-
async with Hooks(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
async with Hooks(
|
|
30
|
+
config,
|
|
31
|
+
source="urn:example:harness",
|
|
32
|
+
harness=event.Harness(name="example", version="1"),
|
|
33
|
+
capabilities=capability.HostCapabilities(
|
|
34
|
+
tool_before=capability.ToolBeforeCapabilities(
|
|
35
|
+
intercept=capability.ToolBeforeIntercept(
|
|
36
|
+
allow=True, deny=True, modify_input=capability.Modify.REPLACE,
|
|
37
|
+
),
|
|
33
38
|
),
|
|
34
39
|
),
|
|
35
|
-
)
|
|
40
|
+
) as hooks:
|
|
36
41
|
result = await hooks.tool_before(event.ToolBeforeInput(
|
|
37
42
|
call_id="call-1", name="read_file", input=existing_arguments,
|
|
38
43
|
path="native", origin="native",
|
|
@@ -40,16 +45,32 @@ async def review(existing_arguments):
|
|
|
40
45
|
return result # Host validation and authorization still precede execution.
|
|
41
46
|
```
|
|
42
47
|
|
|
43
|
-
Generated `Input` objects are distinct from canonical wire events. They flatten only protocol-owned wrappers, never application arguments, and `to_wire()` performs the shared mapping. `path` and `origin` remain separate required facts. Optional host IDs, timestamps, parent IDs, and other facts are retained; `event_id=` overrides an input ID. The SDK supplies type/source and the configured session manifest. Low-level `dispatch` accepts canonical dictionaries and an `event_name: EventType`. `from agenthooksprotocol.event import EventType` exposes the catalogue-derived literal type shared by `dispatch`, `subscribes`, and contextual response parsing. Canonical name literals are accepted directly; static checking rejects dynamic strings, unknown names, and selectors in these event-name positions. Registration selectors remain separate and support their documented wildcard matching.
|
|
48
|
+
Generated `Input` objects are distinct from canonical wire events. They flatten only protocol-owned wrappers, never application arguments, and `to_wire()` performs the shared mapping. `path` and `origin` remain separate required facts. Optional host IDs, timestamps, parent IDs, and other facts are retained; `event_id=` overrides an input ID. The SDK supplies type/source, the configured harness identity, and the configured session manifest. Full canonical wire events must include a matching `harness` identity. Low-level `dispatch` accepts canonical dictionaries and an `event_name: EventType`. `from agenthooksprotocol.event import EventType` exposes the catalogue-derived literal type shared by `dispatch`, `subscribes`, and contextual response parsing. Canonical name literals are accepted directly; static checking rejects dynamic strings, unknown names, and selectors in these event-name positions. Registration selectors remain separate and support their documented wildcard matching.
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
Host capabilities are frozen, keyword-only dataclasses; there are no builder functions. Each event has its own `<Event>Capabilities` declaration. Events that support interception take `intercept=<Event>Intercept(...)` grants, and every declaration takes `observe`, which defaults to `False`, so to advertise both modes you must pass `observe=True` explicitly. Observation-only events such as `SessionEndCapabilities(observe=True)` accept only the flag. `modify_input=capability.Modify.REPLACE` supplies both the effect grant and its operation block, and form/URL elicitation grants are explicit (`elicitation=capability.Elicitation.FORM`, `URL`, or `FORM_AND_URL`). Interception without a control grant and declarations with neither mode are rejected at construction; boundary compatibility is validated before delivery. Omission never implies permission. Per-call `capabilities=` only narrows configured grants.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from agenthooksprotocol import capability
|
|
54
|
+
|
|
55
|
+
capabilities = capability.HostCapabilities(
|
|
56
|
+
tool_before=capability.ToolBeforeCapabilities(
|
|
57
|
+
intercept=capability.ToolBeforeIntercept(
|
|
58
|
+
allow=True, deny=True, modify_input=capability.Modify.REPLACE
|
|
59
|
+
),
|
|
60
|
+
observe=True, # intercept and observe
|
|
61
|
+
),
|
|
62
|
+
session_end=capability.SessionEndCapabilities(observe=True), # observe only
|
|
63
|
+
)
|
|
64
|
+
assert capabilities.to_wire()["tool.before"]["modes"] == ["intercept", "observe"]
|
|
65
|
+
assert capabilities.to_wire()["session.end"] == {"modes": ["observe"]}
|
|
66
|
+
```
|
|
46
67
|
|
|
47
68
|
Runnable, statically checked consumer programs live in [`examples/`](examples):
|
|
48
69
|
|
|
49
70
|
- `value_codecs.py`: independent argument/result/provenance codecs, admission-time invariants, and a form schema derived from its answer type.
|
|
50
71
|
- `typed_tool.py`: JSON registration, generated tool input, typed grants, initial state, real in-process ASGI handler, borrowed HTTP client, and explicit application decoding.
|
|
51
72
|
- `common_cases.py`: rewrite/allow, deny, invalid application shape, host policy, occurrence narrowing, native initial state, and fail-open/fail-closed reports. The host operation is an in-memory execution recorder, not a shell.
|
|
52
|
-
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public
|
|
73
|
+
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public `hooks.Handlers`, denial, and child cleanup.
|
|
53
74
|
- `upload.py`: streamed binary upload, metadata no-read behavior, independent upload credentials, verified upload receipt, and caller-owned immutable storage.
|
|
54
75
|
|
|
55
76
|
- `lifecycle.py`: caller-owned task groups, operation-owned observations, interrupted requests, idempotent close, and proof that an owned subprocess was reaped.
|
|
@@ -98,19 +119,21 @@ The same remaining budget covers queue/readiness waits, provider waits, selected
|
|
|
98
119
|
|
|
99
120
|
The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optional `resolve_credential` tokenRef callbacks at delivery time. OAuth bindings require an explicit provider. Missing configured secrets and unsupported mechanisms fail closed. Anonymous success stays anonymous; an explicit provider can authorize challenge discovery under host trust policy. Interception may replay at most once after an eligible 401 Bearer challenge, retaining request identity and serialized body; observations/uploads are never automatically replayed. Upload bindings are independent: event credentials are never an upload fallback. Transport-owned TLS/workload integration is not replaced by this provider.
|
|
100
121
|
|
|
122
|
+
Registered stdio backends start with a minimal base environment rather than the full host environment. The base is an allowlist of host variables: `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `LANG`, `LC_*`, `TZ`, `TMPDIR`, `TEMP` and `TMP`, plus `SYSTEMROOT`, `WINDIR`, `COMSPEC`, `PATHEXT`, `USERPROFILE`, `APPDATA`, `LOCALAPPDATA` and `PROGRAMDATA` on Windows, where names match case-insensitively. The registration's `transport.env` entries are applied on top as literal strings, with no interpolation, and override base values. `tokenEnv` is still resolved in the host environment and credentials are never copied into the child. Env values are never logged. `Hooks(..., inherit_environment=True)` is an SDK-level opt-out that starts from the full host environment instead. `transports.StdioTransport(..., env=..., inherit_environment=...)` exposes the same controls.
|
|
123
|
+
|
|
101
124
|
## Static host manifest
|
|
102
125
|
|
|
103
126
|
`hooks.subscribes("tool.before")` is a pure synchronous static-interest lookup. Use it before expensive input preparation: `False` means no eligible intercept or observe subscription selects the configured event. Exact selectors, `*`, and prefix selectors such as `tool.*` use the same matching as dispatch. Unknown or unconfigured events return `False`. Payload filters are conservatively ignored, so `True` means potential interest, not permission or guaranteed delivery. The lookup does not prepare input, authenticate, upload content, initialize transports, or contact backends.
|
|
104
127
|
|
|
105
|
-
Configure `Hooks(config, *, source, capabilities=capability.HostCapabilities(...), manifest_metadata=HostManifestMetadata(...))`. Typed host capabilities are the sole source of event modes and effect grants. Optional metadata declares transport/authentication facilities, tool paths, content categories, limits, managed policy, gaps, correlation fields, and opaque extras without granting event authority. Metadata cannot contain events or override known fields through extras. `hooks.manifest` returns a detached canonical snapshot.
|
|
128
|
+
Configure `Hooks(config, *, source, harness=event.Harness(name="example", version="1"), capabilities=capability.HostCapabilities(...), manifest_metadata=HostManifestMetadata(...))`. The required `source`, `harness`, and `capabilities` configure one host. Harness name and version identify the emitter on every event; SDK-generated events use a detached snapshot of that identity. Typed host capabilities are the sole source of event modes and effect grants. Optional metadata declares transport/authentication facilities, tool paths, content categories, limits, managed policy, gaps, correlation fields, and opaque extras without granting event authority. Metadata cannot contain events or override known fields through extras. `hooks.manifest` returns a detached canonical snapshot.
|
|
106
129
|
|
|
107
|
-
The frozen, keyword-only host capability model advertises only explicitly declared events and modes. Each event
|
|
130
|
+
The frozen, keyword-only host capability model advertises only explicitly declared events and modes. Each event has its own declaration type: `ToolBeforeCapabilities(intercept=..., observe=True)` accepts typed interception options and an observation flag, while observation-only events such as `SessionEndCapabilities(observe=True)` accept only the flag. Empty declarations, empty interception, and invalid option values are rejected at construction, while an empty `HostCapabilities()` grants nothing and elicitation-only interception is a valid facility declaration. `Modify.REPLACE`, `Modify.MERGE`, and `Modify.REPLACE_AND_MERGE` declare complete modification grants. `HostCapabilities.to_wire()` returns fresh canonical declarations directly; it does not parse JSON. Runtime admission still validates registration, request-time grants, and responses. The derived manifest records gaps for omitted events and host-level facilities not supplied through metadata. Explicit empty metadata collections declare no such facility; they do not mean universal support.
|
|
108
131
|
|
|
109
|
-
`await hooks.session_start(event.SessionStartInput(session=..., trigger="startup",
|
|
132
|
+
`await hooks.session_start(event.SessionStartInput(session=..., trigger="startup", permission_mode="default", items=[]))` needs only occurrence-specific host facts. The SDK supplies `manifest`, `harness`, `type`, and `source`, and supplies `id`/`time` when absent; `event_id=` takes precedence over a supplied ID. Named host inputs do not expose harness identity; the constructor supplies it. Caller payload fields cannot override the configured type, source, or manifest. Configuration, manifest snapshots, and delivered occurrences are independent copies. See [`examples/session_manifest.py`](examples/session_manifest.py) for a runnable, typechecked receiver example.
|
|
110
133
|
|
|
111
134
|
## Backend servers
|
|
112
135
|
|
|
113
|
-
`from agenthooksprotocol.server import hooks` exposes event-correlated callback maps. Configure `hooks.Handlers(intercept=hooks.InterceptHandlers(tool_before=before), observe=hooks.ObserveHandlers(tool_before=observer))`. A tool interception callback accepts `hooks.ToolBeforeContext` and returns `hooks.ToolBeforeReply`; an observation callback accepts `hooks.ToolBeforeObserveContext` and returns `None`. Callbacks can be synchronous or asynchronous. Contexts expose the typed event, full params/envelope, and detached raw evidence; interception contexts also expose request-time capabilities and state. Reply envelopes and correlation IDs belong to the server.
|
|
136
|
+
`from agenthooksprotocol.server import hooks` exposes event-correlated callback maps. Configure `hooks.Handlers(intercept=hooks.InterceptHandlers(tool_before=before), observe=hooks.ObserveHandlers(tool_before=observer))`. A tool interception callback accepts `hooks.ToolBeforeContext` and returns `hooks.ToolBeforeReply`; an observation callback accepts `hooks.ToolBeforeObserveContext` and returns `None`. Callbacks can be synchronous or asynchronous. Contexts expose the typed event, full params/envelope, and detached raw evidence; interception contexts also expose request-time capabilities and state. Reply envelopes and correlation IDs belong to the server. `hooks.ObserveHandlers(any=observer)` adds an optional catch-all that receives a `hooks.AnyObserveContext` for observed events without their own observer field. `from agenthooksprotocol.server import serve` is the AnyIO stdio server (`server.stdio.serve`).
|
|
114
137
|
|
|
115
138
|
`hooks.ToolBeforeReply(effects=(hooks.ReplaceInput(arguments), effect.Allow()))` preserves effect order. `hooks.ToolBeforeReply()` is a valid no-op. Reply types constrain event-specific controls; runtime validation still checks canonical/contextual legality and dynamic request grants. Tool contexts provide `decode_input(codec)` and `decode_candidate(result_codec, provenance_codec)` for caller-owned application types without discarding original wire evidence. The same dispatcher serves stdio and the framework-neutral ASGI adapter; ASGI does not require Starlette or FastAPI.
|
|
116
139
|
|