harness-sdk-python 0.33.0__tar.gz → 0.34.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.
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/.gitignore +3 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/PKG-INFO +6 -4
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/README.md +4 -2
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/examples/runs_app.py +23 -17
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/pyproject.toml +2 -2
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/run_manager.py +46 -59
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_mint_concurrency.py +6 -5
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/examples/__init__.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/__init__.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/checkpoints.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/deepagents.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/fenced_postgres.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/linear_thread.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/src/harness_sdk/voice.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/run_helpers.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_admit_steer.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_applied_visibility.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_batches.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_branch_anchor.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_checkpoints.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_deepagents_messages.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_dispatching.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_durability.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_edit_dispatched.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_edit_reload.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_enqueue.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_fenced_postgres.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_input_required.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_linear_thread.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_meta.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_outcomes.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_placement.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_prepare_hooks.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_restore.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_rewind_during_run.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_run_crash_sweep.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_run_ownership.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_settle.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_steer.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_stop_continue.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_stopping_window.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_voice.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_voice_delegation.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.0}/tests/test_voice_frontend_tools.py +0 -0
- {harness_sdk_python-0.33.0 → harness_sdk_python-0.34.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.
|
|
3
|
+
Version: 0.34.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.
|
|
9
|
+
Requires-Dist: statewire<0.15,>=0.14.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'
|
|
@@ -50,8 +50,10 @@ class RunsHost(Statewire):
|
|
|
50
50
|
return RunManager.Complete()
|
|
51
51
|
|
|
52
52
|
@command("run/enqueue")
|
|
53
|
-
async def run_enqueue(self, params, *, ctx):
|
|
54
|
-
return await
|
|
53
|
+
async def run_enqueue(self, params, *, ctx, auth_result):
|
|
54
|
+
return await self.run.enqueue(
|
|
55
|
+
params, applied=ctx.applied, auth_result=auth_result
|
|
56
|
+
)
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
## Docs
|
|
@@ -31,8 +31,10 @@ class RunsHost(Statewire):
|
|
|
31
31
|
return RunManager.Complete()
|
|
32
32
|
|
|
33
33
|
@command("run/enqueue")
|
|
34
|
-
async def run_enqueue(self, params, *, ctx):
|
|
35
|
-
return await
|
|
34
|
+
async def run_enqueue(self, params, *, ctx, auth_result):
|
|
35
|
+
return await self.run.enqueue(
|
|
36
|
+
params, applied=ctx.applied, auth_result=auth_result
|
|
37
|
+
)
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
## Docs
|
|
@@ -20,7 +20,7 @@ from typing import Any
|
|
|
20
20
|
from fastapi import FastAPI
|
|
21
21
|
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
|
22
22
|
from pinned import PinnedHost, PinnedWorker
|
|
23
|
-
from pinned.
|
|
23
|
+
from pinned.durability import MemoryStateStore
|
|
24
24
|
from statewire import Statewire, command, plain
|
|
25
25
|
from statewire.langgraph import append_langgraph_event
|
|
26
26
|
|
|
@@ -154,36 +154,42 @@ class RunsHost(Statewire):
|
|
|
154
154
|
await stream.aclose()
|
|
155
155
|
|
|
156
156
|
@command("run/enqueue")
|
|
157
|
-
async def run_enqueue(self, params, *, ctx):
|
|
158
|
-
return await
|
|
157
|
+
async def run_enqueue(self, params, *, ctx, auth_result):
|
|
158
|
+
return await self.run.enqueue(
|
|
159
|
+
params, applied=ctx.applied, auth_result=auth_result
|
|
160
|
+
)
|
|
159
161
|
|
|
160
162
|
@command("run/steer")
|
|
161
|
-
async def run_steer(self, params, *, ctx):
|
|
162
|
-
return await
|
|
163
|
+
async def run_steer(self, params, *, ctx, auth_result):
|
|
164
|
+
return await self.run.steer(
|
|
165
|
+
params, applied=ctx.applied, auth_result=auth_result
|
|
166
|
+
)
|
|
163
167
|
|
|
164
168
|
@command("run/dequeue")
|
|
165
|
-
async def run_dequeue(self, params):
|
|
166
|
-
return await
|
|
169
|
+
async def run_dequeue(self, params, *, auth_result):
|
|
170
|
+
return await self.run.dequeue(params, auth_result=auth_result)
|
|
167
171
|
|
|
168
172
|
@command("run/edit")
|
|
169
|
-
async def run_edit(self, params, *, ctx):
|
|
170
|
-
return await
|
|
173
|
+
async def run_edit(self, params, *, ctx, auth_result):
|
|
174
|
+
return await self.run.edit(params, applied=ctx.applied, auth_result=auth_result)
|
|
171
175
|
|
|
172
176
|
@command("run/reload")
|
|
173
|
-
async def run_reload(self, params, *, ctx):
|
|
174
|
-
return await
|
|
177
|
+
async def run_reload(self, params, *, ctx, auth_result):
|
|
178
|
+
return await self.run.reload(
|
|
179
|
+
params, applied=ctx.applied, auth_result=auth_result
|
|
180
|
+
)
|
|
175
181
|
|
|
176
182
|
@command("run/stop")
|
|
177
|
-
async def run_stop(self, params=None, *, ctx):
|
|
178
|
-
return await
|
|
183
|
+
async def run_stop(self, params=None, *, ctx, auth_result):
|
|
184
|
+
return await self.run.stop(params, applied=ctx.applied, auth_result=auth_result)
|
|
179
185
|
|
|
180
186
|
@command("run/continue")
|
|
181
|
-
async def run_continue(self, params=None, *, ctx):
|
|
182
|
-
return await
|
|
187
|
+
async def run_continue(self, params=None, *, ctx, auth_result):
|
|
188
|
+
return await self.run.continue_run(applied=ctx.applied, auth_result=auth_result)
|
|
183
189
|
|
|
184
190
|
@command("run/input")
|
|
185
|
-
async def run_input(self, params):
|
|
186
|
-
return await
|
|
191
|
+
async def run_input(self, params, *, auth_result):
|
|
192
|
+
return await self.run.input(params, auth_result=auth_result)
|
|
187
193
|
|
|
188
194
|
|
|
189
195
|
worker = PinnedWorker(
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "harness-sdk-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.34.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.
|
|
8
|
+
dependencies = ["statewire>=0.14.0,<0.15", "pinned>=0.15.0,<0.16"]
|
|
9
9
|
|
|
10
10
|
[project.optional-dependencies]
|
|
11
11
|
postgres = ["langgraph-checkpoint-postgres>=2.0.0"]
|
|
@@ -63,7 +63,6 @@ equality per principal.
|
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
65
|
import asyncio
|
|
66
|
-
import contextlib
|
|
67
66
|
import logging
|
|
68
67
|
from dataclasses import dataclass, field
|
|
69
68
|
from typing import Any, AsyncIterator, Callable, Coroutine, Iterable, Protocol
|
|
@@ -220,6 +219,17 @@ def _validated_response(
|
|
|
220
219
|
return response
|
|
221
220
|
|
|
222
221
|
|
|
222
|
+
def _check_rewind_gate(caps: Any, in_flight: bool, command: str) -> None:
|
|
223
|
+
if in_flight:
|
|
224
|
+
if "rewind-during-run" not in caps:
|
|
225
|
+
raise _reject(
|
|
226
|
+
"capability-missing",
|
|
227
|
+
f"{command} during a run requires the rewind-during-run capability",
|
|
228
|
+
)
|
|
229
|
+
elif "rewind" not in caps:
|
|
230
|
+
raise _reject("capability-missing", "the rewind capability is not enabled")
|
|
231
|
+
|
|
232
|
+
|
|
223
233
|
def _check_rewind_anchor(params: Any, source_meta: dict[str, Any]) -> None:
|
|
224
234
|
anchor = params.get("runAnchorMessageId", _ABSENT)
|
|
225
235
|
if anchor is _ABSENT:
|
|
@@ -304,8 +314,9 @@ class _Continue:
|
|
|
304
314
|
@dataclass
|
|
305
315
|
class _Input:
|
|
306
316
|
request_id: str
|
|
307
|
-
response:
|
|
317
|
+
response: Any
|
|
308
318
|
meta: Any
|
|
319
|
+
principal: Any
|
|
309
320
|
future: "asyncio.Future[Any]" = field(default_factory=_future)
|
|
310
321
|
|
|
311
322
|
|
|
@@ -1093,10 +1104,9 @@ class RunManager(Tray):
|
|
|
1093
1104
|
raise _reject("queue-full", f"queue is full ({self.max_queued} messages)")
|
|
1094
1105
|
self._batch.extend(("steerQueue", item) for item in steer)
|
|
1095
1106
|
self._batch.append((e.lane, self._stamped(e.message, e.meta)))
|
|
1096
|
-
self.dispatching.value =
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1107
|
+
self.dispatching.value = _public_record(
|
|
1108
|
+
self._record_of(self._send_trigger(), [item for _, item in self._batch])
|
|
1109
|
+
)
|
|
1100
1110
|
|
|
1101
1111
|
def _dispatch_staged(
|
|
1102
1112
|
self, *, input_outcomes: tuple[tuple[dict[str, Any], Any, Any], ...] = ()
|
|
@@ -1331,14 +1341,7 @@ class RunManager(Tray):
|
|
|
1331
1341
|
def _park_dispatched_edit(self, e: _Send) -> Any:
|
|
1332
1342
|
assert e.message is not None
|
|
1333
1343
|
self._check_run_owner(e.principal)
|
|
1334
|
-
|
|
1335
|
-
if "rewind-during-run" not in self.capabilities:
|
|
1336
|
-
raise _reject(
|
|
1337
|
-
"capability-missing",
|
|
1338
|
-
"editing during a run requires the rewind-during-run capability",
|
|
1339
|
-
)
|
|
1340
|
-
elif "rewind" not in self.capabilities:
|
|
1341
|
-
raise _reject("capability-missing", "the rewind capability is not enabled")
|
|
1344
|
+
_check_rewind_gate(self.capabilities, self._in_flight(), "editing")
|
|
1342
1345
|
if e.source_meta is None:
|
|
1343
1346
|
raise _reject("unknown-id", f"message {e.message_id} is unknown")
|
|
1344
1347
|
self._check_anchor(e)
|
|
@@ -1367,6 +1370,7 @@ class RunManager(Tray):
|
|
|
1367
1370
|
def _apply_input(self, e: _Input) -> Any:
|
|
1368
1371
|
if self._status() != "input-required":
|
|
1369
1372
|
raise _reject("wrong-state", f"run/input is rejected in {self._status()}")
|
|
1373
|
+
self._check_run_owner(e.principal)
|
|
1370
1374
|
index = next(
|
|
1371
1375
|
(
|
|
1372
1376
|
i
|
|
@@ -1382,9 +1386,12 @@ class RunManager(Tray):
|
|
|
1382
1386
|
"already-answered",
|
|
1383
1387
|
f"input request {e.request_id} is already answered",
|
|
1384
1388
|
)
|
|
1385
|
-
|
|
1389
|
+
response = _validated_response(
|
|
1390
|
+
type(self), self._input_requests[index]["type"], e.response
|
|
1391
|
+
)
|
|
1392
|
+
self._input_answers[e.request_id] = (response, e.meta)
|
|
1386
1393
|
requests = self.inputRequests.value
|
|
1387
|
-
requests[index]["response"] =
|
|
1394
|
+
requests[index]["response"] = response
|
|
1388
1395
|
if e.meta is not None:
|
|
1389
1396
|
requests[index]["meta"] = e.meta
|
|
1390
1397
|
self.inputRequests.value = requests
|
|
@@ -1471,19 +1478,9 @@ class RunManager(Tray):
|
|
|
1471
1478
|
async def input(
|
|
1472
1479
|
self, params: dict[str, Any], *, meta: Any = None, auth_result: Any
|
|
1473
1480
|
) -> Any:
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
self._check_run_owner(auth_result)
|
|
1478
|
-
request = next((r for r in self._input_requests if r["id"] == request_id), None)
|
|
1479
|
-
if request is None:
|
|
1480
|
-
raise _reject("unknown-id", f"input request {request_id} is unknown")
|
|
1481
|
-
if request_id in self._input_answers:
|
|
1482
|
-
raise _reject(
|
|
1483
|
-
"already-answered", f"input request {request_id} is already answered"
|
|
1484
|
-
)
|
|
1485
|
-
response = _validated_response(type(self), request["type"], params["response"])
|
|
1486
|
-
return await self._stage(_Input(request_id, response, meta))
|
|
1481
|
+
return await self._stage(
|
|
1482
|
+
_Input(params["requestId"], params["response"], meta, auth_result)
|
|
1483
|
+
)
|
|
1487
1484
|
|
|
1488
1485
|
async def continue_run(
|
|
1489
1486
|
self, *, meta: Any = None, applied: Callable[[], None], auth_result: Any
|
|
@@ -1564,10 +1561,9 @@ class RunManager(Tray):
|
|
|
1564
1561
|
added = [item["id"] for item in items if item["id"] not in known]
|
|
1565
1562
|
if added:
|
|
1566
1563
|
manager.dispatchedIds.value = [*known, *added]
|
|
1567
|
-
manager.dispatching.value =
|
|
1568
|
-
"
|
|
1569
|
-
|
|
1570
|
-
}
|
|
1564
|
+
manager.dispatching.value = _public_record(
|
|
1565
|
+
manager._record_of("steer", items)
|
|
1566
|
+
)
|
|
1571
1567
|
self._ctx._message_meta.update(
|
|
1572
1568
|
{item["id"]: item.get("meta") for item in items}
|
|
1573
1569
|
)
|
|
@@ -1705,23 +1701,20 @@ class RunManagerNamespace(TrayMapNamespace):
|
|
|
1705
1701
|
async def _machine(self, run_id: str, principal: Any) -> tuple[RunManager, bool]:
|
|
1706
1702
|
# No await between the live() check and the mint's publication, and no
|
|
1707
1703
|
# lock held across storage I/O: same-tick callers await the shared mint
|
|
1708
|
-
# task and merge into the machine it mounts
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
tray
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
return tray, True
|
|
1723
|
-
with contextlib.suppress(Exception):
|
|
1724
|
-
await pending
|
|
1704
|
+
# task and merge into the machine it mounts, or raise its failure.
|
|
1705
|
+
tray = self.live()
|
|
1706
|
+
if tray is not None:
|
|
1707
|
+
return tray, False
|
|
1708
|
+
pending = self._mint
|
|
1709
|
+
minted = pending is None
|
|
1710
|
+
if pending is None:
|
|
1711
|
+
pending = self._api.create_task(self.get_or_create(run_id))
|
|
1712
|
+
self._mint = pending
|
|
1713
|
+
pending.add_done_callback(self._mint_settled)
|
|
1714
|
+
tray = await pending
|
|
1715
|
+
if minted and principal is not None and tray.principal.value is None:
|
|
1716
|
+
tray.principal.value = principal
|
|
1717
|
+
return tray, minted
|
|
1725
1718
|
|
|
1726
1719
|
def _mint_settled(self, task: "asyncio.Task[Any]") -> None:
|
|
1727
1720
|
if self._mint is task:
|
|
@@ -1876,15 +1869,9 @@ class RunManagerNamespace(TrayMapNamespace):
|
|
|
1876
1869
|
def _check_rewind_gate(self, command: str) -> None:
|
|
1877
1870
|
cls: type[RunManager] = self._spec.tray_cls # type: ignore[assignment]
|
|
1878
1871
|
live = self.live()
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
raise _reject(
|
|
1883
|
-
"capability-missing",
|
|
1884
|
-
f"{command} during a run requires the rewind-during-run capability",
|
|
1885
|
-
)
|
|
1886
|
-
elif "rewind" not in cls.capabilities:
|
|
1887
|
-
raise _reject("capability-missing", "the rewind capability is not enabled")
|
|
1872
|
+
_check_rewind_gate(
|
|
1873
|
+
cls.capabilities, live is not None and live._in_flight(), command
|
|
1874
|
+
)
|
|
1888
1875
|
|
|
1889
1876
|
async def edit(
|
|
1890
1877
|
self,
|
|
@@ -27,10 +27,10 @@ class WedgeStore(MemoryStateStore):
|
|
|
27
27
|
return await super().get_tray(ns, id, tray_ns, tray_id, epoch)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
async def
|
|
30
|
+
async def test_a_parked_mint_failure_reaches_the_creator_and_its_followers():
|
|
31
31
|
"""No lock is held across mint storage I/O: a wedged mint fails loudly at
|
|
32
|
-
the store deadline,
|
|
33
|
-
|
|
32
|
+
the store deadline, and every command that merged into it raises that
|
|
33
|
+
same store error instead of re-minting."""
|
|
34
34
|
script = Script()
|
|
35
35
|
store = WedgeStore()
|
|
36
36
|
async with run_host(script, store=store) as (drv, host):
|
|
@@ -42,10 +42,11 @@ async def test_a_parked_mint_fails_at_the_deadline_and_followers_progress():
|
|
|
42
42
|
await asyncio.sleep(0.05)
|
|
43
43
|
assert not follower.done() # same-tick mints merge: followers await the pending mint
|
|
44
44
|
store.fail()
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
with pytest.raises(TimeoutError):
|
|
46
|
+
await asyncio.wait_for(follower, 5)
|
|
47
47
|
with pytest.raises(TimeoutError):
|
|
48
48
|
await parked
|
|
49
|
+
assert list(api.runs.instances) == []
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
async def test_concurrent_same_run_id_mints_resolve_to_one_machine():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|