livepilot 1.14.1 → 1.16.0

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 (33) hide show
  1. package/CHANGELOG.md +176 -1
  2. package/README.md +6 -6
  3. package/m4l_device/LivePilot_Analyzer.amxd +0 -0
  4. package/mcp_server/__init__.py +1 -1
  5. package/mcp_server/atlas/device_atlas.json +91219 -7161
  6. package/mcp_server/atlas/tools.py +30 -2
  7. package/mcp_server/runtime/live_version.py +4 -2
  8. package/mcp_server/runtime/remote_commands.py +5 -0
  9. package/mcp_server/sample_engine/tools.py +692 -60
  10. package/mcp_server/splice_client/client.py +511 -65
  11. package/mcp_server/splice_client/http_bridge.py +361 -0
  12. package/mcp_server/splice_client/models.py +266 -2
  13. package/mcp_server/splice_client/quota.py +229 -0
  14. package/mcp_server/tools/_analyzer_engine/__init__.py +4 -0
  15. package/mcp_server/tools/_analyzer_engine/sample.py +73 -0
  16. package/mcp_server/tools/analyzer.py +666 -6
  17. package/mcp_server/tools/browser.py +164 -13
  18. package/mcp_server/tools/devices.py +56 -11
  19. package/mcp_server/tools/mixing.py +64 -15
  20. package/mcp_server/tools/scales.py +18 -6
  21. package/mcp_server/tools/tracks.py +92 -4
  22. package/package.json +2 -2
  23. package/remote_script/LivePilot/__init__.py +2 -1
  24. package/remote_script/LivePilot/_clip_helpers.py +86 -0
  25. package/remote_script/LivePilot/_drum_helpers.py +40 -0
  26. package/remote_script/LivePilot/_scale_helpers.py +87 -0
  27. package/remote_script/LivePilot/arrangement.py +44 -15
  28. package/remote_script/LivePilot/clips.py +182 -2
  29. package/remote_script/LivePilot/devices.py +82 -2
  30. package/remote_script/LivePilot/notes.py +17 -2
  31. package/remote_script/LivePilot/scales.py +31 -16
  32. package/remote_script/LivePilot/simpler_sample.py +186 -0
  33. package/server.json +3 -3
@@ -150,7 +150,11 @@ def atlas_compare(ctx: Context, device_a: str, device_b: str, role: str = "") ->
150
150
 
151
151
 
152
152
  @mcp.tool()
153
- def scan_full_library(ctx: Context, force: bool = False) -> dict:
153
+ def scan_full_library(
154
+ ctx: Context,
155
+ force: bool = False,
156
+ max_per_category: int = 5000,
157
+ ) -> dict:
154
158
  """Scan the full Ableton browser and rebuild the device atlas.
155
159
 
156
160
  Walks every category (instruments, audio_effects, midi_effects, max_for_live,
@@ -158,6 +162,16 @@ def scan_full_library(ctx: Context, force: bool = False) -> dict:
158
162
  Results are merged with curated enrichments and saved to device_atlas.json.
159
163
 
160
164
  force: if True, rescan even if atlas already exists (default False)
165
+ max_per_category: ceiling per category (default 5000). The previous
166
+ hardcoded 1000 cap silently truncated large categories — for
167
+ example, the samples category alone has ~22,000 items per the
168
+ browser tree, so the reported count "1000 samples" was wrong by
169
+ a factor of 22 (BUG-2026-04-22 #12). Raise this if your library
170
+ is huge; lower it for fast smoke scans.
171
+
172
+ Returns a stats dict including `truncated_categories` listing any
173
+ category that hit the cap (so callers know the count is a lower
174
+ bound rather than the true total).
161
175
  """
162
176
  from .scanner import normalize_scan_results
163
177
  from .enrichments import load_enrichments, merge_enrichments
@@ -183,11 +197,23 @@ def scan_full_library(ctx: Context, force: bool = False) -> dict:
183
197
 
184
198
  # Scan browser
185
199
  ableton = _get_ableton(ctx)
186
- raw = ableton.send_command("scan_browser_deep", {"max_per_category": 1000})
200
+ raw = ableton.send_command("scan_browser_deep", {"max_per_category": max_per_category})
187
201
 
188
202
  # Normalize
189
203
  devices = normalize_scan_results(raw)
190
204
 
205
+ # Detect truncation: per-category count == cap means we likely hit it.
206
+ truncated_categories = []
207
+ if isinstance(raw, dict):
208
+ per_cat = raw.get("counts") or raw.get("stats") or {}
209
+ if isinstance(per_cat, dict):
210
+ for cat, count in per_cat.items():
211
+ try:
212
+ if int(count) >= max_per_category:
213
+ truncated_categories.append(cat)
214
+ except (TypeError, ValueError):
215
+ continue
216
+
191
217
  # Load and merge enrichments
192
218
  enrichments = load_enrichments(enrichments_dir)
193
219
  devices = merge_enrichments(devices, enrichments)
@@ -214,6 +240,8 @@ def scan_full_library(ctx: Context, force: bool = False) -> dict:
214
240
  "live_version": live_version,
215
241
  "scanned_at": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
216
242
  "stats": stats,
243
+ "max_per_category": max_per_category,
244
+ "truncated_categories": truncated_categories,
217
245
  "devices": devices,
218
246
  "packs": [],
219
247
  }
@@ -79,8 +79,10 @@ class LiveVersionCapabilities:
79
79
 
80
80
  @property
81
81
  def capability_tier(self) -> str:
82
- """Human-readable tier: core | enhanced_arrangement | full_intelligence."""
83
- if self._version_tuple >= (12, 3, 0):
82
+ """Human-readable tier: core | enhanced_arrangement | full_intelligence | collaborative."""
83
+ if self._version_tuple >= (12, 4, 0):
84
+ return "collaborative"
85
+ elif self._version_tuple >= (12, 3, 0):
84
86
  return "full_intelligence"
85
87
  elif self._version_tuple >= (12, 1, 10):
86
88
  return "enhanced_arrangement"
@@ -48,6 +48,9 @@ REMOTE_COMMANDS: frozenset[str] = frozenset({
48
48
  "insert_device", # 12.3+ native device insertion
49
49
  "insert_rack_chain", # 12.3+ rack chain insertion
50
50
  "set_drum_chain_note", # 12.3+ drum chain note assignment
51
+ "set_chain_name", # Rack chain rename (any rack type)
52
+ "fire_test_note", # Temp-clip MIDI trigger for verify_device_health
53
+ "cleanup_test_note", # Scratch-clip teardown paired with fire_test_note
51
54
  # rack variations + macro CRUD (Live 11+)
52
55
  "get_rack_variations", "store_rack_variation",
53
56
  "recall_rack_variation", "delete_rack_variation",
@@ -117,6 +120,8 @@ REMOTE_COMMANDS: frozenset[str] = frozenset({
117
120
  "get_appointed_device",
118
121
  # ping (built-in)
119
122
  "ping",
123
+ # Live 12.4+ native Simpler sample replacement (Collaborative tier)
124
+ "replace_sample_native",
120
125
  })
121
126
 
122
127
  # M4L bridge commands — routed through TCP but handled by livepilot_bridge.js