awp-python 0.1.0a3__tar.gz → 0.1.0a4__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 (109) hide show
  1. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/CHANGELOG.md +8 -0
  2. awp_python-0.1.0a4/PKG-INFO +130 -0
  3. awp_python-0.1.0a4/README.md +109 -0
  4. awp_python-0.1.0a4/conformance/README.md +53 -0
  5. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/conformance/core-agent-lockstep.json +4 -4
  6. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/conformance/core-agent-streaming.json +2 -2
  7. awp_python-0.1.0a4/conformance/frames.json +4 -0
  8. awp_python-0.1.0a4/conformance/manifest-lockstep.json +157 -0
  9. awp_python-0.1.0a4/conformance/manifest-streaming.json +160 -0
  10. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/pyproject.toml +5 -6
  11. {awp_python-0.1.0a3/src/awp_sim → awp_python-0.1.0a4/src/awp}/demo.py +31 -7
  12. awp_python-0.1.0a4/tests/test_aio.py +190 -0
  13. awp_python-0.1.0a4/tests/test_client.py +31 -0
  14. awp_python-0.1.0a4/tests/test_evidence.py +43 -0
  15. awp_python-0.1.0a4/tests/world.py +348 -0
  16. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/uv.lock +1 -1
  17. awp_python-0.1.0a3/PKG-INFO +0 -130
  18. awp_python-0.1.0a3/README.md +0 -109
  19. awp_python-0.1.0a3/conformance/README.md +0 -86
  20. awp_python-0.1.0a3/conformance/core-world-lockstep.json +0 -1669
  21. awp_python-0.1.0a3/conformance/core-world-streaming.json +0 -1671
  22. awp_python-0.1.0a3/conformance/features-lockstep.json +0 -1671
  23. awp_python-0.1.0a3/conformance/features-streaming.json +0 -1671
  24. awp_python-0.1.0a3/scripts/check_traces.py +0 -57
  25. awp_python-0.1.0a3/src/awp_sim/__init__.py +0 -8
  26. awp_python-0.1.0a3/src/awp_sim/__main__.py +0 -5
  27. awp_python-0.1.0a3/src/awp_sim/arm.py +0 -177
  28. awp_python-0.1.0a3/src/awp_sim/audit.py +0 -138
  29. awp_python-0.1.0a3/src/awp_sim/cli.py +0 -238
  30. awp_python-0.1.0a3/src/awp_sim/config.py +0 -226
  31. awp_python-0.1.0a3/src/awp_sim/loopback.py +0 -222
  32. awp_python-0.1.0a3/src/awp_sim/py.typed +0 -0
  33. awp_python-0.1.0a3/src/awp_sim/recorder.py +0 -72
  34. awp_python-0.1.0a3/src/awp_sim/replay.py +0 -134
  35. awp_python-0.1.0a3/src/awp_sim/scenarios.py +0 -427
  36. awp_python-0.1.0a3/src/awp_sim/server.py +0 -336
  37. awp_python-0.1.0a3/src/awp_sim/session.py +0 -173
  38. awp_python-0.1.0a3/src/awp_sim/world.py +0 -1679
  39. awp_python-0.1.0a3/tests/helpers.py +0 -62
  40. awp_python-0.1.0a3/tests/test_audit.py +0 -62
  41. awp_python-0.1.0a3/tests/test_cli.py +0 -26
  42. awp_python-0.1.0a3/tests/test_e2e.py +0 -118
  43. awp_python-0.1.0a3/tests/test_evidence.py +0 -224
  44. awp_python-0.1.0a3/tests/test_features.py +0 -372
  45. awp_python-0.1.0a3/tests/test_regressions.py +0 -243
  46. awp_python-0.1.0a3/tests/test_scenarios.py +0 -33
  47. awp_python-0.1.0a3/tests/test_streams.py +0 -156
  48. awp_python-0.1.0a3/tests/test_world.py +0 -513
  49. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/.gitignore +0 -0
  50. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/.gitmodules +0 -0
  51. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/.python-version +0 -0
  52. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/LICENSE +0 -0
  53. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/scripts/sync_spec.py +0 -0
  54. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/__init__.py +0 -0
  55. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/lifecycle.json +0 -0
  56. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-cancel-result.schema.json +0 -0
  57. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-ref.schema.json +0 -0
  58. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-schema.schema.json +0 -0
  59. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-status.schema.json +0 -0
  60. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-submit-result.schema.json +0 -0
  61. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/action-submit.schema.json +0 -0
  62. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/agent-manifest.schema.json +0 -0
  63. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/approval-requested.schema.json +0 -0
  64. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/approval-respond.schema.json +0 -0
  65. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/common.schema.json +0 -0
  66. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/embodiment.schema.json +0 -0
  67. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/empty-result.schema.json +0 -0
  68. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/error.schema.json +0 -0
  69. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/frame-inline.schema.json +0 -0
  70. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/frame-tree.schema.json +0 -0
  71. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/obs-report.schema.json +0 -0
  72. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/observation-channel.schema.json +0 -0
  73. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/ping-result.schema.json +0 -0
  74. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/ping.schema.json +0 -0
  75. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/profiles/gui-actions.schema.json +0 -0
  76. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/reset-result.schema.json +0 -0
  77. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/reset.schema.json +0 -0
  78. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/restore.schema.json +0 -0
  79. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/safety-policy.schema.json +0 -0
  80. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-open.schema.json +0 -0
  81. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-ready.schema.json +0 -0
  82. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-resume.schema.json +0 -0
  83. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-state.schema.json +0 -0
  84. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-telemetry.schema.json +0 -0
  85. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-transfer-result.schema.json +0 -0
  86. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/session-transfer.schema.json +0 -0
  87. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/snapshot-result.schema.json +0 -0
  88. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/subscribe-result.schema.json +0 -0
  89. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/subscribe.schema.json +0 -0
  90. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/task-update.schema.json +0 -0
  91. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/tick-result.schema.json +0 -0
  92. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/tick.schema.json +0 -0
  93. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/unsubscribe.schema.json +0 -0
  94. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/world-event.schema.json +0 -0
  95. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/_spec/schemas/world-manifest.schema.json +0 -0
  96. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/aio.py +0 -0
  97. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/client.py +0 -0
  98. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/clock.py +0 -0
  99. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/errors.py +0 -0
  100. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/frames.py +0 -0
  101. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/jsonrpc.py +0 -0
  102. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/lifecycle.py +0 -0
  103. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/py.typed +0 -0
  104. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/src/awp/schema.py +0 -0
  105. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/tests/__init__.py +0 -0
  106. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/tests/conftest.py +0 -0
  107. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/tests/test_client_traces.py +0 -0
  108. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/tests/test_frames.py +0 -0
  109. {awp_python-0.1.0a3 → awp_python-0.1.0a4}/tests/test_schema.py +0 -0
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0a4
4
+
5
+ Targets specification revision `0.1-draft.9`.
6
+
7
+ - The reference world moves to its own package, [awp-sim](https://github.com/Hyperduality/awp-sim), and awp-python is the agent SDK alone.
8
+ - `awp-sim demo` becomes `awp-demo` (`awp.demo`). It reads `$AWP_URL` and `$AWP_TOKEN` when its flags are absent.
9
+ - Tests run against a scripted world over WebSockets instead of awp-sim, and CI runs awp-conformance against `awp-demo` in both time models.
10
+
3
11
  ## 0.1.0a3
4
12
 
5
13
  Targets specification revision `0.1-draft.9`.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.5
2
+ Name: awp-python
3
+ Version: 0.1.0a4
4
+ Summary: Agent World Protocol agent SDK for Python.
5
+ Project-URL: Specification, https://www.agentworldprotocol.com
6
+ Project-URL: Source, https://github.com/Hyperduality/awp-python
7
+ Author: Hyperduality
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: jsonschema>=4.23
19
+ Requires-Dist: websockets>=13.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # awp-python
23
+
24
+ The Python agent SDK for the [Agent World Protocol](https://www.agentworldprotocol.com). `ClientConnection` is the agent side of AWP as a sans-IO state machine, and `awp.aio.AsyncClient` drives it over a WebSocket.
25
+
26
+ It targets specification revision **`0.1-draft.9`**, pinned as the `spec/` submodule. This is an alpha, so the API will change along with the draft. The reference world, [awp-sim](https://github.com/Hyperduality/awp-sim), is a separate package built on this one.
27
+
28
+ ## Status
29
+
30
+ ![AWP: Core Agent, AWP-conformant against 0.1-draft.9](https://img.shields.io/badge/AWP-Core_Agent%2C_conformant_0.1--draft.9-555)
31
+
32
+ In both time models, the demo agent built on the client is **Core Agent: AWP-conformant against 0.1-draft.9**. [awp-conformance](https://github.com/Hyperduality/awp-conformance) reports no failure and nothing untested, and [`conformance/`](conformance/README.md) holds the reports and the evidence for their manual rows. CI runs the suite against the demo agent on every change.
33
+
34
+ | Implemented | Not implemented |
35
+ |---|---|
36
+ | Lockstep and streaming sessions; `world.tick` advances that wait for every per-tick channel | Multi-bind (`embodiments`) |
37
+ | Inline and `ws` stream bindings; binary frame codec (every spec vector) | Other stream bindings (`webrtc`, `webtransport`, `shm`, `grpc`) |
38
+ | Action lifecycle checked against the spec's transition table; idempotent resubmission | |
39
+ | Heartbeats, clock synchronization, receiver reports, resumption with replay and acknowledgement | |
40
+ | Canonical schema validation in receiver and sender forms | |
41
+ | Task, approval and standing approvals, transfer, reset, snapshots, and command channels | |
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install --pre awp-python
47
+ ```
48
+
49
+ Requires Python 3.11+.
50
+
51
+ ## Using the client
52
+
53
+ With asyncio:
54
+
55
+ ```python
56
+ from awp import ClientConnection
57
+ from awp.aio import AsyncClient
58
+
59
+ conn = ClientConnection({"name": "my-agent", "version": "0.1.0", "vendor": "me"}, ["proprio/json"])
60
+ async with AsyncClient(conn, "ws://127.0.0.1:8710") as client:
61
+ await client.initialize()
62
+ await client.open_session("streaming", embodiment="arm_01", subscribe=["proprio"])
63
+ await client.wait_for(lambda e: "proprio" in client.latest)
64
+ record = await client.submit(
65
+ "move_to_pose",
66
+ {"pose": {"frame": "base", "p_m": [0.3, 0.2, 0.5], "q": [0, 0, 0, 1]}},
67
+ basis=client.latest["proprio"].frame, # the observation this intent rests on
68
+ valid_for_ms=200,
69
+ )
70
+ print((await client.wait_terminal(record.action_id)).state)
71
+ await client.close_session()
72
+ ```
73
+
74
+ Without asyncio, feed `ClientConnection` the decoded messages and send whatever it queues:
75
+
76
+ ```python
77
+ events = conn.receive(message) # typed events: ActionUpdated, FrameReceived, ...
78
+ for out in conn.outgoing(): # messages to send, in order
79
+ transport.send(json.dumps(out))
80
+ ```
81
+
82
+ The connection:
83
+
84
+ - tracks the action lifecycle against the spec's transition table;
85
+ - deduplicates replayed statuses;
86
+ - keeps the clock offset from heartbeats;
87
+ - reports any violation by the world as a `ProtocolViolation` event.
88
+
89
+ ## The demo agent
90
+
91
+ `awp-demo` is a scripted agent for awp-sim's arm. It moves, cancels a move, and triggers an envelope refusal. It rides out a lost connection by resuming the session, or by opening a new one if the world no longer holds it.
92
+
93
+ ```bash
94
+ pip install --pre awp-sim
95
+ awp-sim serve & # streaming world on ws://127.0.0.1:8710
96
+ awp-demo # or --url, --token ($AWP_URL, $AWP_TOKEN)
97
+ ```
98
+
99
+ ## Layout
100
+
101
+ ```
102
+ src/awp/ client protocol layer (sans-IO), asyncio adapter, frame codec, schemas, demo agent
103
+ src/awp/_spec/ schemas and lifecycle table bundled from spec/ (scripts/sync_spec.py)
104
+ tests/ unit tests, and a scripted world that serves over WebSockets
105
+ conformance/ conformance reports, the manifests they were run against, and evidence
106
+ spec/ agent-world-protocol, pinned at spec-v0.1-draft.9
107
+ ```
108
+
109
+ ## Development
110
+
111
+ ```bash
112
+ git clone --recurse-submodules https://github.com/Hyperduality/awp-python
113
+ cd awp-python
114
+ uv sync
115
+ uv run ruff check && uv run ruff format --check
116
+ uv run mypy
117
+ uv run pytest --cov
118
+ uv run python scripts/sync_spec.py --check
119
+ ```
120
+
121
+ To move to a new draft revision:
122
+
123
+ 1. Check out its tag in `spec/`.
124
+ 2. Run `scripts/sync_spec.py`.
125
+ 3. Update `SPEC_REVISION` in `src/awp/__init__.py`.
126
+ 4. Fix whatever the tests report.
127
+
128
+ ## License
129
+
130
+ Apache-2.0. See [LICENSE](LICENSE).
@@ -0,0 +1,109 @@
1
+ # awp-python
2
+
3
+ The Python agent SDK for the [Agent World Protocol](https://www.agentworldprotocol.com). `ClientConnection` is the agent side of AWP as a sans-IO state machine, and `awp.aio.AsyncClient` drives it over a WebSocket.
4
+
5
+ It targets specification revision **`0.1-draft.9`**, pinned as the `spec/` submodule. This is an alpha, so the API will change along with the draft. The reference world, [awp-sim](https://github.com/Hyperduality/awp-sim), is a separate package built on this one.
6
+
7
+ ## Status
8
+
9
+ ![AWP: Core Agent, AWP-conformant against 0.1-draft.9](https://img.shields.io/badge/AWP-Core_Agent%2C_conformant_0.1--draft.9-555)
10
+
11
+ In both time models, the demo agent built on the client is **Core Agent: AWP-conformant against 0.1-draft.9**. [awp-conformance](https://github.com/Hyperduality/awp-conformance) reports no failure and nothing untested, and [`conformance/`](conformance/README.md) holds the reports and the evidence for their manual rows. CI runs the suite against the demo agent on every change.
12
+
13
+ | Implemented | Not implemented |
14
+ |---|---|
15
+ | Lockstep and streaming sessions; `world.tick` advances that wait for every per-tick channel | Multi-bind (`embodiments`) |
16
+ | Inline and `ws` stream bindings; binary frame codec (every spec vector) | Other stream bindings (`webrtc`, `webtransport`, `shm`, `grpc`) |
17
+ | Action lifecycle checked against the spec's transition table; idempotent resubmission | |
18
+ | Heartbeats, clock synchronization, receiver reports, resumption with replay and acknowledgement | |
19
+ | Canonical schema validation in receiver and sender forms | |
20
+ | Task, approval and standing approvals, transfer, reset, snapshots, and command channels | |
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install --pre awp-python
26
+ ```
27
+
28
+ Requires Python 3.11+.
29
+
30
+ ## Using the client
31
+
32
+ With asyncio:
33
+
34
+ ```python
35
+ from awp import ClientConnection
36
+ from awp.aio import AsyncClient
37
+
38
+ conn = ClientConnection({"name": "my-agent", "version": "0.1.0", "vendor": "me"}, ["proprio/json"])
39
+ async with AsyncClient(conn, "ws://127.0.0.1:8710") as client:
40
+ await client.initialize()
41
+ await client.open_session("streaming", embodiment="arm_01", subscribe=["proprio"])
42
+ await client.wait_for(lambda e: "proprio" in client.latest)
43
+ record = await client.submit(
44
+ "move_to_pose",
45
+ {"pose": {"frame": "base", "p_m": [0.3, 0.2, 0.5], "q": [0, 0, 0, 1]}},
46
+ basis=client.latest["proprio"].frame, # the observation this intent rests on
47
+ valid_for_ms=200,
48
+ )
49
+ print((await client.wait_terminal(record.action_id)).state)
50
+ await client.close_session()
51
+ ```
52
+
53
+ Without asyncio, feed `ClientConnection` the decoded messages and send whatever it queues:
54
+
55
+ ```python
56
+ events = conn.receive(message) # typed events: ActionUpdated, FrameReceived, ...
57
+ for out in conn.outgoing(): # messages to send, in order
58
+ transport.send(json.dumps(out))
59
+ ```
60
+
61
+ The connection:
62
+
63
+ - tracks the action lifecycle against the spec's transition table;
64
+ - deduplicates replayed statuses;
65
+ - keeps the clock offset from heartbeats;
66
+ - reports any violation by the world as a `ProtocolViolation` event.
67
+
68
+ ## The demo agent
69
+
70
+ `awp-demo` is a scripted agent for awp-sim's arm. It moves, cancels a move, and triggers an envelope refusal. It rides out a lost connection by resuming the session, or by opening a new one if the world no longer holds it.
71
+
72
+ ```bash
73
+ pip install --pre awp-sim
74
+ awp-sim serve & # streaming world on ws://127.0.0.1:8710
75
+ awp-demo # or --url, --token ($AWP_URL, $AWP_TOKEN)
76
+ ```
77
+
78
+ ## Layout
79
+
80
+ ```
81
+ src/awp/ client protocol layer (sans-IO), asyncio adapter, frame codec, schemas, demo agent
82
+ src/awp/_spec/ schemas and lifecycle table bundled from spec/ (scripts/sync_spec.py)
83
+ tests/ unit tests, and a scripted world that serves over WebSockets
84
+ conformance/ conformance reports, the manifests they were run against, and evidence
85
+ spec/ agent-world-protocol, pinned at spec-v0.1-draft.9
86
+ ```
87
+
88
+ ## Development
89
+
90
+ ```bash
91
+ git clone --recurse-submodules https://github.com/Hyperduality/awp-python
92
+ cd awp-python
93
+ uv sync
94
+ uv run ruff check && uv run ruff format --check
95
+ uv run mypy
96
+ uv run pytest --cov
97
+ uv run python scripts/sync_spec.py --check
98
+ ```
99
+
100
+ To move to a new draft revision:
101
+
102
+ 1. Check out its tag in `spec/`.
103
+ 2. Run `scripts/sync_spec.py`.
104
+ 3. Update `SPEC_REVISION` in `src/awp/__init__.py`.
105
+ 4. Fix whatever the tests report.
106
+
107
+ ## License
108
+
109
+ Apache-2.0. See [LICENSE](LICENSE).
@@ -0,0 +1,53 @@
1
+ # Conformance
2
+
3
+ ![AWP: Core Agent, AWP-conformant against 0.1-draft.9](https://img.shields.io/badge/AWP-Core_Agent%2C_conformant_0.1--draft.9-555)
4
+
5
+ | Class | Configuration | Claim | Report |
6
+ |---|---|---|---|
7
+ | Core Agent | `awp-demo`, [`manifest-lockstep.json`](manifest-lockstep.json) | Core Agent (lockstep): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3) | [`core-agent-lockstep.json`](core-agent-lockstep.json) |
8
+ | Core Agent | `awp-demo`, [`manifest-streaming.json`](manifest-streaming.json) | Core Agent (streaming): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3) | [`core-agent-streaming.json`](core-agent-streaming.json) |
9
+
10
+ The reports come from awp-conformance 0.1.0a3 run against awp-python 0.1.0a4. Neither has a failure or anything untested. The evidence for their `manual` rows is below (AWP-CNF-005).
11
+
12
+ ## Reproduce
13
+
14
+ The suite serves each manifest from its harness world and runs the demo agent against it. The manifests are awp-sim's (`awp-sim manifest --mode lockstep`, `--mode streaming`). [`frames.json`](frames.json) gives the harness one sample payload per channel.
15
+
16
+ ```bash
17
+ pip install --pre awp-python awp-conformance
18
+ awp-conformance agent --manifest conformance/manifest-lockstep.json --frames conformance/frames.json \
19
+ --mode lockstep --out report/ -- awp-demo --url '{url}' --token '{token}'
20
+ ```
21
+
22
+ For streaming, use `manifest-streaming.json` with `--mode streaming`. CI runs both on every change. The tests cited below run in CI too:
23
+
24
+ ```bash
25
+ uv run pytest tests/test_frames.py tests/test_evidence.py
26
+ ```
27
+
28
+ ## Manual evidence
29
+
30
+ ### AWP-DAT-001, AWP-DAT-009: loss accounting at the receiver (lockstep)
31
+
32
+ In streaming, the suite reads the agent's loss accounting from its `obs.report`. Lockstep sessions send no `obs.report`, so these rows are `manual` there.
33
+
34
+ - `test_the_receiver_counts_seq_gaps_as_loss_but_not_the_gap_before_a_resync` (`tests/test_evidence.py`) runs in each time model:
35
+ - on one channel, frames that skip two `seq` values count two missing frames in `ClientConnection.delivery()`;
36
+ - on another, a resync frame after a gap of five counts none.
37
+ - The client holds no delta state. Each frame reaches the application whole, with its `keyframe` and `resync` flags.
38
+
39
+ ### AWP-DAT-008: frame test vectors
40
+
41
+ `test_vectors` (`tests/test_frames.py`) runs `awp.frames` over every vector in `schemas/test-vectors/frames.json` of the `spec/` submodule, which is pinned at `spec-v0.1-draft.9`:
42
+
43
+ - the 10 valid vectors decode to their listed fields;
44
+ - the 7 marked `expect_error` are rejected with their listed error.
45
+
46
+ `test_roundtrip_without_vendor` re-encodes the 8 valid vectors that have no unknown extension or reserved bits, and each matches byte for byte.
47
+
48
+ ### AWP-VER-009: the draft revision is named
49
+
50
+ - The [README](../README.md) and the [Python SDK page](https://www.agentworldprotocol.com/sdks/python) name `0.1-draft.9`.
51
+ - `awp.SPEC_REVISION` is `"0.1-draft.9"`.
52
+ - The `spec/` submodule is pinned at the tag `spec-v0.1-draft.9`.
53
+ - Each report records `"specification": "0.1-draft.9"`, and its claim names the revision.
@@ -4,9 +4,9 @@
4
4
  "version": "0.1.0a3"
5
5
  },
6
6
  "specification": "0.1-draft.9",
7
- "target": "/Users/archivec/Documents/Code/Hyperduality/awp-conformance/.venv/bin/awp-sim demo --url {url} --token {token}",
7
+ "target": "awp-demo --url {url} --token {token}",
8
8
  "side": "agent",
9
- "generated": "2026-09-25T19:40:36Z",
9
+ "generated": "2026-09-25T21:46:19Z",
10
10
  "claim": {
11
11
  "status": "conformant",
12
12
  "wording": "Core Agent (lockstep): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3)",
@@ -138,7 +138,7 @@
138
138
  "id": "AWP-AGT-006",
139
139
  "level": "MUST",
140
140
  "outcome": "pass",
141
- "detail": "23 check(s): lockstep/baseline, lockstep/integer-range, lockstep/malformed-frame, lockstep/reconnect, lockstep/restarted-world, lockstep/silent-world"
141
+ "detail": "23 check(s): lockstep/baseline, lockstep/malformed-frame, lockstep/reconnect, lockstep/restarted-world, lockstep/silent-world"
142
142
  },
143
143
  {
144
144
  "id": "AWP-AGT-007",
@@ -1206,7 +1206,7 @@
1206
1206
  "id": "AWP-TIM-003",
1207
1207
  "level": "MUST",
1208
1208
  "outcome": "pass",
1209
- "detail": "8 check(s): lockstep/baseline, lockstep/malformed-frame, lockstep/reconnect, lockstep/restarted-world"
1209
+ "detail": "5 check(s): lockstep/baseline, lockstep/malformed-frame, lockstep/reconnect, lockstep/restarted-world"
1210
1210
  },
1211
1211
  {
1212
1212
  "id": "AWP-TIM-004",
@@ -4,9 +4,9 @@
4
4
  "version": "0.1.0a3"
5
5
  },
6
6
  "specification": "0.1-draft.9",
7
- "target": "/Users/archivec/Documents/Code/Hyperduality/awp-conformance/.venv/bin/awp-sim demo --url {url} --token {token}",
7
+ "target": "awp-demo --url {url} --token {token}",
8
8
  "side": "agent",
9
- "generated": "2026-09-25T19:40:57Z",
9
+ "generated": "2026-09-25T21:46:39Z",
10
10
  "claim": {
11
11
  "status": "conformant",
12
12
  "wording": "Core Agent (streaming): AWP-conformant against 0.1-draft.9 (awp-conformance 0.1.0a3)",
@@ -0,0 +1,4 @@
1
+ {
2
+ "proprio": { "p_m": [0, 0, 0.4], "v_mps": [0, 0, 0] },
3
+ "arm_state": { "phase": "idle", "target_m": null, "action_id": null }
4
+ }
@@ -0,0 +1,157 @@
1
+ {
2
+ "protocol_version": "0.1",
3
+ "world": {
4
+ "name": "awp-sim",
5
+ "version": "0.1.0a4",
6
+ "vendor": "hyperduality"
7
+ },
8
+ "time_models": [
9
+ "lockstep"
10
+ ],
11
+ "capabilities": {},
12
+ "initial_states": [
13
+ "home"
14
+ ],
15
+ "embodiments": [
16
+ {
17
+ "id": "arm_01",
18
+ "kind": "manipulator",
19
+ "action_types": [
20
+ "move_to_pose",
21
+ "stop"
22
+ ],
23
+ "channels": [
24
+ "proprio",
25
+ "arm_state"
26
+ ]
27
+ }
28
+ ],
29
+ "observation_channels": [
30
+ {
31
+ "id": "proprio",
32
+ "modality": "proprio/json",
33
+ "rate_hz": null,
34
+ "loss_class": "latest-wins",
35
+ "stale_after_ms": 50,
36
+ "schema": {
37
+ "fields": [
38
+ "p_m",
39
+ "v_mps"
40
+ ],
41
+ "frame": "base"
42
+ }
43
+ },
44
+ {
45
+ "id": "arm_state",
46
+ "modality": "text/event+json",
47
+ "rate_hz": null,
48
+ "loss_class": "reliable",
49
+ "schema": {
50
+ "type": "object",
51
+ "properties": {
52
+ "phase": {
53
+ "enum": [
54
+ "idle",
55
+ "moving",
56
+ "stopping",
57
+ "servo"
58
+ ]
59
+ },
60
+ "target_m": {
61
+ "type": [
62
+ "array",
63
+ "null"
64
+ ]
65
+ },
66
+ "action_id": {
67
+ "type": [
68
+ "string",
69
+ "null"
70
+ ]
71
+ }
72
+ },
73
+ "required": [
74
+ "phase",
75
+ "target_m",
76
+ "action_id"
77
+ ]
78
+ }
79
+ }
80
+ ],
81
+ "action_schemas": [
82
+ {
83
+ "type": "move_to_pose",
84
+ "params_schema": {
85
+ "$ref": "#/$defs/move_to_pose_params"
86
+ },
87
+ "duration": "extended",
88
+ "preemption": [
89
+ "replace",
90
+ "queue",
91
+ "reject"
92
+ ],
93
+ "concurrency_group": "arm_motion",
94
+ "max_queue": 4,
95
+ "max_abort_ms": 1500,
96
+ "max_duration_ms": 10000,
97
+ "description": "Move the end effector in a straight line to a position."
98
+ },
99
+ {
100
+ "type": "stop",
101
+ "params_schema": {
102
+ "type": "object",
103
+ "additionalProperties": false
104
+ },
105
+ "duration": "instant",
106
+ "preemption": "replace",
107
+ "concurrency_group": "arm_motion",
108
+ "description": "Decelerate to rest; replaces any motion."
109
+ }
110
+ ],
111
+ "safety_policy": {
112
+ "envelopes": [
113
+ {
114
+ "embodiment": "arm_01",
115
+ "spatial": {
116
+ "frame": "base",
117
+ "aabb_m": [
118
+ [
119
+ -0.5,
120
+ -0.5,
121
+ 0.0
122
+ ],
123
+ [
124
+ 0.5,
125
+ 0.5,
126
+ 0.8
127
+ ]
128
+ ]
129
+ },
130
+ "max_velocity_mps": 0.5,
131
+ "max_action_rate_hz": 20.0,
132
+ "enforcement": "command_check",
133
+ "on_violation": "reject"
134
+ }
135
+ ]
136
+ },
137
+ "$defs": {
138
+ "move_to_pose_params": {
139
+ "type": "object",
140
+ "properties": {
141
+ "pose": {
142
+ "$ref": "https://agentworldprotocol.com/schemas/v0.1/common.schema.json#/$defs/pose"
143
+ },
144
+ "max_velocity_mps": {
145
+ "type": "number",
146
+ "exclusiveMinimum": 0
147
+ }
148
+ },
149
+ "required": [
150
+ "pose"
151
+ ],
152
+ "additionalProperties": false
153
+ }
154
+ },
155
+ "tick_policy": "on_tick",
156
+ "tick_authority": "any_session"
157
+ }
@@ -0,0 +1,160 @@
1
+ {
2
+ "protocol_version": "0.1",
3
+ "world": {
4
+ "name": "awp-sim",
5
+ "version": "0.1.0a4",
6
+ "vendor": "hyperduality"
7
+ },
8
+ "time_models": [
9
+ "streaming"
10
+ ],
11
+ "capabilities": {},
12
+ "initial_states": [
13
+ "home"
14
+ ],
15
+ "embodiments": [
16
+ {
17
+ "id": "arm_01",
18
+ "kind": "manipulator",
19
+ "action_types": [
20
+ "move_to_pose",
21
+ "stop"
22
+ ],
23
+ "channels": [
24
+ "proprio",
25
+ "arm_state"
26
+ ]
27
+ }
28
+ ],
29
+ "observation_channels": [
30
+ {
31
+ "id": "proprio",
32
+ "modality": "proprio/json",
33
+ "rate_hz": 100.0,
34
+ "loss_class": "latest-wins",
35
+ "stale_after_ms": 50,
36
+ "schema": {
37
+ "fields": [
38
+ "p_m",
39
+ "v_mps"
40
+ ],
41
+ "frame": "base"
42
+ }
43
+ },
44
+ {
45
+ "id": "arm_state",
46
+ "modality": "text/event+json",
47
+ "rate_hz": 10.0,
48
+ "loss_class": "reliable",
49
+ "schema": {
50
+ "type": "object",
51
+ "properties": {
52
+ "phase": {
53
+ "enum": [
54
+ "idle",
55
+ "moving",
56
+ "stopping",
57
+ "servo"
58
+ ]
59
+ },
60
+ "target_m": {
61
+ "type": [
62
+ "array",
63
+ "null"
64
+ ]
65
+ },
66
+ "action_id": {
67
+ "type": [
68
+ "string",
69
+ "null"
70
+ ]
71
+ }
72
+ },
73
+ "required": [
74
+ "phase",
75
+ "target_m",
76
+ "action_id"
77
+ ]
78
+ }
79
+ }
80
+ ],
81
+ "action_schemas": [
82
+ {
83
+ "type": "move_to_pose",
84
+ "params_schema": {
85
+ "$ref": "#/$defs/move_to_pose_params"
86
+ },
87
+ "duration": "extended",
88
+ "preemption": [
89
+ "replace",
90
+ "queue",
91
+ "reject"
92
+ ],
93
+ "concurrency_group": "arm_motion",
94
+ "max_queue": 4,
95
+ "max_abort_ms": 1500,
96
+ "max_duration_ms": 10000,
97
+ "description": "Move the end effector in a straight line to a position."
98
+ },
99
+ {
100
+ "type": "stop",
101
+ "params_schema": {
102
+ "type": "object",
103
+ "additionalProperties": false
104
+ },
105
+ "duration": "instant",
106
+ "preemption": "replace",
107
+ "concurrency_group": "arm_motion",
108
+ "description": "Decelerate to rest; replaces any motion."
109
+ }
110
+ ],
111
+ "safety_policy": {
112
+ "envelopes": [
113
+ {
114
+ "embodiment": "arm_01",
115
+ "spatial": {
116
+ "frame": "base",
117
+ "aabb_m": [
118
+ [
119
+ -0.5,
120
+ -0.5,
121
+ 0.0
122
+ ],
123
+ [
124
+ 0.5,
125
+ 0.5,
126
+ 0.8
127
+ ]
128
+ ]
129
+ },
130
+ "max_velocity_mps": 0.5,
131
+ "max_action_rate_hz": 20.0,
132
+ "enforcement": "command_check",
133
+ "on_violation": "reject"
134
+ }
135
+ ],
136
+ "safe_state": {
137
+ "behavior": "safe_stop",
138
+ "watchdog_ms": 2000
139
+ },
140
+ "max_basis_age_ms": 500
141
+ },
142
+ "$defs": {
143
+ "move_to_pose_params": {
144
+ "type": "object",
145
+ "properties": {
146
+ "pose": {
147
+ "$ref": "https://agentworldprotocol.com/schemas/v0.1/common.schema.json#/$defs/pose"
148
+ },
149
+ "max_velocity_mps": {
150
+ "type": "number",
151
+ "exclusiveMinimum": 0
152
+ }
153
+ },
154
+ "required": [
155
+ "pose"
156
+ ],
157
+ "additionalProperties": false
158
+ }
159
+ }
160
+ }