agenthooksprotocol 0.1.1__tar.gz → 0.2.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.1.1/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.2.0}/PKG-INFO +61 -18
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/README.md +60 -17
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/pyproject.toml +1 -1
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/__init__.py +6 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_content.py +181 -64
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_hooks.py +142 -7
- agenthooksprotocol-0.2.0/src/agenthooksprotocol/attachment.py +136 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/content.py +4 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0/src/agenthooksprotocol.egg-info}/PKG-INFO +61 -18
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol.egg-info/SOURCES.txt +2 -0
- agenthooksprotocol-0.2.0/tests/test_attachments.py +731 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_content_lifetime.py +3 -2
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_public_content.py +1 -1
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/LICENSE +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/setup.cfg +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_boundaries.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_diagnostics.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_grants.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/__init__.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/capability.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/content.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/effect.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/event.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/registration.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/subscription.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/tool.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/_models/transport.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/ahp-codegen.lock.json +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/auth.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/candidate.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/capability.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/catalogue.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/codec.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/compaction.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/diagnostics.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/effect.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/elicitation.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/event.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/generated.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/integrations/__init__.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/integrations/pydantic.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/interop.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/lifecycle.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/lifecycle_client.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/lifecycle_server.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/lineage.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/observation_chain.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/path.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/permission.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/py.typed +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/registration.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/runtime.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/schemas.json +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/__init__.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/_framing.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/_json.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/asgi.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/attachments.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/hooks.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/server/stdio.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/state.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/subscription.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/tool.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/transport.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/transports/__init__.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/transports/http.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol/transports/stdio.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol.egg-info/dependency_links.txt +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol.egg-info/requires.txt +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/src/agenthooksprotocol.egg-info/top_level.txt +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_catalogue.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_codec.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_compaction.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_compaction_wire.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_content.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_content_receipts.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_fixture_public.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_integration.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_integration_edges.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_interop.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_lifecycle.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_lifecycle_auth.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_observation.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_observation_chain_transport.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_owned_content.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_owned_content_hooks.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_package.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_public_examples.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_public_hooks.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_public_server.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_registration_auth.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_safety.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_schema_corrections.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_sdk_unification.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_session_manifest.py +0 -0
- {agenthooksprotocol-0.1.1 → agenthooksprotocol-0.2.0}/tests/test_structural_facade.py +0 -0
{agenthooksprotocol-0.1.1/src/agenthooksprotocol.egg-info → agenthooksprotocol-0.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenthooksprotocol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Official non-normative Python SDK for the Agent Hooks Protocol
|
|
5
5
|
Author: Agent Hooks Protocol maintainers
|
|
6
6
|
License:
|
|
@@ -228,10 +228,10 @@ A typed, asynchronous SDK for the [Agent Hooks Protocol](https://github.com/agen
|
|
|
228
228
|
|
|
229
229
|
## Install
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
Install the published `agenthooksprotocol` package from [PyPI](https://pypi.org/project/agenthooksprotocol/):
|
|
232
232
|
|
|
233
233
|
```sh
|
|
234
|
-
python -m pip install 'agenthooksprotocol[http]
|
|
234
|
+
python -m pip install 'agenthooksprotocol[http]'
|
|
235
235
|
```
|
|
236
236
|
|
|
237
237
|
HTTPX is an opt-in dependency (`[http]`). Pydantic is an independent opt-in integration (`[pydantic]`); neither a web framework nor Pydantic is needed for the core SDK or stdio.
|
|
@@ -260,7 +260,7 @@ async def review(existing_arguments):
|
|
|
260
260
|
return result # Host validation and authorization still precede execution.
|
|
261
261
|
```
|
|
262
262
|
|
|
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`
|
|
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.
|
|
264
264
|
|
|
265
265
|
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
266
|
|
|
@@ -313,7 +313,7 @@ The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optiona
|
|
|
313
313
|
|
|
314
314
|
## Static host manifest
|
|
315
315
|
|
|
316
|
-
Configure `Hooks(config, *, source, manifest=full_manifest, transport=None, auth_provider=None)` with a complete canonical static manifest, **or** use
|
|
316
|
+
Configure `Hooks(config, *, source, manifest=full_manifest, transport=None, auth_provider=None)` with a complete canonical static manifest, **or** use `capabilities=event_declarations`. These options are mutually exclusive. The full manifest supplies the same event/mode/effect authority used for admission; it is not backend discovery. `hooks.manifest` returns a detached snapshot.
|
|
317
317
|
|
|
318
318
|
The event-map shorthand advertises only explicitly declared events and modes. Its derived manifest records a gap for every omitted canonical event and for host-level facilities that the map cannot establish (transports, authentication, tool paths, content categories, limits, and managed policy). Empty lists do not mean universal support. Use a full manifest to declare those facilities accurately; explicit full manifests are preserved rather than augmented with assumed support.
|
|
319
319
|
|
|
@@ -346,7 +346,7 @@ Pass `content=context` to the named boundary. An elicitation result needs the or
|
|
|
346
346
|
|
|
347
347
|
`OwnedContentSource(stream, max_bytes=..., timeout=...)` wraps an async native `read(size)`/`receive(size)` stream with `aclose()`. Construction does not read. Size/hash expectations are optional and verified, not trusted. Generated inputs expose named `bind_<slot>_source(source)` methods; repeated content slots additionally require `index=`. For example, `input.bind_items_source(source, index=0)` binds an existing metadata item without putting the stream into wire JSON.
|
|
348
348
|
|
|
349
|
-
Pass the bound input to a named hook method with `uploads={backend_id: authorized_upload_callback}`. Each callback receives immutable bytes and returns a receiver-allocated canonical upload receipt; it can use `auth.AuthenticatedHTTPTransport.upload(..., authentication=upload_binding)`. Metadata, omit, and unmatched receivers consume no bytes and require no uploader. Selected body delivery snapshots once within limits, hashes actual raw bytes, uploads independently for each authorized destination, and verifies receipts before event delivery. Missing receiver authorization fails before reading.
|
|
349
|
+
Pass the bound input to a named hook method with `uploads={backend_id: authorized_upload_callback}`. Each callback receives immutable bytes and returns a receiver-allocated canonical upload receipt; it can use `auth.AuthenticatedHTTPTransport.upload(..., authentication=upload_binding)`. Metadata, omit, and unmatched receivers consume no bytes and require no uploader. Selected body delivery snapshots once within limits, hashes actual raw bytes, uploads independently for each authorized destination, and verifies receipts before event delivery. Missing receiver authorization fails before reading. Use `ContentSources` to supply explicit source bindings, or `ContentContext` to resolve canonical references.
|
|
350
350
|
|
|
351
351
|
Ownership transfers to the source wrapper, then to the hook operation when passed. Streams close on success, failure, cancellation, and unused selection; snapshots release when the call finishes. Closed wrappers also release their reader, so retaining a source or result does not retain the reader’s input buffer. Sources are single-operation values, not concurrently reusable. Call `aclose()` or use an async context manager for a source that never reaches a hook call. Cleanup is shielded and bounded separately from the operation budget.
|
|
352
352
|
|
|
@@ -376,14 +376,12 @@ compositions. MCP connection `gaps` parameters accept lists of typed gap models,
|
|
|
376
376
|
and HTTP, SSE, stdio, and custom connection objects expose typed location and gap
|
|
377
377
|
attributes. `ModelVisibleItem` has composed content constructors with typed roles.
|
|
378
378
|
|
|
379
|
-
Facade objects
|
|
379
|
+
Facade objects are mappings with read-only attributes.
|
|
380
380
|
Optional attributes return `None` when absent; mapping membership still records
|
|
381
381
|
presence. Attributes conflicting with mapping methods use a trailing underscore
|
|
382
|
-
(such as `items_`), so `dict.items()` keeps working. Wire parsers
|
|
383
|
-
return lossless mappings, not hydrated facade instances.
|
|
382
|
+
(such as `items_`), so `dict.items()` keeps working. Wire parsers return lossless mappings, not hydrated facade instances.
|
|
384
383
|
|
|
385
|
-
|
|
386
|
-
constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
384
|
+
Use generated nested models for typed constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
387
385
|
check location/evidence alternatives and other structural schema constraints. Application
|
|
388
386
|
JSON, native payloads, and extension values remain intentionally dynamic.
|
|
389
387
|
|
|
@@ -410,7 +408,7 @@ support. The result is **not authorization**: it does not check target, operatio
|
|
|
410
408
|
mode, contextual restrictions, or permission. `EffectName` contains schema-derived
|
|
411
409
|
known identifiers; custom family identifiers remain ordinary strings.
|
|
412
410
|
|
|
413
|
-
Wire-model keyword constructors and `Model.from_dict(mapping)`
|
|
411
|
+
Wire-model keyword constructors and `Model.from_dict(mapping)` validate with
|
|
414
412
|
the same structural descriptor engine as generated `parse_*` functions. The
|
|
415
413
|
original composition is retained, including required members, explicit nulls,
|
|
416
414
|
literals, closed enums, forbidden combinations, and union ambiguity. This is the
|
|
@@ -431,17 +429,14 @@ non-finite numbers and non-JSON values are rejected. Parsing raw JSON through
|
|
|
431
429
|
`parse_*` retains decimal precision; decoding it first with standard-library
|
|
432
430
|
`json.loads` cannot recover precision already lost to a float.
|
|
433
431
|
|
|
434
|
-
|
|
435
|
-
those calls now fail immediately. Provide valid enum/literal values and all
|
|
436
|
-
required wire members to `from_dict`. A null candidate, a candidate with
|
|
432
|
+
Provide valid enum/literal values and all required wire members to `from_dict`. A null candidate, a candidate with
|
|
437
433
|
`{"value": null}`, and a candidate with `{"value": 0}` remain distinct; omitting a
|
|
438
434
|
required candidate is an error. Generated input projections and capability grant
|
|
439
435
|
builders are construction helpers, not wire decode entrypoints; their final wire
|
|
440
|
-
objects are validated at the
|
|
436
|
+
objects are validated at the dispatch/parse boundary. Direct dictionary
|
|
441
437
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
442
438
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
443
439
|
|
|
444
|
-
|
|
445
440
|
Wire constructors also privately hydrate nested mappings: for example,
|
|
446
441
|
`capability.Capabilities(effects=["modify"], modify={}).modify.input` returns
|
|
447
442
|
`None`, not an attribute error. Already typed child models retain their identity;
|
|
@@ -453,6 +448,54 @@ Constructor aliases cannot silently overwrite wire keys. Supplying both
|
|
|
453
448
|
`tool_name=` and `toolName=` raises `TypeError`, even if the values agree. For
|
|
454
449
|
optional/defaulted fields, a lone wire spelling (for example `addressForm=` or
|
|
455
450
|
`protocolVersion=`) is used and validated rather than replaced by a default.
|
|
456
|
-
Required constructor parameters
|
|
451
|
+
Required constructor parameters use their documented Python spelling;
|
|
457
452
|
use `from_dict` to decode complete wire-keyed objects. In `from_dict`, distinct
|
|
458
453
|
keys such as `toolName` and `tool_name` remain distinct wire/extension keys.
|
|
454
|
+
|
|
455
|
+
### Owned attachments
|
|
456
|
+
|
|
457
|
+
The owned attachment APIs in this section are not included in the published 0.1.1 package.
|
|
458
|
+
|
|
459
|
+
Use `Attachment.from_bytes(data)` for immutable Python `bytes`, or
|
|
460
|
+
`Attachment.lazy(async_loader, aclose=async_cleanup)` to defer reading. Bind it
|
|
461
|
+
with the typed input's `bind_*_source` method on an item without a body reference.
|
|
462
|
+
Content IDs, media types, and other metadata belong on the content item. See the
|
|
463
|
+
runnable [file attachment example](examples/file_attachment.py).
|
|
464
|
+
|
|
465
|
+
Dispatch transfers ownership once. Metadata-only and unmatched deliveries do not
|
|
466
|
+
invoke the loader. Selected body deliveries share one immutable snapshot, with
|
|
467
|
+
receiver-specific references. Successful results own the bytes **or still-unread
|
|
468
|
+
loader**, independent of `Hooks` shutdown. Use `async with result:` or
|
|
469
|
+
`await result.aclose()` and read with
|
|
470
|
+
`await result.attachments.read("context.compact.before.items[0]")`. Cleanup runs
|
|
471
|
+
for unopened sources too. Do not reuse an attachment across invocations; create a
|
|
472
|
+
new attachment (which can share the same immutable `bytes`) instead.
|
|
473
|
+
|
|
474
|
+
The default accepted size is 4 MiB and lazy loading timeout is 30 seconds;
|
|
475
|
+
constructors accept `max_bytes` and lazy construction accepts `timeout`. Loaders
|
|
476
|
+
must bound their own allocations. `Attachment(async_stream, ...)` also supports
|
|
477
|
+
the bounded `read(size)`/`receive(max_bytes)` source contract. Stream cleanup
|
|
478
|
+
is shielded and bounded to one second. Cancellation and failure never retry a
|
|
479
|
+
loader. Python mutable buffers are rejected, and reads
|
|
480
|
+
return immutable bytes.
|
|
481
|
+
|
|
482
|
+
Remote body delivery requires an authorized receiver uploader through
|
|
483
|
+
`uploads={backend_id: async_upload}`. The uploader supplies a reference that the
|
|
484
|
+
receiver can read.
|
|
485
|
+
Metadata-only delivery and result reads require no uploader.
|
|
486
|
+
|
|
487
|
+
Attachments own their immutable bytes directly. A lazy loader's `bytes` object
|
|
488
|
+
becomes the attachment snapshot; upload and result reads borrow that same object.
|
|
489
|
+
Receiver applications manage upload storage.
|
|
490
|
+
|
|
491
|
+
Compaction text and elicitation JSON effects read selected attachment
|
|
492
|
+
owners. An accepted replacement creates a new effective attachment; subsequent
|
|
493
|
+
deliveries and the result use that owner. Original attachment size bounds also
|
|
494
|
+
apply to replacements. Generated candidates are available through
|
|
495
|
+
`await result.attachments.read("candidate")`. Binary/file editing is not supported.
|
|
496
|
+
Elicitation results require their original request correlation context;
|
|
497
|
+
`ContentContext.attachments` can provide the original request owner without a
|
|
498
|
+
resolver. A resolver is an optional
|
|
499
|
+
adapter for externally supplied references.
|
|
500
|
+
|
|
501
|
+
Results are invocation-local, not session archives.
|
|
@@ -4,10 +4,10 @@ A typed, asynchronous SDK for the [Agent Hooks Protocol](https://github.com/agen
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install the published `agenthooksprotocol` package from [PyPI](https://pypi.org/project/agenthooksprotocol/):
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
python -m pip install 'agenthooksprotocol[http]
|
|
10
|
+
python -m pip install 'agenthooksprotocol[http]'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
HTTPX is an opt-in dependency (`[http]`). Pydantic is an independent opt-in integration (`[pydantic]`); neither a web framework nor Pydantic is needed for the core SDK or stdio.
|
|
@@ -36,7 +36,7 @@ async def review(existing_arguments):
|
|
|
36
36
|
return result # Host validation and authorization still precede execution.
|
|
37
37
|
```
|
|
38
38
|
|
|
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`
|
|
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.
|
|
40
40
|
|
|
41
41
|
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
42
|
|
|
@@ -89,7 +89,7 @@ The default `auth.EnvironmentAuthProvider` resolves bearer `tokenEnv` or optiona
|
|
|
89
89
|
|
|
90
90
|
## Static host manifest
|
|
91
91
|
|
|
92
|
-
Configure `Hooks(config, *, source, manifest=full_manifest, transport=None, auth_provider=None)` with a complete canonical static manifest, **or** use
|
|
92
|
+
Configure `Hooks(config, *, source, manifest=full_manifest, transport=None, auth_provider=None)` with a complete canonical static manifest, **or** use `capabilities=event_declarations`. These options are mutually exclusive. The full manifest supplies the same event/mode/effect authority used for admission; it is not backend discovery. `hooks.manifest` returns a detached snapshot.
|
|
93
93
|
|
|
94
94
|
The event-map shorthand advertises only explicitly declared events and modes. Its derived manifest records a gap for every omitted canonical event and for host-level facilities that the map cannot establish (transports, authentication, tool paths, content categories, limits, and managed policy). Empty lists do not mean universal support. Use a full manifest to declare those facilities accurately; explicit full manifests are preserved rather than augmented with assumed support.
|
|
95
95
|
|
|
@@ -122,7 +122,7 @@ Pass `content=context` to the named boundary. An elicitation result needs the or
|
|
|
122
122
|
|
|
123
123
|
`OwnedContentSource(stream, max_bytes=..., timeout=...)` wraps an async native `read(size)`/`receive(size)` stream with `aclose()`. Construction does not read. Size/hash expectations are optional and verified, not trusted. Generated inputs expose named `bind_<slot>_source(source)` methods; repeated content slots additionally require `index=`. For example, `input.bind_items_source(source, index=0)` binds an existing metadata item without putting the stream into wire JSON.
|
|
124
124
|
|
|
125
|
-
Pass the bound input to a named hook method with `uploads={backend_id: authorized_upload_callback}`. Each callback receives immutable bytes and returns a receiver-allocated canonical upload receipt; it can use `auth.AuthenticatedHTTPTransport.upload(..., authentication=upload_binding)`. Metadata, omit, and unmatched receivers consume no bytes and require no uploader. Selected body delivery snapshots once within limits, hashes actual raw bytes, uploads independently for each authorized destination, and verifies receipts before event delivery. Missing receiver authorization fails before reading.
|
|
125
|
+
Pass the bound input to a named hook method with `uploads={backend_id: authorized_upload_callback}`. Each callback receives immutable bytes and returns a receiver-allocated canonical upload receipt; it can use `auth.AuthenticatedHTTPTransport.upload(..., authentication=upload_binding)`. Metadata, omit, and unmatched receivers consume no bytes and require no uploader. Selected body delivery snapshots once within limits, hashes actual raw bytes, uploads independently for each authorized destination, and verifies receipts before event delivery. Missing receiver authorization fails before reading. Use `ContentSources` to supply explicit source bindings, or `ContentContext` to resolve canonical references.
|
|
126
126
|
|
|
127
127
|
Ownership transfers to the source wrapper, then to the hook operation when passed. Streams close on success, failure, cancellation, and unused selection; snapshots release when the call finishes. Closed wrappers also release their reader, so retaining a source or result does not retain the reader’s input buffer. Sources are single-operation values, not concurrently reusable. Call `aclose()` or use an async context manager for a source that never reaches a hook call. Cleanup is shielded and bounded separately from the operation budget.
|
|
128
128
|
|
|
@@ -152,14 +152,12 @@ compositions. MCP connection `gaps` parameters accept lists of typed gap models,
|
|
|
152
152
|
and HTTP, SSE, stdio, and custom connection objects expose typed location and gap
|
|
153
153
|
attributes. `ModelVisibleItem` has composed content constructors with typed roles.
|
|
154
154
|
|
|
155
|
-
Facade objects
|
|
155
|
+
Facade objects are mappings with read-only attributes.
|
|
156
156
|
Optional attributes return `None` when absent; mapping membership still records
|
|
157
157
|
presence. Attributes conflicting with mapping methods use a trailing underscore
|
|
158
|
-
(such as `items_`), so `dict.items()` keeps working. Wire parsers
|
|
159
|
-
return lossless mappings, not hydrated facade instances.
|
|
158
|
+
(such as `items_`), so `dict.items()` keeps working. Wire parsers return lossless mappings, not hydrated facade instances.
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
160
|
+
Use generated nested models for typed constructor arguments. Wire constructors, dictionary decoding, and runtime parsing
|
|
163
161
|
check location/evidence alternatives and other structural schema constraints. Application
|
|
164
162
|
JSON, native payloads, and extension values remain intentionally dynamic.
|
|
165
163
|
|
|
@@ -186,7 +184,7 @@ support. The result is **not authorization**: it does not check target, operatio
|
|
|
186
184
|
mode, contextual restrictions, or permission. `EffectName` contains schema-derived
|
|
187
185
|
known identifiers; custom family identifiers remain ordinary strings.
|
|
188
186
|
|
|
189
|
-
Wire-model keyword constructors and `Model.from_dict(mapping)`
|
|
187
|
+
Wire-model keyword constructors and `Model.from_dict(mapping)` validate with
|
|
190
188
|
the same structural descriptor engine as generated `parse_*` functions. The
|
|
191
189
|
original composition is retained, including required members, explicit nulls,
|
|
192
190
|
literals, closed enums, forbidden combinations, and union ambiguity. This is the
|
|
@@ -207,17 +205,14 @@ non-finite numbers and non-JSON values are rejected. Parsing raw JSON through
|
|
|
207
205
|
`parse_*` retains decimal precision; decoding it first with standard-library
|
|
208
206
|
`json.loads` cannot recover precision already lost to a float.
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
those calls now fail immediately. Provide valid enum/literal values and all
|
|
212
|
-
required wire members to `from_dict`. A null candidate, a candidate with
|
|
208
|
+
Provide valid enum/literal values and all required wire members to `from_dict`. A null candidate, a candidate with
|
|
213
209
|
`{"value": null}`, and a candidate with `{"value": 0}` remain distinct; omitting a
|
|
214
210
|
required candidate is an error. Generated input projections and capability grant
|
|
215
211
|
builders are construction helpers, not wire decode entrypoints; their final wire
|
|
216
|
-
objects are validated at the
|
|
212
|
+
objects are validated at the dispatch/parse boundary. Direct dictionary
|
|
217
213
|
mutation, standard-library `json.loads`, TypedDict annotations, and type assertions
|
|
218
214
|
are not SDK validation entrypoints. Re-parse after manually mutating a mapping.
|
|
219
215
|
|
|
220
|
-
|
|
221
216
|
Wire constructors also privately hydrate nested mappings: for example,
|
|
222
217
|
`capability.Capabilities(effects=["modify"], modify={}).modify.input` returns
|
|
223
218
|
`None`, not an attribute error. Already typed child models retain their identity;
|
|
@@ -229,6 +224,54 @@ Constructor aliases cannot silently overwrite wire keys. Supplying both
|
|
|
229
224
|
`tool_name=` and `toolName=` raises `TypeError`, even if the values agree. For
|
|
230
225
|
optional/defaulted fields, a lone wire spelling (for example `addressForm=` or
|
|
231
226
|
`protocolVersion=`) is used and validated rather than replaced by a default.
|
|
232
|
-
Required constructor parameters
|
|
227
|
+
Required constructor parameters use their documented Python spelling;
|
|
233
228
|
use `from_dict` to decode complete wire-keyed objects. In `from_dict`, distinct
|
|
234
229
|
keys such as `toolName` and `tool_name` remain distinct wire/extension keys.
|
|
230
|
+
|
|
231
|
+
### Owned attachments
|
|
232
|
+
|
|
233
|
+
The owned attachment APIs in this section are not included in the published 0.1.1 package.
|
|
234
|
+
|
|
235
|
+
Use `Attachment.from_bytes(data)` for immutable Python `bytes`, or
|
|
236
|
+
`Attachment.lazy(async_loader, aclose=async_cleanup)` to defer reading. Bind it
|
|
237
|
+
with the typed input's `bind_*_source` method on an item without a body reference.
|
|
238
|
+
Content IDs, media types, and other metadata belong on the content item. See the
|
|
239
|
+
runnable [file attachment example](examples/file_attachment.py).
|
|
240
|
+
|
|
241
|
+
Dispatch transfers ownership once. Metadata-only and unmatched deliveries do not
|
|
242
|
+
invoke the loader. Selected body deliveries share one immutable snapshot, with
|
|
243
|
+
receiver-specific references. Successful results own the bytes **or still-unread
|
|
244
|
+
loader**, independent of `Hooks` shutdown. Use `async with result:` or
|
|
245
|
+
`await result.aclose()` and read with
|
|
246
|
+
`await result.attachments.read("context.compact.before.items[0]")`. Cleanup runs
|
|
247
|
+
for unopened sources too. Do not reuse an attachment across invocations; create a
|
|
248
|
+
new attachment (which can share the same immutable `bytes`) instead.
|
|
249
|
+
|
|
250
|
+
The default accepted size is 4 MiB and lazy loading timeout is 30 seconds;
|
|
251
|
+
constructors accept `max_bytes` and lazy construction accepts `timeout`. Loaders
|
|
252
|
+
must bound their own allocations. `Attachment(async_stream, ...)` also supports
|
|
253
|
+
the bounded `read(size)`/`receive(max_bytes)` source contract. Stream cleanup
|
|
254
|
+
is shielded and bounded to one second. Cancellation and failure never retry a
|
|
255
|
+
loader. Python mutable buffers are rejected, and reads
|
|
256
|
+
return immutable bytes.
|
|
257
|
+
|
|
258
|
+
Remote body delivery requires an authorized receiver uploader through
|
|
259
|
+
`uploads={backend_id: async_upload}`. The uploader supplies a reference that the
|
|
260
|
+
receiver can read.
|
|
261
|
+
Metadata-only delivery and result reads require no uploader.
|
|
262
|
+
|
|
263
|
+
Attachments own their immutable bytes directly. A lazy loader's `bytes` object
|
|
264
|
+
becomes the attachment snapshot; upload and result reads borrow that same object.
|
|
265
|
+
Receiver applications manage upload storage.
|
|
266
|
+
|
|
267
|
+
Compaction text and elicitation JSON effects read selected attachment
|
|
268
|
+
owners. An accepted replacement creates a new effective attachment; subsequent
|
|
269
|
+
deliveries and the result use that owner. Original attachment size bounds also
|
|
270
|
+
apply to replacements. Generated candidates are available through
|
|
271
|
+
`await result.attachments.read("candidate")`. Binary/file editing is not supported.
|
|
272
|
+
Elicitation results require their original request correlation context;
|
|
273
|
+
`ContentContext.attachments` can provide the original request owner without a
|
|
274
|
+
resolver. A resolver is an optional
|
|
275
|
+
adapter for externally supplied references.
|
|
276
|
+
|
|
277
|
+
Results are invocation-local, not session archives.
|
|
@@ -15,6 +15,10 @@ from . import (
|
|
|
15
15
|
tool,
|
|
16
16
|
transport,
|
|
17
17
|
)
|
|
18
|
+
from .attachment import (
|
|
19
|
+
Attachment as Attachment,
|
|
20
|
+
AttachmentContents as AttachmentContents,
|
|
21
|
+
)
|
|
18
22
|
from ._content import ContentContext as ContentContext
|
|
19
23
|
from ._models import * # noqa: F403
|
|
20
24
|
from ._models import __all__ as _model_names
|
|
@@ -36,6 +40,8 @@ from .runtime import (
|
|
|
36
40
|
)
|
|
37
41
|
|
|
38
42
|
__all__ = list(_model_names) + [
|
|
43
|
+
"Attachment",
|
|
44
|
+
"AttachmentContents",
|
|
39
45
|
"ContentContext",
|
|
40
46
|
"OwnedContentSource",
|
|
41
47
|
"ContentSources",
|