agenthooksprotocol 0.3.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agenthooksprotocol-0.3.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.4.1}/PKG-INFO +27 -21
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/README.md +26 -20
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/pyproject.toml +1 -1
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/__init__.py +15 -1
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/_boundaries.py +5968 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_content.py +69 -46
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/elicitation.py → agenthooksprotocol-0.4.1/src/agenthooksprotocol/_elicitation.py +50 -13
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_grants.py +6 -21
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_hooks.py +374 -41
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/__init__.py +3173 -522
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/content.py +8 -0
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/_models/effect.py +124 -0
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/_outcomes.py +66 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/ahp-codegen.lock.json +10 -10
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/auth.py +8 -3
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/codec.py +99 -0
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/contract.py +56 -0
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/effect.py +511 -0
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/elicitation.py +8 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/event.py +103 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/generated.py +1793 -347
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/integrations/pydantic.py +152 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/interop.py +9 -8
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/lifecycle.py +25 -7
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/lineage.py +2 -2
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/registration.py +4 -1
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/response.py +268 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/runtime.py +155 -72
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/schemas.json +2465 -358
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/asgi.py +2 -2
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/attachments.py +6 -2
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/hooks.py +13 -10
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/stdio.py +6 -4
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/state.py +3 -4
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/transports/http.py +10 -8
- agenthooksprotocol-0.4.1/src/agenthooksprotocol/wire.py +49 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1/src/agenthooksprotocol.egg-info}/PKG-INFO +27 -21
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol.egg-info/SOURCES.txt +10 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_attachments.py +2 -1
- agenthooksprotocol-0.4.1/tests/test_consumer_outcomes.py +146 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_content.py +4 -1
- agenthooksprotocol-0.4.1/tests/test_correlated_core.py +293 -0
- agenthooksprotocol-0.4.1/tests/test_declared_contracts.py +506 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_inline_messages.py +3 -3
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_integration.py +19 -19
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_integration_edges.py +29 -1
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_interop.py +4 -4
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_lifecycle.py +25 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_observation.py +2 -2
- agenthooksprotocol-0.4.1/tests/test_public_contract_surface.py +86 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_registration_auth.py +20 -25
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_safety.py +22 -8
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_schema_corrections.py +4 -4
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_sdk_unification.py +3 -3
- agenthooksprotocol-0.4.1/tests/test_typed_contract_regressions.py +426 -0
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/_boundaries.py +0 -515
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/_models/effect.py +0 -48
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/codec.py +0 -32
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/effect.py +0 -151
- agenthooksprotocol-0.3.0/src/agenthooksprotocol/integrations/pydantic.py +0 -28
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/LICENSE +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/setup.cfg +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_diagnostics.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/capability.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/event.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/registration.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/subscription.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/tool.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/_models/transport.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/attachment.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/candidate.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/capability.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/catalogue.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/compaction.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/content.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/diagnostics.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/integrations/__init__.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/lifecycle_client.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/lifecycle_server.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/observation_chain.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/path.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/permission.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/py.typed +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/__init__.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/_framing.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/server/_json.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/subscription.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/tool.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/transport.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/transports/__init__.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol/transports/stdio.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_catalogue.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_codec.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_compaction.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_compaction_wire.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_content_lifetime.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_content_receipts.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_elicitation_adapter.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_fixture_public.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_lifecycle_auth.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_observation_chain_transport.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_owned_content.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_owned_content_hooks.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_package.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_public_content.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_public_examples.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_public_hooks.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_public_server.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_session_manifest.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_shared_materialization.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_structural_facade.py +0 -0
- {agenthooksprotocol-0.3.0 → agenthooksprotocol-0.4.1}/tests/test_upload_planning.py +0 -0
{agenthooksprotocol-0.3.0/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.4.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenthooksprotocol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Official non-normative Python SDK for the Agent Hooks Protocol
|
|
5
5
|
Author: Agent Hooks Protocol maintainers
|
|
6
6
|
License:
|
|
@@ -266,6 +266,7 @@ Capability builders are immutable: reusing a base declaration does not widen it.
|
|
|
266
266
|
|
|
267
267
|
Runnable, statically checked consumer programs live in [`examples/`](examples):
|
|
268
268
|
|
|
269
|
+
- `value_codecs.py`: independent argument/result/provenance codecs, admission-time invariants, and a form schema derived from its answer type.
|
|
269
270
|
- `typed_tool.py`: JSON registration, generated tool input, typed grants, initial state, real in-process ASGI handler, borrowed HTTP client, and explicit application decoding.
|
|
270
271
|
- `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.
|
|
271
272
|
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public Handler, denial, and child cleanup.
|
|
@@ -282,9 +283,15 @@ Generated constructors build JSON-shaped protocol values. Generated `TypedDict`
|
|
|
282
283
|
|
|
283
284
|
Capabilities explicitly grant effects and target operations. Supply event capabilities at harness construction; a per-occurrence capability override can narrow them, never widen them. `initial_state` carries the host's native prior decision for one occurrence. A hook-supplied result is a candidate, not an assertion that an operation executed. The host owns its ordinary input validation and actual execution.
|
|
284
285
|
|
|
286
|
+
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
|
+
|
|
288
|
+
`result.decoded_input` exposes the declared argument value. `result.decoded_candidate` is an application `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. Raw `input` and `candidate` views remain detached snapshots. 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
|
+
|
|
290
|
+
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
|
+
|
|
285
292
|
After settlement, explicitly decode effective input with `result.decode_input(codec)`. A codec implements `encode(value)` and `decode(json_value)`; `IdentityCodec` copies JSON. The optional `integrations.pydantic.PydanticCodec` accepts a Pydantic `TypeAdapter`. A decoding error does not retroactively reject protocol effects: retain the settled result, raw effective input, accepted responses, and diagnostics, and report host-level rejection without executing.
|
|
286
293
|
|
|
287
|
-
`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 JSON
|
|
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 JSON. Declared codecs validate accepted modifications; without a declared codec, the host supplies application validation. `decode_input(codec)` remains an explicit, post-settlement host check. Candidate, canonical event, accepted responses, and diagnostics remain accessible.
|
|
288
295
|
|
|
289
296
|
`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.
|
|
290
297
|
|
|
@@ -379,7 +386,7 @@ snapshot; answer-object merge is shallow. Elicitation request bodies are immutab
|
|
|
379
386
|
to modification effects. No text/JSON upload callback is needed. Metadata and omit selections
|
|
380
387
|
cannot authorize body-dependent effects.
|
|
381
388
|
|
|
382
|
-
For custom transports, `begin(canonical_request)` separates acquisition,
|
|
389
|
+
For custom transports, `begin(canonical_request, value_codecs=ValueCodecs(...))` separates acquisition,
|
|
383
390
|
cancellation, and acceptance; `exchange(canonical_request)` uses the same
|
|
384
391
|
settlement engine. Elicitation correlation can be supplied through
|
|
385
392
|
`ContentContext(original_request=..., bindings={"content": ("elicitation", "result")},
|
|
@@ -389,7 +396,7 @@ principal=authenticated_identity)` and `await pending.accept_content()`.
|
|
|
389
396
|
|
|
390
397
|
Semantic namespaces (`event`, `tool`, `effect`, `capability`, and registration models) and all named event boundaries come from schema metadata, not a selected handwritten helper list. Constructors may supply fixed protocol literals; parsers never repair missing input. Open fields are retained for forward-compatible JSON round trips.
|
|
391
398
|
|
|
392
|
-
The low-level `generated` module exports the full schema's typed models, `parse_<root>` / `encode_<root>` codecs, `PROTOCOL_VERSION`, `SCHEMA_REVISION`, and JSON types. `ahp-codegen.lock.json` records immutable generator provenance. Change canonical schemas and generator source in the protocol repository rather than editing generated files.
|
|
399
|
+
The `wire` namespace exposes canonical JSON-shaped request, notification, effect, and response types with structural codecs. Server callbacks use these raw wire contracts. The low-level `generated` module exports the full schema's typed models, `parse_<root>` / `encode_<root>` codecs, `PROTOCOL_VERSION`, `SCHEMA_REVISION`, and JSON types. `ahp-codegen.lock.json` records immutable generator provenance. Change canonical schemas and generator source in the protocol repository rather than editing generated files.
|
|
393
400
|
|
|
394
401
|
## Development
|
|
395
402
|
|
|
@@ -411,8 +418,8 @@ attributes. `ModelVisibleItem` is a canonical message with a typed role and orde
|
|
|
411
418
|
|
|
412
419
|
Facade objects are mappings with read-only attributes.
|
|
413
420
|
Optional attributes return `None` when absent; mapping membership still records
|
|
414
|
-
presence. Attributes conflicting with mapping methods use a trailing underscore
|
|
415
|
-
(such as `items_`), so `dict.items()` keeps working.
|
|
421
|
+
presence. Omitted and explicit-empty response effects are both neutral; decoded effects provide an empty list without modifying the source wire object. Attributes conflicting with mapping methods use a trailing underscore
|
|
422
|
+
(such as `items_`), so `dict.items()` keeps working. Raw wire parsers return lossless mappings. `response.response_for_request(event_type, raw)` returns the generated contextual response selected by the originating event type, not by payload shape.
|
|
416
423
|
|
|
417
424
|
Use generated nested models for typed constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
418
425
|
check location/evidence alternatives and other structural schema constraints. Application
|
|
@@ -446,13 +453,7 @@ the same structural descriptor engine as generated `parse_*` functions. The
|
|
|
446
453
|
original composition is retained, including required members, explicit nulls,
|
|
447
454
|
literals, closed enums, forbidden combinations, and union ambiguity. This is the
|
|
448
455
|
SDK's structural contract, not complete contextual protocol validation.
|
|
449
|
-
`from_dict`
|
|
450
|
-
and privately hydrates nested typed models without recursively calling public
|
|
451
|
-
constructors. For example, a decoded HTTP connection supports
|
|
452
|
-
`connection.gaps[0].reason`, just like a constructed connection. Union selection
|
|
453
|
-
reuses memoized descriptor checks from that decode; subtrees are not repeatedly
|
|
454
|
-
validated during hydration. Extension/application values remain ordinary JSON
|
|
455
|
-
values. Descriptors and hydration metadata are cached.
|
|
456
|
+
`from_dict` checks complete wire values and preserves optional-field presence. Decoded known nested values expose the same attributes as constructed values, such as `connection.gaps[0].reason`. Extension and application bags remain intact and do not acquire effect or attachment authority.
|
|
456
457
|
|
|
457
458
|
Invalid wire models raise `ValueError`; validation failures carry `result`,
|
|
458
459
|
`diagnostics`, and `raw` attributes. Use `parse_*` when you need the full result on
|
|
@@ -470,12 +471,7 @@ objects are validated at the dispatch/parse boundary. Direct dictionary
|
|
|
470
471
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
471
472
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
472
473
|
|
|
473
|
-
|
|
474
|
-
`capability.Capabilities(effects=["modify"], modify={}).modify.input` returns
|
|
475
|
-
`None`, not an attribute error. Already typed child models retain their identity;
|
|
476
|
-
plain nested mappings become typed models after the constructor's single cached
|
|
477
|
-
validation pass. Family queries include all generated per-occurrence capability
|
|
478
|
-
models, such as `capability.ToolBeforeCapabilities`.
|
|
474
|
+
Nested model attributes expose declared protocol fields. For example, `capability.Capabilities(effects=["modify"], modify={}).modify.input` is `None` when that grant is absent. Family queries are available on generic and per-occurrence capability models.
|
|
479
475
|
|
|
480
476
|
Constructor aliases cannot silently overwrite wire keys. Supplying both
|
|
481
477
|
`tool_name=` and `toolName=` raises `TypeError`, even if the values agree. For
|
|
@@ -491,6 +487,8 @@ Use `Attachment.from_bytes(data)` for immutable Python `bytes`, or
|
|
|
491
487
|
`Attachment.lazy(async_loader, aclose=async_cleanup)` to defer reading. Put
|
|
492
488
|
`attachment` directly in an attachment part's `body`. Named `Hooks` methods
|
|
493
489
|
accept both dictionary inputs and typed event inputs with the same owner.
|
|
490
|
+
For a custom `OwnedContentSource`, use the host-only `content.OwnedAttachment(source)` wrapper in an attachment part's body. This wrapper does not read the source and is not a wire value. The SDK transfers and closes its source under the same invocation ownership rules.
|
|
491
|
+
|
|
494
492
|
Metadata belongs on the part. See the runnable
|
|
495
493
|
[file attachment example](examples/file_attachment.py).
|
|
496
494
|
|
|
@@ -521,9 +519,9 @@ matched subscriptions, including observers. One attachment owner is read once
|
|
|
521
519
|
for multiple subscribers. Metadata, omit, and unmatched selections do not read
|
|
522
520
|
or upload its bytes. Result reads and metadata-only delivery require no uploader.
|
|
523
521
|
|
|
524
|
-
Only confirmed receipts are
|
|
522
|
+
Only confirmed receipts are reused, for the exact attachment owner and backend
|
|
525
523
|
ID/subscription index. An uncalled interceptor receives a settled observation
|
|
526
|
-
with its
|
|
524
|
+
with its confirmed reference after denial or stop. Upload failures are handled by
|
|
527
525
|
the affected subscription's failure policy when its delivery is reached; they
|
|
528
526
|
do not prevent independent destinations from preparing their uploads. An upload
|
|
529
527
|
can commit even if an earlier interceptor later removes the attachment or halts
|
|
@@ -539,3 +537,11 @@ not retained.
|
|
|
539
537
|
|
|
540
538
|
Attachments are the sole byte owners. Binary/file editing is not supported.
|
|
541
539
|
Results are invocation-local, not session archives.
|
|
540
|
+
|
|
541
|
+
### Advanced validation and dispatch
|
|
542
|
+
|
|
543
|
+
`runtime.Validator` validates bundled canonical schemas and structural codecs. It does not establish request correlation, effect grants, host approval, or permission to execute. Use `Hooks` for complete interception admission.
|
|
544
|
+
|
|
545
|
+
`server.hooks.Engine` is the framework-neutral backend dispatcher. Supply an explicit validator or static harness manifest when needed. Engine callbacks return protocol effects; the receiving harness validates authority and settles them atomically.
|
|
546
|
+
|
|
547
|
+
Host session counters (`turns`, `modelRequests`, `toolCalls`, `inputTokens`, `outputTokens`, and extra counter names) are exact nonnegative integers. The SDK preserves supplied values and does not infer counters from events.
|
|
@@ -42,6 +42,7 @@ Capability builders are immutable: reusing a base declaration does not widen it.
|
|
|
42
42
|
|
|
43
43
|
Runnable, statically checked consumer programs live in [`examples/`](examples):
|
|
44
44
|
|
|
45
|
+
- `value_codecs.py`: independent argument/result/provenance codecs, admission-time invariants, and a form schema derived from its answer type.
|
|
45
46
|
- `typed_tool.py`: JSON registration, generated tool input, typed grants, initial state, real in-process ASGI handler, borrowed HTTP client, and explicit application decoding.
|
|
46
47
|
- `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.
|
|
47
48
|
- `stdio_tool.py` / `stdio_backend.py`: a real owned backend process using the same public Handler, denial, and child cleanup.
|
|
@@ -58,9 +59,15 @@ Generated constructors build JSON-shaped protocol values. Generated `TypedDict`
|
|
|
58
59
|
|
|
59
60
|
Capabilities explicitly grant effects and target operations. Supply event capabilities at harness construction; a per-occurrence capability override can narrow them, never widen them. `initial_state` carries the host's native prior decision for one occurrence. A hook-supplied result is a candidate, not an assertion that an operation executed. The host owns its ordinary input validation and actual execution.
|
|
60
61
|
|
|
62
|
+
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
|
+
|
|
64
|
+
`result.decoded_input` exposes the declared argument value. `result.decoded_candidate` is an application `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. Raw `input` and `candidate` views remain detached snapshots. 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
|
+
|
|
66
|
+
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
|
+
|
|
61
68
|
After settlement, explicitly decode effective input with `result.decode_input(codec)`. A codec implements `encode(value)` and `decode(json_value)`; `IdentityCodec` copies JSON. The optional `integrations.pydantic.PydanticCodec` accepts a Pydantic `TypeAdapter`. A decoding error does not retroactively reject protocol effects: retain the settled result, raw effective input, accepted responses, and diagnostics, and report host-level rejection without executing.
|
|
62
69
|
|
|
63
|
-
`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 JSON
|
|
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 JSON. Declared codecs validate accepted modifications; without a declared codec, the host supplies application validation. `decode_input(codec)` remains an explicit, post-settlement host check. Candidate, canonical event, accepted responses, and diagnostics remain accessible.
|
|
64
71
|
|
|
65
72
|
`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.
|
|
66
73
|
|
|
@@ -155,7 +162,7 @@ snapshot; answer-object merge is shallow. Elicitation request bodies are immutab
|
|
|
155
162
|
to modification effects. No text/JSON upload callback is needed. Metadata and omit selections
|
|
156
163
|
cannot authorize body-dependent effects.
|
|
157
164
|
|
|
158
|
-
For custom transports, `begin(canonical_request)` separates acquisition,
|
|
165
|
+
For custom transports, `begin(canonical_request, value_codecs=ValueCodecs(...))` separates acquisition,
|
|
159
166
|
cancellation, and acceptance; `exchange(canonical_request)` uses the same
|
|
160
167
|
settlement engine. Elicitation correlation can be supplied through
|
|
161
168
|
`ContentContext(original_request=..., bindings={"content": ("elicitation", "result")},
|
|
@@ -165,7 +172,7 @@ principal=authenticated_identity)` and `await pending.accept_content()`.
|
|
|
165
172
|
|
|
166
173
|
Semantic namespaces (`event`, `tool`, `effect`, `capability`, and registration models) and all named event boundaries come from schema metadata, not a selected handwritten helper list. Constructors may supply fixed protocol literals; parsers never repair missing input. Open fields are retained for forward-compatible JSON round trips.
|
|
167
174
|
|
|
168
|
-
The low-level `generated` module exports the full schema's typed models, `parse_<root>` / `encode_<root>` codecs, `PROTOCOL_VERSION`, `SCHEMA_REVISION`, and JSON types. `ahp-codegen.lock.json` records immutable generator provenance. Change canonical schemas and generator source in the protocol repository rather than editing generated files.
|
|
175
|
+
The `wire` namespace exposes canonical JSON-shaped request, notification, effect, and response types with structural codecs. Server callbacks use these raw wire contracts. The low-level `generated` module exports the full schema's typed models, `parse_<root>` / `encode_<root>` codecs, `PROTOCOL_VERSION`, `SCHEMA_REVISION`, and JSON types. `ahp-codegen.lock.json` records immutable generator provenance. Change canonical schemas and generator source in the protocol repository rather than editing generated files.
|
|
169
176
|
|
|
170
177
|
## Development
|
|
171
178
|
|
|
@@ -187,8 +194,8 @@ attributes. `ModelVisibleItem` is a canonical message with a typed role and orde
|
|
|
187
194
|
|
|
188
195
|
Facade objects are mappings with read-only attributes.
|
|
189
196
|
Optional attributes return `None` when absent; mapping membership still records
|
|
190
|
-
presence. Attributes conflicting with mapping methods use a trailing underscore
|
|
191
|
-
(such as `items_`), so `dict.items()` keeps working.
|
|
197
|
+
presence. Omitted and explicit-empty response effects are both neutral; decoded effects provide an empty list without modifying the source wire object. Attributes conflicting with mapping methods use a trailing underscore
|
|
198
|
+
(such as `items_`), so `dict.items()` keeps working. Raw wire parsers return lossless mappings. `response.response_for_request(event_type, raw)` returns the generated contextual response selected by the originating event type, not by payload shape.
|
|
192
199
|
|
|
193
200
|
Use generated nested models for typed constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
194
201
|
check location/evidence alternatives and other structural schema constraints. Application
|
|
@@ -222,13 +229,7 @@ the same structural descriptor engine as generated `parse_*` functions. The
|
|
|
222
229
|
original composition is retained, including required members, explicit nulls,
|
|
223
230
|
literals, closed enums, forbidden combinations, and union ambiguity. This is the
|
|
224
231
|
SDK's structural contract, not complete contextual protocol validation.
|
|
225
|
-
`from_dict`
|
|
226
|
-
and privately hydrates nested typed models without recursively calling public
|
|
227
|
-
constructors. For example, a decoded HTTP connection supports
|
|
228
|
-
`connection.gaps[0].reason`, just like a constructed connection. Union selection
|
|
229
|
-
reuses memoized descriptor checks from that decode; subtrees are not repeatedly
|
|
230
|
-
validated during hydration. Extension/application values remain ordinary JSON
|
|
231
|
-
values. Descriptors and hydration metadata are cached.
|
|
232
|
+
`from_dict` checks complete wire values and preserves optional-field presence. Decoded known nested values expose the same attributes as constructed values, such as `connection.gaps[0].reason`. Extension and application bags remain intact and do not acquire effect or attachment authority.
|
|
232
233
|
|
|
233
234
|
Invalid wire models raise `ValueError`; validation failures carry `result`,
|
|
234
235
|
`diagnostics`, and `raw` attributes. Use `parse_*` when you need the full result on
|
|
@@ -246,12 +247,7 @@ objects are validated at the dispatch/parse boundary. Direct dictionary
|
|
|
246
247
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
247
248
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
248
249
|
|
|
249
|
-
|
|
250
|
-
`capability.Capabilities(effects=["modify"], modify={}).modify.input` returns
|
|
251
|
-
`None`, not an attribute error. Already typed child models retain their identity;
|
|
252
|
-
plain nested mappings become typed models after the constructor's single cached
|
|
253
|
-
validation pass. Family queries include all generated per-occurrence capability
|
|
254
|
-
models, such as `capability.ToolBeforeCapabilities`.
|
|
250
|
+
Nested model attributes expose declared protocol fields. For example, `capability.Capabilities(effects=["modify"], modify={}).modify.input` is `None` when that grant is absent. Family queries are available on generic and per-occurrence capability models.
|
|
255
251
|
|
|
256
252
|
Constructor aliases cannot silently overwrite wire keys. Supplying both
|
|
257
253
|
`tool_name=` and `toolName=` raises `TypeError`, even if the values agree. For
|
|
@@ -267,6 +263,8 @@ Use `Attachment.from_bytes(data)` for immutable Python `bytes`, or
|
|
|
267
263
|
`Attachment.lazy(async_loader, aclose=async_cleanup)` to defer reading. Put
|
|
268
264
|
`attachment` directly in an attachment part's `body`. Named `Hooks` methods
|
|
269
265
|
accept both dictionary inputs and typed event inputs with the same owner.
|
|
266
|
+
For a custom `OwnedContentSource`, use the host-only `content.OwnedAttachment(source)` wrapper in an attachment part's body. This wrapper does not read the source and is not a wire value. The SDK transfers and closes its source under the same invocation ownership rules.
|
|
267
|
+
|
|
270
268
|
Metadata belongs on the part. See the runnable
|
|
271
269
|
[file attachment example](examples/file_attachment.py).
|
|
272
270
|
|
|
@@ -297,9 +295,9 @@ matched subscriptions, including observers. One attachment owner is read once
|
|
|
297
295
|
for multiple subscribers. Metadata, omit, and unmatched selections do not read
|
|
298
296
|
or upload its bytes. Result reads and metadata-only delivery require no uploader.
|
|
299
297
|
|
|
300
|
-
Only confirmed receipts are
|
|
298
|
+
Only confirmed receipts are reused, for the exact attachment owner and backend
|
|
301
299
|
ID/subscription index. An uncalled interceptor receives a settled observation
|
|
302
|
-
with its
|
|
300
|
+
with its confirmed reference after denial or stop. Upload failures are handled by
|
|
303
301
|
the affected subscription's failure policy when its delivery is reached; they
|
|
304
302
|
do not prevent independent destinations from preparing their uploads. An upload
|
|
305
303
|
can commit even if an earlier interceptor later removes the attachment or halts
|
|
@@ -315,3 +313,11 @@ not retained.
|
|
|
315
313
|
|
|
316
314
|
Attachments are the sole byte owners. Binary/file editing is not supported.
|
|
317
315
|
Results are invocation-local, not session archives.
|
|
316
|
+
|
|
317
|
+
### Advanced validation and dispatch
|
|
318
|
+
|
|
319
|
+
`runtime.Validator` validates bundled canonical schemas and structural codecs. It does not establish request correlation, effect grants, host approval, or permission to execute. Use `Hooks` for complete interception admission.
|
|
320
|
+
|
|
321
|
+
`server.hooks.Engine` is the framework-neutral backend dispatcher. Supply an explicit validator or static harness manifest when needed. Engine callbacks return protocol effects; the receiving harness validates authority and settles them atomically.
|
|
322
|
+
|
|
323
|
+
Host session counters (`turns`, `modelRequests`, `toolCalls`, `inputTokens`, `outputTokens`, and extra counter names) are exact nonnegative integers. The SDK preserves supplied values and does not infer counters from events.
|
|
@@ -24,21 +24,30 @@ from ._models import * # noqa: F403
|
|
|
24
24
|
from ._models import __all__ as _model_names
|
|
25
25
|
from ._hooks import (
|
|
26
26
|
HookResult as HookResult,
|
|
27
|
+
SettlementDecodeError as SettlementDecodeError,
|
|
27
28
|
Hooks as Hooks,
|
|
28
29
|
PendingInvocation as PendingInvocation,
|
|
29
30
|
HooksClosedError as HooksClosedError,
|
|
30
31
|
)
|
|
32
|
+
from ._outcomes import SchemaCandidate as SchemaCandidate
|
|
31
33
|
from .permission import Permission as Permission
|
|
32
34
|
from .content import (
|
|
33
35
|
OwnedContentSource as OwnedContentSource,
|
|
34
36
|
ContentSources as ContentSources,
|
|
35
37
|
)
|
|
36
|
-
from .codec import
|
|
38
|
+
from .codec import (
|
|
39
|
+
ValueCodecs as ValueCodecs,
|
|
40
|
+
Codec as Codec,
|
|
41
|
+
IdentityCodec as IdentityCodec,
|
|
42
|
+
Candidate as Candidate,
|
|
43
|
+
)
|
|
37
44
|
from .runtime import (
|
|
38
45
|
ProtocolError as ProtocolError,
|
|
39
46
|
OperationCancelledError as OperationCancelledError,
|
|
40
47
|
)
|
|
41
48
|
|
|
49
|
+
wire = generated
|
|
50
|
+
|
|
42
51
|
__all__ = list(_model_names) + [
|
|
43
52
|
"Attachment",
|
|
44
53
|
"AttachmentContents",
|
|
@@ -62,9 +71,14 @@ __all__ = list(_model_names) + [
|
|
|
62
71
|
"transport",
|
|
63
72
|
"Hooks",
|
|
64
73
|
"HookResult",
|
|
74
|
+
"SettlementDecodeError",
|
|
75
|
+
"SchemaCandidate",
|
|
65
76
|
"PendingInvocation",
|
|
66
77
|
"ProtocolError",
|
|
67
78
|
"Codec",
|
|
79
|
+
"ValueCodecs",
|
|
80
|
+
"Candidate",
|
|
68
81
|
"IdentityCodec",
|
|
69
82
|
"generated",
|
|
83
|
+
"wire",
|
|
70
84
|
]
|