harness-sdk-python 0.35.0__tar.gz → 0.36.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 (46) hide show
  1. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/PKG-INFO +5 -3
  2. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/README.md +3 -1
  3. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/examples/runs_app.py +2 -0
  4. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/pyproject.toml +2 -2
  5. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/__init__.py +2 -0
  6. harness_sdk_python-0.36.0/src/harness_sdk/protocol.py +5 -0
  7. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/run_helpers.py +23 -7
  8. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_admit_steer.py +1 -1
  9. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_batches.py +5 -4
  10. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/.gitignore +0 -0
  11. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/examples/__init__.py +0 -0
  12. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/checkpoints.py +0 -0
  13. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/deepagents.py +0 -0
  14. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/fenced_postgres.py +0 -0
  15. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/linear_thread.py +0 -0
  16. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/run_manager.py +0 -0
  17. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/src/harness_sdk/voice.py +0 -0
  18. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_applied_visibility.py +0 -0
  19. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_branch_anchor.py +0 -0
  20. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_checkpoints.py +0 -0
  21. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_deepagents_messages.py +0 -0
  22. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_dispatching.py +0 -0
  23. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_durability.py +0 -0
  24. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_edit_dispatched.py +0 -0
  25. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_edit_reload.py +0 -0
  26. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_enqueue.py +0 -0
  27. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_fenced_postgres.py +0 -0
  28. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_input_required.py +0 -0
  29. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_linear_thread.py +0 -0
  30. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_meta.py +0 -0
  31. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_mint_concurrency.py +0 -0
  32. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_outcomes.py +0 -0
  33. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_placement.py +0 -0
  34. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_prepare_hooks.py +0 -0
  35. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_restore.py +0 -0
  36. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_rewind_during_run.py +0 -0
  37. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_run_crash_sweep.py +0 -0
  38. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_run_ownership.py +0 -0
  39. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_settle.py +0 -0
  40. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_steer.py +0 -0
  41. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_stop_continue.py +0 -0
  42. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_stopping_window.py +0 -0
  43. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_voice.py +0 -0
  44. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_voice_delegation.py +0 -0
  45. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/test_voice_frontend_tools.py +0 -0
  46. {harness_sdk_python-0.35.0 → harness_sdk_python-0.36.0}/tests/voice_helpers.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: harness-sdk-python
3
- Version: 0.35.0
3
+ Version: 0.36.0
4
4
  Summary: RunManager: the harness-sdk runs subsystem for Python Statewire hosts
5
5
  Project-URL: Repository, https://github.com/assistant-ui/harness-sdk
6
6
  License-Expression: MIT
7
7
  Requires-Python: <4.0,>=3.12
8
8
  Requires-Dist: pinned<0.16,>=0.15.0
9
- Requires-Dist: statewire<0.16,>=0.15.0
9
+ Requires-Dist: statewire<0.17,>=0.16.0
10
10
  Provides-Extra: deepagents
11
11
  Requires-Dist: deepagents>=0.6.12; extra == 'deepagents'
12
12
  Requires-Dist: langchain-core>=0.3; extra == 'deepagents'
@@ -34,10 +34,12 @@ Extras: `uv add "harness-sdk-python[postgres]"`, `uv add "harness-sdk-python[dee
34
34
  ```python
35
35
  from statewire import Statewire, command
36
36
 
37
- from harness_sdk import RunManager, run_manager
37
+ from harness_sdk import HARNESS_PROTOCOL, RunManager, run_manager
38
38
 
39
39
 
40
40
  class RunsHost(Statewire):
41
+ protocols = (HARNESS_PROTOCOL,)
42
+
41
43
  async def lifespan(self):
42
44
  self.state = {"messages": []}
43
45
  yield
@@ -15,10 +15,12 @@ Extras: `uv add "harness-sdk-python[postgres]"`, `uv add "harness-sdk-python[dee
15
15
  ```python
16
16
  from statewire import Statewire, command
17
17
 
18
- from harness_sdk import RunManager, run_manager
18
+ from harness_sdk import HARNESS_PROTOCOL, RunManager, run_manager
19
19
 
20
20
 
21
21
  class RunsHost(Statewire):
22
+ protocols = (HARNESS_PROTOCOL,)
23
+
22
24
  async def lifespan(self):
23
25
  self.state = {"messages": []}
24
26
  yield
@@ -74,6 +74,8 @@ def user_text(message: dict[str, Any]) -> str:
74
74
 
75
75
 
76
76
  class RunsHost(Statewire):
77
+ protocols = (Statewire.Protocol("harness-sdk"),)
78
+
77
79
  async def lifespan(self):
78
80
  self.agent = build_agent()
79
81
  self.state = {"messages": []}
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "harness-sdk-python"
3
- version = "0.35.0"
3
+ version = "0.36.0"
4
4
  description = "RunManager: the harness-sdk runs subsystem for Python Statewire hosts"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  requires-python = ">=3.12,<4.0"
8
- dependencies = ["statewire>=0.15.0,<0.16", "pinned>=0.15.0,<0.16"]
8
+ dependencies = ["statewire>=0.16.0,<0.17", "pinned>=0.15.0,<0.16"]
9
9
 
10
10
  [project.optional-dependencies]
11
11
  postgres = ["langgraph-checkpoint-postgres>=2.0.0"]
@@ -1,3 +1,4 @@
1
+ from .protocol import HARNESS_PROTOCOL
1
2
  from .checkpoints import (
2
3
  CheckpointIndex,
3
4
  CheckpointStore,
@@ -36,6 +37,7 @@ from .voice import (
36
37
  )
37
38
 
38
39
  __all__ = [
40
+ "HARNESS_PROTOCOL",
39
41
  "CheckpointIndex",
40
42
  "CheckpointStore",
41
43
  "CheckpointThread",
@@ -0,0 +1,5 @@
1
+ from statewire import Statewire
2
+
3
+ HARNESS_PROTOCOL = Statewire.Protocol(
4
+ "harness-sdk", version="2026-09-07", min_version="2026-09-07"
5
+ )
@@ -157,7 +157,12 @@ _RES_TYPES = {
157
157
 
158
158
 
159
159
  def _statement(method: str, params: Any, seq: int) -> dict[str, Any]:
160
- return {"method": method, "params": [] if params is None else [params], "seq": seq}
160
+ return {
161
+ "protocol": "default",
162
+ "method": method,
163
+ "params": [] if params is None else [params],
164
+ "seq": seq,
165
+ }
161
166
 
162
167
 
163
168
  class RunDriver:
@@ -177,7 +182,9 @@ class RunDriver:
177
182
  self.replica: dict[str, Any] = {}
178
183
  self.envelopes: list[dict[str, Any]] = []
179
184
 
180
- async def _submit(self, frame: dict[str, Any], *, client_id: str | None = None) -> None:
185
+ async def _submit(
186
+ self, frame: dict[str, Any], *, client_id: str | None = None
187
+ ) -> None:
181
188
  client_id = client_id or self.client_id
182
189
  while True:
183
190
  response = await post_frame(self._client, frame, client_id=client_id)
@@ -197,7 +204,16 @@ class RunDriver:
197
204
  {
198
205
  "ctx": [
199
206
  {
200
- "ops": [{"op": "replace", "path": [], "value": value}],
207
+ "ops": [
208
+ {
209
+ "op": "mount",
210
+ "protocol": "default",
211
+ "main": True,
212
+ "value": value,
213
+ }
214
+ ]
215
+ if self._ctx_seq == 1
216
+ else [{"op": "replace", "path": [], "value": value}],
201
217
  "seq": self._ctx_seq,
202
218
  }
203
219
  ]
@@ -264,9 +280,7 @@ class RunDriver:
264
280
  await self.pump(timeout=timeout)
265
281
 
266
282
  async def wait_status(self, *statuses: str, timeout: float = 5) -> None:
267
- await self.wait(
268
- lambda s: s["status"] in statuses, timeout=timeout
269
- )
283
+ await self.wait(lambda s: s["status"] in statuses, timeout=timeout)
270
284
 
271
285
  @property
272
286
  def run(self) -> dict[str, Any]:
@@ -313,7 +327,9 @@ async def run_host(
313
327
  prepare_input=prepare_input,
314
328
  authorize=authorize,
315
329
  )
316
- worker = PinnedWorker(state_store=store if store is not None else MemoryStateStore())
330
+ worker = PinnedWorker(
331
+ state_store=store if store is not None else MemoryStateStore()
332
+ )
317
333
  async with statewire_client(host_cls, worker=worker) as (app, client):
318
334
  # Fresh client id per session, like a production client.
319
335
  client_id = f"c-{uuid4().hex[:8]}"
@@ -50,7 +50,7 @@ def make_host(script: Script):
50
50
 
51
51
 
52
52
  def statement(seq, method, params):
53
- return {"method": method, "params": [params], "seq": seq}
53
+ return {"protocol": "default", "method": method, "params": [params], "seq": seq}
54
54
 
55
55
 
56
56
  async def cmds_until(stream, entry, limit=10):
@@ -87,7 +87,7 @@ async def test_handler_tail_staging_decides_in_its_own_envelope():
87
87
  env
88
88
  for env in drv.envelopes
89
89
  if any(
90
- op["path"] == ["status"] and op.get("value") == "running"
90
+ op.get("path") == ["status"] and op.get("value") == "running"
91
91
  for op in env.get("ops", [])
92
92
  )
93
93
  )
@@ -100,9 +100,10 @@ async def test_handler_tail_staging_decides_in_its_own_envelope():
100
100
 
101
101
  async def test_rewind_outranks_send_and_continue_in_one_batch():
102
102
  script = Script()
103
- async with run_host(
104
- script, capabilities=("rewind", "incomplete-continuation")
105
- ) as (drv, host):
103
+ async with run_host(script, capabilities=("rewind", "incomplete-continuation")) as (
104
+ drv,
105
+ host,
106
+ ):
106
107
  drv.post("run/enqueue", add("m1"))
107
108
  call = await script.next_call()
108
109
  script.thread["m1"] = {