agenthooksprotocol 0.4.0__tar.gz → 0.5.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.4.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.5.0}/PKG-INFO +29 -24
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/README.md +28 -23
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/pyproject.toml +1 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/__init__.py +5 -1
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/_boundaries.py +5968 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_content.py +0 -13
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/_grants.py +1859 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_hooks.py +333 -133
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/_host_metadata.py +133 -0
- agenthooksprotocol-0.4.0/src/agenthooksprotocol/content.py → agenthooksprotocol-0.5.0/src/agenthooksprotocol/_interop_content.py +4 -30
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/_interop_hooks.py +48 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/__init__.py +391 -1755
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/content.py +6 -10
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/event.py +2 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/tool.py +0 -2
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/_outcomes.py +67 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/ahp-codegen.lock.json +1 -1
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/capability.py +60 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/catalogue.py +33 -6
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/codec.py +2 -2
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/content.py +28 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/diagnostics.py +0 -3
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/effect.py +0 -275
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/event.py +1 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/integrations/pydantic.py +3 -3
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/interop.py +23 -25
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/lifecycle_client.py +1 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/lifecycle_server.py +17 -27
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/observation_chain.py +3 -3
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/response.py +2 -2
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/runtime.py +20 -10
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/server/_admission.py +43 -0
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/server/_handlers.py +1019 -0
- agenthooksprotocol-0.5.0/src/agenthooksprotocol/server/_slots.py +45 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/asgi.py +2 -6
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/hooks.py +79 -25
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/stdio.py +2 -2
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/tool.py +0 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0/src/agenthooksprotocol.egg-info}/PKG-INFO +29 -24
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol.egg-info/SOURCES.txt +13 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_attachments.py +55 -25
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_compaction.py +11 -3
- agenthooksprotocol-0.5.0/tests/test_consumer_outcomes.py +398 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_content.py +1 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_content_lifetime.py +4 -2
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_content_receipts.py +5 -2
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_correlated_core.py +52 -13
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_declared_contracts.py +226 -70
- agenthooksprotocol-0.5.0/tests/test_event_types.py +40 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_fixture_public.py +2 -2
- agenthooksprotocol-0.5.0/tests/test_handwritten_surface.py +125 -0
- agenthooksprotocol-0.5.0/tests/test_host_metadata.py +130 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_inline_messages.py +72 -34
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_lifecycle_auth.py +1 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_owned_content_hooks.py +18 -4
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_public_content.py +174 -64
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_public_contract_surface.py +9 -3
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_public_examples.py +20 -1
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_public_hooks.py +59 -39
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_public_server.py +273 -30
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_sdk_unification.py +94 -36
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_session_manifest.py +66 -28
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_structural_facade.py +148 -39
- agenthooksprotocol-0.5.0/tests/test_subscribes.py +64 -0
- agenthooksprotocol-0.5.0/tests/test_typed_capabilities.py +144 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_typed_contract_regressions.py +141 -32
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_upload_planning.py +35 -9
- agenthooksprotocol-0.4.0/src/agenthooksprotocol/_boundaries.py +0 -1358
- agenthooksprotocol-0.4.0/src/agenthooksprotocol/_grants.py +0 -619
- agenthooksprotocol-0.4.0/src/agenthooksprotocol/capability.py +0 -155
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/LICENSE +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/setup.cfg +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_diagnostics.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_elicitation.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/capability.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/effect.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/registration.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/subscription.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/_models/transport.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/attachment.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/auth.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/candidate.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/compaction.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/contract.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/elicitation.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/generated.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/integrations/__init__.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/lifecycle.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/lineage.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/path.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/permission.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/py.typed +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/registration.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/schemas.json +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/__init__.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/_framing.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/_json.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/server/attachments.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/state.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/subscription.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/transport.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/transports/__init__.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/transports/http.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/transports/stdio.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol/wire.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_catalogue.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_codec.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_compaction_wire.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_elicitation_adapter.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_integration.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_integration_edges.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_interop.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_lifecycle.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_observation.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_observation_chain_transport.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_owned_content.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_package.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_registration_auth.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_safety.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_schema_corrections.py +0 -0
- {agenthooksprotocol-0.4.0 → agenthooksprotocol-0.5.0}/tests/test_shared_materialization.py +0 -0
{agenthooksprotocol-0.4.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.5.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenthooksprotocol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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,9 +250,13 @@ 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=
|
|
254
|
-
capability.
|
|
255
|
-
|
|
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,
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
)) as hooks:
|
|
256
260
|
result = await hooks.tool_before(event.ToolBeforeInput(
|
|
257
261
|
call_id="call-1", name="read_file", input=existing_arguments,
|
|
258
262
|
path="native", origin="native",
|
|
@@ -260,7 +264,7 @@ async def review(existing_arguments):
|
|
|
260
264
|
return result # Host validation and authorization still precede execution.
|
|
261
265
|
```
|
|
262
266
|
|
|
263
|
-
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.
|
|
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.
|
|
264
268
|
|
|
265
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.
|
|
266
270
|
|
|
@@ -285,17 +289,17 @@ Capabilities explicitly grant effects and target operations. Supply event capabi
|
|
|
285
289
|
|
|
286
290
|
Supply `input_codec=`, `result_codec=`, and optional `provenance_codec=` to a named hook method or `dispatch` to declare caller types. Complete staged values are decoded and validated before publication. Object merge is shallow: omitted fields remain unchanged, literal null is retained, and nested values replace whole values. An invalid response publishes none of its effects; earlier accepted responses remain visible to later interceptors. A codec must preserve the wire data it receives, including opaque extra fields. Lossy serialization rejects admission rather than dropping fields.
|
|
287
291
|
|
|
288
|
-
`result.
|
|
292
|
+
`result.input` exposes the declared argument value. `result.candidate` is an SDK-owned `Candidate[Result, Provenance]`, or `None` when there is no candidate. Its value can itself be `None`; optional provenance is omitted from `candidate.to_dict()` when absent. Both values are detached snapshots. Without caller codecs, protocol-owned candidate values use canonical typed models and application-owned values remain JSON. `result.event` is the canonical typed event; `result.accepted_responses` retains complete contextual response envelopes in settlement order. `result.messages` and `result.injections` contain typed effects, including injection delivery timing. `result.has_result` distinguishes absence from a supplied null deliverable; `result.result` raises `KeyError` when absent. `result.flow` is the settled `state.Flow`, or `None` when no flow state is supplied; explicit neutral flow is `state.Flow.NONE`. `result.instructions` includes inherited and accepted continuation instructions, with `None` when absent and `[]` for an effective empty list. `result.continuation_remaining` is the settled budget, or `None` when no budget is declared. For explicitly selected opaque host slots, use `event_codecs={("params",): codec}` with event-relative paths. Unspecified native, provider, task, permission, and discovery values are not interpreted.
|
|
289
293
|
|
|
290
294
|
For forms, construct `PydanticFormContract(TypeAdapter(Answer))` from one answer declaration. `request(message)` derives the restricted MCP schema; `result_codec()` decodes the complete MCP result and its accepted answer while preserving opaque result metadata. Unsupported shapes and schema constraints are rejected during construction. Decline, cancel, and URL results have no form content. See [`examples/value_codecs.py`](examples/value_codecs.py).
|
|
291
295
|
|
|
292
|
-
|
|
296
|
+
A codec implements `encode(value)` and `decode(json_value)`; `IdentityCodec` copies JSON. The optional `integrations.pydantic.PydanticCodec` accepts a Pydantic `TypeAdapter`. Hosts can call `codec.decode(result.input)` for an explicit application check when no input codec is declared. A post-settlement application error does not retroactively reject protocol effects: retain the settled result, accepted responses, and diagnostics, and report host-level rejection without executing.
|
|
293
297
|
|
|
294
|
-
`result.permission` is the generated `Permission` enum (`NONE`, `ALLOW`, `ASK`, `DENY`) read from canonical settlement, not an independent decision engine. `NONE` is not approval; `ASK` needs host approval; interruption never authorizes execution. `result.input` is detached accepted
|
|
298
|
+
`result.permission` is the generated `Permission` enum (`NONE`, `ALLOW`, `ASK`, `DENY`) read from canonical settlement, not an independent decision engine. `NONE` is not approval; `ASK` needs host approval; interruption never authorizes execution. `result.input` is the detached accepted argument value. Declared codecs validate accepted modifications; without a declared codec, the host supplies application validation. Candidate, canonical event, accepted responses, and diagnostics remain accessible.
|
|
295
299
|
|
|
296
300
|
`state.initial(Permission.ALLOW)` represents a native decision **already made for this exact occurrence**, not an authorization shortcut. Its default candidate is absent (`null`); `state.Candidate(value=None)` instead means a supplied JSON-null result. Optional typed provenance records facts, not authenticated identity or execution proof. Canonical modifications still invalidate candidates/approval as specified.
|
|
297
301
|
|
|
298
|
-
Generated backend helpers such as `effect.
|
|
302
|
+
Generated backend helpers such as `effect.DenyEffect(reason="policy")`, `effect.replace_input(new_args)` and `effect.merge_input(changes)`, and `effect.ReturnToolEffect(value=...)` fill discriminants and return canonical effect models. They cannot grant themselves authority: unsupported compound responses are rejected atomically.
|
|
299
303
|
|
|
300
304
|
### Diagnostics and one call budget
|
|
301
305
|
|
|
@@ -320,15 +324,19 @@ The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optiona
|
|
|
320
324
|
|
|
321
325
|
## Static host manifest
|
|
322
326
|
|
|
323
|
-
|
|
327
|
+
`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.
|
|
324
328
|
|
|
325
|
-
|
|
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.
|
|
330
|
+
|
|
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.
|
|
326
332
|
|
|
327
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.
|
|
328
334
|
|
|
329
335
|
## Backend servers
|
|
330
336
|
|
|
331
|
-
`from agenthooksprotocol.server import hooks` exposes
|
|
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.
|
|
338
|
+
|
|
339
|
+
`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.
|
|
332
340
|
|
|
333
341
|
Observation is one-way. It does not acquire effect authority and never waits for an acknowledgement. An optional static **harness** manifest can expose `hooks/capabilities`; backend callbacks are not capability discovery and do not manufacture grants.
|
|
334
342
|
|
|
@@ -434,8 +442,9 @@ or `sha256`. Metadata-only content and body gaps may still disclose size/digest.
|
|
|
434
442
|
|
|
435
443
|
```python
|
|
436
444
|
from agenthooksprotocol import ContentReference, capability, effect
|
|
445
|
+
from agenthooksprotocol._models.capability import Capabilities
|
|
437
446
|
|
|
438
|
-
caps =
|
|
447
|
+
caps = Capabilities.from_dict({"effects": ["deny", "vendor.custom"]})
|
|
439
448
|
assert caps.supports(capability.EffectName.DENY)
|
|
440
449
|
assert caps.supports("vendor.custom")
|
|
441
450
|
assert effect.EffectName is capability.EffectName
|
|
@@ -465,21 +474,17 @@ non-finite numbers and non-JSON values are rejected. Parsing raw JSON through
|
|
|
465
474
|
|
|
466
475
|
Provide valid enum/literal values and all required wire members to `from_dict`. A null candidate, a candidate with
|
|
467
476
|
`{"value": null}`, and a candidate with `{"value": 0}` remain distinct; omitting a
|
|
468
|
-
required candidate is an error. Generated input projections and capability
|
|
469
|
-
|
|
470
|
-
|
|
477
|
+
required candidate is an error. Generated input projections and frozen host capability
|
|
478
|
+
models are construction APIs, not wire decode entrypoints; their canonical wire
|
|
479
|
+
values are validated at the dispatch/parse boundary. Direct dictionary
|
|
471
480
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
472
481
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
473
482
|
|
|
474
|
-
Nested model attributes expose declared protocol fields. For example, `
|
|
483
|
+
Nested model attributes expose declared protocol fields. For example, `Capabilities(effects=["modify"], modify={}).modify.input` is `None` when that grant is absent. Family queries are available on generic and per-occurrence capability models.
|
|
475
484
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
`protocolVersion=`) is used and validated rather than replaced by a default.
|
|
480
|
-
Required constructor parameters use their documented Python spelling;
|
|
481
|
-
use `from_dict` to decode complete wire-keyed objects. In `from_dict`, distinct
|
|
482
|
-
keys such as `toolName` and `tool_name` remain distinct wire/extension keys.
|
|
485
|
+
Use the documented Python spelling for constructor parameters. Use `from_dict`
|
|
486
|
+
to decode complete wire-keyed objects. In `from_dict`, distinct keys such as
|
|
487
|
+
`toolName` and `tool_name` remain distinct wire/extension keys.
|
|
483
488
|
|
|
484
489
|
### Owned attachments
|
|
485
490
|
|
|
@@ -26,9 +26,13 @@ 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=
|
|
30
|
-
capability.
|
|
31
|
-
|
|
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,
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
)) as hooks:
|
|
32
36
|
result = await hooks.tool_before(event.ToolBeforeInput(
|
|
33
37
|
call_id="call-1", name="read_file", input=existing_arguments,
|
|
34
38
|
path="native", origin="native",
|
|
@@ -36,7 +40,7 @@ async def review(existing_arguments):
|
|
|
36
40
|
return result # Host validation and authorization still precede execution.
|
|
37
41
|
```
|
|
38
42
|
|
|
39
|
-
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.
|
|
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.
|
|
40
44
|
|
|
41
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.
|
|
42
46
|
|
|
@@ -61,17 +65,17 @@ Capabilities explicitly grant effects and target operations. Supply event capabi
|
|
|
61
65
|
|
|
62
66
|
Supply `input_codec=`, `result_codec=`, and optional `provenance_codec=` to a named hook method or `dispatch` to declare caller types. Complete staged values are decoded and validated before publication. Object merge is shallow: omitted fields remain unchanged, literal null is retained, and nested values replace whole values. An invalid response publishes none of its effects; earlier accepted responses remain visible to later interceptors. A codec must preserve the wire data it receives, including opaque extra fields. Lossy serialization rejects admission rather than dropping fields.
|
|
63
67
|
|
|
64
|
-
`result.
|
|
68
|
+
`result.input` exposes the declared argument value. `result.candidate` is an SDK-owned `Candidate[Result, Provenance]`, or `None` when there is no candidate. Its value can itself be `None`; optional provenance is omitted from `candidate.to_dict()` when absent. Both values are detached snapshots. Without caller codecs, protocol-owned candidate values use canonical typed models and application-owned values remain JSON. `result.event` is the canonical typed event; `result.accepted_responses` retains complete contextual response envelopes in settlement order. `result.messages` and `result.injections` contain typed effects, including injection delivery timing. `result.has_result` distinguishes absence from a supplied null deliverable; `result.result` raises `KeyError` when absent. `result.flow` is the settled `state.Flow`, or `None` when no flow state is supplied; explicit neutral flow is `state.Flow.NONE`. `result.instructions` includes inherited and accepted continuation instructions, with `None` when absent and `[]` for an effective empty list. `result.continuation_remaining` is the settled budget, or `None` when no budget is declared. For explicitly selected opaque host slots, use `event_codecs={("params",): codec}` with event-relative paths. Unspecified native, provider, task, permission, and discovery values are not interpreted.
|
|
65
69
|
|
|
66
70
|
For forms, construct `PydanticFormContract(TypeAdapter(Answer))` from one answer declaration. `request(message)` derives the restricted MCP schema; `result_codec()` decodes the complete MCP result and its accepted answer while preserving opaque result metadata. Unsupported shapes and schema constraints are rejected during construction. Decline, cancel, and URL results have no form content. See [`examples/value_codecs.py`](examples/value_codecs.py).
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
A codec implements `encode(value)` and `decode(json_value)`; `IdentityCodec` copies JSON. The optional `integrations.pydantic.PydanticCodec` accepts a Pydantic `TypeAdapter`. Hosts can call `codec.decode(result.input)` for an explicit application check when no input codec is declared. A post-settlement application error does not retroactively reject protocol effects: retain the settled result, accepted responses, and diagnostics, and report host-level rejection without executing.
|
|
69
73
|
|
|
70
|
-
`result.permission` is the generated `Permission` enum (`NONE`, `ALLOW`, `ASK`, `DENY`) read from canonical settlement, not an independent decision engine. `NONE` is not approval; `ASK` needs host approval; interruption never authorizes execution. `result.input` is detached accepted
|
|
74
|
+
`result.permission` is the generated `Permission` enum (`NONE`, `ALLOW`, `ASK`, `DENY`) read from canonical settlement, not an independent decision engine. `NONE` is not approval; `ASK` needs host approval; interruption never authorizes execution. `result.input` is the detached accepted argument value. Declared codecs validate accepted modifications; without a declared codec, the host supplies application validation. Candidate, canonical event, accepted responses, and diagnostics remain accessible.
|
|
71
75
|
|
|
72
76
|
`state.initial(Permission.ALLOW)` represents a native decision **already made for this exact occurrence**, not an authorization shortcut. Its default candidate is absent (`null`); `state.Candidate(value=None)` instead means a supplied JSON-null result. Optional typed provenance records facts, not authenticated identity or execution proof. Canonical modifications still invalidate candidates/approval as specified.
|
|
73
77
|
|
|
74
|
-
Generated backend helpers such as `effect.
|
|
78
|
+
Generated backend helpers such as `effect.DenyEffect(reason="policy")`, `effect.replace_input(new_args)` and `effect.merge_input(changes)`, and `effect.ReturnToolEffect(value=...)` fill discriminants and return canonical effect models. They cannot grant themselves authority: unsupported compound responses are rejected atomically.
|
|
75
79
|
|
|
76
80
|
### Diagnostics and one call budget
|
|
77
81
|
|
|
@@ -96,15 +100,19 @@ The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optiona
|
|
|
96
100
|
|
|
97
101
|
## Static host manifest
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
`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.
|
|
100
104
|
|
|
101
|
-
|
|
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.
|
|
106
|
+
|
|
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.
|
|
102
108
|
|
|
103
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.
|
|
104
110
|
|
|
105
111
|
## Backend servers
|
|
106
112
|
|
|
107
|
-
`from agenthooksprotocol.server import hooks` exposes
|
|
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.
|
|
114
|
+
|
|
115
|
+
`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.
|
|
108
116
|
|
|
109
117
|
Observation is one-way. It does not acquire effect authority and never waits for an acknowledgement. An optional static **harness** manifest can expose `hooks/capabilities`; backend callbacks are not capability discovery and do not manufacture grants.
|
|
110
118
|
|
|
@@ -210,8 +218,9 @@ or `sha256`. Metadata-only content and body gaps may still disclose size/digest.
|
|
|
210
218
|
|
|
211
219
|
```python
|
|
212
220
|
from agenthooksprotocol import ContentReference, capability, effect
|
|
221
|
+
from agenthooksprotocol._models.capability import Capabilities
|
|
213
222
|
|
|
214
|
-
caps =
|
|
223
|
+
caps = Capabilities.from_dict({"effects": ["deny", "vendor.custom"]})
|
|
215
224
|
assert caps.supports(capability.EffectName.DENY)
|
|
216
225
|
assert caps.supports("vendor.custom")
|
|
217
226
|
assert effect.EffectName is capability.EffectName
|
|
@@ -241,21 +250,17 @@ non-finite numbers and non-JSON values are rejected. Parsing raw JSON through
|
|
|
241
250
|
|
|
242
251
|
Provide valid enum/literal values and all required wire members to `from_dict`. A null candidate, a candidate with
|
|
243
252
|
`{"value": null}`, and a candidate with `{"value": 0}` remain distinct; omitting a
|
|
244
|
-
required candidate is an error. Generated input projections and capability
|
|
245
|
-
|
|
246
|
-
|
|
253
|
+
required candidate is an error. Generated input projections and frozen host capability
|
|
254
|
+
models are construction APIs, not wire decode entrypoints; their canonical wire
|
|
255
|
+
values are validated at the dispatch/parse boundary. Direct dictionary
|
|
247
256
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
248
257
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
249
258
|
|
|
250
|
-
Nested model attributes expose declared protocol fields. For example, `
|
|
259
|
+
Nested model attributes expose declared protocol fields. For example, `Capabilities(effects=["modify"], modify={}).modify.input` is `None` when that grant is absent. Family queries are available on generic and per-occurrence capability models.
|
|
251
260
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
`protocolVersion=`) is used and validated rather than replaced by a default.
|
|
256
|
-
Required constructor parameters use their documented Python spelling;
|
|
257
|
-
use `from_dict` to decode complete wire-keyed objects. In `from_dict`, distinct
|
|
258
|
-
keys such as `toolName` and `tool_name` remain distinct wire/extension keys.
|
|
261
|
+
Use the documented Python spelling for constructor parameters. Use `from_dict`
|
|
262
|
+
to decode complete wire-keyed objects. In `from_dict`, distinct keys such as
|
|
263
|
+
`toolName` and `tool_name` remain distinct wire/extension keys.
|
|
259
264
|
|
|
260
265
|
### Owned attachments
|
|
261
266
|
|
|
@@ -9,6 +9,7 @@ from . import (
|
|
|
9
9
|
effect,
|
|
10
10
|
event,
|
|
11
11
|
generated,
|
|
12
|
+
wire,
|
|
12
13
|
path,
|
|
13
14
|
registration,
|
|
14
15
|
subscription,
|
|
@@ -24,10 +25,12 @@ from ._models import * # noqa: F403
|
|
|
24
25
|
from ._models import __all__ as _model_names
|
|
25
26
|
from ._hooks import (
|
|
26
27
|
HookResult as HookResult,
|
|
28
|
+
SettlementDecodeError as SettlementDecodeError,
|
|
27
29
|
Hooks as Hooks,
|
|
28
30
|
PendingInvocation as PendingInvocation,
|
|
29
31
|
HooksClosedError as HooksClosedError,
|
|
30
32
|
)
|
|
33
|
+
from ._host_metadata import HostManifestMetadata as HostManifestMetadata
|
|
31
34
|
from .permission import Permission as Permission
|
|
32
35
|
from .content import (
|
|
33
36
|
OwnedContentSource as OwnedContentSource,
|
|
@@ -44,7 +47,6 @@ from .runtime import (
|
|
|
44
47
|
OperationCancelledError as OperationCancelledError,
|
|
45
48
|
)
|
|
46
49
|
|
|
47
|
-
wire = generated
|
|
48
50
|
|
|
49
51
|
__all__ = list(_model_names) + [
|
|
50
52
|
"Attachment",
|
|
@@ -53,6 +55,7 @@ __all__ = list(_model_names) + [
|
|
|
53
55
|
"OwnedContentSource",
|
|
54
56
|
"ContentSources",
|
|
55
57
|
"Permission",
|
|
58
|
+
"HostManifestMetadata",
|
|
56
59
|
"state",
|
|
57
60
|
"candidate",
|
|
58
61
|
"diagnostics",
|
|
@@ -69,6 +72,7 @@ __all__ = list(_model_names) + [
|
|
|
69
72
|
"transport",
|
|
70
73
|
"Hooks",
|
|
71
74
|
"HookResult",
|
|
75
|
+
"SettlementDecodeError",
|
|
72
76
|
"PendingInvocation",
|
|
73
77
|
"ProtocolError",
|
|
74
78
|
"Codec",
|