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.
Files changed (117) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +10 -7
  3. package/bin/livepilot.js +156 -37
  4. package/livepilot/.Codex-plugin/plugin.json +1 -1
  5. package/livepilot/.claude-plugin/plugin.json +1 -1
  6. package/livepilot/skills/livepilot-core/SKILL.md +33 -1
  7. package/livepilot/skills/livepilot-core/references/atlas-tool-notes.md +69 -0
  8. package/livepilot/skills/livepilot-core/references/overview.md +18 -11
  9. package/livepilot/skills/livepilot-core/references/perception.md +125 -0
  10. package/livepilot/skills/livepilot-devices/references/device-parameter-units.md +66 -0
  11. package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +1 -1
  12. package/livepilot/skills/livepilot-mix-engine/SKILL.md +8 -0
  13. package/livepilot/skills/livepilot-release/SKILL.md +1 -1
  14. package/livepilot/skills/livepilot-sample-engine/references/splice-tools-notes.md +56 -0
  15. package/livepilot/skills/livepilot-wonder/SKILL.md +6 -0
  16. package/m4l_device/LivePilot_Analyzer.amxd +0 -0
  17. package/m4l_device/livepilot_bridge.js +48 -11
  18. package/mcp_server/__init__.py +1 -1
  19. package/mcp_server/atlas/__init__.py +181 -40
  20. package/mcp_server/atlas/overlays.py +46 -3
  21. package/mcp_server/atlas/tools.py +226 -86
  22. package/mcp_server/audit/checks.py +50 -22
  23. package/mcp_server/audit/state.py +10 -2
  24. package/mcp_server/audit/tools.py +27 -6
  25. package/mcp_server/composer/develop/apply.py +7 -7
  26. package/mcp_server/composer/fast/apply.py +29 -23
  27. package/mcp_server/composer/fast/brief_builder.py +6 -2
  28. package/mcp_server/composer/framework/atlas_resolver.py +16 -1
  29. package/mcp_server/composer/full/apply.py +40 -34
  30. package/mcp_server/composer/full/engine.py +66 -32
  31. package/mcp_server/composer/tools.py +16 -9
  32. package/mcp_server/connection.py +69 -10
  33. package/mcp_server/creative_constraints/engine.py +10 -2
  34. package/mcp_server/creative_constraints/tools.py +24 -7
  35. package/mcp_server/curves.py +30 -7
  36. package/mcp_server/device_forge/builder.py +40 -11
  37. package/mcp_server/device_forge/models.py +9 -0
  38. package/mcp_server/device_forge/tools.py +30 -11
  39. package/mcp_server/experiment/engine.py +29 -22
  40. package/mcp_server/experiment/tools.py +15 -5
  41. package/mcp_server/m4l_bridge.py +217 -62
  42. package/mcp_server/memory/taste_graph.py +43 -0
  43. package/mcp_server/memory/technique_store.py +26 -3
  44. package/mcp_server/memory/tools.py +62 -4
  45. package/mcp_server/mix_engine/critics.py +187 -30
  46. package/mcp_server/mix_engine/models.py +21 -1
  47. package/mcp_server/mix_engine/state_builder.py +87 -8
  48. package/mcp_server/mix_engine/tools.py +16 -4
  49. package/mcp_server/performance_engine/tools.py +56 -8
  50. package/mcp_server/persistence/base_store.py +11 -0
  51. package/mcp_server/persistence/project_store.py +132 -8
  52. package/mcp_server/persistence/taste_store.py +90 -7
  53. package/mcp_server/preview_studio/engine.py +40 -10
  54. package/mcp_server/preview_studio/models.py +40 -0
  55. package/mcp_server/preview_studio/tools.py +56 -12
  56. package/mcp_server/project_brain/arrangement_graph.py +4 -0
  57. package/mcp_server/project_brain/models.py +2 -0
  58. package/mcp_server/project_brain/role_graph.py +13 -7
  59. package/mcp_server/reference_engine/gap_analyzer.py +58 -3
  60. package/mcp_server/reference_engine/profile_builder.py +47 -4
  61. package/mcp_server/reference_engine/tools.py +6 -0
  62. package/mcp_server/runtime/execution_router.py +57 -1
  63. package/mcp_server/runtime/mcp_dispatch.py +22 -0
  64. package/mcp_server/runtime/remote_commands.py +9 -4
  65. package/mcp_server/runtime/tools.py +0 -1
  66. package/mcp_server/sample_engine/sources.py +0 -2
  67. package/mcp_server/sample_engine/tools.py +19 -38
  68. package/mcp_server/semantic_moves/mix_compilers.py +276 -51
  69. package/mcp_server/semantic_moves/performance_compilers.py +51 -17
  70. package/mcp_server/semantic_moves/resolvers.py +45 -0
  71. package/mcp_server/semantic_moves/sound_design_compilers.py +14 -6
  72. package/mcp_server/semantic_moves/tools.py +80 -2
  73. package/mcp_server/semantic_moves/transition_compilers.py +26 -9
  74. package/mcp_server/server.py +27 -25
  75. package/mcp_server/session_continuity/tracker.py +51 -3
  76. package/mcp_server/song_brain/builder.py +47 -5
  77. package/mcp_server/song_brain/tools.py +21 -7
  78. package/mcp_server/sound_design/critics.py +1 -0
  79. package/mcp_server/splice_client/client.py +117 -33
  80. package/mcp_server/splice_client/http_bridge.py +15 -3
  81. package/mcp_server/splice_client/quota.py +28 -0
  82. package/mcp_server/stuckness_detector/detector.py +8 -5
  83. package/mcp_server/synthesis_brain/adapters/drift.py +13 -0
  84. package/mcp_server/synthesis_brain/adapters/meld.py +13 -0
  85. package/mcp_server/tools/_analyzer_engine/sample.py +36 -10
  86. package/mcp_server/tools/_perception_engine.py +6 -0
  87. package/mcp_server/tools/agent_os.py +4 -1
  88. package/mcp_server/tools/analyzer.py +198 -209
  89. package/mcp_server/tools/arrangement.py +7 -4
  90. package/mcp_server/tools/automation.py +24 -4
  91. package/mcp_server/tools/browser.py +25 -11
  92. package/mcp_server/tools/clips.py +6 -0
  93. package/mcp_server/tools/composition.py +33 -2
  94. package/mcp_server/tools/devices.py +53 -53
  95. package/mcp_server/tools/generative.py +14 -14
  96. package/mcp_server/tools/harmony.py +7 -7
  97. package/mcp_server/tools/mixing.py +4 -4
  98. package/mcp_server/tools/planner.py +68 -6
  99. package/mcp_server/tools/research.py +20 -2
  100. package/mcp_server/tools/theory.py +10 -10
  101. package/mcp_server/tools/transport.py +7 -2
  102. package/mcp_server/transition_engine/critics.py +13 -1
  103. package/mcp_server/user_corpus/tools.py +30 -1
  104. package/mcp_server/wonder_mode/engine.py +82 -9
  105. package/mcp_server/wonder_mode/session.py +32 -10
  106. package/mcp_server/wonder_mode/tools.py +14 -1
  107. package/package.json +1 -1
  108. package/remote_script/LivePilot/__init__.py +21 -8
  109. package/remote_script/LivePilot/arrangement.py +93 -33
  110. package/remote_script/LivePilot/browser.py +60 -4
  111. package/remote_script/LivePilot/devices.py +132 -62
  112. package/remote_script/LivePilot/mixing.py +31 -5
  113. package/remote_script/LivePilot/server.py +94 -22
  114. package/remote_script/LivePilot/tracks.py +11 -5
  115. package/remote_script/LivePilot/transport.py +11 -0
  116. package/requirements.txt +5 -5
  117. 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.1
41
+ ## What's New in v1.27.3
42
42
 
43
- A maintenance release that fixes 35 issues from a deep audit and restores tools that had silently broken:
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
- - **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.
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.9+ |
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 >= 9) {
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
- // Check if venv already exists and has our deps
96
- if (fs.existsSync(venvPy)) {
190
+ let stamp = null;
191
+ if (venvExists) {
97
192
  try {
98
- execFileSync(venvPy, ["-c", "import fastmcp; import midiutil; import pretty_midi; import numpy; import pyloudnorm; import soundfile; import scipy; import mutagen"], {
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 exists but deps missing — reinstall
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
- // Create venv from scratch
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
- execFileSync(venvPython(), ["-m", "pip", "install", "-q", "-r", REQUIREMENTS], {
126
- cwd: ROOT,
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.9)");
269
- console.log(" Fix: install Python 3.9+ and add to PATH");
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.9 not found");
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
- console.error("Error: Python >= 3.9 is required but was not found.");
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
- main().catch((err) => {
969
- console.error(err);
970
- process.exit(1);
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.1",
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.1",
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`: run `npx livepilot --install` (NOT `node installer/install.js` — that raw file only module-exports the install function and silently no-ops as a script), then call `reload_handlers` (MCP tool, domain: diagnostics). 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
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).