livepilot 1.27.0 → 1.27.2
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 +68 -0
- package/README.md +26 -7
- package/bin/livepilot.js +86 -23
- package/installer/install.js +21 -12
- package/livepilot/.Codex-plugin/plugin.json +1 -1
- package/livepilot/.claude-plugin/plugin.json +1 -1
- package/livepilot/skills/livepilot-core/SKILL.md +8 -8
- package/livepilot/skills/livepilot-core/references/overview.md +2 -2
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +1 -1
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/livepilot_bridge.js +48 -11
- 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/fast/brief_builder.py +6 -2
- package/mcp_server/composer/full/apply.py +9 -0
- package/mcp_server/composer/full/layer_planner.py +16 -6
- package/mcp_server/composer/tools.py +12 -6
- package/mcp_server/connection.py +2 -1
- 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 +145 -54
- 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/execution_router.py +6 -0
- package/mcp_server/runtime/live_version.py +27 -8
- package/mcp_server/runtime/mcp_dispatch.py +22 -0
- package/mcp_server/runtime/remote_commands.py +9 -4
- package/mcp_server/runtime/safety_kernel.py +11 -0
- 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/server.py +1 -1
- 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/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 +1 -1
- package/remote_script/LivePilot/__init__.py +21 -8
- package/remote_script/LivePilot/server.py +23 -3
- package/remote_script/LivePilot/tracks.py +11 -5
- package/requirements.txt +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.27.2 — 2026-06-23
|
|
4
|
+
|
|
5
|
+
Maintenance release: M4L bridge response-correlation + chunk-reassembly hardening, plan-routing fixes, a Python 3.11 floor with clearer install diagnostics, and a Remote Script Group-track crash guard. No change to the tool surface (467 tools / 56 domains). Re-frozen LivePilot_Analyzer.amxd (analyzer reports 1.27.2).
|
|
6
|
+
|
|
7
|
+
### Fixed — M4L bridge
|
|
8
|
+
- Correlate responses by request id across batched reads (`get_params` / `get_hidden_params` / `get_auto_state`): the id is captured per command in the analyzer JS and echoed on both single-packet and chunked responses, so an interleaved or timed-out command can no longer resolve another command's future.
|
|
9
|
+
- Chunk reassembly buckets per request id, bounds-checks the chunk index, and requires every index present before reassembling — fixes a `KeyError` / silent-loss + full-timeout path on duplicate or out-of-range chunks.
|
|
10
|
+
- Non-blocking UDP socket so the miditool response path cannot stall the asyncio event loop; clear the capture future after completion.
|
|
11
|
+
|
|
12
|
+
### Fixed — routing & Remote Script
|
|
13
|
+
- `compressor_set_sidechain` now classifies as an MCP tool (TCP Remote Script path) instead of being mis-routed to the M4L JS bridge; `get_master_rms` is now dispatchable in plans; removed the dead duplicate `get_simpler_file_path` bridge entry.
|
|
14
|
+
- `get_track_info` no longer crashes on Group/Return tracks (guards the LOM-fragile arm/input properties).
|
|
15
|
+
- `reload_handlers` reports per-module reload errors instead of silently swallowing them.
|
|
16
|
+
- Connection retry tears down the stale socket under the lock.
|
|
17
|
+
|
|
18
|
+
### Changed — install & docs
|
|
19
|
+
- Raised the Python floor to 3.11 (numpy/scipy publish no wheels for 3.9/3.10) with a clear pre-flight message; pip failures surface captured output + a version hint, and the expected grpcio-tools resolver warning is pre-announced.
|
|
20
|
+
- `consult_ableton_knowledge` docstrings corrected; fast brief now notes the `mcp__Ableton_Knowledge__` prefix and that the knowledge MCP is optional; fixed an over-permissive tool-name test.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## v1.27.1 — 2026-06-21
|
|
24
|
+
|
|
25
|
+
Maintenance release: 35 verified fixes from a deep multi-agent audit, recursive installed-plugin scanning, and Windows-CI hardening. No change to the tool surface (467 tools / 56 domains).
|
|
26
|
+
|
|
27
|
+
### Restored — tools that were silently broken
|
|
28
|
+
- `augment_with_samples`, `get_composition_plan`, `propose_composer_branches` — crashed or returned nothing since the v1.24 refactor removed section templates; they now degrade to a single full-length section.
|
|
29
|
+
- `check_clip_key_consistency` — always returned `"unknown"` (removed FastMCP `.fn` accessor).
|
|
30
|
+
- `compare_phrase_renders` — returned an identical empty critique; now analyzes each render.
|
|
31
|
+
|
|
32
|
+
### Fixed — correctness
|
|
33
|
+
- Reference engine: project spectrum and stereo width are now populated (gap analysis previously ran against an all-zero project).
|
|
34
|
+
- `infer_section_purposes`: drops are no longer mislabeled as tension.
|
|
35
|
+
- Grader: master/return/group tracks identified by their real field names (group containers were inflating track counts).
|
|
36
|
+
- Hook salience: the memorability boost no longer fires on every candidate.
|
|
37
|
+
- Mix: the `flat_dynamics` critic is now reachable (`over_compressed` is a 3–6 dB band).
|
|
38
|
+
- Wavetable adapter uses the real `Osc 1 Pos` parameter name.
|
|
39
|
+
- Simpler slice playback uses the correct base note (36+N); `vibe_fit` energy proxy normalized.
|
|
40
|
+
- Harmony and phrase-grid analysis read from the correct clip slot.
|
|
41
|
+
- `exclude_globs` now match files inside named directories.
|
|
42
|
+
- A single shared technique store so in-session saves are visible to recall/search.
|
|
43
|
+
- Read-only prefix matcher no longer misclassifies mutating tools as safe.
|
|
44
|
+
- Atlas id/name collisions no longer shadow entries; the overlay index is no longer rescanned per namespace.
|
|
45
|
+
- AMXD device-type map recognizes MIDI Tool devices (Live 12.1+).
|
|
46
|
+
- `apply_full_plan_v2` postflight no longer deletes a reused existing track.
|
|
47
|
+
- Fatigue level no longer diluted by low-severity issues; energy-arc no longer desyncs on skipped scenes; `create_preview_set` no longer silently overwrites an existing set.
|
|
48
|
+
|
|
49
|
+
### Fixed — installed-plugin scanning (#44)
|
|
50
|
+
- The plugin scanner now recurses into vendor subfolders (e.g. `VST3/<Vendor>/Plugin.vst3`); nested plugins are found and vendor folders are no longer emitted as junk inventory records.
|
|
51
|
+
|
|
52
|
+
### Fixed — safety and resources
|
|
53
|
+
- Splice `is_free` misclassification that could bypass credit/quota gating.
|
|
54
|
+
- Experiment rollback undoes only `remote_command` steps (no longer reverts unrelated edits).
|
|
55
|
+
- Timed-out write commands are dropped instead of re-executing later on the main thread.
|
|
56
|
+
- Order-tolerant M4L bridge chunk reassembly (no permanent response loss on UDP reordering).
|
|
57
|
+
- Installer install-path guard hardened; Splice bearer token gated to splice.com HTTPS hosts; non-numeric Live version strings tolerated instead of crashing the capability probe.
|
|
58
|
+
|
|
59
|
+
### Performance
|
|
60
|
+
- Blocking sample I/O (SQLite, file decode, FFT, network) moved off the asyncio event loop.
|
|
61
|
+
- Capped previously-unbounded tool responses (`atlas_device_info`, `extension_atlas_search`, corpus synthesis briefs, piano-roll matrix, plugin `sdk_metadata`).
|
|
62
|
+
- Deduped redundant session round-trips (`enter_wonder_mode`, `build_project_brain`).
|
|
63
|
+
|
|
64
|
+
### CI and tooling
|
|
65
|
+
- Fixed the Windows CI matrix (cp1252 `UnicodeEncodeError` in verifier scripts; POSIX-path test fixture).
|
|
66
|
+
- `build_mcpb.sh` enforces `.mcpbignore`; portable dev scripts.
|
|
67
|
+
|
|
68
|
+
### Dependencies
|
|
69
|
+
- fastmcp, soundfile 0.14.0, grpcio 1.81.1, protobuf 7.35.1.
|
|
70
|
+
|
|
3
71
|
## v1.27.0 — 2026-06-16
|
|
4
72
|
|
|
5
73
|
Probe-first Live 12.4 capability release.
|
package/README.md
CHANGED
|
@@ -38,6 +38,20 @@
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
+
## What's New in v1.27.1
|
|
42
|
+
|
|
43
|
+
A maintenance release that fixes 35 issues from a deep audit and restores tools that had silently broken:
|
|
44
|
+
|
|
45
|
+
- **Restored tools** — `augment_with_samples`, `get_composition_plan`, `propose_composer_branches`, `check_clip_key_consistency`, and `compare_phrase_renders` were crashing or returning empty results; they now work.
|
|
46
|
+
- **Recursive plugin scanning (#44)** — the User Corpus scanner now finds plugins nested in vendor subfolders (e.g. `VST3/<Vendor>/Plugin.vst3`) instead of only the top level.
|
|
47
|
+
- **Truer analysis** — reference-gap, hook-salience, dynamics, drop-detection, harmony-slot, and grader fixes that previously produced fabricated or empty results.
|
|
48
|
+
- **Safer & faster** — Splice credit-gating fixed, blocking sample I/O moved off the event loop, unbounded tool responses capped, and the M4L bridge no longer loses responses on UDP reordering.
|
|
49
|
+
- **v1.27.0** added two read-only Live 12.4 capability-probe tools (`probe_link_audio`, `probe_stem_workflow`).
|
|
50
|
+
|
|
51
|
+
Full details in the [CHANGELOG](CHANGELOG.md).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
41
55
|
## What LivePilot Does
|
|
42
56
|
|
|
43
57
|
Most MCP servers are tool collections — they execute commands. LivePilot is an **agentic production system**. It has eight layers that work together:
|
|
@@ -473,7 +487,7 @@ Full track with song form: intro, verse, hook, breakdown, outro. Uses a two-phas
|
|
|
473
487
|
- Native arrangement clips via `create_native_arrangement_clip` (one clip per section, looped to fill section length)
|
|
474
488
|
- Zombie-track cleanup in postflight (removes tracks with no clips and no instrument device)
|
|
475
489
|
- Drum-role pitch repair ported from fast mode
|
|
476
|
-
- **
|
|
490
|
+
- **Atlas anchors:** role-level device knowledge is supplied through `KnowledgePack.atlas_anchors`, which consults the indexed atlas rather than filename matching. The older `atlas_candidates_per_role` field is a deprecated legacy stub, kept empty for back-compat.
|
|
477
491
|
- Invoke with: *"Write a full [genre] track at [tempo] BPM"* or *"Build a full arrangement"*
|
|
478
492
|
|
|
479
493
|
#### develop mode — `develop_apply`
|
|
@@ -492,7 +506,7 @@ All three modes share a `KnowledgePack` that provides structured creative contex
|
|
|
492
506
|
- `event_lexicon` — 42 structural events across 7 categories (drum density, harmonic, texture, vocal, rhythm feel, tension, fx gesture)
|
|
493
507
|
- `genre_context` — parses the 15-genre `genre-vocabularies.md` at load time
|
|
494
508
|
- `artist_context` — parses the ~25-producer `artist-vocabularies.md` at load time
|
|
495
|
-
- `
|
|
509
|
+
- `atlas_anchors` — indexed-atlas device anchors per role (replaced the legacy `atlas_candidates_per_role` stub)
|
|
496
510
|
|
|
497
511
|
#### Core composer tools
|
|
498
512
|
|
|
@@ -550,7 +564,7 @@ The V2 intelligence layer. These tools analyze, diagnose, plan, evaluate, and le
|
|
|
550
564
|
|
|
551
565
|
### Easiest: Claude Desktop Extension (1 click)
|
|
552
566
|
|
|
553
|
-
Download
|
|
567
|
+
Download the latest `livepilot-<version>.mcpb` from the [Releases page](https://github.com/dreamrec/LivePilot/releases/latest) and double-click it.
|
|
554
568
|
Claude Desktop installs everything automatically. Then:
|
|
555
569
|
|
|
556
570
|
1. Open Ableton Live 12
|
|
@@ -584,10 +598,14 @@ Restart Ableton → Preferences → Link, Tempo & MIDI → Control Surface → *
|
|
|
584
598
|
<details>
|
|
585
599
|
<summary><strong>2. MCP Client</strong></summary>
|
|
586
600
|
|
|
587
|
-
**Claude Code
|
|
601
|
+
**Claude Code** — pick one (installing both registers the MCP server twice and collides on port 9878):
|
|
588
602
|
```bash
|
|
603
|
+
# MCP server only:
|
|
589
604
|
claude mcp add LivePilot -- npx livepilot
|
|
590
|
-
|
|
605
|
+
|
|
606
|
+
# Or the full plugin (MCP server + skills + slash commands):
|
|
607
|
+
claude plugin marketplace add github:dreamrec/LivePilot
|
|
608
|
+
claude plugin install livepilot@dreamrec-LivePilot
|
|
591
609
|
```
|
|
592
610
|
|
|
593
611
|
**Codex App:**
|
|
@@ -680,7 +698,8 @@ npx livepilot --install-codex-plugin
|
|
|
680
698
|
**Claude Code**
|
|
681
699
|
|
|
682
700
|
```bash
|
|
683
|
-
claude plugin add github:dreamrec/LivePilot
|
|
701
|
+
claude plugin marketplace add github:dreamrec/LivePilot
|
|
702
|
+
claude plugin install livepilot@dreamrec-LivePilot
|
|
684
703
|
```
|
|
685
704
|
|
|
686
705
|
| Command | What |
|
|
@@ -732,7 +751,7 @@ npx livepilot --version # Show version
|
|
|
732
751
|
| Requirement | Minimum |
|
|
733
752
|
|-------------|---------|
|
|
734
753
|
| Ableton Live | **12** (any edition). Suite required for Max for Live bridge and stock instruments |
|
|
735
|
-
| Python | 3.
|
|
754
|
+
| Python | 3.11+ (numpy/scipy ship no wheels for 3.9/3.10) |
|
|
736
755
|
| Node.js | 18+ |
|
|
737
756
|
| OS | macOS / Windows |
|
|
738
757
|
| Splice | Desktop app with downloaded samples (optional — enables SQLite metadata search) |
|
package/bin/livepilot.js
CHANGED
|
@@ -15,6 +15,13 @@ const REQUIREMENTS = path.join(ROOT, "requirements.txt");
|
|
|
15
15
|
// Python detection
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
|
|
18
|
+
// Minimum Python is 3.11, NOT 3.9: numpy>=2.4.6 and scipy>=1.17.1 (see
|
|
19
|
+
// requirements.txt) publish no wheels and declare requires-python ">=3.11".
|
|
20
|
+
// A 3.9/3.10 interpreter passes a looser gate, the venv is created, then
|
|
21
|
+
// `pip install` aborts with a cryptic "no matching distribution" — the exact
|
|
22
|
+
// install failure users hit. Gate here so we fail early with a clear message.
|
|
23
|
+
const MIN_PY_MINOR = 11;
|
|
24
|
+
|
|
18
25
|
function findPython() {
|
|
19
26
|
// On Windows, also try the "py -3" launcher which avoids the
|
|
20
27
|
// Microsoft Store stub that "python3" resolves to.
|
|
@@ -22,6 +29,7 @@ function findPython() {
|
|
|
22
29
|
? ["python", "python3", "py"]
|
|
23
30
|
: ["python3", "python"];
|
|
24
31
|
|
|
32
|
+
let tooOld = null; // highest 3.x below the floor we saw, for a clear error
|
|
25
33
|
for (const cmd of candidates) {
|
|
26
34
|
try {
|
|
27
35
|
const args = cmd === "py" ? ["-3", "--version"] : ["--version"];
|
|
@@ -33,17 +41,26 @@ function findPython() {
|
|
|
33
41
|
if (match) {
|
|
34
42
|
const major = parseInt(match[1], 10);
|
|
35
43
|
const minor = parseInt(match[2], 10);
|
|
36
|
-
if (major === 3 && minor >=
|
|
44
|
+
if (major === 3 && minor >= MIN_PY_MINOR) {
|
|
37
45
|
// For "py" launcher, the actual command to use is "py -3"
|
|
38
46
|
const actualCmd = cmd === "py" ? "py" : cmd;
|
|
39
47
|
const actualArgs = cmd === "py" ? ["-3"] : [];
|
|
40
48
|
return { cmd: actualCmd, version: out, prefixArgs: actualArgs };
|
|
41
49
|
}
|
|
50
|
+
// Found Python 3 but below 3.11 — remember the newest for diagnostics.
|
|
51
|
+
if (major === 3 && (!tooOld || minor > tooOld.minor)) {
|
|
52
|
+
tooOld = { version: out, minor };
|
|
53
|
+
}
|
|
42
54
|
}
|
|
43
55
|
} catch {
|
|
44
56
|
// command not found or failed — try next
|
|
45
57
|
}
|
|
46
58
|
}
|
|
59
|
+
// Signal "present but too old" distinctly from "absent" so callers can give
|
|
60
|
+
// an actionable message instead of a misleading "not found".
|
|
61
|
+
if (tooOld) {
|
|
62
|
+
return { tooOld: true, version: tooOld.version };
|
|
63
|
+
}
|
|
47
64
|
return null;
|
|
48
65
|
}
|
|
49
66
|
|
|
@@ -84,6 +101,41 @@ function findOtherLiveClient(host, port) {
|
|
|
84
101
|
// Virtual environment bootstrap
|
|
85
102
|
// ---------------------------------------------------------------------------
|
|
86
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Run `pip install -r requirements.txt` with captured stderr so a failure
|
|
106
|
+
* yields an actionable message instead of an opaque "Command failed".
|
|
107
|
+
*/
|
|
108
|
+
function pipInstall(venvPy) {
|
|
109
|
+
// One pip resolver warning about grpcio-tools / protobuf<7 is EXPECTED and
|
|
110
|
+
// harmless: LivePilot imports only the pre-generated Splice stubs at runtime,
|
|
111
|
+
// never grpcio-tools. Pre-announce it so it doesn't read as a failure.
|
|
112
|
+
console.error(" (a single grpcio-tools/protobuf resolver warning is expected and safe)");
|
|
113
|
+
try {
|
|
114
|
+
execFileSync(venvPy, ["-m", "pip", "install", "-q", "-r", REQUIREMENTS], {
|
|
115
|
+
cwd: ROOT,
|
|
116
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
117
|
+
encoding: "utf-8",
|
|
118
|
+
timeout: 120000,
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const stderr = (err && err.stderr ? String(err.stderr) : "").trim();
|
|
122
|
+
console.error("");
|
|
123
|
+
console.error("LivePilot: dependency installation failed.");
|
|
124
|
+
if (/No matching distribution|requires-python|Could not find a version/i.test(stderr)) {
|
|
125
|
+
console.error(" Most likely your Python is too old — LivePilot needs Python >= 3.11");
|
|
126
|
+
console.error(" (numpy/scipy ship no wheels for 3.9/3.10). Install 3.11+, delete the");
|
|
127
|
+
console.error(" .venv folder, and retry.");
|
|
128
|
+
}
|
|
129
|
+
const tail = stderr.split("\n").filter(Boolean).slice(-12);
|
|
130
|
+
if (tail.length) {
|
|
131
|
+
console.error("");
|
|
132
|
+
console.error(" pip output (last lines):");
|
|
133
|
+
for (const line of tail) console.error(" " + line);
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
87
139
|
/**
|
|
88
140
|
* Ensure a local .venv exists with dependencies installed.
|
|
89
141
|
* Returns the path to the venv Python binary.
|
|
@@ -104,11 +156,7 @@ function ensureVenv(systemPython, prefixArgs) {
|
|
|
104
156
|
} catch {
|
|
105
157
|
// venv exists but deps missing — reinstall
|
|
106
158
|
console.error("LivePilot: reinstalling Python dependencies...");
|
|
107
|
-
|
|
108
|
-
cwd: ROOT,
|
|
109
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
110
|
-
timeout: 120000,
|
|
111
|
-
});
|
|
159
|
+
pipInstall(venvPy);
|
|
112
160
|
return venvPy;
|
|
113
161
|
}
|
|
114
162
|
}
|
|
@@ -122,11 +170,7 @@ function ensureVenv(systemPython, prefixArgs) {
|
|
|
122
170
|
});
|
|
123
171
|
|
|
124
172
|
console.error("LivePilot: installing dependencies...");
|
|
125
|
-
|
|
126
|
-
cwd: ROOT,
|
|
127
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
128
|
-
timeout: 120000,
|
|
129
|
-
});
|
|
173
|
+
pipInstall(venvPython());
|
|
130
174
|
|
|
131
175
|
return venvPython();
|
|
132
176
|
}
|
|
@@ -262,11 +306,15 @@ async function doctor() {
|
|
|
262
306
|
|
|
263
307
|
// 1. Python
|
|
264
308
|
const pyInfo = findPython();
|
|
265
|
-
if (pyInfo) {
|
|
309
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
266
310
|
console.log(" Python: %s (%s)", pyInfo.version, pyInfo.cmd);
|
|
311
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
312
|
+
console.log(" Python: %s found, but LivePilot needs >= 3.11", pyInfo.version);
|
|
313
|
+
console.log(" Fix: install Python 3.11+ (numpy/scipy ship no wheels for 3.9/3.10)");
|
|
314
|
+
ok = false;
|
|
267
315
|
} else {
|
|
268
|
-
console.log(" Python: NOT FOUND (need >= 3.
|
|
269
|
-
console.log(" Fix: install Python 3.
|
|
316
|
+
console.log(" Python: NOT FOUND (need >= 3.11)");
|
|
317
|
+
console.log(" Fix: install Python 3.11+ and add to PATH");
|
|
270
318
|
ok = false;
|
|
271
319
|
}
|
|
272
320
|
|
|
@@ -686,10 +734,15 @@ async function setup() {
|
|
|
686
734
|
// 1. Python
|
|
687
735
|
console.log("Step 1/5: Checking Python...");
|
|
688
736
|
const pyInfo = findPython();
|
|
689
|
-
if (pyInfo) {
|
|
737
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
690
738
|
console.log(" ✓ %s", pyInfo.version);
|
|
739
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
740
|
+
console.log(" ✗ %s found, but LivePilot needs Python >= 3.11", pyInfo.version);
|
|
741
|
+
console.log(" (numpy/scipy ship no wheels for 3.9/3.10 — pip would fail)");
|
|
742
|
+
console.log(" Install: brew install python@3.12 (macOS) or python.org (Windows)");
|
|
743
|
+
ok = false;
|
|
691
744
|
} else {
|
|
692
|
-
console.log(" ✗ Python >= 3.
|
|
745
|
+
console.log(" ✗ Python >= 3.11 not found");
|
|
693
746
|
console.log(" Install: brew install python@3.12 (macOS) or python.org (Windows)");
|
|
694
747
|
ok = false;
|
|
695
748
|
}
|
|
@@ -722,7 +775,7 @@ async function setup() {
|
|
|
722
775
|
// 3. Bootstrap Python venv
|
|
723
776
|
console.log("");
|
|
724
777
|
console.log("Step 3/5: Setting up Python environment...");
|
|
725
|
-
if (pyInfo) {
|
|
778
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
726
779
|
try {
|
|
727
780
|
ensureVenv(pyInfo.cmd, pyInfo.prefixArgs);
|
|
728
781
|
console.log(" ✓ Virtual environment ready");
|
|
@@ -730,6 +783,8 @@ async function setup() {
|
|
|
730
783
|
console.log(" ✗ Failed: %s", err.message);
|
|
731
784
|
ok = false;
|
|
732
785
|
}
|
|
786
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
787
|
+
console.log(" ⊘ Skipped (%s found, need Python >= 3.11)", pyInfo.version);
|
|
733
788
|
} else {
|
|
734
789
|
console.log(" ⊘ Skipped (no Python)");
|
|
735
790
|
}
|
|
@@ -823,8 +878,10 @@ async function main() {
|
|
|
823
878
|
console.log(" --help Show this help");
|
|
824
879
|
console.log("");
|
|
825
880
|
console.log("Environment:");
|
|
826
|
-
console.log(" LIVE_MCP_HOST
|
|
827
|
-
console.log(" LIVE_MCP_PORT
|
|
881
|
+
console.log(" LIVE_MCP_HOST Remote Script host (default: 127.0.0.1)");
|
|
882
|
+
console.log(" LIVE_MCP_PORT Remote Script port (default: 9878)");
|
|
883
|
+
console.log(" LIVEPILOT_AUTO_INSTALL Auto-install Remote Script on launch (set to 'true')");
|
|
884
|
+
console.log(" LIVEPILOT_TCP_PORT Override Remote Script port (Desktop Extension/MCPB)");
|
|
828
885
|
return;
|
|
829
886
|
}
|
|
830
887
|
|
|
@@ -915,12 +972,18 @@ async function main() {
|
|
|
915
972
|
|
|
916
973
|
// Default: start MCP server
|
|
917
974
|
const pyInfo = findPython();
|
|
918
|
-
if (!pyInfo) {
|
|
919
|
-
|
|
975
|
+
if (!pyInfo || pyInfo.tooOld) {
|
|
976
|
+
if (pyInfo && pyInfo.tooOld) {
|
|
977
|
+
console.error("Error: found %s, but LivePilot requires Python >= 3.11.", pyInfo.version);
|
|
978
|
+
console.error(" numpy>=2.4.6 and scipy>=1.17.1 publish no wheels for Python 3.9/3.10,");
|
|
979
|
+
console.error(" so dependency installation would fail. Install Python 3.11 or newer.");
|
|
980
|
+
} else {
|
|
981
|
+
console.error("Error: Python >= 3.11 is required but was not found.");
|
|
982
|
+
console.error(" Install Python 3.11+ and ensure 'python3' or 'python' is on your PATH.");
|
|
983
|
+
}
|
|
920
984
|
console.error("");
|
|
921
|
-
console.error("Install Python 3.9+ and ensure 'python3' or 'python' is on your PATH.");
|
|
922
985
|
console.error(" macOS: brew install python@3.12");
|
|
923
|
-
console.error(" Ubuntu: sudo apt install python3");
|
|
986
|
+
console.error(" Ubuntu: sudo apt install python3.12");
|
|
924
987
|
console.error(" Windows: https://www.python.org/downloads/");
|
|
925
988
|
process.exit(1);
|
|
926
989
|
}
|
package/installer/install.js
CHANGED
|
@@ -46,23 +46,32 @@ function _assertSafeInstallPath(resolvedPath, candidates) {
|
|
|
46
46
|
"/Applications/Ableton",
|
|
47
47
|
"C:\\ProgramData\\Ableton",
|
|
48
48
|
];
|
|
49
|
-
//
|
|
49
|
+
// True iff `child` equals `root` or is a descendant of it. The path.sep
|
|
50
|
+
// suffix prevents `/Applications/Ableton-evil` from matching the
|
|
51
|
+
// `/Applications/Ableton` prefix (prefix-boundary check).
|
|
52
|
+
const within = (child, root) => {
|
|
53
|
+
const c = path.resolve(child);
|
|
54
|
+
const r = path.resolve(root);
|
|
55
|
+
return c === r || c.startsWith(r + path.sep);
|
|
56
|
+
};
|
|
57
|
+
// The destination must be a detected Ableton Remote Scripts dir, a descendant
|
|
58
|
+
// of one, or under a known-safe prefix. We deliberately do NOT treat an
|
|
59
|
+
// *ancestor* of a candidate as safe — that previously let
|
|
60
|
+
// LIVEPILOT_INSTALL_PATH=/ or /Applications slip through (candidate path
|
|
61
|
+
// "starts with" the broad parent), defeating the guard.
|
|
50
62
|
for (const c of candidates) {
|
|
51
|
-
if (
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (path.resolve(resolvedPath).startsWith(path.resolve(c.path))) {
|
|
63
|
+
if (within(resolvedPath, c.path)) {
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new InstallerAbort(
|
|
61
|
-
`LIVEPILOT_INSTALL_PATH=${resolvedPath} is outside permitted directories. ` +
|
|
62
|
-
`Refusing to install. Allowed roots: ${allowedPrefixes.join(", ")}`,
|
|
63
|
-
{ recoverable: false }
|
|
64
|
-
);
|
|
67
|
+
if (allowedPrefixes.some((p) => within(resolvedPath, p))) {
|
|
68
|
+
return;
|
|
65
69
|
}
|
|
70
|
+
throw new InstallerAbort(
|
|
71
|
+
`LIVEPILOT_INSTALL_PATH=${resolvedPath} is outside permitted directories. ` +
|
|
72
|
+
`Refusing to install. Allowed roots: ${allowedPrefixes.join(", ")}`,
|
|
73
|
+
{ recoverable: false }
|
|
74
|
+
);
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.2",
|
|
4
4
|
"description": "Agentic production system for Ableton Live 12 \u2014 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, neo-Riemannian harmony, Euclidean rhythm, species counterpoint, MIDI I/O",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pilot Studio"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.2",
|
|
4
4
|
"description": "Agentic production system for Ableton Live 12 \u2014 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, neo-Riemannian harmony, Euclidean rhythm, species counterpoint, MIDI I/O",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pilot Studio"
|
|
@@ -198,14 +198,14 @@ Use when the user wants options, variants, or is stuck ("surprise me", "try some
|
|
|
198
198
|
**Rule of thumb**: if the user asked for a specific fix, Flow A. If they asked "what would you do?" or mentioned feel/vibe without parameters, Flow B.
|
|
199
199
|
|
|
200
200
|
### Semantic Moves
|
|
201
|
-
High-level musical intents that compile to deterministic tool sequences. 7 families (44 moves as of v1.27.
|
|
202
|
-
- **mix** — `tighten_low_end`, `widen_stereo`, `make_punchier`, `darken_without_losing_width`, `reduce_repetition_fatigue`, `make_kick_bass_lock`, `
|
|
203
|
-
- **
|
|
204
|
-
- **
|
|
205
|
-
- **
|
|
206
|
-
- **performance** — `recover_energy`, `decompress_tension`, `safe_spotlight`, `emergency_simplify`
|
|
207
|
-
- **
|
|
208
|
-
- **
|
|
201
|
+
High-level musical intents that compile to deterministic tool sequences. 7 families (44 moves as of v1.27.1):
|
|
202
|
+
- **mix** (9) — `tighten_low_end`, `widen_stereo`, `make_punchier`, `darken_without_losing_width`, `reduce_repetition_fatigue`, `make_kick_bass_lock`, `set_track_routing`, `set_track_metadata`, `configure_send_architecture`
|
|
203
|
+
- **device_creation** (9) — `create_chaos_modulator`, `create_feedback_resonator`, `create_wavefolder_effect`, `create_bitcrusher_effect`, `create_karplus_string`, `create_stochastic_texture`, `create_fdn_reverb`, `create_drum_rack_pad`, `build_send_chain` (procedural M4L device generation)
|
|
204
|
+
- **sound_design** (7) — `add_warmth`, `add_texture`, `shape_transients`, `add_space`, `configure_device`, `remove_device`, `load_chord_source`
|
|
205
|
+
- **sample** (6) — `sample_chop_rhythm`, `sample_texture_layer`, `sample_vocal_ghost`, `sample_break_layer`, `sample_resample_destroy`, `sample_one_shot_accent` (registered from `sample_engine/moves.py`)
|
|
206
|
+
- **performance** (5) — `recover_energy`, `decompress_tension`, `safe_spotlight`, `emergency_simplify`, `configure_record_readiness`
|
|
207
|
+
- **arrangement** (4) — `create_buildup_tension`, `smooth_scene_handoff`, `configure_groove`, `set_scene_metadata`
|
|
208
|
+
- **transition** (4) — `bridge_sections`, `increase_forward_motion`, `open_chorus`, `create_breakdown`
|
|
209
209
|
|
|
210
210
|
Use `list_semantic_moves(domain="mix")` to discover available moves.
|
|
211
211
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# LivePilot v1.27.
|
|
1
|
+
# LivePilot v1.27.2 — Architecture & Tool Reference
|
|
2
2
|
|
|
3
3
|
Agentic production system for Ableton Live 12. 467 tools across 56 domains. Device atlas (5264 devices, 120 enriched, 47 with aesthetic-tagged `signature_techniques`), spectral perception (M4L analyzer with 9-band FFT — sub_low / sub / low / low_mid / mid / high_mid / high / presence / air), 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, **LIVE Splice describe-a-sound + variations via captured GraphQL endpoints (v1.17)**, drum-rack pad-by-pad construction, live dead-device detection via meter sampling, role-aware Simpler defaults, session-record arrangement-automation workaround.
|
|
4
4
|
|
|
@@ -218,7 +218,7 @@ This turns "set EQ band 3 to -4 dB" into "cut 400 Hz by 4 dB, then read the spec
|
|
|
218
218
|
| `memory_update` | Updates name, tags, or qualities | `technique_id`, `name`, `tags`, `qualities` |
|
|
219
219
|
| `memory_delete` | Removes technique (backs up first) | `technique_id` |
|
|
220
220
|
|
|
221
|
-
### Analyzer (
|
|
221
|
+
### Analyzer (38) — Real-time DSP analysis (requires LivePilot Analyzer M4L device on master track)
|
|
222
222
|
|
|
223
223
|
| Tool | What it does | Key params |
|
|
224
224
|
|------|-------------|------------|
|
|
@@ -173,7 +173,7 @@ Live 12.4 introduces a new capability tier that unlocks native LOM access for sa
|
|
|
173
173
|
**Tool signatures:** unchanged. Callers do not need to detect the tier —
|
|
174
174
|
routing is transparent.
|
|
175
175
|
|
|
176
|
-
**Probe-first 12.4 surfaces in v1.27.
|
|
176
|
+
**Probe-first 12.4 surfaces in v1.27.2:**
|
|
177
177
|
- `probe_link_audio()` reports observed peer/input/routing visibility and
|
|
178
178
|
returns `manual_only`, `readable`, `routable`, or `unavailable`.
|
|
179
179
|
- `probe_stem_workflow()` reports observed callable paths and returns
|
|
Binary file
|
|
@@ -34,7 +34,7 @@ outlets = 2; // 0: to udpsend (responses), 1: to buffer~/status
|
|
|
34
34
|
// Single source of truth for the bridge version — bumped alongside the
|
|
35
35
|
// rest of the release manifest. Surfaced in the UI via messnamed("livepilot_version", ...)
|
|
36
36
|
// so the frozen .amxd visibly reports which build it was last exported from.
|
|
37
|
-
var VERSION = "1.27.
|
|
37
|
+
var VERSION = "1.27.2";
|
|
38
38
|
|
|
39
39
|
// ── State ──────────────────────────────────────────────────────────────────
|
|
40
40
|
|
|
@@ -45,6 +45,7 @@ var pitch_history = []; // Rolling buffer for key detection
|
|
|
45
45
|
var MAX_PITCH_HISTORY = 128;
|
|
46
46
|
var detected_key = "";
|
|
47
47
|
var detected_scale = "";
|
|
48
|
+
var current_response_request_id = "";
|
|
48
49
|
|
|
49
50
|
// Capture state
|
|
50
51
|
var capture_active = false;
|
|
@@ -85,9 +86,19 @@ function anything() {
|
|
|
85
86
|
var cmd = messagename;
|
|
86
87
|
if (cmd.charAt(0) === "/") cmd = cmd.substring(1);
|
|
87
88
|
var args = _decode_arg_strings(arrayfromargs(arguments));
|
|
89
|
+
var request_id = "";
|
|
90
|
+
if (args.length > 0 && typeof args[args.length - 1] === "string") {
|
|
91
|
+
var last_arg = args[args.length - 1];
|
|
92
|
+
var request_prefix = "__livepilot_request_id:";
|
|
93
|
+
if (last_arg.indexOf(request_prefix) === 0) {
|
|
94
|
+
request_id = last_arg.substring(request_prefix.length);
|
|
95
|
+
args.pop();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
88
98
|
|
|
89
99
|
// Defer to low-priority thread for LiveAPI safety
|
|
90
100
|
var task = new Task(function() {
|
|
101
|
+
current_response_request_id = request_id;
|
|
91
102
|
try {
|
|
92
103
|
dispatch(cmd, args);
|
|
93
104
|
} catch(e) {
|
|
@@ -223,6 +234,11 @@ function cmd_get_params(args) {
|
|
|
223
234
|
|
|
224
235
|
var batch_size = 8;
|
|
225
236
|
var current = 0;
|
|
237
|
+
// Capture the request id now: read_batch reschedules itself via
|
|
238
|
+
// Task.schedule(20), and an interleaved command arriving in that gap can
|
|
239
|
+
// overwrite the module-level current_response_request_id before the final
|
|
240
|
+
// send_response fires. The closure keeps OUR id stable across batches.
|
|
241
|
+
var captured_request_id = current_response_request_id;
|
|
226
242
|
|
|
227
243
|
function read_batch() {
|
|
228
244
|
try {
|
|
@@ -246,7 +262,7 @@ function cmd_get_params(args) {
|
|
|
246
262
|
var next_task = new Task(read_batch);
|
|
247
263
|
next_task.schedule(20);
|
|
248
264
|
} else {
|
|
249
|
-
send_response({"track": track_idx, "device": device_idx, "params": params});
|
|
265
|
+
send_response({"track": track_idx, "device": device_idx, "params": params}, captured_request_id);
|
|
250
266
|
}
|
|
251
267
|
} catch (e) {
|
|
252
268
|
send_response({
|
|
@@ -254,7 +270,7 @@ function cmd_get_params(args) {
|
|
|
254
270
|
"track": track_idx,
|
|
255
271
|
"device": device_idx,
|
|
256
272
|
"partial_params": params
|
|
257
|
-
});
|
|
273
|
+
}, captured_request_id);
|
|
258
274
|
}
|
|
259
275
|
}
|
|
260
276
|
|
|
@@ -275,6 +291,9 @@ function cmd_get_hidden_params(args) {
|
|
|
275
291
|
var params = [];
|
|
276
292
|
var current = 0;
|
|
277
293
|
var batch_size = 8;
|
|
294
|
+
// See cmd_get_params: capture the request id so the closure survives the
|
|
295
|
+
// Task.schedule(20) gaps without being clobbered by an interleaved command.
|
|
296
|
+
var captured_request_id = current_response_request_id;
|
|
278
297
|
|
|
279
298
|
function read_batch() {
|
|
280
299
|
try {
|
|
@@ -307,7 +326,7 @@ function cmd_get_hidden_params(args) {
|
|
|
307
326
|
"device_name": device_name,
|
|
308
327
|
"total_params": param_count,
|
|
309
328
|
"params": params
|
|
310
|
-
});
|
|
329
|
+
}, captured_request_id);
|
|
311
330
|
}
|
|
312
331
|
} catch (e) {
|
|
313
332
|
send_response({
|
|
@@ -316,7 +335,7 @@ function cmd_get_hidden_params(args) {
|
|
|
316
335
|
"device": device_idx,
|
|
317
336
|
"device_name": device_name,
|
|
318
337
|
"partial_params": params
|
|
319
|
-
});
|
|
338
|
+
}, captured_request_id);
|
|
320
339
|
}
|
|
321
340
|
}
|
|
322
341
|
|
|
@@ -334,6 +353,9 @@ function cmd_get_auto_state(args) {
|
|
|
334
353
|
var results = [];
|
|
335
354
|
var current = 0;
|
|
336
355
|
var batch_size = 8;
|
|
356
|
+
// See cmd_get_params: capture the request id so the closure survives the
|
|
357
|
+
// Task.schedule(20) gaps without being clobbered by an interleaved command.
|
|
358
|
+
var captured_request_id = current_response_request_id;
|
|
337
359
|
|
|
338
360
|
function read_batch() {
|
|
339
361
|
try {
|
|
@@ -362,7 +384,7 @@ function cmd_get_auto_state(args) {
|
|
|
362
384
|
"total_params": param_count,
|
|
363
385
|
"automated_params": results,
|
|
364
386
|
"automated_count": results.length
|
|
365
|
-
});
|
|
387
|
+
}, captured_request_id);
|
|
366
388
|
}
|
|
367
389
|
} catch (e) {
|
|
368
390
|
send_response({
|
|
@@ -372,7 +394,7 @@ function cmd_get_auto_state(args) {
|
|
|
372
394
|
"total_params": param_count,
|
|
373
395
|
"automated_params": results,
|
|
374
396
|
"automated_count": results.length
|
|
375
|
-
});
|
|
397
|
+
}, captured_request_id);
|
|
376
398
|
}
|
|
377
399
|
}
|
|
378
400
|
|
|
@@ -689,22 +711,37 @@ function correlate(a, b) {
|
|
|
689
711
|
|
|
690
712
|
// ── Response Encoding ──────────────────────────────────────────────────────
|
|
691
713
|
|
|
692
|
-
function send_response(obj) {
|
|
693
|
-
var
|
|
714
|
+
function send_response(obj, explicit_id) {
|
|
715
|
+
var response = obj || {};
|
|
716
|
+
// Async/batched handlers (cmd_get_params et al.) capture the request id in
|
|
717
|
+
// a closure and pass it explicitly here, because the module-level
|
|
718
|
+
// current_response_request_id can be clobbered by an interleaved command
|
|
719
|
+
// that arrives during a Task.schedule() gap between batches.
|
|
720
|
+
var rid = (typeof explicit_id === "string" && explicit_id !== "")
|
|
721
|
+
? explicit_id
|
|
722
|
+
: current_response_request_id;
|
|
723
|
+
if (rid) {
|
|
724
|
+
response._livepilot_request_id = rid;
|
|
725
|
+
}
|
|
726
|
+
var json = JSON.stringify(response);
|
|
694
727
|
var encoded = base64_encode(json);
|
|
695
728
|
|
|
696
729
|
// Check if chunking needed (Max OSC packet limit ~8KB)
|
|
697
730
|
if (encoded.length < 1400) {
|
|
698
731
|
outlet(0, "/response", encoded);
|
|
699
732
|
} else {
|
|
700
|
-
// Split into chunks
|
|
733
|
+
// Split into chunks. The request id rides the chunk header (first arg)
|
|
734
|
+
// so the Python side buckets chunks per-response and never mixes two
|
|
735
|
+
// commands' chunks — the id inside the JSON can't be read until the
|
|
736
|
+
// chunks are reassembled.
|
|
701
737
|
var chunk_size = 1400;
|
|
702
738
|
var total = Math.ceil(encoded.length / chunk_size);
|
|
703
739
|
for (var i = 0; i < total; i++) {
|
|
704
740
|
var piece = encoded.substring(i * chunk_size, (i + 1) * chunk_size);
|
|
705
|
-
outlet(0, "/response_chunk", i, total, piece);
|
|
741
|
+
outlet(0, "/response_chunk", (rid || ""), i, total, piece);
|
|
706
742
|
}
|
|
707
743
|
}
|
|
744
|
+
current_response_request_id = "";
|
|
708
745
|
}
|
|
709
746
|
|
|
710
747
|
function base64_encode(str) {
|
package/mcp_server/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
|
|
2
|
-
__version__ = "1.27.
|
|
2
|
+
__version__ = "1.27.2"
|