harness-sdk-python 0.36.4__tar.gz → 0.36.5__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 (51) hide show
  1. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/PKG-INFO +1 -1
  2. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/pyproject.toml +1 -1
  3. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/run_manager.py +11 -1
  4. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_run_admission.py +49 -0
  5. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/.gitignore +0 -0
  6. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/README.md +0 -0
  7. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/examples/__init__.py +0 -0
  8. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/examples/runs_app.py +0 -0
  9. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/__init__.py +0 -0
  10. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/checkpoints.py +0 -0
  11. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/deepagents.py +0 -0
  12. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/fenced_postgres.py +0 -0
  13. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/langgraph.py +0 -0
  14. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/linear_thread.py +0 -0
  15. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/protocol.py +0 -0
  16. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/src/harness_sdk/voice.py +0 -0
  17. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/run_helpers.py +0 -0
  18. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_admit_steer.py +0 -0
  19. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_applied_visibility.py +0 -0
  20. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_batches.py +0 -0
  21. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_branch_anchor.py +0 -0
  22. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_checkpoints.py +0 -0
  23. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_deepagents_messages.py +0 -0
  24. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_dispatching.py +0 -0
  25. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_durability.py +0 -0
  26. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_edit_dispatched.py +0 -0
  27. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_edit_reload.py +0 -0
  28. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_enqueue.py +0 -0
  29. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_fenced_postgres.py +0 -0
  30. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_input_required.py +0 -0
  31. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_langgraph.py +0 -0
  32. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_linear_thread.py +0 -0
  33. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_meta.py +0 -0
  34. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_mint_concurrency.py +0 -0
  35. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_outcomes.py +0 -0
  36. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_placement.py +0 -0
  37. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_prepare_hooks.py +0 -0
  38. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_restore.py +0 -0
  39. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_rewind_during_run.py +0 -0
  40. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_run_crash_sweep.py +0 -0
  41. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_run_eviction.py +0 -0
  42. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_run_ownership.py +0 -0
  43. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_run_sync.py +0 -0
  44. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_settle.py +0 -0
  45. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_steer.py +0 -0
  46. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_stop_continue.py +0 -0
  47. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_stopping_window.py +0 -0
  48. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_voice.py +0 -0
  49. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_voice_delegation.py +0 -0
  50. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/test_voice_frontend_tools.py +0 -0
  51. {harness_sdk_python-0.36.4 → harness_sdk_python-0.36.5}/tests/voice_helpers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: harness-sdk-python
3
- Version: 0.36.4
3
+ Version: 0.36.5
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "harness-sdk-python"
3
- version = "0.36.4"
3
+ version = "0.36.5"
4
4
  description = "RunManager: the harness-sdk runs subsystem for Python Statewire hosts"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -455,6 +455,12 @@ class RunManager(Tray):
455
455
  self._replay_settle()
456
456
  self._outcome = settled
457
457
  recorded = self.outbox.value
458
+ if recorded is not None and not recorded["item"].get("durableAdmission", False):
459
+ self.steerQueue.untake(self.confirmedTakes.value)
460
+ self.confirmedTakes.value = 0
461
+ self.batchApplied.value = False
462
+ recorded = {**recorded, "item": {**recorded["item"], "durableAdmission": True}}
463
+ self.outbox.value = recorded
458
464
  if recorded is not None and not self.batchApplied.value:
459
465
  record = recorded["item"]["record"]
460
466
  lanes = recorded["item"]["lanes"]
@@ -801,7 +807,11 @@ class RunManager(Tray):
801
807
  root_meta=root_meta,
802
808
  input_outcomes=input_outcomes,
803
809
  )
804
- item = {"record": record, "lanes": [lane for lane, _ in self._batch]}
810
+ item = {
811
+ "record": record,
812
+ "lanes": [lane for lane, _ in self._batch],
813
+ "durableAdmission": True,
814
+ }
805
815
  self.kick()
806
816
  self.stopReason.value = None
807
817
  self.epoch.value = self.epoch.value + 1
@@ -144,3 +144,52 @@ async def test_unconfirmed_rewind_replacement_can_be_enqueued_after_stop():
144
144
  assert [m["id"] for m in retry.ctx.messages] == ["m2"]
145
145
  retry.applied()
146
146
  retry.finish(RunManager.Complete())
147
+
148
+
149
+ @pytest.mark.parametrize("takes", [0, 2])
150
+ @pytest.mark.parametrize("checkpoint", [None, {"graph_input_checkpointed": True}])
151
+ async def test_pre_split_records_replay_inputs_and_restore_confirmed_steers(
152
+ takes, checkpoint
153
+ ):
154
+ store = MemoryStateStore()
155
+ script = Script()
156
+ host_type = make_host(script)
157
+ async with instance(host_type, state_store=store) as host:
158
+ await host.runs.enqueue(
159
+ add("m1"), meta={"author": "first"}, applied=lambda: None, auth_result=None
160
+ )
161
+ call = await script.next_call()
162
+ call.ctx.set_state(checkpoint)
163
+ call.applied()
164
+ for index in range(takes):
165
+ await host.runs.steer(
166
+ add(f"s{index}"),
167
+ meta={"author": f"author{index}"},
168
+ applied=lambda: None,
169
+ auth_result=None,
170
+ )
171
+ if takes:
172
+ call.ctx.steering.take()
173
+ call.applied()
174
+ await host.frontier.wait()
175
+ for _, doc in store.trays.values():
176
+ if "outbox" in doc:
177
+ doc["outbox"]["item"].pop("durableAdmission")
178
+
179
+ async with instance(host_type, state_store=store) as host:
180
+ replay = await script.next_call()
181
+ assert not replay.ctx.resume
182
+ assert [m["id"] for m in replay.ctx.messages] == ["m1"]
183
+ assert replay.ctx.meta("m1") == {"author": "first"}
184
+ replay.applied()
185
+ if takes:
186
+ assert [m["id"] for m in replay.ctx.steering.take()] == ["s0", "s1"]
187
+ assert replay.ctx.meta("s0") == {"author": "author0"}
188
+ assert replay.ctx.meta("s1") == {"author": "author1"}
189
+ replay.applied()
190
+ await host.frontier.wait()
191
+ async with instance(host_type, state_store=store):
192
+ confirmed = await script.next_call()
193
+ assert confirmed.ctx.resume
194
+ assert confirmed.ctx.steering.take() == ()
195
+ confirmed.finish(RunManager.Complete())