livepilot 1.26.2 → 1.27.1
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.
- package/CHANGELOG.md +92 -0
- package/README.md +34 -14
- package/bin/livepilot.js +4 -2
- package/installer/install.js +21 -12
- package/livepilot/.Codex-plugin/plugin.json +2 -2
- package/livepilot/.claude-plugin/plugin.json +2 -2
- package/livepilot/skills/livepilot-core/SKILL.md +12 -12
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-space.md +2 -1
- package/livepilot/skills/livepilot-core/references/overview.md +6 -4
- package/livepilot/skills/livepilot-core/references/sound-design.md +5 -2
- package/livepilot/skills/livepilot-creative-director/SKILL.md +36 -0
- package/livepilot/skills/livepilot-creative-director/references/move-family-diversity-rule.md +1 -1
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +15 -9
- package/livepilot/skills/livepilot-release/SKILL.md +7 -5
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/livepilot_bridge.js +1 -1
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/atlas/__init__.py +20 -1
- package/mcp_server/atlas/tools.py +83 -22
- package/mcp_server/composer/full/apply.py +9 -0
- package/mcp_server/composer/full/layer_planner.py +16 -6
- package/mcp_server/creative_constraints/engine.py +46 -0
- package/mcp_server/experiment/engine.py +10 -3
- package/mcp_server/grader/client.py +30 -2
- package/mcp_server/grader/tools.py +8 -2
- package/mcp_server/hook_hunter/analyzer.py +15 -2
- package/mcp_server/m4l_bridge.py +19 -21
- package/mcp_server/memory/taste_graph.py +16 -0
- package/mcp_server/memory/technique_store.py +23 -3
- package/mcp_server/mix_engine/state_builder.py +4 -2
- package/mcp_server/musical_intelligence/detectors.py +11 -2
- package/mcp_server/musical_intelligence/tools.py +15 -2
- package/mcp_server/preview_studio/engine.py +30 -1
- package/mcp_server/project_brain/tools.py +56 -52
- package/mcp_server/reference_engine/tools.py +22 -2
- package/mcp_server/runtime/capability_probe.py +1 -1
- package/mcp_server/runtime/capability_state.py +66 -9
- package/mcp_server/runtime/live_version.py +45 -8
- package/mcp_server/runtime/safety_kernel.py +11 -0
- package/mcp_server/runtime/session_kernel.py +7 -0
- package/mcp_server/runtime/tools.py +324 -22
- package/mcp_server/sample_engine/critics.py +7 -3
- package/mcp_server/sample_engine/slice_workflow.py +3 -2
- package/mcp_server/sample_engine/tools.py +53 -21
- package/mcp_server/song_brain/builder.py +17 -1
- package/mcp_server/sound_design/tools.py +1 -1
- package/mcp_server/splice_client/http_bridge.py +43 -1
- package/mcp_server/splice_client/models.py +7 -3
- package/mcp_server/synthesis_brain/adapters/analog.py +13 -1
- package/mcp_server/synthesis_brain/adapters/operator.py +5 -2
- package/mcp_server/synthesis_brain/adapters/wavetable.py +4 -4
- package/mcp_server/tools/_composition_engine/models.py +6 -0
- package/mcp_server/tools/_composition_engine/sections.py +4 -0
- package/mcp_server/tools/analyzer.py +38 -1
- package/mcp_server/tools/clips.py +5 -4
- package/mcp_server/tools/composition.py +5 -1
- package/mcp_server/tools/midi_io.py +40 -1
- package/mcp_server/tools/transport.py +1 -1
- package/mcp_server/user_corpus/plugin_engine/detector.py +66 -11
- package/mcp_server/user_corpus/runner.py +7 -1
- package/mcp_server/user_corpus/scanners/amxd.py +24 -13
- package/mcp_server/user_corpus/tools.py +45 -9
- package/mcp_server/wonder_mode/tools.py +66 -27
- package/package.json +2 -2
- package/remote_script/LivePilot/__init__.py +1 -1
- package/remote_script/LivePilot/server.py +23 -3
- package/remote_script/LivePilot/version_detect.py +3 -0
- package/requirements.txt +4 -4
- package/server.json +3 -3
|
@@ -50,6 +50,20 @@ from .plugin_engine.research import (
|
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
# ─── Inline plugin serialization helper ────────────────────────────────────────
|
|
54
|
+
# corpus_detect_plugins persists the FULL plugin dict (incl. raw sdk_metadata =
|
|
55
|
+
# entire moduleinfo.json / Info.plist) to _inventory.json. The inline MCP
|
|
56
|
+
# response, however, only needs lightweight identity fields — returning the full
|
|
57
|
+
# sdk_metadata for every plugin bloats the payload and duplicates the file.
|
|
58
|
+
# Callers that need the raw metadata read it back from _inventory.json.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _slim_plugin(plugin_dict: dict) -> dict:
|
|
62
|
+
"""Return a copy of a serialized DetectedPlugin without the heavy
|
|
63
|
+
``sdk_metadata`` blob, for inline (non-persisted) MCP responses."""
|
|
64
|
+
return {k: v for k, v in plugin_dict.items() if k != "sdk_metadata"}
|
|
65
|
+
|
|
66
|
+
|
|
53
67
|
# ─── Vendor canonicalization helpers (used by corpus_canonicalize_plugins) ───
|
|
54
68
|
# Strips common vendor-suffix words ("DSP", "LLC", "GmbH", etc.) so different
|
|
55
69
|
# spellings of the same vendor ("Valhalla DSP, LLC" + "Valhalladsp") collapse
|
|
@@ -390,7 +404,7 @@ def corpus_detect_plugins(
|
|
|
390
404
|
)
|
|
391
405
|
|
|
392
406
|
return {
|
|
393
|
-
"plugins": plugins_serialized,
|
|
407
|
+
"plugins": [_slim_plugin(p) for p in plugins_serialized],
|
|
394
408
|
"totals": {"all": len(detected), "by_format": by_format},
|
|
395
409
|
"inventory_path": str(inventory_path) if inventory_path else None,
|
|
396
410
|
"search_paths": [str(p) for p, _ in default_plugin_dir()],
|
|
@@ -821,6 +835,7 @@ def corpus_research_targets(
|
|
|
821
835
|
def corpus_emit_synthesis_briefs(
|
|
822
836
|
ctx: Context,
|
|
823
837
|
plugin_ids: list = None,
|
|
838
|
+
inline_limit: int = 5,
|
|
824
839
|
) -> dict:
|
|
825
840
|
"""Phase 4 — emit sonnet-subagent briefs for plugin identity synthesis.
|
|
826
841
|
|
|
@@ -832,10 +847,18 @@ def corpus_emit_synthesis_briefs(
|
|
|
832
847
|
----------
|
|
833
848
|
plugin_ids : list of plugin_ids to emit briefs for. If empty, emits for
|
|
834
849
|
every plugin in the inventory.
|
|
850
|
+
inline_limit : maximum number of FULL briefs returned inline (default 5,
|
|
851
|
+
matching the 'Cap parallel subagents at ~5' instruction).
|
|
852
|
+
Any plugins beyond this cap are returned as lightweight stubs
|
|
853
|
+
({plugin_id, output_path}) in `deferred` so a single call can
|
|
854
|
+
never return a multi-MB response over a large inventory. Pass
|
|
855
|
+
explicit `plugin_ids` (or a larger inline_limit) to get the
|
|
856
|
+
full brief for a specific batch.
|
|
835
857
|
|
|
836
858
|
Returns
|
|
837
859
|
-------
|
|
838
|
-
{briefs: [{plugin_id, brief, output_path}, ...],
|
|
860
|
+
{briefs: [{plugin_id, brief, output_path}, ...], deferred: [{plugin_id,
|
|
861
|
+
output_path}, ...], total, inline_count, inline_limit}
|
|
839
862
|
"""
|
|
840
863
|
inventory_path = DEFAULT_OUTPUT_ROOT / "plugins" / "_inventory.json"
|
|
841
864
|
if not inventory_path.exists():
|
|
@@ -844,7 +867,9 @@ def corpus_emit_synthesis_briefs(
|
|
|
844
867
|
all_plugins = inventory.get("plugins", [])
|
|
845
868
|
|
|
846
869
|
target_ids = set(plugin_ids) if plugin_ids else None
|
|
870
|
+
cap = inline_limit if inline_limit and inline_limit > 0 else 5
|
|
847
871
|
briefs = []
|
|
872
|
+
deferred = []
|
|
848
873
|
for plugin_dict in all_plugins:
|
|
849
874
|
pid = plugin_dict.get("plugin_id")
|
|
850
875
|
if target_ids is not None and pid not in target_ids:
|
|
@@ -866,21 +891,32 @@ def corpus_emit_synthesis_briefs(
|
|
|
866
891
|
research_root = DEFAULT_OUTPUT_ROOT / "plugins" / pid / "research"
|
|
867
892
|
|
|
868
893
|
brief = build_synthesis_brief(plugin, local_manual, research_root if research_root.exists() else None)
|
|
869
|
-
briefs
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
894
|
+
if len(briefs) < cap:
|
|
895
|
+
briefs.append({
|
|
896
|
+
"plugin_id": pid,
|
|
897
|
+
"brief": brief,
|
|
898
|
+
"output_path": brief["output_path"],
|
|
899
|
+
})
|
|
900
|
+
else:
|
|
901
|
+
deferred.append({
|
|
902
|
+
"plugin_id": pid,
|
|
903
|
+
"output_path": brief["output_path"],
|
|
904
|
+
})
|
|
874
905
|
|
|
875
906
|
return {
|
|
876
907
|
"briefs": briefs,
|
|
877
|
-
"
|
|
908
|
+
"deferred": deferred,
|
|
909
|
+
"total": len(briefs) + len(deferred),
|
|
910
|
+
"inline_count": len(briefs),
|
|
911
|
+
"inline_limit": cap,
|
|
878
912
|
"instruction": (
|
|
879
913
|
"For each brief: dispatch one sonnet subagent (Agent tool with "
|
|
880
914
|
"subagent_type='general-purpose', model='sonnet') passing the brief "
|
|
881
915
|
"as context. The subagent reads brief['synthesis_inputs'] and writes "
|
|
882
916
|
"the YAML at brief['output_path']. Cap parallel subagents at ~5 to "
|
|
883
|
-
"avoid main-context bloat."
|
|
917
|
+
"avoid main-context bloat. Plugins beyond inline_limit are listed in "
|
|
918
|
+
"'deferred' as stubs — re-call corpus_emit_synthesis_briefs with their "
|
|
919
|
+
"plugin_ids to get the full briefs for the next batch."
|
|
884
920
|
),
|
|
885
921
|
}
|
|
886
922
|
|
|
@@ -17,13 +17,20 @@ from . import engine
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def _build_synth_profiles_for_wonder(
|
|
20
|
+
def _build_synth_profiles_for_wonder(
|
|
21
|
+
ctx, request_text: str, diagnosis: dict, session_info: dict | None = None
|
|
22
|
+
) -> list:
|
|
21
23
|
"""Build SynthProfile objects for every track holding a native synth.
|
|
22
24
|
|
|
23
25
|
Wires the synthesis_brain producer into enter_wonder_mode's runtime
|
|
24
26
|
flow — without this helper, ``propose_synth_branches`` is registered
|
|
25
27
|
but unreachable from the MCP surface.
|
|
26
28
|
|
|
29
|
+
``session_info``: optional pre-fetched get_session_info payload. When
|
|
30
|
+
a non-empty dict is supplied the helper reuses it instead of issuing
|
|
31
|
+
its own round-trip on the single-client TCP socket. Callers should
|
|
32
|
+
always thread the shared payload to avoid redundant fetches.
|
|
33
|
+
|
|
27
34
|
Returns a list of SynthProfile objects (possibly empty). All errors
|
|
28
35
|
are swallowed and logged — missing devices, disconnected Ableton,
|
|
29
36
|
unsupported devices all land as "no synth branches" rather than
|
|
@@ -39,11 +46,16 @@ def _build_synth_profiles_for_wonder(ctx, request_text: str, diagnosis: dict) ->
|
|
|
39
46
|
if ableton is None:
|
|
40
47
|
return []
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
# Reuse the pre-fetched session payload when available; only fall back
|
|
50
|
+
# to a fresh round-trip when called without one.
|
|
51
|
+
if isinstance(session_info, dict) and session_info and "error" not in session_info:
|
|
52
|
+
session = session_info
|
|
53
|
+
else:
|
|
54
|
+
try:
|
|
55
|
+
session = ableton.send_command("get_session_info", {})
|
|
56
|
+
except Exception as exc:
|
|
57
|
+
logger.debug("session fetch for synth profiles failed: %s", exc)
|
|
58
|
+
return []
|
|
47
59
|
if not isinstance(session, dict) or "error" in session:
|
|
48
60
|
return []
|
|
49
61
|
|
|
@@ -164,21 +176,35 @@ def _get_ledger_entries(ctx: Context) -> list[dict]:
|
|
|
164
176
|
return []
|
|
165
177
|
|
|
166
178
|
|
|
167
|
-
def _get_stuckness_report(
|
|
168
|
-
|
|
179
|
+
def _get_stuckness_report(
|
|
180
|
+
ctx: Context,
|
|
181
|
+
song_brain: dict,
|
|
182
|
+
action_ledger: list[dict] | None = None,
|
|
183
|
+
session_info: dict | None = None,
|
|
184
|
+
) -> dict | None:
|
|
185
|
+
"""Run stuckness detection on recent actions if available.
|
|
186
|
+
|
|
187
|
+
``action_ledger`` and ``session_info`` may be supplied pre-fetched by
|
|
188
|
+
the caller. enter_wonder_mode already fetches both once at the top of
|
|
189
|
+
the flow, so threading them here avoids a duplicate ledger read and a
|
|
190
|
+
redundant get_session_info round-trip on the single-client TCP socket.
|
|
191
|
+
"""
|
|
169
192
|
try:
|
|
170
193
|
from ..stuckness_detector.detector import detect_stuckness
|
|
171
|
-
action_ledger
|
|
194
|
+
if action_ledger is None:
|
|
195
|
+
action_ledger = _get_ledger_entries(ctx)
|
|
172
196
|
if not action_ledger:
|
|
173
197
|
return None
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
198
|
+
# Reuse the pre-fetched session payload; only round-trip if the
|
|
199
|
+
# caller did not supply one.
|
|
200
|
+
if session_info is None:
|
|
201
|
+
session_info = {}
|
|
202
|
+
try:
|
|
203
|
+
ableton = ctx.lifespan_context.get("ableton")
|
|
204
|
+
if ableton:
|
|
205
|
+
session_info = ableton.send_command("get_session_info", {})
|
|
206
|
+
except Exception as exc:
|
|
207
|
+
logger.warning("session_info fetch for stuckness failed: %s", exc)
|
|
182
208
|
report = detect_stuckness(
|
|
183
209
|
action_history=action_ledger,
|
|
184
210
|
session_info=session_info,
|
|
@@ -218,7 +244,24 @@ def enter_wonder_mode(
|
|
|
218
244
|
taste_graph = _get_taste_graph(ctx)
|
|
219
245
|
active_constraints = _get_active_constraints()
|
|
220
246
|
action_ledger = _get_ledger_entries(ctx)
|
|
221
|
-
|
|
247
|
+
|
|
248
|
+
# Single get_session_info round-trip for the whole flow. The Remote
|
|
249
|
+
# Script is single-client on TCP 9878, so each redundant fetch is a
|
|
250
|
+
# serialized round-trip — fetch once and thread the payload into the
|
|
251
|
+
# stuckness report, the kernel dict, and the synth-profile builder.
|
|
252
|
+
session_info: dict = {}
|
|
253
|
+
try:
|
|
254
|
+
ableton = ctx.lifespan_context.get("ableton")
|
|
255
|
+
if ableton:
|
|
256
|
+
session_info = ableton.send_command("get_session_info", {})
|
|
257
|
+
except Exception as exc:
|
|
258
|
+
logger.warning("session_info fetch failed: %s", exc)
|
|
259
|
+
if not isinstance(session_info, dict) or "error" in session_info:
|
|
260
|
+
session_info = {}
|
|
261
|
+
|
|
262
|
+
stuckness_report = _get_stuckness_report(
|
|
263
|
+
ctx, song_brain, action_ledger=action_ledger, session_info=session_info
|
|
264
|
+
)
|
|
222
265
|
|
|
223
266
|
# 1. Build diagnosis
|
|
224
267
|
diagnosis = build_diagnosis(
|
|
@@ -249,14 +292,8 @@ def enter_wonder_mode(
|
|
|
249
292
|
# Graceful degradation — analytical variants still work
|
|
250
293
|
logger.warning("sample opportunity search failed: %s", exc)
|
|
251
294
|
|
|
252
|
-
# 1c.
|
|
253
|
-
|
|
254
|
-
try:
|
|
255
|
-
ableton = ctx.lifespan_context.get("ableton")
|
|
256
|
-
if ableton:
|
|
257
|
-
session_info = ableton.send_command("get_session_info", {})
|
|
258
|
-
except Exception as exc:
|
|
259
|
-
logger.warning("session_info fetch for kernel failed: %s", exc)
|
|
295
|
+
# 1c. session_info for the kernel was already fetched once above and
|
|
296
|
+
# threaded into the stuckness report — reuse it here, no extra round-trip.
|
|
260
297
|
|
|
261
298
|
# 2. Generate variants (legacy path)
|
|
262
299
|
result = engine.generate_wonder_variants(
|
|
@@ -291,7 +328,9 @@ def enter_wonder_mode(
|
|
|
291
328
|
# track that holds a native synth. Lets propose_synth_branches reach
|
|
292
329
|
# the runtime — without this the producer is dark despite being
|
|
293
330
|
# registered in the conductor.
|
|
294
|
-
synth_profiles = _build_synth_profiles_for_wonder(
|
|
331
|
+
synth_profiles = _build_synth_profiles_for_wonder(
|
|
332
|
+
ctx, request_text, diag_dict, session_info=session_info
|
|
333
|
+
)
|
|
295
334
|
|
|
296
335
|
# Composer branches fire when the base conductor routes to
|
|
297
336
|
# composition — otherwise we'd be emitting composition scaffolding
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.1",
|
|
4
4
|
"mcpName": "io.github.dreamrec/livepilot",
|
|
5
|
-
"description": "Agentic production system for Ableton Live 12 —
|
|
5
|
+
"description": "Agentic production system for Ableton Live 12 — 467 tools, 56 domains, 44 semantic moves. Device atlas (5264 devices, 120 enriched, 7 indexes), Splice intelligence (gRPC + GraphQL describe-a-sound + preview + collections + presets), 9-band spectral perception auto-loaded via ensure_analyzer_on_master, Creative Director skill, technique memory, 12 creative intelligence engines",
|
|
6
6
|
"author": "Pilot Studio",
|
|
7
7
|
"license": "BSL-1.1",
|
|
8
8
|
"type": "commonjs",
|
|
@@ -5,7 +5,7 @@ Entry point for the ControlSurface. Ableton calls create_instance(c_instance)
|
|
|
5
5
|
when this script is selected in Preferences > Link, Tempo & MIDI.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "1.
|
|
8
|
+
__version__ = "1.27.1"
|
|
9
9
|
|
|
10
10
|
from _Framework.ControlSurface import ControlSurface
|
|
11
11
|
from . import router
|
|
@@ -346,9 +346,14 @@ class LivePilotServer(object):
|
|
|
346
346
|
else:
|
|
347
347
|
timeout = 10
|
|
348
348
|
|
|
349
|
-
# Per-command response queue
|
|
349
|
+
# Per-command response queue + cancellation flag. The flag is shared
|
|
350
|
+
# between this TCP thread and the main thread that dequeues the item:
|
|
351
|
+
# if we time out below, we set it so _process_next_command skips the
|
|
352
|
+
# (now-abandoned) dispatch instead of mutating Live after the client
|
|
353
|
+
# has already been told the command timed out (phantom write).
|
|
350
354
|
response_queue = queue.Queue()
|
|
351
|
-
|
|
355
|
+
cancelled = threading.Event()
|
|
356
|
+
self._command_queue.put((command, response_queue, cancelled))
|
|
352
357
|
|
|
353
358
|
# Schedule processing on Ableton's main thread
|
|
354
359
|
try:
|
|
@@ -385,6 +390,13 @@ class LivePilotServer(object):
|
|
|
385
390
|
try:
|
|
386
391
|
resp = response_queue.get(timeout=timeout)
|
|
387
392
|
except queue.Empty:
|
|
393
|
+
# Mark the queued command as abandoned. If it hasn't been dequeued
|
|
394
|
+
# yet (or is awaiting its settle hop), _process_next_command sees
|
|
395
|
+
# the flag and skips router.dispatch — preventing a write that the
|
|
396
|
+
# client was just told timed out from executing on Live's main
|
|
397
|
+
# thread later. If dispatch already happened, setting the flag is
|
|
398
|
+
# a harmless no-op.
|
|
399
|
+
cancelled.set()
|
|
388
400
|
resp = {
|
|
389
401
|
"id": request_id,
|
|
390
402
|
"ok": False,
|
|
@@ -399,10 +411,18 @@ class LivePilotServer(object):
|
|
|
399
411
|
"""Called on Ableton's main thread via schedule_message.
|
|
400
412
|
Processes one command from the queue."""
|
|
401
413
|
try:
|
|
402
|
-
command, response_queue = self._command_queue.get_nowait()
|
|
414
|
+
command, response_queue, cancelled = self._command_queue.get_nowait()
|
|
403
415
|
except queue.Empty:
|
|
404
416
|
return
|
|
405
417
|
|
|
418
|
+
# The TCP thread already gave up on this command (timed out) and told
|
|
419
|
+
# the client so. Do NOT dispatch it — running the write now would be a
|
|
420
|
+
# phantom mutation. Nobody is waiting on response_queue, so just keep
|
|
421
|
+
# the main-thread pump alive by draining whatever is left.
|
|
422
|
+
if cancelled.is_set():
|
|
423
|
+
self._drain_queue()
|
|
424
|
+
return
|
|
425
|
+
|
|
406
426
|
cmd_type = command.get("type", "")
|
|
407
427
|
is_write = is_write_command(cmd_type)
|
|
408
428
|
|
|
@@ -25,6 +25,9 @@ FEATURES = {
|
|
|
25
25
|
"drum_chain_in_note": (12, 3, 0),
|
|
26
26
|
"stem_separation": (12, 3, 0),
|
|
27
27
|
"device_ab_compare": (12, 3, 0),
|
|
28
|
+
"link_audio": (12, 4, 0),
|
|
29
|
+
"stem_time_selection": (12, 4, 0),
|
|
30
|
+
"stem_merge_selected": (12, 4, 0),
|
|
28
31
|
"replace_sample_native": (12, 4, 0),
|
|
29
32
|
"groove_pool_api": (11, 0, 0),
|
|
30
33
|
"rack_variations_api": (11, 0, 0),
|
package/requirements.txt
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# LivePilot MCP Server dependencies
|
|
2
2
|
numpy>=2.4.6
|
|
3
|
-
fastmcp>=3.
|
|
3
|
+
fastmcp>=3.4.2,<3.5.0 # pinned upper bound — _get_all_tools() accesses private internals
|
|
4
4
|
midiutil>=1.2.1
|
|
5
5
|
pretty_midi>=0.2.11
|
|
6
6
|
# v1.8 Perception Layer (offline analysis)
|
|
7
7
|
pyloudnorm>=0.2.0
|
|
8
|
-
soundfile>=0.
|
|
8
|
+
soundfile>=0.14.0
|
|
9
9
|
scipy>=1.17.1
|
|
10
10
|
mutagen>=1.47.0
|
|
11
11
|
# v1.10.5 Splice online catalog integration — required, not optional.
|
|
12
12
|
# Without these, SpliceGRPCClient silently disables itself and search_samples
|
|
13
13
|
# falls back to the SQLite sounds.db which only returns locally downloaded
|
|
14
14
|
# samples (see docs/2026-04-14-bugs-discovered.md — P0-2).
|
|
15
|
-
grpcio>=1.
|
|
16
|
-
protobuf>=7.35.
|
|
15
|
+
grpcio>=1.81.1
|
|
16
|
+
protobuf>=7.35.1
|
|
17
17
|
|
|
18
18
|
# Known benign warning during install:
|
|
19
19
|
# ERROR: pip's dependency resolver does not currently take into account
|
package/server.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.dreamrec/livepilot",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "467-tool agentic MCP production system for Ableton Live 12 \u2014 56 domains, 44 semantic moves, device atlas (5264 devices), Splice intelligence (gRPC + GraphQL), 9-band spectral perception auto-loaded, Creative Director skill, technique memory, 12 creative engines",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/dreamrec/LivePilot",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.27.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "livepilot",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.27.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|