statewire 0.3.3__tar.gz → 0.4.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.
Files changed (32) hide show
  1. {statewire-0.3.3 → statewire-0.4.0}/PKG-INFO +1 -1
  2. {statewire-0.3.3 → statewire-0.4.0}/pyproject.toml +1 -1
  3. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/assistant_transport.py +15 -6
  4. {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport.py +15 -10
  5. {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport_client.py +2 -2
  6. {statewire-0.3.3 → statewire-0.4.0}/.gitignore +0 -0
  7. {statewire-0.3.3 → statewire-0.4.0}/README.md +0 -0
  8. {statewire-0.3.3 → statewire-0.4.0}/examples/__init__.py +0 -0
  9. {statewire-0.3.3 → statewire-0.4.0}/examples/demo_app.py +0 -0
  10. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/__init__.py +0 -0
  11. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/api.py +0 -0
  12. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/assistant_transport_client.py +0 -0
  13. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/client.py +0 -0
  14. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/langgraph.py +0 -0
  15. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/ops.py +0 -0
  16. {statewire-0.3.3 → statewire-0.4.0}/src/statewire/state.py +0 -0
  17. {statewire-0.3.3 → statewire-0.4.0}/tests/client_helpers.py +0 -0
  18. {statewire-0.3.3 → statewire-0.4.0}/tests/statewire_helpers.py +0 -0
  19. {statewire-0.3.3 → statewire-0.4.0}/tests/test_assistant_transport_facade.py +0 -0
  20. {statewire-0.3.3 → statewire-0.4.0}/tests/test_authorize.py +0 -0
  21. {statewire-0.3.3 → statewire-0.4.0}/tests/test_client.py +0 -0
  22. {statewire-0.3.3 → statewire-0.4.0}/tests/test_client_ws.py +0 -0
  23. {statewire-0.3.3 → statewire-0.4.0}/tests/test_commands.py +0 -0
  24. {statewire-0.3.3 → statewire-0.4.0}/tests/test_context.py +0 -0
  25. {statewire-0.3.3 → statewire-0.4.0}/tests/test_langgraph.py +0 -0
  26. {statewire-0.3.3 → statewire-0.4.0}/tests/test_lifespan.py +0 -0
  27. {statewire-0.3.3 → statewire-0.4.0}/tests/test_meta.py +0 -0
  28. {statewire-0.3.3 → statewire-0.4.0}/tests/test_state_proxy.py +0 -0
  29. {statewire-0.3.3 → statewire-0.4.0}/tests/test_statewire_hostable.py +0 -0
  30. {statewire-0.3.3 → statewire-0.4.0}/tests/test_stream.py +0 -0
  31. {statewire-0.3.3 → statewire-0.4.0}/tests/test_writer_lease.py +0 -0
  32. {statewire-0.3.3 → statewire-0.4.0}/tests/test_ws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statewire
3
- Version: 0.3.3
3
+ Version: 0.4.0
4
4
  Summary: Replicate one JSON object over an SSE op stream + a command endpoint
5
5
  Project-URL: Repository, https://github.com/assistant-ui/harness-sdk
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "statewire"
3
- version = "0.3.3"
3
+ version = "0.4.0"
4
4
  description = "Replicate one JSON object over an SSE op stream + a command endpoint"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -17,8 +17,9 @@ the legacy commands, which are normalized to the canonical ``run/*`` dialect
17
17
  (a host-registered handler of the same legacy name always wins and receives
18
18
  the command whole):
19
19
 
20
- - ``add-message`` becomes ``run/steer`` with ``{"message": <wire message>}``:
21
- a fresh ``legacy_`` id is synthesized, image parts become
20
+ - ``add-message`` becomes ``run/steer`` with ``{"message": <wire message>,
21
+ "anchorMessageId": <body parentId>}`` (body ``parentId`` null or absent →
22
+ ``null``): a fresh ``legacy_`` id is synthesized, image parts become
22
23
  ``{"type": "file", "mediaType": "image/*", "url"}`` parts, and command-level
23
24
  anchors are dropped. A command-level non-null ``sourceId`` makes it
24
25
  ``run/edit`` with that ``sourceId`` directly; otherwise, when the body
@@ -31,8 +32,8 @@ the command whole):
31
32
  unanswered tool-call request in ``state["inputRequests"]`` (None rejects),
32
33
  and a ``modelContent`` field rejects (unsupported in ``run/input``).
33
34
  - A body with a ``parentId`` and no commands is the legacy reload: it becomes
34
- ``run/reload`` with ``{"sourceId": <child>}`` via ``get_message_child_id``
35
- (None rejects).
35
+ ``run/reload`` with ``{"sourceId": <child>, "messageId": <fresh legacy_
36
+ id>}`` via ``get_message_child_id`` (None rejects).
36
37
 
37
38
  A host without the target ``run/*`` handler rejects the batch via the
38
39
  ordinary unknown-command path. The response streams state changes as legacy
@@ -464,7 +465,10 @@ class AssistantTransport(Statewire):
464
465
  "method": "run/edit",
465
466
  "params": [{"sourceId": source_id, "message": message}],
466
467
  }
467
- return {"method": "run/steer", "params": [{"message": message}]}
468
+ return {
469
+ "method": "run/steer",
470
+ "params": [{"message": message, "anchorMessageId": parent_id}],
471
+ }
468
472
  if kind == "add-tool-result" and "add-tool-result" not in registered:
469
473
  return {
470
474
  "method": "run/input",
@@ -494,7 +498,12 @@ class AssistantTransport(Statewire):
494
498
  source_id = await self.get_message_child_id(parent_id)
495
499
  if source_id is None:
496
500
  raise _RunError(f"reload: no child message for parentId {parent_id!r}")
497
- return {"method": "run/reload", "params": [{"sourceId": source_id}]}
501
+ return {
502
+ "method": "run/reload",
503
+ "params": [
504
+ {"sourceId": source_id, "messageId": f"legacy_{uuid4().hex}"}
505
+ ],
506
+ }
498
507
 
499
508
  async def _assistant_transport_dispatch(self, method: str, identity: Any) -> None:
500
509
  client_id = "at-" + secrets.token_urlsafe(9)
@@ -27,7 +27,7 @@ class Harness(AssistantTransport):
27
27
  self.state = {"messages": [], "text": "", "meta": {"title": None}}
28
28
  yield
29
29
 
30
- @command("message/add")
30
+ @command("demo/echo")
31
31
  async def add_message(self, cmd):
32
32
  self.seen.append(cmd)
33
33
  self.state["messages"].append(cmd["message"])
@@ -120,7 +120,7 @@ async def test_data_stream_response_streams_snapshot_then_ops_until_idle():
120
120
  {
121
121
  "commands": [
122
122
  {
123
- "type": "message/add",
123
+ "type": "demo/echo",
124
124
  "message": {
125
125
  "role": "user",
126
126
  "parts": [{"type": "text", "text": "hi"}],
@@ -229,7 +229,7 @@ async def test_splice_and_remove_fall_back_to_parent_set():
229
229
  {
230
230
  "commands": [
231
231
  {
232
- "type": "message/add",
232
+ "type": "demo/echo",
233
233
  "message": {
234
234
  "role": "user",
235
235
  "parts": [{"type": "text", "text": "hi"}],
@@ -266,7 +266,7 @@ async def test_assistant_transport_sse_mode():
266
266
  {
267
267
  "commands": [
268
268
  {
269
- "type": "message/add",
269
+ "type": "demo/echo",
270
270
  "message": {
271
271
  "role": "user",
272
272
  "parts": [{"type": "text", "text": "hi"}],
@@ -428,7 +428,7 @@ async def test_resume_after_completed_run_replays_with_completed_header():
428
428
  {
429
429
  "commands": [
430
430
  {
431
- "type": "message/add",
431
+ "type": "demo/echo",
432
432
  "message": {
433
433
  "role": "user",
434
434
  "parts": [{"type": "text", "text": "hi"}],
@@ -453,7 +453,7 @@ async def test_resume_rejects_commands_and_malformed_bodies():
453
453
  async with statewire_client(Harness) as (app, client):
454
454
  response = await client.post(
455
455
  "/threads/t1/assistant-transport/api/resume",
456
- json={"commands": [{"type": "message/add"}]},
456
+ json={"commands": [{"type": "demo/echo"}]},
457
457
  )
458
458
  assert response.status_code == 400
459
459
  assert (
@@ -840,7 +840,8 @@ async def test_add_message_is_normalized_to_run_steer():
840
840
  assert response.status_code == 200
841
841
  instance = (await app.state.pinned_host.directory.get("t1")).instance
842
842
  [(params, caller_id)] = instance.steered
843
- assert set(params) == {"message"}
843
+ assert set(params) == {"message", "anchorMessageId"}
844
+ assert params["anchorMessageId"] is None
844
845
  message = params["message"]
845
846
  assert message["id"].startswith("legacy_")
846
847
  assert message["role"] == "user"
@@ -1094,7 +1095,9 @@ async def test_parent_id_without_commands_is_normalized_to_run_reload():
1094
1095
  frames = data_stream_frames(response.text)
1095
1096
  assert all(prefix == "aui-state" for prefix, _ in frames)
1096
1097
  instance = (await app.state.pinned_host.directory.get("t1")).instance
1097
- assert instance.reloaded == [{"sourceId": "m2"}]
1098
+ [params] = instance.reloaded
1099
+ assert params["sourceId"] == "m2"
1100
+ assert params["messageId"].startswith("legacy_")
1098
1101
 
1099
1102
 
1100
1103
  async def test_reload_without_a_resolvable_child_is_an_error_frame():
@@ -1184,7 +1187,8 @@ async def test_add_message_with_a_null_source_id_and_no_parent_is_run_steer():
1184
1187
  instance = (await app.state.pinned_host.directory.get("t1")).instance
1185
1188
  assert instance.edited == []
1186
1189
  [(params, _)] = instance.steered
1187
- assert set(params) == {"message"}
1190
+ assert set(params) == {"message", "anchorMessageId"}
1191
+ assert params["anchorMessageId"] is None
1188
1192
 
1189
1193
 
1190
1194
  async def test_add_message_with_an_unresolvable_parent_falls_back_to_run_steer():
@@ -1209,4 +1213,5 @@ async def test_add_message_with_an_unresolvable_parent_falls_back_to_run_steer()
1209
1213
  instance = (await app.state.pinned_host.directory.get("t1")).instance
1210
1214
  assert instance.edited == []
1211
1215
  [(params, _)] = instance.steered
1212
- assert set(params) == {"message"}
1216
+ assert set(params) == {"message", "anchorMessageId"}
1217
+ assert params["anchorMessageId"] == "unknown"
@@ -124,11 +124,11 @@ async def test_client_posts_the_legacy_body_and_applies_state_ops():
124
124
  body={"system": "be nice"},
125
125
  on_chunk=seen.append,
126
126
  )
127
- assert await client.send([{"type": "message/add", "message": "hi"}]) is True
127
+ assert await client.send([{"type": "demo/echo", "message": "hi"}]) is True
128
128
  assert client.state == {"text": "hello"}
129
129
  assert received == [
130
130
  {
131
- "commands": [{"type": "message/add", "message": "hi"}],
131
+ "commands": [{"type": "demo/echo", "message": "hi"}],
132
132
  "state": {"text": ""},
133
133
  "threadId": "t1",
134
134
  "system": "be nice",
File without changes
File without changes
File without changes
File without changes