livepilot 1.27.1 → 1.27.3
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 +86 -0
- package/README.md +10 -7
- package/bin/livepilot.js +156 -37
- package/livepilot/.Codex-plugin/plugin.json +1 -1
- package/livepilot/.claude-plugin/plugin.json +1 -1
- package/livepilot/skills/livepilot-core/SKILL.md +33 -1
- package/livepilot/skills/livepilot-core/references/atlas-tool-notes.md +69 -0
- package/livepilot/skills/livepilot-core/references/overview.md +18 -11
- package/livepilot/skills/livepilot-core/references/perception.md +125 -0
- package/livepilot/skills/livepilot-devices/references/device-parameter-units.md +66 -0
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +1 -1
- package/livepilot/skills/livepilot-mix-engine/SKILL.md +8 -0
- package/livepilot/skills/livepilot-release/SKILL.md +1 -1
- package/livepilot/skills/livepilot-sample-engine/references/splice-tools-notes.md +56 -0
- package/livepilot/skills/livepilot-wonder/SKILL.md +6 -0
- 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 +181 -40
- package/mcp_server/atlas/overlays.py +46 -3
- package/mcp_server/atlas/tools.py +226 -86
- package/mcp_server/audit/checks.py +50 -22
- package/mcp_server/audit/state.py +10 -2
- package/mcp_server/audit/tools.py +27 -6
- package/mcp_server/composer/develop/apply.py +7 -7
- package/mcp_server/composer/fast/apply.py +29 -23
- package/mcp_server/composer/fast/brief_builder.py +6 -2
- package/mcp_server/composer/framework/atlas_resolver.py +16 -1
- package/mcp_server/composer/full/apply.py +40 -34
- package/mcp_server/composer/full/engine.py +66 -32
- package/mcp_server/composer/tools.py +16 -9
- package/mcp_server/connection.py +69 -10
- package/mcp_server/creative_constraints/engine.py +10 -2
- package/mcp_server/creative_constraints/tools.py +24 -7
- package/mcp_server/curves.py +30 -7
- package/mcp_server/device_forge/builder.py +40 -11
- package/mcp_server/device_forge/models.py +9 -0
- package/mcp_server/device_forge/tools.py +30 -11
- package/mcp_server/experiment/engine.py +29 -22
- package/mcp_server/experiment/tools.py +15 -5
- package/mcp_server/m4l_bridge.py +217 -62
- package/mcp_server/memory/taste_graph.py +43 -0
- package/mcp_server/memory/technique_store.py +26 -3
- package/mcp_server/memory/tools.py +62 -4
- package/mcp_server/mix_engine/critics.py +187 -30
- package/mcp_server/mix_engine/models.py +21 -1
- package/mcp_server/mix_engine/state_builder.py +87 -8
- package/mcp_server/mix_engine/tools.py +16 -4
- package/mcp_server/performance_engine/tools.py +56 -8
- package/mcp_server/persistence/base_store.py +11 -0
- package/mcp_server/persistence/project_store.py +132 -8
- package/mcp_server/persistence/taste_store.py +90 -7
- package/mcp_server/preview_studio/engine.py +40 -10
- package/mcp_server/preview_studio/models.py +40 -0
- package/mcp_server/preview_studio/tools.py +56 -12
- package/mcp_server/project_brain/arrangement_graph.py +4 -0
- package/mcp_server/project_brain/models.py +2 -0
- package/mcp_server/project_brain/role_graph.py +13 -7
- package/mcp_server/reference_engine/gap_analyzer.py +58 -3
- package/mcp_server/reference_engine/profile_builder.py +47 -4
- package/mcp_server/reference_engine/tools.py +6 -0
- package/mcp_server/runtime/execution_router.py +57 -1
- package/mcp_server/runtime/mcp_dispatch.py +22 -0
- package/mcp_server/runtime/remote_commands.py +9 -4
- package/mcp_server/runtime/tools.py +0 -1
- package/mcp_server/sample_engine/sources.py +0 -2
- package/mcp_server/sample_engine/tools.py +19 -38
- package/mcp_server/semantic_moves/mix_compilers.py +276 -51
- package/mcp_server/semantic_moves/performance_compilers.py +51 -17
- package/mcp_server/semantic_moves/resolvers.py +45 -0
- package/mcp_server/semantic_moves/sound_design_compilers.py +14 -6
- package/mcp_server/semantic_moves/tools.py +80 -2
- package/mcp_server/semantic_moves/transition_compilers.py +26 -9
- package/mcp_server/server.py +27 -25
- package/mcp_server/session_continuity/tracker.py +51 -3
- package/mcp_server/song_brain/builder.py +47 -5
- package/mcp_server/song_brain/tools.py +21 -7
- package/mcp_server/sound_design/critics.py +1 -0
- package/mcp_server/splice_client/client.py +117 -33
- package/mcp_server/splice_client/http_bridge.py +15 -3
- package/mcp_server/splice_client/quota.py +28 -0
- package/mcp_server/stuckness_detector/detector.py +8 -5
- package/mcp_server/synthesis_brain/adapters/drift.py +13 -0
- package/mcp_server/synthesis_brain/adapters/meld.py +13 -0
- package/mcp_server/tools/_analyzer_engine/sample.py +36 -10
- package/mcp_server/tools/_perception_engine.py +6 -0
- package/mcp_server/tools/agent_os.py +4 -1
- package/mcp_server/tools/analyzer.py +198 -209
- package/mcp_server/tools/arrangement.py +7 -4
- package/mcp_server/tools/automation.py +24 -4
- package/mcp_server/tools/browser.py +25 -11
- package/mcp_server/tools/clips.py +6 -0
- package/mcp_server/tools/composition.py +33 -2
- package/mcp_server/tools/devices.py +53 -53
- package/mcp_server/tools/generative.py +14 -14
- package/mcp_server/tools/harmony.py +7 -7
- package/mcp_server/tools/mixing.py +4 -4
- package/mcp_server/tools/planner.py +68 -6
- package/mcp_server/tools/research.py +20 -2
- package/mcp_server/tools/theory.py +10 -10
- package/mcp_server/tools/transport.py +7 -2
- package/mcp_server/transition_engine/critics.py +13 -1
- package/mcp_server/user_corpus/tools.py +30 -1
- package/mcp_server/wonder_mode/engine.py +82 -9
- package/mcp_server/wonder_mode/session.py +32 -10
- package/mcp_server/wonder_mode/tools.py +14 -1
- package/package.json +1 -1
- package/remote_script/LivePilot/__init__.py +21 -8
- package/remote_script/LivePilot/arrangement.py +93 -33
- package/remote_script/LivePilot/browser.py +60 -4
- package/remote_script/LivePilot/devices.py +132 -62
- package/remote_script/LivePilot/mixing.py +31 -5
- package/remote_script/LivePilot/server.py +94 -22
- package/remote_script/LivePilot/tracks.py +11 -5
- package/remote_script/LivePilot/transport.py +11 -0
- package/requirements.txt +5 -5
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.27.3 — 2026-07-10
|
|
4
|
+
|
|
5
|
+
Deep-review remediation campaign (2026-07-09, on top of the 2026-06-24 fix batch): event-loop
|
|
6
|
+
blocking eliminated tree-wide, release-pipeline gates hardened, atlas scan truncation fixed,
|
|
7
|
+
Splice connection health, state-layer race/replay guards, docs drift automation, and +262 tests
|
|
8
|
+
(4326 → 4597 passing). No change to the tool surface (467 tools / 56 domains).
|
|
9
|
+
|
|
10
|
+
### Fixed — event loop (systemic)
|
|
11
|
+
- Every remaining blocking call in async tool paths offloaded (~80 sites across 13 files):
|
|
12
|
+
the composer fast/full/develop apply executors, the shared plan-step executor
|
|
13
|
+
(`execution_router._execute_step_async`), preview/experiment paths, analyzer helper call
|
|
14
|
+
sites, automation session-record, agent_os iteration, device-forge/file I/O, and lifespan
|
|
15
|
+
subprocess probes. New canonical wrapper `AbletonConnection.send_command_async()`.
|
|
16
|
+
- The AST regression guard now scans ALL of `mcp_server/` (was analyzer.py-only) with
|
|
17
|
+
one-hop transitive detection and file-I/O/subprocess patterns (`scripts/scan_async_blocking.py`).
|
|
18
|
+
|
|
19
|
+
Live-verified on Ableton 12.4.2 (2026-07-10): per-track volume reporting, force_arrangement
|
|
20
|
+
with return tracks, sidechain name resolution (both casings), analyzer bridge reconnect, and a
|
|
21
|
+
full 40k-device library rescan with zero category truncation.
|
|
22
|
+
|
|
23
|
+
### Fixed — correctness
|
|
24
|
+
- Sidechain source matching gained a case-insensitive EXACT tier (found live: Live 12.4
|
|
25
|
+
routing menus can list bare unprefixed track names, where the "-name" suffix fallback
|
|
26
|
+
never fires); matching ladder extracted into a tested helper.
|
|
27
|
+
- `audit_layer` §5.1 timbre check was a silent double no-op (wrong call signature + uppercase
|
|
28
|
+
band table vs lowercase producers) — now plumbs cached analyzer spectrum and case-folds.
|
|
29
|
+
- Atlas scans no longer truncate alphabetically at 1000 entries/category (drum kits previously
|
|
30
|
+
cut off before reaching most of the alphabet); scans record per-category counts + truncation
|
|
31
|
+
flags, and atlas searches warn when a truncated category is queried. Rescan required to
|
|
32
|
+
rebuild an existing user atlas (`scan_full_library(force=True)`).
|
|
33
|
+
- Semantic mix/sound-design/transition/performance moves now nudge track volume RELATIVE to the
|
|
34
|
+
current level (bounded, direction-safe) instead of writing absolute constants; the Remote
|
|
35
|
+
Script now reports per-track volume in `get_session_info`.
|
|
36
|
+
- Splice client marks itself degraded on RPC failure and self-heals with a one-shot reconnect —
|
|
37
|
+
a Splice desktop restart no longer yields silent fake "0 results" for the session; quota
|
|
38
|
+
check-and-record is atomic; HTTP retry no longer sleeps after the final attempt.
|
|
39
|
+
- Preview/wonder cached plans carry a session fingerprint and refuse commit with STATE_ERROR
|
|
40
|
+
when the session changed shape; shared caches are lock-guarded; persisted taste/project
|
|
41
|
+
stores back up before any version-mismatch reset; `song_brain` ids are content-aware.
|
|
42
|
+
- `get_emotional_arc` harmonic-instability term computed (was structurally zero);
|
|
43
|
+
flucoma probe no longer reports empty dirs as installed; composer engine renumbers layers
|
|
44
|
+
after unresolved-sample drops on the experiment-escalation path.
|
|
45
|
+
|
|
46
|
+
### Changed — release gates & CI
|
|
47
|
+
- New CI jobs: MCPB bundle build (asset previously shipped missing for 4 releases),
|
|
48
|
+
Python 3.11 compatibility for `remote_script/` (compileall + vermin), content-level
|
|
49
|
+
.amxd freeze check (full bridge command set, not just the version string).
|
|
50
|
+
- `bin/livepilot.js` venv staleness keyed on a requirements.txt content hash (hardcoded import
|
|
51
|
+
list had drifted — upgrading users silently never installed the Splice gRPC dependencies).
|
|
52
|
+
- `sync_metadata.py` validates JSON manifests version-field-by-field (caught a live
|
|
53
|
+
server.json split-version); docs Domain Map is now generated from the live registry;
|
|
54
|
+
banner assets and README What's New are drift-guarded.
|
|
55
|
+
|
|
56
|
+
### Changed — performance & analysis quality
|
|
57
|
+
- Server import ~2.0s → ~0.9s (lazy overlay index + libyaml CSafeLoader); atlas duplicate
|
|
58
|
+
warnings throttled; test suite wall-clock ~41s → ~32s.
|
|
59
|
+
- Mix-critic confidence values are data-derived (sample size, measured-vs-heuristic basis,
|
|
60
|
+
margin) instead of hardcoded literals; `analyze_mix`/`get_mix_issues` accept
|
|
61
|
+
`target_style="loud_master"` to suppress the over-compressed flag for intentionally loud
|
|
62
|
+
masters; Wonder Mode ranking surfaces a `boldest_executable` slot alongside the safe pick.
|
|
63
|
+
- Security hardening: corpus plugin-id path validation, loopback-only UDP analyzer ingestion,
|
|
64
|
+
competing-TCP-client identification in eviction errors.
|
|
65
|
+
|
|
66
|
+
### Changed — dependencies
|
|
67
|
+
- **2026-06-25** — Raised the Python floor to 3.12: numpy>=2.5.0 and scipy>=1.18.0 declare `Requires-Python >=3.12`, so the prior 3.11 floor blocked both dependency bumps across the CI matrix. CI now tests only Python 3.12; the install-time Python-version diagnostic and manifest/docs floor references were updated to match. `remote_script/` stays 3.11-syntax-compatible for Ableton's embedded Python (Live 12.3) — the CI matrix change does not relax that constraint.
|
|
68
|
+
|
|
69
|
+
## v1.27.2 — 2026-06-23
|
|
70
|
+
|
|
71
|
+
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).
|
|
72
|
+
|
|
73
|
+
### Fixed — M4L bridge
|
|
74
|
+
- 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.
|
|
75
|
+
- 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.
|
|
76
|
+
- Non-blocking UDP socket so the miditool response path cannot stall the asyncio event loop; clear the capture future after completion.
|
|
77
|
+
|
|
78
|
+
### Fixed — routing & Remote Script
|
|
79
|
+
- `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.
|
|
80
|
+
- `get_track_info` no longer crashes on Group/Return tracks (guards the LOM-fragile arm/input properties).
|
|
81
|
+
- `reload_handlers` reports per-module reload errors instead of silently swallowing them.
|
|
82
|
+
- Connection retry tears down the stale socket under the lock.
|
|
83
|
+
|
|
84
|
+
### Changed — install & docs
|
|
85
|
+
- 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.
|
|
86
|
+
- `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.
|
|
87
|
+
|
|
88
|
+
|
|
3
89
|
## v1.27.1 — 2026-06-21
|
|
4
90
|
|
|
5
91
|
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).
|
package/README.md
CHANGED
|
@@ -38,14 +38,17 @@
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
## What's New in v1.27.
|
|
41
|
+
## What's New in v1.27.3
|
|
42
42
|
|
|
43
|
-
A
|
|
43
|
+
A deep-review remediation release — ~150 fixes across two audit campaigns, no change to the tool surface (467 tools / 56 domains):
|
|
44
44
|
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
48
|
-
- **Safer
|
|
45
|
+
- **Event-loop blocking eliminated tree-wide** — every remaining blocking call in async tool paths (composer apply executors, the shared plan-step executor, preview/experiment paths, ~80 sites in 13 files) now offloads to a worker thread; a repo-wide AST regression guard keeps it that way. Long composes no longer freeze concurrent tools or the analyzer bridge.
|
|
46
|
+
- **Atlas scans no longer truncate** — library scans previously capped each category at 1,000 entries alphabetically (most drum one-shots were unreachable); the cap is now 25,000 with per-category truncation flags and rescan warnings. Run `scan_full_library(force=True)` once to rebuild your atlas.
|
|
47
|
+
- **Splice self-heals** — a Splice desktop restart no longer silently turns every search into fake "0 results"; the client marks itself degraded and reconnects on the next call.
|
|
48
|
+
- **Safer state** — cached preview/wonder plans carry a session fingerprint and refuse to replay against a session that changed shape; persisted taste/project stores back up before any reset.
|
|
49
|
+
- **Stronger release gates** — the MCPB bundle, .amxd freeze content, and Python 3.11 Remote Script compatibility are now all CI-enforced.
|
|
50
|
+
- **Python 3.12 floor** for the MCP server (numpy ≥2.5 / scipy ≥1.18); the Remote Script stays 3.11-compatible for Ableton's embedded Python.
|
|
51
|
+
- **v1.27.1** fixed 35 issues from a deep audit and restored tools that had silently broken (`augment_with_samples`, `get_composition_plan`, `propose_composer_branches`, `check_clip_key_consistency`, `compare_phrase_renders`), plus recursive installed-plugin scanning.
|
|
49
52
|
- **v1.27.0** added two read-only Live 12.4 capability-probe tools (`probe_link_audio`, `probe_stem_workflow`).
|
|
50
53
|
|
|
51
54
|
Full details in the [CHANGELOG](CHANGELOG.md).
|
|
@@ -751,7 +754,7 @@ npx livepilot --version # Show version
|
|
|
751
754
|
| Requirement | Minimum |
|
|
752
755
|
|-------------|---------|
|
|
753
756
|
| Ableton Live | **12** (any edition). Suite required for Max for Live bridge and stock instruments |
|
|
754
|
-
| Python | 3.
|
|
757
|
+
| Python | 3.12+ (numpy>=2.5 / scipy>=1.18 require Python 3.12) |
|
|
755
758
|
| Node.js | 18+ |
|
|
756
759
|
| OS | macOS / Windows |
|
|
757
760
|
| 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.12: numpy>=2.5.0 and scipy>=1.18.0 (see requirements.txt)
|
|
19
|
+
// declare requires-python ">=3.12". An older interpreter passes a looser gate,
|
|
20
|
+
// the venv is created, then `pip install` aborts with a cryptic "no matching
|
|
21
|
+
// distribution" — the exact install failure users hit. Gate here so we fail
|
|
22
|
+
// early with a clear message.
|
|
23
|
+
const MIN_PY_MINOR = 12;
|
|
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.12 — 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,83 @@ 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.12");
|
|
126
|
+
console.error(" (numpy>=2.5 / scipy>=1.18 require Python 3.12). Install 3.12+, 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
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Content hash of requirements.txt — the venv staleness signal.
|
|
141
|
+
*
|
|
142
|
+
* The previous check was a hardcoded import list that silently drifted from
|
|
143
|
+
* requirements.txt (grpcio/protobuf were added for the Splice gRPC surface
|
|
144
|
+
* but never joined the list, so upgrading users kept a venv without them and
|
|
145
|
+
* the feature silently disabled itself). Hashing the file makes staleness
|
|
146
|
+
* detection automatically correct for every future dependency change.
|
|
147
|
+
*/
|
|
148
|
+
function requirementsHash() {
|
|
149
|
+
const crypto = require("crypto");
|
|
150
|
+
const reqs = fs.readFileSync(path.join(ROOT, "requirements.txt"));
|
|
151
|
+
return crypto.createHash("sha256").update(reqs).digest("hex");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function venvStampPath() {
|
|
155
|
+
return path.join(ROOT, VENV_DIR, ".requirements-sha256");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function writeVenvStamp() {
|
|
159
|
+
try {
|
|
160
|
+
fs.writeFileSync(venvStampPath(), requirementsHash() + "\n");
|
|
161
|
+
} catch {
|
|
162
|
+
// Non-fatal: worst case the next run reinstalls.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Pure decision function for venv staleness — extracted from ensureVenv so
|
|
168
|
+
* the reinstall/reuse/create branching can be unit-tested without touching
|
|
169
|
+
* the filesystem or spawning pip/venv. Behavior-identical to the inline
|
|
170
|
+
* logic it replaces:
|
|
171
|
+
* - no venv on disk -> "create"
|
|
172
|
+
* - venv exists, stamp matches hash -> "reuse"
|
|
173
|
+
* - venv exists, stamp missing/mismatched -> "update"
|
|
174
|
+
*/
|
|
175
|
+
function decideVenvAction(venvExists, storedStamp, currentHash) {
|
|
176
|
+
if (!venvExists) return "create";
|
|
177
|
+
if (storedStamp === currentHash) return "reuse";
|
|
178
|
+
return "update";
|
|
179
|
+
}
|
|
180
|
+
|
|
87
181
|
/**
|
|
88
182
|
* Ensure a local .venv exists with dependencies installed.
|
|
89
183
|
* Returns the path to the venv Python binary.
|
|
@@ -91,29 +185,31 @@ function findOtherLiveClient(host, port) {
|
|
|
91
185
|
function ensureVenv(systemPython, prefixArgs) {
|
|
92
186
|
const prefix = prefixArgs || [];
|
|
93
187
|
const venvPy = venvPython();
|
|
188
|
+
const venvExists = fs.existsSync(venvPy);
|
|
94
189
|
|
|
95
|
-
|
|
96
|
-
if (
|
|
190
|
+
let stamp = null;
|
|
191
|
+
if (venvExists) {
|
|
97
192
|
try {
|
|
98
|
-
|
|
99
|
-
encoding: "utf-8",
|
|
100
|
-
timeout: 10000,
|
|
101
|
-
stdio: "pipe",
|
|
102
|
-
});
|
|
103
|
-
return venvPy; // venv exists and all deps importable
|
|
193
|
+
stamp = fs.readFileSync(venvStampPath(), "utf-8").trim();
|
|
104
194
|
} catch {
|
|
105
|
-
// venv
|
|
106
|
-
console.error("LivePilot: reinstalling Python dependencies...");
|
|
107
|
-
execFileSync(venvPy, ["-m", "pip", "install", "-q", "-r", REQUIREMENTS], {
|
|
108
|
-
cwd: ROOT,
|
|
109
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
110
|
-
timeout: 120000,
|
|
111
|
-
});
|
|
112
|
-
return venvPy;
|
|
195
|
+
stamp = null; // pre-hash venv (or stamp deleted) — verify by reinstall
|
|
113
196
|
}
|
|
114
197
|
}
|
|
115
198
|
|
|
116
|
-
|
|
199
|
+
const action = decideVenvAction(venvExists, stamp, requirementsHash());
|
|
200
|
+
|
|
201
|
+
if (action === "reuse") {
|
|
202
|
+
return venvPy; // venv exists and was installed from this exact requirements.txt
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (action === "update") {
|
|
206
|
+
console.error("LivePilot: requirements changed — updating Python dependencies...");
|
|
207
|
+
pipInstall(venvPy);
|
|
208
|
+
writeVenvStamp();
|
|
209
|
+
return venvPy;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// action === "create"
|
|
117
213
|
console.error("LivePilot: setting up Python environment (first run)...");
|
|
118
214
|
execFileSync(systemPython, [...prefix, "-m", "venv", VENV_DIR], {
|
|
119
215
|
cwd: ROOT,
|
|
@@ -122,11 +218,8 @@ function ensureVenv(systemPython, prefixArgs) {
|
|
|
122
218
|
});
|
|
123
219
|
|
|
124
220
|
console.error("LivePilot: installing dependencies...");
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
128
|
-
timeout: 120000,
|
|
129
|
-
});
|
|
221
|
+
pipInstall(venvPython());
|
|
222
|
+
writeVenvStamp();
|
|
130
223
|
|
|
131
224
|
return venvPython();
|
|
132
225
|
}
|
|
@@ -262,11 +355,15 @@ async function doctor() {
|
|
|
262
355
|
|
|
263
356
|
// 1. Python
|
|
264
357
|
const pyInfo = findPython();
|
|
265
|
-
if (pyInfo) {
|
|
358
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
266
359
|
console.log(" Python: %s (%s)", pyInfo.version, pyInfo.cmd);
|
|
360
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
361
|
+
console.log(" Python: %s found, but LivePilot needs >= 3.12", pyInfo.version);
|
|
362
|
+
console.log(" Fix: install Python 3.12+ (numpy>=2.5 / scipy>=1.18 require Python 3.12)");
|
|
363
|
+
ok = false;
|
|
267
364
|
} else {
|
|
268
|
-
console.log(" Python: NOT FOUND (need >= 3.
|
|
269
|
-
console.log(" Fix: install Python 3.
|
|
365
|
+
console.log(" Python: NOT FOUND (need >= 3.12)");
|
|
366
|
+
console.log(" Fix: install Python 3.12+ and add to PATH");
|
|
270
367
|
ok = false;
|
|
271
368
|
}
|
|
272
369
|
|
|
@@ -686,10 +783,15 @@ async function setup() {
|
|
|
686
783
|
// 1. Python
|
|
687
784
|
console.log("Step 1/5: Checking Python...");
|
|
688
785
|
const pyInfo = findPython();
|
|
689
|
-
if (pyInfo) {
|
|
786
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
690
787
|
console.log(" ✓ %s", pyInfo.version);
|
|
788
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
789
|
+
console.log(" ✗ %s found, but LivePilot needs Python >= 3.12", pyInfo.version);
|
|
790
|
+
console.log(" (numpy>=2.5 / scipy>=1.18 require Python 3.12 — pip would fail)");
|
|
791
|
+
console.log(" Install: brew install python@3.12 (macOS) or python.org (Windows)");
|
|
792
|
+
ok = false;
|
|
691
793
|
} else {
|
|
692
|
-
console.log(" ✗ Python >= 3.
|
|
794
|
+
console.log(" ✗ Python >= 3.12 not found");
|
|
693
795
|
console.log(" Install: brew install python@3.12 (macOS) or python.org (Windows)");
|
|
694
796
|
ok = false;
|
|
695
797
|
}
|
|
@@ -722,7 +824,7 @@ async function setup() {
|
|
|
722
824
|
// 3. Bootstrap Python venv
|
|
723
825
|
console.log("");
|
|
724
826
|
console.log("Step 3/5: Setting up Python environment...");
|
|
725
|
-
if (pyInfo) {
|
|
827
|
+
if (pyInfo && !pyInfo.tooOld) {
|
|
726
828
|
try {
|
|
727
829
|
ensureVenv(pyInfo.cmd, pyInfo.prefixArgs);
|
|
728
830
|
console.log(" ✓ Virtual environment ready");
|
|
@@ -730,6 +832,8 @@ async function setup() {
|
|
|
730
832
|
console.log(" ✗ Failed: %s", err.message);
|
|
731
833
|
ok = false;
|
|
732
834
|
}
|
|
835
|
+
} else if (pyInfo && pyInfo.tooOld) {
|
|
836
|
+
console.log(" ⊘ Skipped (%s found, need Python >= 3.12)", pyInfo.version);
|
|
733
837
|
} else {
|
|
734
838
|
console.log(" ⊘ Skipped (no Python)");
|
|
735
839
|
}
|
|
@@ -917,12 +1021,18 @@ async function main() {
|
|
|
917
1021
|
|
|
918
1022
|
// Default: start MCP server
|
|
919
1023
|
const pyInfo = findPython();
|
|
920
|
-
if (!pyInfo) {
|
|
921
|
-
|
|
1024
|
+
if (!pyInfo || pyInfo.tooOld) {
|
|
1025
|
+
if (pyInfo && pyInfo.tooOld) {
|
|
1026
|
+
console.error("Error: found %s, but LivePilot requires Python >= 3.12.", pyInfo.version);
|
|
1027
|
+
console.error(" numpy>=2.5.0 and scipy>=1.18.0 require Python >= 3.12,");
|
|
1028
|
+
console.error(" so dependency installation would fail. Install Python 3.12 or newer.");
|
|
1029
|
+
} else {
|
|
1030
|
+
console.error("Error: Python >= 3.12 is required but was not found.");
|
|
1031
|
+
console.error(" Install Python 3.12+ and ensure 'python3' or 'python' is on your PATH.");
|
|
1032
|
+
}
|
|
922
1033
|
console.error("");
|
|
923
|
-
console.error("Install Python 3.9+ and ensure 'python3' or 'python' is on your PATH.");
|
|
924
1034
|
console.error(" macOS: brew install python@3.12");
|
|
925
|
-
console.error(" Ubuntu: sudo apt install python3");
|
|
1035
|
+
console.error(" Ubuntu: sudo apt install python3.12");
|
|
926
1036
|
console.error(" Windows: https://www.python.org/downloads/");
|
|
927
1037
|
process.exit(1);
|
|
928
1038
|
}
|
|
@@ -965,7 +1075,16 @@ async function main() {
|
|
|
965
1075
|
});
|
|
966
1076
|
}
|
|
967
1077
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1078
|
+
// Only auto-run the CLI when this file is executed directly (`node
|
|
1079
|
+
// bin/livepilot.js ...` or `npx livepilot ...`). When `require()`d as a
|
|
1080
|
+
// module — e.g. from tests exercising `decideVenvAction` in isolation —
|
|
1081
|
+
// skip the side-effecting entrypoint. Behavior-identical for real CLI
|
|
1082
|
+
// invocations; this guard only matters when the file is `require()`d.
|
|
1083
|
+
if (require.main === module) {
|
|
1084
|
+
main().catch((err) => {
|
|
1085
|
+
console.error(err);
|
|
1086
|
+
process.exit(1);
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
module.exports = { decideVenvAction, requirementsHash, venvStampPath };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.3",
|
|
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.3",
|
|
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"
|
|
@@ -47,7 +47,7 @@ Agentic production system for Ableton Live 12. 467 tools across 56 domains, thre
|
|
|
47
47
|
- **Parameter ranges are NOT always 0-1.** Auto Filter Frequency is 20-135. Bit Depth is 1-16. Always read `value_string` to see actual units.
|
|
48
48
|
16. **NEVER apply automation recipes without understanding the target parameter's range** — recipes generate 0-1 curves that get auto-scaled for device parameters, but always verify the result
|
|
49
49
|
17. **LivePilot_Analyzer must be LAST on master chain** — always place after ALL effects (EQ, Compressor, Utility, etc.) so it measures the final post-processing output, not the raw signal. When loading effects on master, either load them before the analyzer or move the analyzer to end afterward
|
|
50
|
-
18. **Remote Script reload workflow** — after any edit to `remote_script/LivePilot/*.py`:
|
|
50
|
+
18. **Remote Script reload workflow** — after any edit to `remote_script/LivePilot/*.py`: install, then call `reload_handlers` (MCP tool, domain: diagnostics). From a LOCAL CHECKOUT the install command is `node bin/livepilot.js --install` — `npx livepilot --install` fetches the PUBLISHED npm package and overwrites local edits with stale code. Use `npx livepilot --install` only for the published package (end-user installs). Either way, NOT `node installer/install.js` — that raw file only module-exports the install function and silently no-ops as a script. NEVER instruct the user to toggle the Control Surface in Live Preferences. The tool uses pkgutil + importlib to re-fire `@register` decorators in-place in <1s while the TCP connection stays open. Standard procedure for every handler change — not just releases
|
|
51
51
|
|
|
52
52
|
## Tool Speed Tiers
|
|
53
53
|
|
|
@@ -79,6 +79,38 @@ Report ALL errors to the user immediately. Common failure modes:
|
|
|
79
79
|
- **M4L Analyzer not connected** — if `get_master_spectrum` errors with "Analyzer not detected", call `ensure_analyzer_on_master`. If it returns `install_required`, call `install_m4l_device(source_path="<repo>/m4l_device/LivePilot_Analyzer.amxd")` and retry. If it errors with "UDP bridge not connected", try `reconnect_bridge` first
|
|
80
80
|
- **Another client connected** — Remote Script only accepts one TCP client on port 9878. If you see this error, the MCP server is already connected. Use MCP tools instead of raw TCP
|
|
81
81
|
|
|
82
|
+
## Response Fields You Must Check
|
|
83
|
+
|
|
84
|
+
These fields were added to existing tool responses. They aren't optional
|
|
85
|
+
extras — each one changes what you're allowed to claim to the user.
|
|
86
|
+
|
|
87
|
+
- **`degraded_reason`** (`enter_wonder_mode` / Wonder Mode) — non-empty when
|
|
88
|
+
fewer than 3 of the returned variants are actually executable, or when no
|
|
89
|
+
matching moves were found and variants are cold-start fallbacks. Contract:
|
|
90
|
+
if `degraded_reason` is set, do NOT present the set as "three genuinely
|
|
91
|
+
different options" — disclose the degradation honestly. See
|
|
92
|
+
`livepilot-wonder`.
|
|
93
|
+
- **`unenforced_constraints`** (`apply_creative_constraint_set` / Creative
|
|
94
|
+
Constraints) — lists constraint names that are advisory-only; the engine
|
|
95
|
+
cannot mechanically verify them against compiled steps. Contract: for every
|
|
96
|
+
name in this list, YOU must self-police that constraint manually — the
|
|
97
|
+
tool returning `valid: true` does not mean an advisory constraint wasn't
|
|
98
|
+
violated.
|
|
99
|
+
- **`measured` / `severity_basis`** (`get_masking_report` / Mix Engine) — a
|
|
100
|
+
masking entry's `severity` is either a real per-track spectral-overlap
|
|
101
|
+
measurement (`measured=True`, `severity_basis="spectral_overlap"`) or a
|
|
102
|
+
role-pair heuristic constant (`measured=False`,
|
|
103
|
+
`severity_basis="role_heuristic"`). Contract: treat `measured=False`
|
|
104
|
+
entries as low-confidence priors, not verified problems — solo the tracks
|
|
105
|
+
and read `get_master_spectrum` before taking a mixing action based on them
|
|
106
|
+
(§3 Analysis Before Action). See `livepilot-mix-engine`.
|
|
107
|
+
- **`load_via` / `browse_hint`** (Atlas — `atlas_search`, `atlas_suggest`,
|
|
108
|
+
`atlas_device_info`, etc.) — set when an atlas entry's `uri` is cleared
|
|
109
|
+
because it's a known-broken M4L pack-instrument reference. Contract: when
|
|
110
|
+
`load_via == "preset"`, do NOT call `load_browser_item` with the atlas
|
|
111
|
+
`uri` (it's empty/invalid) — call `search_browser` with `browse_hint`
|
|
112
|
+
instead to resolve a real URI first.
|
|
113
|
+
|
|
82
114
|
## Technique Memory
|
|
83
115
|
|
|
84
116
|
Three modes:
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Atlas Tool Notes
|
|
2
|
+
|
|
3
|
+
Implementation history and rationale for `mcp_server/atlas/tools.py`
|
|
4
|
+
tools. The tool docstrings carry the operational contract (params,
|
|
5
|
+
return shape, behavior); this file carries the bug history and
|
|
6
|
+
step-by-step internals that explain *why* a tool behaves a certain way,
|
|
7
|
+
for when that context is actually needed.
|
|
8
|
+
|
|
9
|
+
## `scan_full_library` — scan cap history
|
|
10
|
+
|
|
11
|
+
`max_per_category` defaults to 25000, matching the remote script's own
|
|
12
|
+
default. The original hardcoded 1000 cap silently truncated large
|
|
13
|
+
categories in browser-tree (alphabetical) order — e.g. `drum_kits`
|
|
14
|
+
stopped at "Crash" (0 kicks, 2 hats), and the `samples` category alone
|
|
15
|
+
has ~22,000 items per the browser tree, so a "1000 samples" count was
|
|
16
|
+
wrong by a factor of 22 (BUG-2026-04-22#12). Raise the cap further for
|
|
17
|
+
even bigger libraries; lower it for fast smoke scans.
|
|
18
|
+
|
|
19
|
+
`truncated_categories` / `stats.category_truncated` are persisted into
|
|
20
|
+
`device_atlas.json` so `AtlasManager` can warn future
|
|
21
|
+
`atlas_search`/`atlas_suggest` calls that touch a truncated category
|
|
22
|
+
without requiring a fresh scan first (P3-47).
|
|
23
|
+
|
|
24
|
+
Scans always write to the user atlas path
|
|
25
|
+
(`~/.livepilot/atlas/device_atlas.json`), never the bundled baseline —
|
|
26
|
+
this keeps personal inventories (packs, user_library, plugins) out of
|
|
27
|
+
the repo and surviving npm updates (v1.22.0). Enrichments are still
|
|
28
|
+
read from the bundled package.
|
|
29
|
+
|
|
30
|
+
## `atlas_describe_chain` — internals
|
|
31
|
+
|
|
32
|
+
Free-text → chain proposal pipeline, mirroring `splice_describe_sound`
|
|
33
|
+
for the device library (`atlas_chain_suggest` is the structured-input
|
|
34
|
+
sibling — role + genre instead of a sentence):
|
|
35
|
+
|
|
36
|
+
1. Parse role hints from the description (bass/lead/pad/keys/
|
|
37
|
+
percussion/drums/vocal/fx keyword buckets)
|
|
38
|
+
2. Parse aesthetic hints — artist names map to genre/character tags
|
|
39
|
+
(cross-reference `artist-vocabularies.md`), plus explicit genre
|
|
40
|
+
names and character words (warm/cold/bright/dark/lush/...)
|
|
41
|
+
3. Search the atlas (factory + user overlay namespaces) with those terms
|
|
42
|
+
4. Propose the top devices per role with brief rationale, then take the
|
|
43
|
+
top suggestion per role as a simple ordered `chain_proposal`
|
|
44
|
+
|
|
45
|
+
Does not autoload anything — the caller reviews/adjusts, then executes
|
|
46
|
+
via `load_browser_item` + effects.
|
|
47
|
+
|
|
48
|
+
## `atlas_techniques_for_device` — index
|
|
49
|
+
|
|
50
|
+
Backed by the reverse-index file `device_techniques_index.json`
|
|
51
|
+
(auto-generated from the knowledge base — regenerate via the
|
|
52
|
+
post-v1.17 reverse-index builder script when adding new techniques;
|
|
53
|
+
rare, since most additions happen through enrichment YAMLs that the
|
|
54
|
+
index reads directly). `kind` values: `signature_technique` (from the
|
|
55
|
+
device's own atlas entry), `sample_technique` (from
|
|
56
|
+
`sample-techniques.md`), `sound_design_principle` (from
|
|
57
|
+
`sound-design-deep.md`).
|
|
58
|
+
|
|
59
|
+
## `atlas_search` — overlay budget split
|
|
60
|
+
|
|
61
|
+
Searches both the bundled factory atlas (5,264 devices / 33 packs) and
|
|
62
|
+
the user-local overlay corpus (`~/.livepilot/atlas-overlays/` — user-
|
|
63
|
+
scanned Max devices, racks, plugin presets, AI-synthesized plugin
|
|
64
|
+
identity YAMLs). When both sources have hits, `limit` splits roughly
|
|
65
|
+
50/50 (factory gets the rounding-up extra slot); a source with zero
|
|
66
|
+
hits doesn't consume budget. M4L pack instruments occasionally carry a
|
|
67
|
+
bogus `query:Synths#` URI in the atlas — `atlas_search` clears it and
|
|
68
|
+
surfaces a `browse_hint` so callers don't hit `INVALID_PARAM` from
|
|
69
|
+
`load_browser_item` (LIVE#3).
|