livepilot 1.9.6 → 1.9.8
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/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +6 -0
- package/livepilot/.claude-plugin/plugin.json +1 -1
- package/livepilot/skills/livepilot-core/references/overview.md +1 -1
- package/m4l_device/livepilot_bridge.js +1 -1
- package/mcp_server/__init__.py +1 -1
- package/package.json +1 -1
- package/remote_script/LivePilot/__init__.py +1 -1
- package/remote_script/LivePilot/arrangement.py +20 -21
- package/remote_script/LivePilot/tracks.py +18 -3
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "livepilot",
|
|
12
12
|
"description": "Agentic production system for Ableton Live 12 — 178 tools, 17 domains, device atlas, spectral perception, technique memory, neo-Riemannian harmony, Euclidean rhythm, species counterpoint, MIDI I/O",
|
|
13
|
-
"version": "1.9.
|
|
13
|
+
"version": "1.9.8",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Pilot Studio"
|
|
16
16
|
},
|
package/AGENTS.md
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.7 — Safe automation fallback + correct clip length reporting (March 2026)
|
|
4
|
+
|
|
5
|
+
- Fix(P1): set_arrangement_automation places replacement BEFORE deleting original — no data loss if placement fails
|
|
6
|
+
- Fix(P2): get_arrangement_clips reports timeline length (not loop span) as length/end_time; loop info as separate fields
|
|
7
|
+
- Reverted the effective-length mangling that misreported looped clip sizes
|
|
8
|
+
|
|
3
9
|
## 1.9.6 — Arrangement clip identification + expression data (March 2026)
|
|
4
10
|
|
|
5
11
|
- Fix(P1): create_arrangement_clip now identifies new clips by object identity, not position match — prevents mutating pre-existing overlapping clips
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.8",
|
|
4
4
|
"description": "Agentic production system for Ableton Live 12 — 178 tools, 17 domains, device atlas, spectral perception, technique memory, neo-Riemannian harmony, Euclidean rhythm, species counterpoint, MIDI I/O",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pilot Studio"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# LivePilot v1.9.
|
|
1
|
+
# LivePilot v1.9.8 — Architecture & Tool Reference
|
|
2
2
|
|
|
3
3
|
Agentic production system for Ableton Live 12. 178 tools across 17 domains. Device atlas (280+ devices), spectral perception (M4L analyzer), technique memory, automation intelligence (16 curve types, 15 recipes), music theory (Krumhansl-Schmuckler, species counterpoint), generative algorithms (Euclidean rhythm, tintinnabuli, phase shift, additive process), neo-Riemannian harmony (PRL transforms, Tonnetz), MIDI file I/O.
|
|
4
4
|
|
package/mcp_server/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
|
|
2
|
-
__version__ = "1.9.
|
|
2
|
+
__version__ = "1.9.8"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.8",
|
|
4
4
|
"mcpName": "io.github.dreamrec/livepilot",
|
|
5
5
|
"description": "Agentic production system for Ableton Live 12 — 178 tools, 17 domains, device atlas, spectral perception, technique memory, neo-Riemannian harmony, Euclidean rhythm, species counterpoint, MIDI I/O",
|
|
6
6
|
"author": "Pilot Studio",
|
|
@@ -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.9.
|
|
8
|
+
__version__ = "1.9.8"
|
|
9
9
|
|
|
10
10
|
from _Framework.ControlSurface import ControlSurface
|
|
11
11
|
from .server import LivePilotServer
|
|
@@ -13,27 +13,24 @@ def get_arrangement_clips(song, params):
|
|
|
13
13
|
track = get_track(song, track_index)
|
|
14
14
|
clips = []
|
|
15
15
|
for i, clip in enumerate(track.arrangement_clips):
|
|
16
|
-
|
|
17
|
-
# Report effective length based on loop_end if looping is active
|
|
18
|
-
# (arrangement clips may have been trimmed by create_arrangement_clip)
|
|
19
|
-
effective_length = timeline_length
|
|
20
|
-
try:
|
|
21
|
-
if clip.looping:
|
|
22
|
-
loop_len = clip.loop_end - clip.loop_start
|
|
23
|
-
if 0 < loop_len < timeline_length:
|
|
24
|
-
effective_length = loop_len
|
|
25
|
-
except (AttributeError, RuntimeError):
|
|
26
|
-
pass
|
|
27
|
-
clips.append({
|
|
16
|
+
info = {
|
|
28
17
|
"index": i,
|
|
29
18
|
"name": clip.name,
|
|
30
19
|
"start_time": clip.start_time,
|
|
31
|
-
"end_time": clip.start_time +
|
|
32
|
-
"length":
|
|
33
|
-
"timeline_length": timeline_length,
|
|
20
|
+
"end_time": clip.start_time + clip.length,
|
|
21
|
+
"length": clip.length,
|
|
34
22
|
"color_index": clip.color_index,
|
|
35
23
|
"is_audio_clip": clip.is_audio_clip,
|
|
36
|
-
}
|
|
24
|
+
}
|
|
25
|
+
# Add loop info if available
|
|
26
|
+
try:
|
|
27
|
+
if clip.looping:
|
|
28
|
+
info["looping"] = True
|
|
29
|
+
info["loop_start"] = clip.loop_start
|
|
30
|
+
info["loop_end"] = clip.loop_end
|
|
31
|
+
except (AttributeError, RuntimeError):
|
|
32
|
+
pass
|
|
33
|
+
clips.append(info)
|
|
37
34
|
return {"track_index": track_index, "clips": clips}
|
|
38
35
|
|
|
39
36
|
|
|
@@ -611,8 +608,13 @@ def set_arrangement_automation(song, params):
|
|
|
611
608
|
except Exception:
|
|
612
609
|
pass # Non-MIDI clips or errors — automation-only is still valid
|
|
613
610
|
|
|
614
|
-
#
|
|
615
|
-
#
|
|
611
|
+
# Place the session clip (with automation + notes) into arrangement.
|
|
612
|
+
# Do this BEFORE deleting the original — if placement fails, the
|
|
613
|
+
# original clip is preserved (no data loss on partial failure).
|
|
614
|
+
track.duplicate_clip_to_arrangement(temp_clip, arr_start)
|
|
615
|
+
|
|
616
|
+
# Placement succeeded — now safe to remove the original clip
|
|
617
|
+
# to avoid a second overlapping clip at the same position.
|
|
616
618
|
try:
|
|
617
619
|
clip.delete_clip()
|
|
618
620
|
except (AttributeError, RuntimeError):
|
|
@@ -620,9 +622,6 @@ def set_arrangement_automation(song, params):
|
|
|
620
622
|
# in that case we accept the overlap as a known limitation.
|
|
621
623
|
pass
|
|
622
624
|
|
|
623
|
-
# Place the session clip (with automation + notes) into arrangement
|
|
624
|
-
track.duplicate_clip_to_arrangement(temp_clip, arr_start)
|
|
625
|
-
|
|
626
625
|
# Clean up the temporary session clip
|
|
627
626
|
slot.delete_clip()
|
|
628
627
|
|
|
@@ -298,13 +298,28 @@ def freeze_track(song, params):
|
|
|
298
298
|
"is_frozen": True,
|
|
299
299
|
"note": "Track is already frozen",
|
|
300
300
|
}
|
|
301
|
-
#
|
|
301
|
+
# Try track.freeze() first (available in some Live versions),
|
|
302
|
+
# then fall back to song-level freeze API
|
|
303
|
+
frozen = False
|
|
302
304
|
try:
|
|
303
305
|
track.freeze()
|
|
306
|
+
frozen = True
|
|
304
307
|
except AttributeError:
|
|
308
|
+
pass
|
|
309
|
+
|
|
310
|
+
if not frozen:
|
|
311
|
+
# Song-level API: freeze by track index
|
|
312
|
+
try:
|
|
313
|
+
song.freeze_track(track_index)
|
|
314
|
+
frozen = True
|
|
315
|
+
except AttributeError:
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
if not frozen:
|
|
305
319
|
raise ValueError(
|
|
306
|
-
"freeze() not available
|
|
307
|
-
"
|
|
320
|
+
"freeze() not available via ControlSurface API. "
|
|
321
|
+
"Use Ableton's Freeze Track command (Cmd+F) manually, "
|
|
322
|
+
"or use the M4L bridge for programmatic freeze."
|
|
308
323
|
)
|
|
309
324
|
return {
|
|
310
325
|
"track_index": track_index,
|