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.
Files changed (128) hide show
  1. {agenthooksprotocol-0.5.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.7.0}/PKG-INFO +36 -13
  2. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/README.md +35 -12
  3. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/pyproject.toml +1 -1
  4. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_grants.py +250 -144
  5. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_hooks.py +26 -1
  6. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_hooks.py +3 -1
  7. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/__init__.py +282 -49
  8. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/event.py +2 -0
  9. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/tool.py +8 -0
  10. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/ahp-codegen.lock.json +11 -11
  11. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/capability.py +17 -1
  12. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/catalogue.py +36 -22
  13. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/event.py +1 -0
  14. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/generated.py +327 -295
  15. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/interop.py +6 -2
  16. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/observation_chain.py +2 -0
  17. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/schemas.json +150 -16
  18. agenthooksprotocol-0.7.0/src/agenthooksprotocol/server/__init__.py +10 -0
  19. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_handlers.py +46 -2
  20. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/hooks.py +59 -11
  21. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/stdio.py +2 -2
  22. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/tool.py +4 -0
  23. agenthooksprotocol-0.7.0/src/agenthooksprotocol/transports/stdio.py +297 -0
  24. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0/src/agenthooksprotocol.egg-info}/PKG-INFO +36 -13
  25. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/SOURCES.txt +5 -0
  26. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_catalogue.py +1 -0
  27. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_compaction_wire.py +1 -0
  28. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_consumer_outcomes.py +4 -0
  29. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content_receipts.py +1 -0
  30. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_event_types.py +2 -0
  31. agenthooksprotocol-0.7.0/tests/test_harness_identity.py +125 -0
  32. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_inline_messages.py +7 -1
  33. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_integration.py +4 -1
  34. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_integration_edges.py +1 -0
  35. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_interop.py +1 -0
  36. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle.py +2 -0
  37. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_observation.py +1 -0
  38. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content_hooks.py +8 -3
  39. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_content.py +3 -0
  40. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_hooks.py +44 -5
  41. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_server.py +2 -0
  42. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_schema_corrections.py +1 -0
  43. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_sdk_unification.py +12 -1
  44. agenthooksprotocol-0.7.0/tests/test_server_catch_all.py +101 -0
  45. agenthooksprotocol-0.7.0/tests/test_server_error_codes.py +171 -0
  46. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_session_manifest.py +16 -5
  47. agenthooksprotocol-0.7.0/tests/test_stdio_env.py +155 -0
  48. agenthooksprotocol-0.7.0/tests/test_stdio_transport.py +132 -0
  49. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_subscribes.py +15 -3
  50. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_typed_capabilities.py +65 -8
  51. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_typed_contract_regressions.py +2 -0
  52. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_upload_planning.py +3 -0
  53. agenthooksprotocol-0.5.0/src/agenthooksprotocol/server/__init__.py +0 -8
  54. agenthooksprotocol-0.5.0/src/agenthooksprotocol/transports/stdio.py +0 -92
  55. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/LICENSE +0 -0
  56. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/setup.cfg +0 -0
  57. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/__init__.py +0 -0
  58. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_boundaries.py +0 -0
  59. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_content.py +0 -0
  60. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_diagnostics.py +0 -0
  61. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_elicitation.py +0 -0
  62. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_host_metadata.py +0 -0
  63. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_content.py +0 -0
  64. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/capability.py +0 -0
  65. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/content.py +0 -0
  66. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/effect.py +0 -0
  67. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/registration.py +0 -0
  68. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/subscription.py +0 -0
  69. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/transport.py +0 -0
  70. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_outcomes.py +0 -0
  71. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/attachment.py +0 -0
  72. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/auth.py +0 -0
  73. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/candidate.py +0 -0
  74. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/codec.py +0 -0
  75. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/compaction.py +0 -0
  76. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/content.py +0 -0
  77. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/contract.py +0 -0
  78. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/diagnostics.py +0 -0
  79. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/effect.py +0 -0
  80. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/elicitation.py +0 -0
  81. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/__init__.py +0 -0
  82. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/pydantic.py +0 -0
  83. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle.py +0 -0
  84. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_client.py +0 -0
  85. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_server.py +0 -0
  86. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lineage.py +0 -0
  87. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/path.py +0 -0
  88. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/permission.py +0 -0
  89. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/py.typed +0 -0
  90. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/registration.py +0 -0
  91. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/response.py +0 -0
  92. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/runtime.py +0 -0
  93. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_admission.py +0 -0
  94. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_framing.py +0 -0
  95. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_json.py +0 -0
  96. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_slots.py +0 -0
  97. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/asgi.py +0 -0
  98. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/attachments.py +0 -0
  99. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/state.py +0 -0
  100. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/subscription.py +0 -0
  101. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transport.py +0 -0
  102. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/__init__.py +0 -0
  103. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/http.py +0 -0
  104. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/wire.py +0 -0
  105. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
  106. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
  107. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
  108. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_attachments.py +0 -0
  109. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_codec.py +0 -0
  110. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_compaction.py +0 -0
  111. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content.py +0 -0
  112. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_content_lifetime.py +0 -0
  113. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_correlated_core.py +0 -0
  114. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_declared_contracts.py +0 -0
  115. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_elicitation_adapter.py +0 -0
  116. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_fixture_public.py +0 -0
  117. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_handwritten_surface.py +0 -0
  118. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_host_metadata.py +0 -0
  119. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle_auth.py +0 -0
  120. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_observation_chain_transport.py +0 -0
  121. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content.py +0 -0
  122. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_package.py +0 -0
  123. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_contract_surface.py +0 -0
  124. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_public_examples.py +0 -0
  125. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_registration_auth.py +0 -0
  126. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_safety.py +0 -0
  127. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_shared_materialization.py +0 -0
  128. {agenthooksprotocol-0.5.0 → agenthooksprotocol-0.7.0}/tests/test_structural_facade.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenthooksprotocol
3
- Version: 0.5.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(config, source="urn:example:harness", capabilities=capability.HostCapabilities(
254
- tool_before=capability.ToolBeforeCapabilities(
255
- intercept=capability.ToolBeforeIntercept(
256
- allow=True, deny=True, modify_input=capability.Modify.REPLACE,
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
- )) as hooks:
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
- Capability builders are immutable: reusing a base declaration does not widen it. `intercept()` deliberately advertises **both intercept and observe**; `observe()` is observation-only. `modify_input(replace=True)` supplies both the effect grant and operation block. Empty operations and observation effect grants are rejected; boundary compatibility is validated before delivery. Form and URL elicitation grants are explicit (`elicitation_form()` / `elicitation_url()`). Raw manifests retain their exact modes; omission never implies permission. Per-call `capabilities=` only narrows configured grants.
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 Handler, denial, and child cleanup.
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 accepts its own typed interception options; `Observe()` declares observation interest. 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.
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", harness=..., permission_mode="default", items=[]))` needs only occurrence-specific host facts. The SDK supplies `manifest`, `type`, and `source`, and supplies `id`/`time` when absent; `event_id=` takes precedence over a supplied ID. 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.
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(config, source="urn:example:harness", capabilities=capability.HostCapabilities(
30
- tool_before=capability.ToolBeforeCapabilities(
31
- intercept=capability.ToolBeforeIntercept(
32
- allow=True, deny=True, modify_input=capability.Modify.REPLACE,
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
- )) as hooks:
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
- Capability builders are immutable: reusing a base declaration does not widen it. `intercept()` deliberately advertises **both intercept and observe**; `observe()` is observation-only. `modify_input(replace=True)` supplies both the effect grant and operation block. Empty operations and observation effect grants are rejected; boundary compatibility is validated before delivery. Form and URL elicitation grants are explicit (`elicitation_form()` / `elicitation_url()`). Raw manifests retain their exact modes; omission never implies permission. Per-call `capabilities=` only narrows configured grants.
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 Handler, denial, and child cleanup.
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 accepts its own typed interception options; `Observe()` declares observation interest. 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.
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", harness=..., permission_mode="default", items=[]))` needs only occurrence-specific host facts. The SDK supplies `manifest`, `type`, and `source`, and supplies `id`/`time` when absent; `event_id=` takes precedence over a supplied ID. 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.
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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agenthooksprotocol"
7
- version = "0.5.0"
7
+ version = "0.7.0"
8
8
  description = "Official non-normative Python SDK for the Agent Hooks Protocol"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"