power-loop 3.5.0__tar.gz → 3.7.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.
- {power_loop-3.5.0 → power_loop-3.7.0}/PKG-INFO +1 -1
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/__init__.py +1 -1
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/history_projector.py +5 -4
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/representation.py +6 -4
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/default_manifest.py +23 -8
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/default_tools.py +18 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop.egg-info/PKG-INFO +1 -1
- {power_loop-3.5.0 → power_loop-3.7.0}/LICENSE +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/README.md +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/capabilities.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/interface.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/llm_factory.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/_vendor/llm_client/multimodal.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/follow_up.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/sink.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/stateful_loop.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/system_prompt.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/agent/types.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/errors.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/event_payloads.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/events.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/handlers.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/hook_contexts.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/hooks.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/messages.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/protocols.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contracts/tools.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/_redact.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/jsonl_sink.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/logging_sink.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/mcp.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/metrics_sink.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/contrib/otel_sink.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/agent_context.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/events.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/hooks.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/phase.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/pipeline.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/runner.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/core/state.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/py.typed +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/blackboard.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/budget.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/cancellation.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/compact.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/env.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/exec_backend.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/fold.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/fold_adapter.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/history_sanitize.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/human_input.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/memory.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/notes.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/provider.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/retry.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/runtime_state.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/session_store.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/skills.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/spec.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/backends/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/backends/mysql.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/backends/postgres.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/backends/sqlite.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/capabilities.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/db.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/dialect.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/factory.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/schema.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/store.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/store/types.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/structured.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/stub_provider.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/runtime/timers.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/blackboard.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/registry.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/tools/spawn_agent.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/__init__.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/api.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/engine.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/introspect.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/journal.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/result.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/resume.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/runner.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/spec.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/subprocess_executor.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/tool.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop/workflow/worker.py +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop.egg-info/SOURCES.txt +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop.egg-info/dependency_links.txt +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop.egg-info/requires.txt +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/power_loop.egg-info/top_level.txt +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/pyproject.toml +0 -0
- {power_loop-3.5.0 → power_loop-3.7.0}/setup.cfg +0 -0
|
@@ -15,7 +15,7 @@ Stability tiers
|
|
|
15
15
|
无版本承诺,可随时变更或删除。
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
__version__ = "3.
|
|
18
|
+
__version__ = "3.7.0"
|
|
19
19
|
|
|
20
20
|
# Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
|
|
21
21
|
# a custom LLMService) don't reach into the internal vendored transport package (H3.4).
|
|
@@ -117,7 +117,9 @@ class HistoryProjector(Protocol):
|
|
|
117
117
|
|
|
118
118
|
def _truncate(s: Any, max_chars: int) -> str:
|
|
119
119
|
t = "" if s is None else str(s)
|
|
120
|
-
|
|
120
|
+
if max_chars <= 0 or len(t) <= max_chars: # max_chars <= 0 → no truncation (unlimited)
|
|
121
|
+
return t
|
|
122
|
+
return t[:max_chars] + "…"
|
|
121
123
|
|
|
122
124
|
|
|
123
125
|
def _parse_args(raw: Any) -> Any:
|
|
@@ -225,7 +227,7 @@ class DefaultDeterministicProjector:
|
|
|
225
227
|
terse plain text with NO tool-protocol structure."""
|
|
226
228
|
|
|
227
229
|
version: int = 1
|
|
228
|
-
max_chars: int = 300 # per-field truncation budget
|
|
230
|
+
max_chars: int = 300 # per-field truncation budget; <= 0 = no truncation (unlimited)
|
|
229
231
|
keep_last_sends: int = 4 # most-recent sends ALWAYS kept individually (never folded)
|
|
230
232
|
#: Fold older sends into a compact row once the rendered projected prefix reaches
|
|
231
233
|
#: ``loop max_tokens × trigger_ratio`` (mirrors DefaultCompactor's policy) — token-driven,
|
|
@@ -244,8 +246,7 @@ class DefaultDeterministicProjector:
|
|
|
244
246
|
keep_last_sends=self.keep_last_sends,
|
|
245
247
|
trigger_ratio=self.trigger_ratio,
|
|
246
248
|
)
|
|
247
|
-
|
|
248
|
-
raise ValueError(f"max_chars must be > 0; got {self.max_chars!r}")
|
|
249
|
+
# max_chars <= 0 → no truncation (unlimited); any int is accepted.
|
|
249
250
|
if self.max_compact_chars < 0:
|
|
250
251
|
raise ValueError(
|
|
251
252
|
f"max_compact_chars must be >= 0 (0 = unbounded); got {self.max_compact_chars!r}"
|
|
@@ -93,7 +93,9 @@ class Representation(Protocol):
|
|
|
93
93
|
|
|
94
94
|
def _truncate(s: Any, max_chars: int) -> str:
|
|
95
95
|
t = "" if s is None else str(s)
|
|
96
|
-
|
|
96
|
+
if max_chars <= 0 or len(t) <= max_chars: # max_chars <= 0 → no truncation (unlimited)
|
|
97
|
+
return t
|
|
98
|
+
return t[:max_chars] + "…"
|
|
97
99
|
|
|
98
100
|
|
|
99
101
|
def _parse_args(raw: Any) -> Any:
|
|
@@ -143,8 +145,8 @@ def _validate_representation_params(*, version: int, max_chars: int | None = Non
|
|
|
143
145
|
raise ValueError(
|
|
144
146
|
f"representation version must be >= 1 (the compatibility key); got {version!r}"
|
|
145
147
|
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
# max_chars <= 0 is now VALID and means "no truncation" (unlimited) — let a host disable the
|
|
149
|
+
# library's per-field cap and do its own limiting. Any int is accepted.
|
|
148
150
|
|
|
149
151
|
|
|
150
152
|
def _render_compact_row(row: ProjectMessageRow) -> LoopMessage:
|
|
@@ -249,7 +251,7 @@ class ProjectedRepresentation:
|
|
|
249
251
|
|
|
250
252
|
kind: str = "projection"
|
|
251
253
|
version: int = 1
|
|
252
|
-
max_chars: int = 300 # per-field truncation budget
|
|
254
|
+
max_chars: int = 300 # per-field truncation budget; <= 0 = no truncation (unlimited)
|
|
253
255
|
#: Format knobs for render() — see :class:`ProjectionRenderConfig`. A plain dict is coerced (so a
|
|
254
256
|
#: host can pass JSON config straight through). Subclasses can ALSO override the render_* methods.
|
|
255
257
|
render_config: ProjectionRenderConfig = field(default_factory=ProjectionRenderConfig)
|
|
@@ -188,11 +188,11 @@ DEFAULT_TOOL_DEFINITIONS: list[ToolDefinition] = [
|
|
|
188
188
|
ToolDefinition(
|
|
189
189
|
name="note_add",
|
|
190
190
|
description=(
|
|
191
|
-
"Save a short persistent note to your own memory. Notes survive context compaction
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"hidden or auto-evicted."
|
|
191
|
+
"Save a short persistent note to your own memory. Notes survive context compaction; read "
|
|
192
|
+
"them back with note_list (and, when the memory-recall hook is enabled, they are also shown "
|
|
193
|
+
"at the start of each turn). Use for durable facts, preferences, ongoing task state — not "
|
|
194
|
+
"for transcripts or long content (put those in files). When notes are full you must delete "
|
|
195
|
+
"or merge old ones first. Set pinned=true for notes that must never be hidden or auto-evicted."
|
|
196
196
|
),
|
|
197
197
|
input_schema={
|
|
198
198
|
"type": "object",
|
|
@@ -213,7 +213,7 @@ DEFAULT_TOOL_DEFINITIONS: list[ToolDefinition] = [
|
|
|
213
213
|
input_schema={
|
|
214
214
|
"type": "object",
|
|
215
215
|
"properties": {
|
|
216
|
-
"note_id": {"type": "integer", "description": "The #id
|
|
216
|
+
"note_id": {"type": "integer", "description": "The #id from note_list (or YOUR NOTES)."},
|
|
217
217
|
"content": {"type": "string", "description": "New text replacing the old content."},
|
|
218
218
|
"pinned": {"type": "boolean", "description": "Set or clear the pinned flag."},
|
|
219
219
|
},
|
|
@@ -223,16 +223,31 @@ DEFAULT_TOOL_DEFINITIONS: list[ToolDefinition] = [
|
|
|
223
223
|
),
|
|
224
224
|
ToolDefinition(
|
|
225
225
|
name="note_delete",
|
|
226
|
-
description=
|
|
226
|
+
description=(
|
|
227
|
+
"Delete one of your notes by its #id (from note_list) when it is stale or no longer "
|
|
228
|
+
"worth remembering."
|
|
229
|
+
),
|
|
227
230
|
input_schema={
|
|
228
231
|
"type": "object",
|
|
229
232
|
"properties": {
|
|
230
|
-
"note_id": {"type": "integer", "description": "The #id
|
|
233
|
+
"note_id": {"type": "integer", "description": "The #id from note_list (or YOUR NOTES)."},
|
|
231
234
|
},
|
|
232
235
|
"required": ["note_id"],
|
|
233
236
|
},
|
|
234
237
|
required_params=("note_id",),
|
|
235
238
|
),
|
|
239
|
+
ToolDefinition(
|
|
240
|
+
name="note_list",
|
|
241
|
+
description=(
|
|
242
|
+
"List your persistent notes — each note's #id, pinned flag, and content — so you can "
|
|
243
|
+
"read back what you remember and get the #id needed to note_update / note_delete. These "
|
|
244
|
+
"are the same notes you maintain with note_add / note_update / note_delete; they survive "
|
|
245
|
+
"context compaction. Call this when you want to check your memory (e.g. at the start of a "
|
|
246
|
+
"task) rather than relying on it being auto-shown."
|
|
247
|
+
),
|
|
248
|
+
input_schema={"type": "object", "properties": {}},
|
|
249
|
+
required_params=(),
|
|
250
|
+
),
|
|
236
251
|
ToolDefinition(
|
|
237
252
|
name="schedule_wakeup",
|
|
238
253
|
description=(
|
|
@@ -1471,6 +1471,19 @@ async def run_note_delete(note_id: int) -> str:
|
|
|
1471
1471
|
return f"note #{note_id} deleted ({await store.count_notes(sid)} remaining)"
|
|
1472
1472
|
|
|
1473
1473
|
|
|
1474
|
+
async def run_note_list() -> str:
|
|
1475
|
+
"""Read back the agent's persistent notes (rendered with #id, pinned flag, content), so the
|
|
1476
|
+
agent can inspect its memory and get the #ids for note_update / note_delete WITHOUT relying on
|
|
1477
|
+
the (optional) memory-recall hook auto-injecting them."""
|
|
1478
|
+
from power_loop.runtime.notes import render_notes
|
|
1479
|
+
|
|
1480
|
+
store, sid = _notes_store_and_session()
|
|
1481
|
+
notes = await store.list_notes(sid)
|
|
1482
|
+
if not notes:
|
|
1483
|
+
return "(no notes yet — use note_add to remember durable facts, preferences, or task state)"
|
|
1484
|
+
return render_notes(notes, policy=_notes_policy())
|
|
1485
|
+
|
|
1486
|
+
|
|
1474
1487
|
def _timers_store_and_session() -> tuple[Any, str]:
|
|
1475
1488
|
store, sid = _current_store_and_session()
|
|
1476
1489
|
if store is None or sid is None:
|
|
@@ -1684,6 +1697,10 @@ async def _h_note_delete(**kw: Any) -> Any:
|
|
|
1684
1697
|
return await run_note_delete(kw["note_id"])
|
|
1685
1698
|
|
|
1686
1699
|
|
|
1700
|
+
async def _h_note_list(**kw: Any) -> Any:
|
|
1701
|
+
return await run_note_list()
|
|
1702
|
+
|
|
1703
|
+
|
|
1687
1704
|
async def _h_schedule_wakeup(**kw: Any) -> Any:
|
|
1688
1705
|
return await run_schedule_wakeup(kw["delay_seconds"], kw["note"], kw.get("every_seconds"))
|
|
1689
1706
|
|
|
@@ -1740,6 +1757,7 @@ DEFAULT_TOOL_HANDLERS: dict[str, Any] = {
|
|
|
1740
1757
|
"note_add": _h_note_add,
|
|
1741
1758
|
"note_update": _h_note_update,
|
|
1742
1759
|
"note_delete": _h_note_delete,
|
|
1760
|
+
"note_list": _h_note_list,
|
|
1743
1761
|
"schedule_wakeup": _h_schedule_wakeup,
|
|
1744
1762
|
"list_wakeups": _h_list_wakeups,
|
|
1745
1763
|
"cancel_wakeup": _h_cancel_wakeup,
|
|
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
|
|
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
|
|
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
|