agenthooksprotocol 0.6.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 (126) hide show
  1. {agenthooksprotocol-0.6.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.7.0}/PKG-INFO +14 -9
  2. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/README.md +13 -8
  3. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/pyproject.toml +1 -1
  4. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_hooks.py +18 -1
  5. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_hooks.py +3 -1
  6. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/__init__.py +273 -48
  7. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/event.py +2 -0
  8. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/tool.py +8 -0
  9. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/ahp-codegen.lock.json +10 -10
  10. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/catalogue.py +4 -0
  11. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/event.py +1 -0
  12. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/generated.py +326 -295
  13. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/interop.py +6 -2
  14. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/observation_chain.py +2 -0
  15. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/schemas.json +143 -16
  16. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/tool.py +4 -0
  17. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0/src/agenthooksprotocol.egg-info}/PKG-INFO +14 -9
  18. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/SOURCES.txt +1 -0
  19. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_catalogue.py +1 -0
  20. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_compaction_wire.py +1 -0
  21. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_consumer_outcomes.py +4 -0
  22. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_content_receipts.py +1 -0
  23. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_event_types.py +2 -0
  24. agenthooksprotocol-0.7.0/tests/test_harness_identity.py +125 -0
  25. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_inline_messages.py +7 -1
  26. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_integration.py +4 -1
  27. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_integration_edges.py +1 -0
  28. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_interop.py +1 -0
  29. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle.py +2 -0
  30. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_observation.py +1 -0
  31. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content_hooks.py +6 -1
  32. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_public_content.py +3 -0
  33. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_public_hooks.py +41 -4
  34. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_public_server.py +2 -0
  35. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_schema_corrections.py +1 -0
  36. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_sdk_unification.py +9 -0
  37. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_session_manifest.py +13 -2
  38. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_stdio_env.py +2 -0
  39. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_subscribes.py +13 -2
  40. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_typed_capabilities.py +34 -6
  41. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_typed_contract_regressions.py +2 -0
  42. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_upload_planning.py +3 -0
  43. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/LICENSE +0 -0
  44. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/setup.cfg +0 -0
  45. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/__init__.py +0 -0
  46. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_boundaries.py +0 -0
  47. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_content.py +0 -0
  48. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_diagnostics.py +0 -0
  49. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_elicitation.py +0 -0
  50. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_grants.py +0 -0
  51. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_host_metadata.py +0 -0
  52. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_interop_content.py +0 -0
  53. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/capability.py +0 -0
  54. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/content.py +0 -0
  55. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/effect.py +0 -0
  56. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/registration.py +0 -0
  57. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/subscription.py +0 -0
  58. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_models/transport.py +0 -0
  59. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/_outcomes.py +0 -0
  60. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/attachment.py +0 -0
  61. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/auth.py +0 -0
  62. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/candidate.py +0 -0
  63. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/capability.py +0 -0
  64. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/codec.py +0 -0
  65. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/compaction.py +0 -0
  66. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/content.py +0 -0
  67. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/contract.py +0 -0
  68. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/diagnostics.py +0 -0
  69. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/effect.py +0 -0
  70. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/elicitation.py +0 -0
  71. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/__init__.py +0 -0
  72. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/integrations/pydantic.py +0 -0
  73. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle.py +0 -0
  74. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_client.py +0 -0
  75. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lifecycle_server.py +0 -0
  76. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/lineage.py +0 -0
  77. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/path.py +0 -0
  78. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/permission.py +0 -0
  79. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/py.typed +0 -0
  80. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/registration.py +0 -0
  81. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/response.py +0 -0
  82. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/runtime.py +0 -0
  83. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/__init__.py +0 -0
  84. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_admission.py +0 -0
  85. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_framing.py +0 -0
  86. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_handlers.py +0 -0
  87. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_json.py +0 -0
  88. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/_slots.py +0 -0
  89. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/asgi.py +0 -0
  90. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/attachments.py +0 -0
  91. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/hooks.py +0 -0
  92. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/server/stdio.py +0 -0
  93. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/state.py +0 -0
  94. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/subscription.py +0 -0
  95. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transport.py +0 -0
  96. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/__init__.py +0 -0
  97. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/http.py +0 -0
  98. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/transports/stdio.py +0 -0
  99. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol/wire.py +0 -0
  100. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
  101. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
  102. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
  103. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_attachments.py +0 -0
  104. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_codec.py +0 -0
  105. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_compaction.py +0 -0
  106. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_content.py +0 -0
  107. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_content_lifetime.py +0 -0
  108. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_correlated_core.py +0 -0
  109. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_declared_contracts.py +0 -0
  110. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_elicitation_adapter.py +0 -0
  111. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_fixture_public.py +0 -0
  112. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_handwritten_surface.py +0 -0
  113. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_host_metadata.py +0 -0
  114. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_lifecycle_auth.py +0 -0
  115. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_observation_chain_transport.py +0 -0
  116. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_owned_content.py +0 -0
  117. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_package.py +0 -0
  118. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_public_contract_surface.py +0 -0
  119. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_public_examples.py +0 -0
  120. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_registration_auth.py +0 -0
  121. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_safety.py +0 -0
  122. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_server_catch_all.py +0 -0
  123. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_server_error_codes.py +0 -0
  124. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_shared_materialization.py +0 -0
  125. {agenthooksprotocol-0.6.0 → agenthooksprotocol-0.7.0}/tests/test_stdio_transport.py +0 -0
  126. {agenthooksprotocol-0.6.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.6.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,7 +269,7 @@ 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.
270
275
 
@@ -344,11 +349,11 @@ Registered stdio backends start with a minimal base environment rather than the
344
349
 
345
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.
346
351
 
347
- 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.
348
353
 
349
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.
350
355
 
351
- `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.
352
357
 
353
358
  ## Backend servers
354
359
 
@@ -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,7 +45,7 @@ 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.
46
51
 
@@ -120,11 +125,11 @@ Registered stdio backends start with a minimal base environment rather than the
120
125
 
121
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.
122
127
 
123
- 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.
124
129
 
125
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.
126
131
 
127
- `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.
128
133
 
129
134
  ## Backend servers
130
135
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agenthooksprotocol"
7
- version = "0.6.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"
@@ -11,7 +11,7 @@ from typing import Any, Callable, Generic, Mapping, TypeVar, cast, overload
11
11
  from types import TracebackType
12
12
  from .codec import ValueCodecs, Codec, Candidate, _decode_preserving
13
13
  from .response import ContextualResponse
14
- from .event import Event, EventType
14
+ from .event import Event, EventType, Harness
15
15
  from .capability import HostCapabilities
16
16
  from ._host_metadata import HostManifestMetadata
17
17
  from . import _models as models
@@ -648,6 +648,7 @@ class Hooks(BoundaryMixin):
648
648
  config: dict[str, Any],
649
649
  *,
650
650
  source: str,
651
+ harness: Harness,
651
652
  capabilities: HostCapabilities,
652
653
  manifest_metadata: HostManifestMetadata | None = None,
653
654
  transport: Any = None,
@@ -665,6 +666,7 @@ class Hooks(BoundaryMixin):
665
666
  self._configure(
666
667
  config,
667
668
  source=source,
669
+ harness=harness,
668
670
  declarations=capabilities.to_wire(),
669
671
  manifest_metadata=manifest_metadata,
670
672
  transport=transport,
@@ -679,6 +681,7 @@ class Hooks(BoundaryMixin):
679
681
  config: dict[str, Any],
680
682
  *,
681
683
  source: str,
684
+ harness: Harness,
682
685
  declarations: dict[EventType, Any],
683
686
  manifest_metadata: HostManifestMetadata | None = None,
684
687
  transport: Any = None,
@@ -707,6 +710,15 @@ class Hooks(BoundaryMixin):
707
710
  or not FormatChecker().conforms(source, "uri")
708
711
  ):
709
712
  raise ProtocolError("source must be an absolute URI")
713
+ if not isinstance(harness, Harness):
714
+ raise TypeError("harness must be Harness")
715
+ identity = deepcopy(dict(harness))
716
+ common = self.validator.validators["common.schema.json"]
717
+ if not common.evolve(
718
+ schema={"$ref": common.schema["$id"] + "#/$defs/harness"}
719
+ ).is_valid(identity):
720
+ raise ProtocolError("harness must contain canonical name and version")
721
+ self._harness = identity
710
722
  self._pending: WeakSet[PendingInvocation] = WeakSet()
711
723
  self.config = deepcopy(config)
712
724
  self.source = source
@@ -950,6 +962,8 @@ class Hooks(BoundaryMixin):
950
962
  ) -> None:
951
963
  if event["source"] != self.source:
952
964
  raise ProtocolError("Occurrence source conflicts with configured source")
965
+ if not _json_equal(event["harness"], self._harness):
966
+ raise ProtocolError("Occurrence harness conflicts with configured harness")
953
967
  declaration = self.capabilities.get(event["type"])
954
968
  if declaration is None or mode not in declaration["modes"]:
955
969
  raise ProtocolError("Occurrence event or delivery mode was not advertised")
@@ -1199,6 +1213,8 @@ class Hooks(BoundaryMixin):
1199
1213
  event = deepcopy(input.to_wire() if hasattr(input, "to_wire") else input)
1200
1214
  if not isinstance(event, dict):
1201
1215
  raise TypeError("Event input must be a canonical event payload object")
1216
+ if "harness" in event and not _json_equal(event["harness"], self._harness):
1217
+ raise ProtocolError("Occurrence harness conflicts with configured harness")
1202
1218
  from ._content import normalized_content_slots, normalized_values
1203
1219
 
1204
1220
  paths, _ = normalized_content_slots().get(event_name, ((), ()))
@@ -1218,6 +1234,7 @@ class Hooks(BoundaryMixin):
1218
1234
  event.update(
1219
1235
  id=event_id if event_id is not None else event.get("id", str(uuid4())),
1220
1236
  source=self.source,
1237
+ harness=deepcopy(self._harness),
1221
1238
  type=event_name,
1222
1239
  time=event.get("time", datetime.now(timezone.utc).isoformat()),
1223
1240
  )
@@ -6,7 +6,7 @@ uses the same validated core without making wire mappings a public Hooks input.
6
6
 
7
7
  from typing import Any, cast, get_args
8
8
 
9
- from .event import EventType
9
+ from .event import EventType, Harness
10
10
  from .runtime import ProtocolError
11
11
 
12
12
  from ._hooks import Hooks
@@ -16,6 +16,7 @@ def _hooks_for_wire(
16
16
  config: dict[str, Any],
17
17
  *,
18
18
  source: str,
19
+ harness: Harness,
19
20
  declarations: dict[str, Any],
20
21
  transport: Any,
21
22
  ) -> Hooks:
@@ -25,6 +26,7 @@ def _hooks_for_wire(
25
26
  hooks._configure(
26
27
  config,
27
28
  source=source,
29
+ harness=harness,
28
30
  declarations=cast(dict[EventType, Any], declarations),
29
31
  transport=transport,
30
32
  )