@totalreclaw/totalreclaw 3.3.12 → 3.3.13

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 (128) hide show
  1. package/SKILL.md +25 -8
  2. package/{billing-cache.ts → billing/billing-cache.ts} +1 -1
  3. package/{relay-headers.ts → billing/relay-headers.ts} +1 -1
  4. package/{tr-cli-export-helper.ts → cli/tr-cli-export-helper.ts} +5 -5
  5. package/{tr-cli.ts → cli/tr-cli.ts} +20 -20
  6. package/{contradiction-sync.ts → contradiction/contradiction-sync.ts} +13 -13
  7. package/{digest-sync.ts → digest/digest-sync.ts} +32 -2
  8. package/dist/{billing-cache.js → billing/billing-cache.js} +1 -1
  9. package/dist/{relay-headers.js → billing/relay-headers.js} +1 -1
  10. package/dist/{tr-cli-export-helper.js → cli/tr-cli-export-helper.js} +5 -5
  11. package/dist/{tr-cli.js → cli/tr-cli.js} +19 -19
  12. package/dist/{contradiction-sync.js → contradiction/contradiction-sync.js} +13 -13
  13. package/dist/{digest-sync.js → digest/digest-sync.js} +32 -2
  14. package/dist/{download-ux.js → embedding/download-ux.js} +1 -1
  15. package/dist/{reranker.js → embedding/reranker.js} +13 -4
  16. package/dist/{claims-helper.js → extraction/claims-helper.js} +54 -58
  17. package/dist/extraction/consolidation.js +170 -0
  18. package/dist/{extractor.js → extraction/extractor.js} +17 -17
  19. package/dist/extraction/importance-filter.js +38 -0
  20. package/dist/{semantic-dedup.js → extraction/semantic-dedup.js} +2 -2
  21. package/dist/{batch-gate.js → import/batch-gate.js} +1 -1
  22. package/dist/import/import-runtime.js +692 -0
  23. package/dist/import-adapters/chatgpt-adapter.js +7 -7
  24. package/dist/import-adapters/gemini-adapter.js +13 -13
  25. package/dist/import-adapters/mem0-adapter.js +10 -10
  26. package/dist/index.js +111 -1094
  27. package/dist/{llm-client.js → llm/llm-client.js} +2 -2
  28. package/dist/{hot-cache-wrapper.js → memory/hot-cache-wrapper.js} +5 -5
  29. package/dist/{memory-runtime.js → memory/memory-runtime.js} +1 -1
  30. package/dist/{pin.js → memory/pin.js} +18 -18
  31. package/dist/{retype-setscope.js → memory/retype-setscope.js} +19 -19
  32. package/dist/{credential-provider.js → pairing/credential-provider.js} +2 -2
  33. package/dist/{first-run.js → pairing/first-run.js} +1 -1
  34. package/dist/{onboarding-cli.js → pairing/onboarding-cli.js} +20 -20
  35. package/dist/{pair-cli-relay.js → pairing/pair-cli-relay.js} +10 -10
  36. package/dist/{pair-crypto.js → pairing/pair-crypto.js} +1 -1
  37. package/dist/{pair-http.js → pairing/pair-http.js} +3 -3
  38. package/dist/{pair-remote-client.js → pairing/pair-remote-client.js} +10 -10
  39. package/dist/{pair-session-store.js → pairing/pair-session-store.js} +4 -4
  40. package/dist/runtime/config-schema.js +59 -0
  41. package/dist/runtime/format-helpers.js +257 -0
  42. package/dist/runtime/types.js +11 -0
  43. package/dist/{confirm-indexed.js → subgraph/confirm-indexed.js} +1 -1
  44. package/dist/{subgraph-search.js → subgraph/subgraph-search.js} +14 -14
  45. package/dist/{subgraph-store.js → subgraph/subgraph-store.js} +6 -6
  46. package/{download-ux.ts → embedding/download-ux.ts} +1 -1
  47. package/{reranker.ts → embedding/reranker.ts} +13 -4
  48. package/{claims-helper.ts → extraction/claims-helper.ts} +52 -60
  49. package/{consolidation.ts → extraction/consolidation.ts} +63 -154
  50. package/{extractor.ts → extraction/extractor.ts} +18 -17
  51. package/extraction/importance-filter.ts +50 -0
  52. package/{semantic-dedup.ts → extraction/semantic-dedup.ts} +2 -2
  53. package/{batch-gate.ts → import/batch-gate.ts} +1 -1
  54. package/import/import-runtime.ts +853 -0
  55. package/import-adapters/chatgpt-adapter.ts +7 -7
  56. package/import-adapters/gemini-adapter.ts +13 -13
  57. package/import-adapters/mem0-adapter.ts +10 -10
  58. package/index.ts +129 -1377
  59. package/{llm-client.ts → llm/llm-client.ts} +2 -2
  60. package/{hot-cache-wrapper.ts → memory/hot-cache-wrapper.ts} +5 -5
  61. package/{memory-runtime.ts → memory/memory-runtime.ts} +1 -1
  62. package/{pin.ts → memory/pin.ts} +20 -20
  63. package/{retype-setscope.ts → memory/retype-setscope.ts} +21 -21
  64. package/{tool-gating.ts → memory/tool-gating.ts} +1 -1
  65. package/package.json +6 -6
  66. package/{credential-provider.ts → pairing/credential-provider.ts} +2 -2
  67. package/{first-run.ts → pairing/first-run.ts} +1 -1
  68. package/{onboarding-cli.ts → pairing/onboarding-cli.ts} +20 -20
  69. package/{pair-cli-relay.ts → pairing/pair-cli-relay.ts} +10 -10
  70. package/{pair-crypto.ts → pairing/pair-crypto.ts} +1 -1
  71. package/{pair-http.ts → pairing/pair-http.ts} +3 -3
  72. package/{pair-remote-client.ts → pairing/pair-remote-client.ts} +10 -10
  73. package/{pair-session-store.ts → pairing/pair-session-store.ts} +4 -4
  74. package/runtime/config-schema.ts +64 -0
  75. package/runtime/format-helpers.ts +293 -0
  76. package/runtime/types.ts +118 -0
  77. package/skill.json +44 -10
  78. package/{confirm-indexed.ts → subgraph/confirm-indexed.ts} +1 -1
  79. package/{subgraph-search.ts → subgraph/subgraph-search.ts} +14 -14
  80. package/{subgraph-store.ts → subgraph/subgraph-store.ts} +10 -10
  81. package/dist/consolidation.js +0 -249
  82. /package/{api-client.ts → billing/api-client.ts} +0 -0
  83. /package/{gateway-url.ts → billing/gateway-url.ts} +0 -0
  84. /package/{inbound-user-tracker.ts → billing/inbound-user-tracker.ts} +0 -0
  85. /package/{relay.ts → billing/relay.ts} +0 -0
  86. /package/{crypto.ts → crypto/crypto.ts} +0 -0
  87. /package/{vault-crypto.ts → crypto/vault-crypto.ts} +0 -0
  88. /package/dist/{api-client.js → billing/api-client.js} +0 -0
  89. /package/dist/{gateway-url.js → billing/gateway-url.js} +0 -0
  90. /package/dist/{inbound-user-tracker.js → billing/inbound-user-tracker.js} +0 -0
  91. /package/dist/{relay.js → billing/relay.js} +0 -0
  92. /package/dist/{crypto.js → crypto/crypto.js} +0 -0
  93. /package/dist/{vault-crypto.js → crypto/vault-crypto.js} +0 -0
  94. /package/dist/{embedder-cache.js → embedding/embedder-cache.js} +0 -0
  95. /package/dist/{embedder-loader.js → embedding/embedder-loader.js} +0 -0
  96. /package/dist/{embedder-network.js → embedding/embedder-network.js} +0 -0
  97. /package/dist/{embedding.js → embedding/embedding.js} +0 -0
  98. /package/dist/{lsh.js → embedding/lsh.js} +0 -0
  99. /package/dist/{import-state-manager.js → import/import-state-manager.js} +0 -0
  100. /package/dist/{llm-profile-reader.js → llm/llm-profile-reader.js} +0 -0
  101. /package/dist/{native-memory.js → memory/native-memory.js} +0 -0
  102. /package/dist/{tool-gating.js → memory/tool-gating.js} +0 -0
  103. /package/dist/{tools.js → memory/tools.js} +0 -0
  104. /package/dist/{generate-mnemonic.js → pairing/generate-mnemonic.js} +0 -0
  105. /package/dist/{pair-cli.js → pairing/pair-cli.js} +0 -0
  106. /package/dist/{pair-page.js → pairing/pair-page.js} +0 -0
  107. /package/dist/{pair-qr.js → pairing/pair-qr.js} +0 -0
  108. /package/dist/{restart-auth.js → pairing/restart-auth.js} +0 -0
  109. /package/dist/{qa-bug-report.js → setup/qa-bug-report.js} +0 -0
  110. /package/dist/{skill-register.js → setup/skill-register.js} +0 -0
  111. /package/dist/{trajectory-poller.js → subgraph/trajectory-poller.js} +0 -0
  112. /package/{embedder-cache.ts → embedding/embedder-cache.ts} +0 -0
  113. /package/{embedder-loader.ts → embedding/embedder-loader.ts} +0 -0
  114. /package/{embedder-network.ts → embedding/embedder-network.ts} +0 -0
  115. /package/{embedding.ts → embedding/embedding.ts} +0 -0
  116. /package/{lsh.ts → embedding/lsh.ts} +0 -0
  117. /package/{import-state-manager.ts → import/import-state-manager.ts} +0 -0
  118. /package/{llm-profile-reader.ts → llm/llm-profile-reader.ts} +0 -0
  119. /package/{native-memory.ts → memory/native-memory.ts} +0 -0
  120. /package/{tools.ts → memory/tools.ts} +0 -0
  121. /package/{generate-mnemonic.ts → pairing/generate-mnemonic.ts} +0 -0
  122. /package/{pair-cli.ts → pairing/pair-cli.ts} +0 -0
  123. /package/{pair-page.ts → pairing/pair-page.ts} +0 -0
  124. /package/{pair-qr.ts → pairing/pair-qr.ts} +0 -0
  125. /package/{restart-auth.ts → pairing/restart-auth.ts} +0 -0
  126. /package/{qa-bug-report.ts → setup/qa-bug-report.ts} +0 -0
  127. /package/{skill-register.ts → setup/skill-register.ts} +0 -0
  128. /package/{trajectory-poller.ts → subgraph/trajectory-poller.ts} +0 -0
@@ -21,10 +21,10 @@ function getWasm() {
21
21
  if (!_wasm) _wasm = requireWasm('@totalreclaw/core');
22
22
  return _wasm;
23
23
  }
24
- import { CONFIG, getDataEdgeAddressOverride } from './config.js';
25
- import { buildRelayHeaders } from './relay-headers.js';
26
- import { rpcRequest, rpcWithRetry } from './relay.js';
27
- import { signUserOp } from './vault-crypto.js';
24
+ import { CONFIG, getDataEdgeAddressOverride } from '../config.js';
25
+ import { buildRelayHeaders } from '../billing/relay-headers.js';
26
+ import { rpcRequest, rpcWithRetry } from '../billing/relay.js';
27
+ import { signUserOp } from '../crypto/vault-crypto.js';
28
28
 
29
29
  // ---------------------------------------------------------------------------
30
30
  // Types
@@ -501,7 +501,7 @@ async function submitFactOnChainLocked(
501
501
  // AA10 can occur at pm_sponsorUserOperation (initCode present on deployed sender)
502
502
  // or eth_sendUserOperation (same root cause). This loop handles both.
503
503
  let userOpHash: string | undefined;
504
- let lastErr: any;
504
+ let lastErr: unknown;
505
505
  let attempt = 0;
506
506
  const maxAttempts = 2;
507
507
 
@@ -555,8 +555,8 @@ async function submitFactOnChainLocked(
555
555
  userOpHash = await rpc('eth_sendUserOperation', [unsignedOp, entryPoint]);
556
556
  // Success — break out of retry loop
557
557
  break;
558
- } catch (err: any) {
559
- const msg = err?.message || '';
558
+ } catch (err: unknown) {
559
+ const msg = err instanceof Error ? err.message : String(err);
560
560
  // AA10 "sender already constructed" or AA25 invalid nonce → retry
561
561
  if (/AA25|AA10|invalid account nonce|already being processed/i.test(msg)) {
562
562
  lastErr = err;
@@ -715,7 +715,7 @@ async function submitFactBatchOnChainLocked(
715
715
  // Single retry loop: getInitCode → build UserOp → estimate → sponsor → sign → send
716
716
  // On AA10 "sender already constructed", mark sender as force-deployed and retry.
717
717
  let userOpHash: string | undefined;
718
- let lastErr: any;
718
+ let lastErr: unknown;
719
719
  let attempt = 0;
720
720
  const maxAttempts = 2;
721
721
 
@@ -783,8 +783,8 @@ async function submitFactBatchOnChainLocked(
783
783
  userOpHash = await rpc('eth_sendUserOperation', [unsignedOp, entryPoint]);
784
784
  // Success — break out of retry loop
785
785
  break;
786
- } catch (err: any) {
787
- const msg = err?.message || '';
786
+ } catch (err: unknown) {
787
+ const msg = err instanceof Error ? err.message : String(err);
788
788
  // AA10 "sender already constructed" or AA25 invalid nonce → retry
789
789
  if (/AA25|AA10|invalid account nonce|already being processed/i.test(msg)) {
790
790
  lastErr = err;
@@ -1,249 +0,0 @@
1
- /**
2
- * TotalReclaw Plugin - Memory Consolidation & Near-Duplicate Detection
3
- *
4
- * Provides cross-session / cross-vault deduplication of stored facts using
5
- * cosine similarity on their embeddings. Unlike semantic-dedup.ts (which
6
- * handles within-batch dedup at threshold 0.9), this module handles:
7
- *
8
- * 1. Store-time dedup — before writing a new fact, check whether a
9
- * near-duplicate already exists in the vault (findNearDuplicate).
10
- * 2. Supersede logic — when a near-duplicate is found, decide whether
11
- * the new fact should replace or be skipped (shouldSupersede).
12
- * 3. Bulk consolidation — cluster all facts in the vault and identify
13
- * groups of near-duplicates for cleanup (clusterFacts).
14
- *
15
- * Delegates core computation to `@totalreclaw/core` Rust WASM module where
16
- * bindings are available. `shouldSupersede` uses the core directly.
17
- * `findNearDuplicate` and `clusterFacts` use the core's `findBestNearDuplicate`
18
- * and `clusterFacts` WASM functions when available, falling back to local
19
- * implementations that use WASM-backed `cosineSimilarity`.
20
- *
21
- * Threshold helpers remain local; their env reads are centralized in
22
- * entry.ts (env-reading seam, Task 1.3 of the OpenClaw native
23
- * integration plan, 2026-06-21).
24
- */
25
- import { createRequire } from 'node:module';
26
- import { cosineSimilarity } from './reranker.js';
27
- import { envNumber } from './entry.js';
28
- // ---------------------------------------------------------------------------
29
- // Lazy-load WASM core (mirrors claims-helper.ts / contradiction-sync.ts
30
- // pattern — plays nicely under both the OpenClaw runtime (CJS-ish tsx) and
31
- // bare Node ESM used by tests).
32
- // ---------------------------------------------------------------------------
33
- const requireWasm = createRequire(import.meta.url);
34
- let _wasm = null;
35
- function getWasm() {
36
- if (!_wasm)
37
- _wasm = requireWasm('@totalreclaw/core');
38
- return _wasm;
39
- }
40
- // ---------------------------------------------------------------------------
41
- // Configuration
42
- // ---------------------------------------------------------------------------
43
- /**
44
- * Get the cosine similarity threshold for store-time dedup.
45
- *
46
- * Configurable via TOTALRECLAW_STORE_DEDUP_THRESHOLD env var.
47
- * Must be a number in [0, 1]. Falls back to 0.85 if invalid or unset.
48
- */
49
- export function getStoreDedupThreshold() {
50
- return envNumber('TOTALRECLAW_STORE_DEDUP_THRESHOLD', 0.85, { min: 0, max: 1 });
51
- }
52
- /**
53
- * Get the cosine similarity threshold for bulk consolidation clustering.
54
- *
55
- * Configurable via TOTALRECLAW_CONSOLIDATION_THRESHOLD env var.
56
- * Must be a number in [0, 1]. Falls back to 0.88 if invalid or unset.
57
- */
58
- export function getConsolidationThreshold() {
59
- return envNumber('TOTALRECLAW_CONSOLIDATION_THRESHOLD', 0.88, { min: 0, max: 1 });
60
- }
61
- /** Maximum candidates to compare against during store-time dedup. */
62
- export const STORE_DEDUP_MAX_CANDIDATES = 200;
63
- // ---------------------------------------------------------------------------
64
- // Store-time dedup
65
- // ---------------------------------------------------------------------------
66
- /**
67
- * Find the best near-duplicate match for a new fact among existing candidates.
68
- *
69
- * Compares the new fact's embedding against all candidates using cosine
70
- * similarity. Returns the candidate with the highest similarity above the
71
- * threshold, or null if no match is found.
72
- *
73
- * Candidates without embeddings are skipped (fail-safe).
74
- *
75
- * @param newFactEmbedding - Embedding vector for the new fact
76
- * @param candidates - Existing facts to compare against
77
- * @param threshold - Cosine similarity threshold (e.g. 0.85)
78
- * @returns - Best match above threshold, or null
79
- */
80
- export function findNearDuplicate(newFactEmbedding, candidates, threshold) {
81
- const wasm = getWasm();
82
- // Use core's findBestNearDuplicate if available (added in core >=1.5.0;
83
- // guaranteed present in core >=2.0.0 which this plugin depends on).
84
- if (typeof wasm.findBestNearDuplicate === 'function') {
85
- const existing = candidates
86
- .filter((c) => c.embedding && c.embedding.length > 0)
87
- .map((c) => ({ id: c.id, embedding: c.embedding }));
88
- if (existing.length === 0)
89
- return null;
90
- const resultJs = wasm.findBestNearDuplicate(JSON.stringify(newFactEmbedding), JSON.stringify(existing), threshold);
91
- if (resultJs == null)
92
- return null;
93
- const result = typeof resultJs === 'string' ? JSON.parse(resultJs) : resultJs;
94
- const matched = candidates.find((c) => c.id === result.fact_id);
95
- if (!matched)
96
- return null;
97
- return { existingFact: matched, similarity: result.similarity };
98
- }
99
- // Fallback: local loop using WASM-backed cosineSimilarity. Defensive only
100
- // — core >=2.0.0 always exposes findBestNearDuplicate.
101
- let bestMatch = null;
102
- for (const candidate of candidates) {
103
- if (!candidate.embedding || candidate.embedding.length === 0)
104
- continue;
105
- const similarity = cosineSimilarity(newFactEmbedding, candidate.embedding);
106
- if (similarity >= threshold) {
107
- if (!bestMatch || similarity > bestMatch.similarity) {
108
- bestMatch = { existingFact: candidate, similarity };
109
- }
110
- }
111
- }
112
- return bestMatch;
113
- }
114
- // ---------------------------------------------------------------------------
115
- // Supersede logic
116
- // ---------------------------------------------------------------------------
117
- /**
118
- * Decide whether a new fact should supersede an existing near-duplicate.
119
- *
120
- * - Higher importance wins.
121
- * - Equal importance: new fact supersedes (newer is preferred).
122
- *
123
- * Delegates to `@totalreclaw/core` WASM `shouldSupersede`.
124
- *
125
- * @param newImportance - Importance score of the new fact
126
- * @param existingFact - The existing near-duplicate candidate
127
- * @returns - 'supersede' if new fact should replace, 'skip' otherwise
128
- */
129
- export function shouldSupersede(newImportance, existingFact) {
130
- const wasm = getWasm();
131
- return wasm.shouldSupersede(newImportance, existingFact.importance) ? 'supersede' : 'skip';
132
- }
133
- // ---------------------------------------------------------------------------
134
- // Bulk consolidation
135
- // ---------------------------------------------------------------------------
136
- /**
137
- * Cluster facts by semantic similarity using greedy single-pass clustering.
138
- *
139
- * Delegates to `@totalreclaw/core` WASM `clusterFacts` which performs the
140
- * same greedy single-pass algorithm and representative selection. The WASM
141
- * function returns ID-only clusters; this wrapper maps IDs back to full
142
- * `DecryptedCandidate` objects for callers.
143
- *
144
- * Only returns clusters that have duplicates (i.e. more than one member).
145
- * Facts without embeddings are not clustered.
146
- *
147
- * @param facts - All facts to cluster
148
- * @param threshold - Cosine similarity threshold (e.g. 0.88)
149
- * @returns - Clusters with duplicates (representative + duplicates)
150
- */
151
- export function clusterFacts(facts, threshold) {
152
- const wasm = getWasm();
153
- // Use core's clusterFacts if available (added in core >=1.5.0;
154
- // guaranteed present in core >=2.0.0 which this plugin depends on).
155
- if (typeof wasm.clusterFacts === 'function') {
156
- // Build ConsolidationCandidate JSON for WASM (snake_case fields).
157
- const wasmCandidates = facts
158
- .filter((f) => f.embedding && f.embedding.length > 0)
159
- .map((f) => ({
160
- id: f.id,
161
- text: f.text,
162
- embedding: f.embedding,
163
- importance: f.importance,
164
- decay_score: f.decayScore,
165
- created_at: f.createdAt,
166
- version: f.version,
167
- }));
168
- if (wasmCandidates.length === 0)
169
- return [];
170
- const resultJs = wasm.clusterFacts(JSON.stringify(wasmCandidates), threshold);
171
- // WASM returns a JSON string: [{ representative: string, duplicates: string[] }]
172
- const wasmClusters = typeof resultJs === 'string' ? JSON.parse(resultJs) : resultJs;
173
- // Build a lookup map for fast ID -> DecryptedCandidate resolution.
174
- const byId = new Map();
175
- for (const f of facts)
176
- byId.set(f.id, f);
177
- // Map ID-only clusters back to full DecryptedCandidate objects.
178
- // Filter out singleton clusters (no duplicates) to match the pre-WASM
179
- // plugin contract — callers rely on `clusters.length === 0` when nothing
180
- // duplicates anything.
181
- const result = [];
182
- for (const wc of wasmClusters) {
183
- const rep = byId.get(wc.representative);
184
- if (!rep)
185
- continue;
186
- const dups = wc.duplicates
187
- .map((id) => byId.get(id))
188
- .filter((d) => d !== undefined);
189
- if (dups.length > 0) {
190
- result.push({ representative: rep, duplicates: dups });
191
- }
192
- }
193
- return result;
194
- }
195
- // Fallback: local greedy single-pass clustering using WASM-backed
196
- // cosineSimilarity. Defensive only — core >=2.0.0 always exposes clusterFacts.
197
- const clusters = [];
198
- for (const fact of facts) {
199
- if (!fact.embedding || fact.embedding.length === 0)
200
- continue;
201
- let assigned = false;
202
- for (const cluster of clusters) {
203
- const seed = cluster.members[0];
204
- if (!seed.embedding)
205
- continue;
206
- const similarity = cosineSimilarity(fact.embedding, seed.embedding);
207
- if (similarity >= threshold) {
208
- cluster.members.push(fact);
209
- assigned = true;
210
- break;
211
- }
212
- }
213
- if (!assigned) {
214
- clusters.push({ members: [fact] });
215
- }
216
- }
217
- const result = [];
218
- for (const cluster of clusters) {
219
- if (cluster.members.length < 2)
220
- continue;
221
- const representative = pickRepresentative(cluster.members);
222
- const duplicates = cluster.members.filter((m) => m !== representative);
223
- result.push({ representative, duplicates });
224
- }
225
- return result;
226
- }
227
- // ---------------------------------------------------------------------------
228
- // Local helpers (used only in fallback paths)
229
- // ---------------------------------------------------------------------------
230
- /**
231
- * Pick the best representative from a group of near-duplicate facts.
232
- *
233
- * Tiebreak order:
234
- * 1. Highest decayScore
235
- * 2. Most recent (highest createdAt)
236
- * 3. Longest text
237
- */
238
- function pickRepresentative(facts) {
239
- let best = facts[0];
240
- for (let i = 1; i < facts.length; i++) {
241
- const f = facts[i];
242
- if (f.decayScore > best.decayScore ||
243
- (f.decayScore === best.decayScore && f.createdAt > best.createdAt) ||
244
- (f.decayScore === best.decayScore && f.createdAt === best.createdAt && f.text.length > best.text.length)) {
245
- best = f;
246
- }
247
- }
248
- return best;
249
- }
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