agentenv-framework-protocol 0.1.277__tar.gz → 0.1.279__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/PKG-INFO +14 -1
  2. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/README.md +13 -0
  3. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/pyproject.toml +1 -1
  4. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/__init__.py +4 -0
  5. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/extensions.py +21 -0
  6. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_a2a_agent.py +82 -0
  7. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/.gitignore +0 -0
  8. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/LICENSE +0 -0
  9. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/NOTICE +0 -0
  10. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/THIRD_PARTY_NOTICES.md +0 -0
  11. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/__init__.py +0 -0
  12. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/advanced_agent.py +0 -0
  13. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/basic_agent.py +0 -0
  14. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/custom_extensions_agent.py +0 -0
  15. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/multimodal_agent.py +0 -0
  16. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/reference_model.py +0 -0
  17. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/examples/streaming_agent.py +0 -0
  18. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/__init__.py +0 -0
  19. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  20. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  21. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  22. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  23. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  24. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/agent_env_environment.py +0 -0
  25. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/client.py +0 -0
  26. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/manifest.py +0 -0
  27. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/preflight.py +0 -0
  28. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/transfers.py +0 -0
  29. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/src/agentenv_protocol/types.py +0 -0
  30. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_a2a_agent_examples.py +0 -0
  31. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_a2a_object_transfers.py +0 -0
  32. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_agent_env_environment.py +0 -0
  33. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_client.py +0 -0
  34. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_intake.py +0 -0
  35. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_manifest.py +0 -0
  36. {agentenv_framework_protocol-0.1.277 → agentenv_framework_protocol-0.1.279}/tests/test_preflight.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentenv-framework-protocol
3
- Version: 0.1.277
3
+ Version: 0.1.279
4
4
  Summary: Open data-plane protocol and server SDK for agent environments.
5
5
  Project-URL: Homepage, https://www.agentenvframework.com
6
6
  Project-URL: Documentation, https://www.agentenvframework.com/docs
@@ -503,6 +503,19 @@ tools with (agent-env relays the env card's name: the MultiEnv's declared name,
503
503
  `env` + 4 random digits, giving e.g. `mcp__env4821__<tool>`); when absent the agent mints
504
504
  `mcp_<8 hex>`.
505
505
 
506
+ `ATTRIBUTION_PROBE_V1.probe` lets a caller check that an agent forwards attribution
507
+ upstream. Attribution is an open map of string dimensions, like AgentEnv's cost
508
+ attribution: the SDK assigns no keys, and each deployment uses whatever dimensions
509
+ its model gateway understands (the AgentEnv control plane configures `project_id`
510
+ and `task_id` on agents that declare those config fields). An agent records the
511
+ dimensions it last sent with a model request and returns them from a bodyless
512
+ `POST /ext/attribution-probe` as an `AttributionProbeResponse`:
513
+ `{"last_seen_attribution": {<dimension>: <value>, ...}, "last_seen_at_utc": ...}`.
514
+ The SDK does not record attribution itself, because only the agent knows what it sent.
515
+ The answer is agent-wide rather than per caller, and like every extension route the
516
+ probe carries no access check of its own, so expose an agent only to the control plane
517
+ that drives it.
518
+
506
519
  Runnable, self-contained reference agents live in [`examples/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples):
507
520
  the normal, streaming, and multimodal `run(request)` paths, single- and
508
521
  multi-operation custom extensions, and advanced ASGI-lifespan plus
@@ -474,6 +474,19 @@ tools with (agent-env relays the env card's name: the MultiEnv's declared name,
474
474
  `env` + 4 random digits, giving e.g. `mcp__env4821__<tool>`); when absent the agent mints
475
475
  `mcp_<8 hex>`.
476
476
 
477
+ `ATTRIBUTION_PROBE_V1.probe` lets a caller check that an agent forwards attribution
478
+ upstream. Attribution is an open map of string dimensions, like AgentEnv's cost
479
+ attribution: the SDK assigns no keys, and each deployment uses whatever dimensions
480
+ its model gateway understands (the AgentEnv control plane configures `project_id`
481
+ and `task_id` on agents that declare those config fields). An agent records the
482
+ dimensions it last sent with a model request and returns them from a bodyless
483
+ `POST /ext/attribution-probe` as an `AttributionProbeResponse`:
484
+ `{"last_seen_attribution": {<dimension>: <value>, ...}, "last_seen_at_utc": ...}`.
485
+ The SDK does not record attribution itself, because only the agent knows what it sent.
486
+ The answer is agent-wide rather than per caller, and like every extension route the
487
+ probe carries no access check of its own, so expose an agent only to the control plane
488
+ that drives it.
489
+
477
490
  Runnable, self-contained reference agents live in [`examples/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples):
478
491
  the normal, streaming, and multimodal `run(request)` paths, single- and
479
492
  multi-operation custom extensions, and advanced ASGI-lifespan plus
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentenv-framework-protocol"
3
- version = "0.1.277"
3
+ version = "0.1.279"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -4,6 +4,7 @@ from a2a.types import AgentCapabilities
4
4
 
5
5
  from .extensions import (
6
6
  AGENT_CONFIG_V1,
7
+ ATTRIBUTION_PROBE_V1,
7
8
  INSTALL_V1,
8
9
  MCP_CONFIG_V1,
9
10
  PEER_AGENTS_V1,
@@ -12,6 +13,7 @@ from .extensions import (
12
13
  STANDARD_EXTENSIONS,
13
14
  TRAJECTORY_V1,
14
15
  TRIGGERS_V1,
16
+ AttributionProbeResponse,
15
17
  BundleSkillRequest,
16
18
  ChangelogIncrement,
17
19
  ContextObjectTrajectoryRequest,
@@ -108,6 +110,7 @@ from ..transfers import (
108
110
 
109
111
  __all__ = [
110
112
  "AGENT_CONFIG_V1",
113
+ "ATTRIBUTION_PROBE_V1",
111
114
  "INSTALL_V1",
112
115
  "MCP_CONFIG_V1",
113
116
  "PEER_AGENTS_V1",
@@ -122,6 +125,7 @@ __all__ = [
122
125
  "AgentEnvAgent",
123
126
  "AgentIdentity",
124
127
  "AgentRunResult",
128
+ "AttributionProbeResponse",
125
129
  "BundleSkillRequest",
126
130
  "ChangelogIncrement",
127
131
  "ContextObjectTrajectoryRequest",
@@ -231,6 +231,11 @@ class PeerAgentsSetRequest(ExtensionRequest):
231
231
  peers: list[PeerAgent]
232
232
 
233
233
 
234
+ class AttributionProbeResponse(ExtensionResponse):
235
+ last_seen_attribution: dict[str, str]
236
+ last_seen_at_utc: str | None = None
237
+
238
+
234
239
  class TriggerRegisterRequest(ExtensionRequest):
235
240
  triggers: list[dict[str, Any]]
236
241
 
@@ -1037,6 +1042,21 @@ PEER_AGENTS_V1 = ExtensionDefinition(
1037
1042
  },
1038
1043
  )
1039
1044
 
1045
+ ATTRIBUTION_PROBE_V1 = ExtensionDefinition(
1046
+ uri="urn:agentenv:attribution-probe/v1",
1047
+ description="Report the attribution dimensions last sent with a model request.",
1048
+ endpoint="/ext/attribution-probe",
1049
+ core_operations={
1050
+ "probe": _op(
1051
+ "probe",
1052
+ "POST",
1053
+ "/ext/attribution-probe",
1054
+ RUNTIME,
1055
+ response_model=AttributionProbeResponse,
1056
+ ),
1057
+ },
1058
+ )
1059
+
1040
1060
  TRIGGERS_V1 = ExtensionDefinition(
1041
1061
  uri="urn:agentenv:triggers/v1",
1042
1062
  description="Register triggers and decide deterministic responses.",
@@ -1084,6 +1104,7 @@ STANDARD_EXTENSIONS: Mapping[str, ExtensionDefinition] = MappingProxyType(
1084
1104
  TRAJECTORY_V1,
1085
1105
  SNAPSHOT_V1,
1086
1106
  PEER_AGENTS_V1,
1107
+ ATTRIBUTION_PROBE_V1,
1087
1108
  TRIGGERS_V1,
1088
1109
  INSTALL_V1,
1089
1110
  )
@@ -30,6 +30,7 @@ from a2a.types import (
30
30
  )
31
31
  from agentenv_protocol.a2a_agent import (
32
32
  AGENT_CONFIG_V1,
33
+ ATTRIBUTION_PROBE_V1,
33
34
  MCP_CONFIG_V1,
34
35
  PEER_AGENTS_V1,
35
36
  SKILL_CONFIG_V1,
@@ -40,6 +41,7 @@ from agentenv_protocol.a2a_agent import (
40
41
  AgentConfig,
41
42
  AgentEnvAgent,
42
43
  AgentIdentity,
44
+ AttributionProbeResponse,
43
45
  BundleSkillRequest,
44
46
  ContextObjectTrajectoryRequest,
45
47
  ContextTrajectoryRequest,
@@ -1348,6 +1350,86 @@ def test_handler_receives_the_canonical_request_model() -> None:
1348
1350
  }
1349
1351
 
1350
1352
 
1353
+ def test_attribution_probe_reports_what_the_agent_last_sent() -> None:
1354
+ @a2a_agent(
1355
+ identity=AgentIdentity(name="attributing-agent", description="test", version="1")
1356
+ )
1357
+ class Agent(AgentEnvAgent):
1358
+ def __init__(self) -> None:
1359
+ self.sent: dict[str, str] = {}
1360
+
1361
+ async def run(self, request: TaskRequest) -> TaskResult:
1362
+ self.sent = {"project_id": "project-1", "task_id": "task-1", "team": "evals"}
1363
+ return TaskResult.text("ok")
1364
+
1365
+ @extension(ATTRIBUTION_PROBE_V1.probe)
1366
+ async def probe(self):
1367
+ return AttributionProbeResponse(
1368
+ last_seen_attribution=self.sent, last_seen_at_utc="2026-01-01T00:00:00Z"
1369
+ )
1370
+
1371
+ agent = Agent()
1372
+ with TestClient(agent.create_app()) as client:
1373
+ card = client.get("/.well-known/agent.json").json()
1374
+ assert _extension(card, ATTRIBUTION_PROBE_V1.uri)["params"]["endpoint"] == (
1375
+ "/ext/attribution-probe"
1376
+ )
1377
+ assert _operation(client, card, ATTRIBUTION_PROBE_V1.uri, "probe", {}).json() == {
1378
+ "last_seen_attribution": {},
1379
+ "last_seen_at_utc": "2026-01-01T00:00:00Z",
1380
+ }
1381
+ response = client.post("/a2a", json=_message_request())
1382
+ assert response.json()["result"]["status"]["state"] == "completed"
1383
+ assert _operation(client, card, ATTRIBUTION_PROBE_V1.uri, "probe", {}).json() == {
1384
+ "last_seen_attribution": {
1385
+ "project_id": "project-1",
1386
+ "task_id": "task-1",
1387
+ "team": "evals",
1388
+ },
1389
+ "last_seen_at_utc": "2026-01-01T00:00:00Z",
1390
+ }
1391
+ assert (
1392
+ _operation(
1393
+ client, card, ATTRIBUTION_PROBE_V1.uri, "probe", {"project_id": "x"}
1394
+ ).status_code
1395
+ == 400
1396
+ )
1397
+
1398
+
1399
+ def test_attribution_probe_rejects_an_undeclared_response_field() -> None:
1400
+ @a2a_agent(
1401
+ identity=AgentIdentity(name="attributing-agent", description="test", version="1")
1402
+ )
1403
+ class Agent(AgentEnvAgent):
1404
+ async def run(self, request: TaskRequest) -> TaskResult:
1405
+ return TaskResult.text("ok")
1406
+
1407
+ @extension(ATTRIBUTION_PROBE_V1.probe)
1408
+ async def probe(self):
1409
+ return {"last_seen_attribution": {}, "headers": {"x-secret": "value"}}
1410
+
1411
+ with TestClient(Agent().create_app(), raise_server_exceptions=False) as client:
1412
+ card = client.get("/.well-known/agent.json").json()
1413
+ response = _operation(client, card, ATTRIBUTION_PROBE_V1.uri, "probe", {})
1414
+ assert response.status_code == 500
1415
+ assert "x-secret" not in response.text
1416
+
1417
+
1418
+ def test_attribution_probe_is_only_declared_by_a_handler() -> None:
1419
+ @a2a_agent(
1420
+ identity=AgentIdentity(name="plain-agent", description="test", version="1")
1421
+ )
1422
+ class Agent(AgentEnvAgent):
1423
+ async def run(self, request: TaskRequest) -> TaskResult:
1424
+ return TaskResult.text("ok")
1425
+
1426
+ with TestClient(Agent().create_app()) as client:
1427
+ card = client.get("/.well-known/agent.json").json()
1428
+ uris = {item["uri"] for item in card["capabilities"]["extensions"]}
1429
+ assert ATTRIBUTION_PROBE_V1.uri not in uris
1430
+ assert client.post("/ext/attribution-probe", json={}).status_code in (404, 405)
1431
+
1432
+
1351
1433
  def test_handler_advertises_the_canonical_request_model() -> None:
1352
1434
  class Agent:
1353
1435
  @extension(SNAPSHOT_V1.save)