livepilot 1.20.2 → 1.21.0
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 +222 -0
- package/README.md +64 -37
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/livepilot_bridge.js +1 -1
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/affordances/__init__.py +31 -0
- package/mcp_server/affordances/_schema.py +143 -0
- package/mcp_server/affordances/devices/auto-filter.yaml +14 -0
- package/mcp_server/affordances/devices/delay.yaml +18 -0
- package/mcp_server/affordances/devices/reverb.yaml +16 -0
- package/mcp_server/affordances/presets.py +74 -0
- package/mcp_server/experiment/tools.py +113 -0
- package/mcp_server/memory/tools.py +10 -0
- package/mcp_server/runtime/tools.py +7 -0
- package/mcp_server/semantic_moves/device_creation_compilers.py +37 -3
- package/mcp_server/semantic_moves/device_creation_moves.py +7 -7
- package/mcp_server/semantic_moves/device_mutation_compilers.py +66 -12
- package/mcp_server/semantic_moves/performance_compilers.py +157 -0
- package/mcp_server/semantic_moves/performance_moves.py +46 -1
- package/mcp_server/semantic_moves/tools.py +8 -5
- package/mcp_server/song_brain/tools.py +6 -0
- package/mcp_server/stuckness_detector/tools.py +4 -0
- package/mcp_server/tools/_agent_os_engine/taste.py +6 -0
- package/mcp_server/tools/analyzer.py +125 -0
- package/mcp_server/tools/memory.py +7 -0
- package/mcp_server/wonder_mode/tools.py +4 -0
- package/package.json +2 -2
- package/remote_script/LivePilot/__init__.py +1 -1
- package/server.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,227 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.21.0 — Consolidation: experiment ledger + preset library + record-readiness + reader audit (April 24 2026)
|
|
4
|
+
|
|
5
|
+
Consolidation release closing five items from the v1.20 plan §12 non-goals
|
|
6
|
+
that were tractable in a 2-session scope. Adds 1 new semantic move + 1
|
|
7
|
+
major-tool auto-ledger-write + minimal preset-library infrastructure +
|
|
8
|
+
a store-purpose audit that turns the v1.20 director-SKILL fix into a
|
|
9
|
+
codebase-wide invariant, all gated behind a test-first wire-format
|
|
10
|
+
parity extension that caught 1 pre-v1.20 drift bug and closed the
|
|
11
|
+
audit window.
|
|
12
|
+
|
|
13
|
+
Registry: 43 → 44 moves. Test suite: 3043 → 3118 pass (+75 across all
|
|
14
|
+
chunks). Wire-format parity suite: 10 → 44 scenarios. Zero regressions.
|
|
15
|
+
|
|
16
|
+
### New semantic moves (1)
|
|
17
|
+
|
|
18
|
+
- **`configure_record_readiness`** (performance family, low risk, protects
|
|
19
|
+
`signal_integrity=0.7`)
|
|
20
|
+
- seed_args: `{track_index, armed, exclusive?}`
|
|
21
|
+
- Non-exclusive: single step `set_track_arm(track_index, arm=armed)` — note
|
|
22
|
+
the wire-format key is `arm`, not `armed` (the MCP tool accepts `armed`
|
|
23
|
+
as an ergonomic kwarg but renames to `arm` before send_command; the
|
|
24
|
+
remote_command backend bypasses that rename).
|
|
25
|
+
- Exclusive (`exclusive=True` + `armed=True`): N+1 step plan —
|
|
26
|
+
`set_track_arm(arm=False)` for every other regular track, then
|
|
27
|
+
`set_track_arm(target, arm=True)` for the target. Emulates Ableton's
|
|
28
|
+
exclusive-arm mode manually because `song.exclusive_arm` has no
|
|
29
|
+
Python setter in Live 12.4 (pre-existing v1.20.3 Remote Script bug
|
|
30
|
+
surfaced during v1.21 live-test pre-flight — see plan correction #6).
|
|
31
|
+
Requires `session_info.tracks` (automatically built by
|
|
32
|
+
`apply_semantic_move`).
|
|
33
|
+
- Rejects `exclusive=True + armed=False` (contradictory), missing `armed`
|
|
34
|
+
seed_arg, and negative `track_index` (return tracks can't be armed
|
|
35
|
+
per Ableton's handler at `remote_script/LivePilot/tracks.py:261`).
|
|
36
|
+
- Closes the one tech_debt entry seeded during v1.20 live test 6.
|
|
37
|
+
|
|
38
|
+
### Experiment-engine ledger writer
|
|
39
|
+
|
|
40
|
+
- **`commit_experiment`** now writes to `SessionLedger` after a successful
|
|
41
|
+
commit, mirroring v1.20's `apply_semantic_move` pattern (commit `0b3489b`).
|
|
42
|
+
Anti-repetition filters downstream now see experiment commits the same
|
|
43
|
+
way they see direct semantic-move applies.
|
|
44
|
+
- `engine` tag reflects branch SOURCE (not escalation success): `"composer"`
|
|
45
|
+
when `target.seed.source == "composer"`, else `"experiment"`. A composer-
|
|
46
|
+
sourced branch that fell back to scaffold execution is still
|
|
47
|
+
`engine=composer` — the escalation-success detail lives in
|
|
48
|
+
`target.evaluation["composer_escalation"]`.
|
|
49
|
+
- `move_class` via `_infer_move_family(target)`: `seed.family` when set,
|
|
50
|
+
else inspect first compiled_plan step's tool via `_TOOL_TO_FAMILY`
|
|
51
|
+
lookup, else default `"mix"`.
|
|
52
|
+
- `actions` = one entry per ok row in `commit_result["execution_log"]`
|
|
53
|
+
(the router's actual execution record — captures the post-escalation
|
|
54
|
+
plan when composer escalation fired).
|
|
55
|
+
- Response gains `ledger_entry_id` field (same pattern as
|
|
56
|
+
`apply_semantic_move`). Best-effort — a ledger-write exception is logged
|
|
57
|
+
and swallowed so the commit never fails on a bookkeeping path.
|
|
58
|
+
|
|
59
|
+
### Minimal affordance preset library
|
|
60
|
+
|
|
61
|
+
- New path `mcp_server/affordances/` with loader (`presets.py`), schema
|
|
62
|
+
validator (`_schema.py`), and 3 seed YAML files:
|
|
63
|
+
- `reverb.yaml` — preset `dub-cathedral` (Basic Channel-adjacent huge
|
|
64
|
+
space; Decay 0.85, Room 0.95, Dry/Wet 0.40, Predelay 0.45, Diffusion 0.80)
|
|
65
|
+
- `delay.yaml` — preset `ping-pong-dub` (dotted 8th, feedback 0.45,
|
|
66
|
+
HP+LP filtered)
|
|
67
|
+
- `auto-filter.yaml` — preset `slow-sweep` (LP type, bar-long LFO,
|
|
68
|
+
moderate resonance)
|
|
69
|
+
- `configure_device` compiler gained optional `preset` + `device_slug`
|
|
70
|
+
seed_args (additive — v1.20 callers unaffected). Merge: preset resolves
|
|
71
|
+
first, explicit `param_overrides` win on per-key conflict (last-write-wins).
|
|
72
|
+
v1.21 requires explicit `device_slug` when `preset` is used; v1.22
|
|
73
|
+
adds class_name → slug auto-inference.
|
|
74
|
+
- `livepilot-creative-director/references/phase-6-execution.md` §Affordance-
|
|
75
|
+
preset resolution rewritten to document the live import path and the
|
|
76
|
+
three dispatch patterns (preset reference, preset + explicit override,
|
|
77
|
+
fallback to Python resolve).
|
|
78
|
+
|
|
79
|
+
### Wire-format parity retroactive gate
|
|
80
|
+
|
|
81
|
+
- `tests/test_compiler_wire_format_parity.py` extended from 10 to 44
|
|
82
|
+
scenarios (10 v1.20 + 33 pre-v1.20 + 1 new `configure_record_readiness`).
|
|
83
|
+
Gate surfaced **1 pre-v1.20 drift pattern** affecting 7 moves:
|
|
84
|
+
`create_chaos_modulator`, `create_feedback_resonator`,
|
|
85
|
+
`create_wavefolder_effect`, `create_bitcrusher_effect`,
|
|
86
|
+
`create_karplus_string`, `create_stochastic_texture`, `create_fdn_reverb`
|
|
87
|
+
all shipped with a `find_and_load_device` plan_template emitting
|
|
88
|
+
`{"query": "Wonder X"}` but no `track_index`. The `remote_command`
|
|
89
|
+
backend bypasses MCP normalization, so Ableton's handler threw
|
|
90
|
+
`KeyError` on `params["track_index"]` — broken at runtime since
|
|
91
|
+
pre-v1.20, caught now.
|
|
92
|
+
- Fix: `device_creation_compilers.py::_compile_device_creation` threads
|
|
93
|
+
`track_index` from `seed_args` into `find_and_load_device` steps.
|
|
94
|
+
Plan templates updated from `{"query": ...}` to `{"device_name": ...}`
|
|
95
|
+
across all 7 Device Forge moves.
|
|
96
|
+
- Variant-B' consideration (>3 drift bugs → pause v1.21, ship patch):
|
|
97
|
+
all 7 failures were one pattern in one file, fixable in one commit.
|
|
98
|
+
Path B (inline fix as part of Task 1.1.5a) taken — single-pattern
|
|
99
|
+
single-file fixes are exactly what Task 1.1.5a was written for.
|
|
100
|
+
See `docs/plans/v1.21-impl-status.md` §3 for the decision log.
|
|
101
|
+
|
|
102
|
+
### Store-purpose reader audit
|
|
103
|
+
|
|
104
|
+
- Every file in `mcp_server/` that imports `SessionLedger` or calls
|
|
105
|
+
`memory_list` / `get_session_memory` now carries a
|
|
106
|
+
`# store_purpose: <purpose>` comment naming its intent.
|
|
107
|
+
- Allowed purposes (closed set): `writer`, `anti_repetition`,
|
|
108
|
+
`audit_readonly`, `technique_library`, `session_observations`,
|
|
109
|
+
`escape_hatch_log`, `mcp_tool_definition`.
|
|
110
|
+
- **`tests/test_ledger_readers.py`** new (5 test cases): enforces the
|
|
111
|
+
annotation contract at CI, AND guards against any file annotated
|
|
112
|
+
`anti_repetition` also calling `memory_list` — which would be the
|
|
113
|
+
latent v1.20 store-confusion bug (director SKILL originally pointed
|
|
114
|
+
at `memory_list` for recency; `memory_list` reads the persistent
|
|
115
|
+
technique library, not the action ledger).
|
|
116
|
+
- Audit was clean across the entire `mcp_server` tree — no anti-repetition
|
|
117
|
+
caller was found reading the wrong store. The audit is purely
|
|
118
|
+
documentation contracts enforced at CI.
|
|
119
|
+
|
|
120
|
+
### Plan corrections applied during execution (6, logged in impl-status doc)
|
|
121
|
+
|
|
122
|
+
Documented for future release planners in `docs/plans/v1.21-impl-status.md`:
|
|
123
|
+
|
|
124
|
+
1. **Baseline drift** — v1.20.2 and v1.20.3 shipped between plan-write and
|
|
125
|
+
execution; all version-string literals in the plan needed `1.20.1 →
|
|
126
|
+
1.20.3` remapping.
|
|
127
|
+
2. **Wire-format drift in device_creation** — plan_templates emitted
|
|
128
|
+
`{"query": ...}`; handler requires `{"track_index", "device_name"}`.
|
|
129
|
+
3. **`set_track_arm` wire format** — plan §3.1 used `{"armed": ...}`;
|
|
130
|
+
handler reads `params["arm"]`. MCP tool renames `armed → arm` before
|
|
131
|
+
send_command; remote_command backend bypasses the rename.
|
|
132
|
+
4. **`set_exclusive_arm` shape** — plan §3.1 assumed a per-track signature
|
|
133
|
+
`(track_index=...)`; handler is a global mode toggle taking
|
|
134
|
+
`{"enabled": bool}`.
|
|
135
|
+
5. **Return-track arm rejection** — plan's test expected negative
|
|
136
|
+
`track_index` to succeed; handler raises `ValueError: "Cannot arm a
|
|
137
|
+
return track"`. Corrected test pins compile-time rejection.
|
|
138
|
+
6. **`set_exclusive_arm` is broken in Live 12.4 LOM** — live-test pre-flight
|
|
139
|
+
(2026-04-24) surfaced that `song.exclusive_arm` is a property WITHOUT a
|
|
140
|
+
Python setter in Live 12.4's Remote Script API. The v1.20.3 handler's
|
|
141
|
+
direct assignment `song.exclusive_arm = bool(...)` errors with
|
|
142
|
+
"property of 'Song' object has no setter". v1.21's
|
|
143
|
+
`configure_record_readiness` exclusive-mode now emulates the behavior
|
|
144
|
+
manually (disarm-all-others + arm-target loop) — correctness is
|
|
145
|
+
identical, works in any Live version. The broken v1.20.3
|
|
146
|
+
`set_exclusive_arm` handler is LEFT AS-IS for potential future fix
|
|
147
|
+
(when/if Ableton re-exposes the setter, or someone finds a LOM method
|
|
148
|
+
alternative); it's tracked as an independent bug in the Remote Script.
|
|
149
|
+
|
|
150
|
+
### Non-goals (deferred)
|
|
151
|
+
|
|
152
|
+
- **Hard cutover of the escape hatch.** v1.22+ target, conditional on
|
|
153
|
+
zero `tech_debt` log entries accumulating in one month of production.
|
|
154
|
+
- **Standard / Deep preset catalog.** v1.22+. Minimal library from v1.21
|
|
155
|
+
proves the loader; catalog fills in after real-usage signal.
|
|
156
|
+
- **class_name → slug auto-inference** for the preset library. v1.22+.
|
|
157
|
+
- **Pre-v1.20 move compiler refactors** beyond the 1 drift fix. v1.22+.
|
|
158
|
+
- **New move families beyond the canonical 7.** The performance family
|
|
159
|
+
already exists; `configure_record_readiness` joins it.
|
|
160
|
+
- **Taste-graph integration for preset ranking.** v1.22+ once the catalog
|
|
161
|
+
exists.
|
|
162
|
+
|
|
163
|
+
### Scope stats
|
|
164
|
+
|
|
165
|
+
- 7 atomic commits on `v1.21.0-dev` (fix + gate + 4 feature + refactor + release)
|
|
166
|
+
- 28 files changed, +1933 / -63 lines
|
|
167
|
+
- 3 new test files (commit_experiment_ledger, performance_moves,
|
|
168
|
+
affordance_presets, ledger_readers) + 1 doc (impl-status)
|
|
169
|
+
- 1 new package (`mcp_server/affordances/`) + 3 YAML seed files
|
|
170
|
+
- 15 version-string sites bumped + `.amxd` binary patch (2 bytes)
|
|
171
|
+
- 10 files annotated with `# store_purpose:`
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 1.20.3 — Automated analyzer pre-flight (April 24 2026)
|
|
176
|
+
|
|
177
|
+
Micro-release closing one class of operator error that broke the v1.20.1
|
|
178
|
+
five-project live-test campaign: the LLM operator had a clear memory
|
|
179
|
+
instruction to load `LivePilot_Analyzer` on master at the start of a
|
|
180
|
+
fresh Ableton session but missed it in 5 of 5 projects — producing
|
|
181
|
+
basic mixes instead of the intended mix-polish outcomes because every
|
|
182
|
+
analyzer-gated move (`tighten_low_end`, `sculpt_midrange`,
|
|
183
|
+
`balance_stereo_image`, etc.) silently degraded. Fixed forward with a
|
|
184
|
+
new idempotent pre-flight tool + Director skill wiring.
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
|
|
188
|
+
**New tool: `ensure_analyzer_on_master`** (`mcp_server/tools/analyzer.py`).
|
|
189
|
+
Idempotent pre-flight that loads `LivePilot_Analyzer.amxd` on master
|
|
190
|
+
when missing, no-ops when already loaded. Returns one of:
|
|
191
|
+
- `already_loaded` (with `is_last_on_master`, `duplicate_count`)
|
|
192
|
+
- `loaded` (first-time load from Ableton browser)
|
|
193
|
+
- `install_required` (device not in browser — actionable hint points at
|
|
194
|
+
`install_m4l_device`)
|
|
195
|
+
- `failed` (any other error)
|
|
196
|
+
|
|
197
|
+
Post-load report surfaces the CLAUDE.md invariant "LivePilot_Analyzer
|
|
198
|
+
must be LAST on master" via `is_last_on_master: bool` and warns when
|
|
199
|
+
violated. Duplicate-count warning covers the edge case of multiple
|
|
200
|
+
analyzers on the master chain.
|
|
201
|
+
|
|
202
|
+
Safe to call every turn — subsequent calls short-circuit via one
|
|
203
|
+
`get_master_track` read. Tool count: 429 → 430. 6 new contract tests
|
|
204
|
+
covering already-loaded, missing-loads, install-required,
|
|
205
|
+
duplicate-handling, is-last warning, and two-call idempotence.
|
|
206
|
+
|
|
207
|
+
### Changed
|
|
208
|
+
|
|
209
|
+
**Director Phase 1** (`livepilot-creative-director/SKILL.md`). Added
|
|
210
|
+
`ensure_analyzer_on_master` at the top of the "Ground" reads as a
|
|
211
|
+
REQUIRED call, ahead of `get_session_info`. Wording explicitly connects
|
|
212
|
+
the step to the failure it prevents so future agents don't rationalize
|
|
213
|
+
skipping it: "Skipping it is how the v1.20.1 live-test campaign
|
|
214
|
+
produced basic mixes — the analyzer-gated moves degrade silently when
|
|
215
|
+
there's no master spectrum to read."
|
|
216
|
+
|
|
217
|
+
### Notes
|
|
218
|
+
|
|
219
|
+
No breaking changes. Calling code that assumed the analyzer was loaded
|
|
220
|
+
continues to work; the new tool adds an explicit pre-flight path.
|
|
221
|
+
`install_m4l_device` contract unchanged.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
3
225
|
## 1.20.2 — 5 bugs + 1 race condition from the live-test campaign (April 24 2026)
|
|
4
226
|
|
|
5
227
|
Patch release fixing every issue surfaced during the v1.20.1 five-project
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
19
|
An agentic production system for Ableton Live 12.<br>
|
|
20
|
-
|
|
20
|
+
430 tools. 53 domains. Device atlas. Plan-aware Splice integration. Auto-composition. Spectral perception. Technique memory. Drum-rack pad builder. Live dead-device detection.
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
<br>
|
|
@@ -38,12 +38,12 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
38
38
|
| Layer | What it provides |
|
|
39
39
|
|-------|-----------------|
|
|
40
40
|
| **Deterministic Tools** | Direct control: transport, tracks, clips, notes, devices, scenes, mixing, arrangement, browser, automation |
|
|
41
|
-
| **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed
|
|
42
|
-
| **Concept Surface** |
|
|
43
|
-
| **Sample Engine** | Three-source sample intelligence — Ableton's browser, your filesystem, and Splice's catalog (plan-aware: Ableton Live plan uses daily quota, Sounds+/Creator uses credits, free samples bypass both). 6 fitness critics. 29 processing techniques. Collections, presets, preview-URL audition,
|
|
44
|
-
| **Spectral Perception** | Real-time ears via M4L —
|
|
41
|
+
| **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed 7 ways (by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack — 641 pack-indexed). 120 enriched with sonic intelligence (47 with aesthetic-tagged `signature_techniques`). 683 drum kits mapped. Free-text `atlas_describe_chain` ("a granular pad like Tim Hecker") and reverse-lookup `atlas_techniques_for_device` cross-reference 146 techniques across 58 devices |
|
|
42
|
+
| **Concept Surface** | Two reference files let the LLM's training translate into LivePilot: `artist-vocabularies.md` maps ~25 producers (Villalobos, Hawtin, Basic Channel, Gas, Basinski, Hecker, Aphex, Autechre, Dilla, Burial, Henke, Daft Punk, …) to `reach_for` / `avoid` / `key_techniques`; `genre-vocabularies.md` maps 15 genres to tempo / kick / bass / percussion / harmonic / texture / devices. The LLM reads "sound like Gas" and gets a concrete device chain, not guesswork |
|
|
43
|
+
| **Sample Engine** | Three-source sample intelligence — Ableton's browser, your filesystem, and Splice's catalog (plan-aware: Ableton Live plan uses daily quota, Sounds+/Creator uses credits, free samples bypass both). 6 fitness critics. 29 processing techniques. Collections, presets, preview-URL audition, LIVE Describe-a-Sound + Variations via Splice GraphQL |
|
|
44
|
+
| **Spectral Perception** | Real-time ears via M4L — 9-band FFT (with sub_low split at 20-60 Hz for kick fundamentals), RMS/peak metering, Krumhansl-Schmuckler key detection, pitch tracking, FluCoMa mel/chroma/onset. Auto-loaded via `ensure_analyzer_on_master` (v1.20.3) — no more silently-degraded mix moves from forgotten analyzer |
|
|
45
45
|
| **Technique Memory** | Persistent library of production decisions. Save a beat pattern, device chain, or mix template. Recall by mood, genre, or texture across sessions |
|
|
46
|
-
| **Creative Intelligence** |
|
|
46
|
+
| **Creative Intelligence** | 12 engines on top of the tools: SongBrain, Taste Graph, Wonder Mode, Mix/Sound-Design/Transition/Reference/Translation engines, Hook Hunter, Stuckness Detector, Session Continuity, Preview Studio. **43 semantic moves** (v1.20) — musical intents like "tighten the low end" or "make kick and bass lock" that compile into tool sequences with risk levels and target dimensions |
|
|
47
47
|
|
|
48
48
|
<br>
|
|
49
49
|
|
|
@@ -80,7 +80,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
80
80
|
│ └─────────────────┼──────────────────┘ │
|
|
81
81
|
│ ▼ │
|
|
82
82
|
│ ┌─────────────────┐ │
|
|
83
|
-
│ │
|
|
83
|
+
│ │ 430 MCP Tools │ │
|
|
84
84
|
│ │ 53 domains │ │
|
|
85
85
|
│ └────────┬────────┘ │
|
|
86
86
|
│ │ │
|
|
@@ -103,7 +103,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
103
103
|
|
|
104
104
|
**M4L Bridge** (`m4l_device/`) — Optional Max for Live Audio Effect on the master track. Provides deep LOM access through Max's LiveAPI that the ControlSurface API can't reach. UDP 9880 (M4L to server) carries spectral data and LiveAPI responses. OSC 9881 (server to M4L) sends commands. The 32 spectral/analyzer tools strictly require the bridge; device and sample tools that call the bridge also have graceful fallbacks, so core functionality works without it. Backed by 31 bridge commands for hidden parameters, Simpler internals, warp markers, display values, and Simpler warp / Compressor sidechain writes that live on child objects Python can't reach.
|
|
105
105
|
|
|
106
|
-
**Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs, 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains).
|
|
106
|
+
**Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs, 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack (641 devices mapped to their source pack). Reverse-index `device_techniques_index.json` powers `atlas_techniques_for_device` (146 cross-references across 58 devices). The AI never hallucinates a device name or preset — it always resolves against the atlas first.
|
|
107
107
|
|
|
108
108
|
**Sample Engine** (`mcp_server/sample_engine/`) — Searches three sources simultaneously: BrowserSource (Ableton's library), SpliceSource (local Splice catalog via SQLite), FilesystemSource (user directories). Every result passes through a 6-critic fitness battery (key, tempo, spectral, genre, mood, technical). 29 processing techniques (Surgeon precision vs. Alchemist experimentation). Builds complete sample processing plans with warp, slice, and effect recommendations.
|
|
109
109
|
|
|
@@ -121,7 +121,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
121
121
|
|
|
122
122
|
## The Intelligence Layer
|
|
123
123
|
|
|
124
|
-
12 engines sit on top of the
|
|
124
|
+
12 engines sit on top of the 430 tools. They give the AI musical judgment, not just musical execution.
|
|
125
125
|
|
|
126
126
|
### SongBrain — What the Song Is
|
|
127
127
|
|
|
@@ -133,7 +133,7 @@ Learns your production preferences across sessions. Tracks which move families y
|
|
|
133
133
|
|
|
134
134
|
### Semantic Moves — Musical Actions, Not Parameters
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
**43 high-level intents** across 7 families (mix, arrangement, transition, sound_design, performance, device_creation, sample) — "add contrast," "tighten the low end," "make kick and bass lock," "sample vocal ghost," "destroy then rebuild." Each move compiles into a concrete tool sequence with risk level, target dimensions, and protection thresholds. Analyzer-gated moves (`tighten_low_end`, `make_kick_bass_lock`) mark their spectrum pre-reads as optional so the plan continues even when the analyzer isn't available. The AI knows what it's risking with every action.
|
|
137
137
|
|
|
138
138
|
### Wonder Mode — Stuck-Rescue Workflow
|
|
139
139
|
|
|
@@ -173,11 +173,11 @@ Every engine follows: **measure before → act → measure after → compare**.
|
|
|
173
173
|
|
|
174
174
|
## Tools
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
430 tools across 53 domains. Highlights below — [full catalog here](docs/manual/tool-catalog.md).
|
|
177
177
|
|
|
178
178
|
<br>
|
|
179
179
|
|
|
180
|
-
### Core
|
|
180
|
+
### Core Ableton Control — highlights
|
|
181
181
|
|
|
182
182
|
| Domain | # | What it covers |
|
|
183
183
|
|--------|:-:|----------------|
|
|
@@ -200,12 +200,12 @@ Every engine follows: **measure before → act → measure after → compare**.
|
|
|
200
200
|
|
|
201
201
|
<br>
|
|
202
202
|
|
|
203
|
-
### M4L Bridge —
|
|
203
|
+
### M4L Bridge — 38 analyzer tools `[optional]`, 31 bridge commands
|
|
204
204
|
|
|
205
|
-
The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back.
|
|
205
|
+
The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back. The `ensure_analyzer_on_master` pre-flight (v1.20.3) loads the analyzer idempotently on first use — call it once at session start and forget about it.
|
|
206
206
|
|
|
207
207
|
> [!TIP]
|
|
208
|
-
> Most tools work without the analyzer — it adds
|
|
208
|
+
> Most tools work without the analyzer — it adds 38 spectral/analyzer tools (frequency, loudness, perception, Simpler, warp) and closes the feedback loop.
|
|
209
209
|
|
|
210
210
|
```
|
|
211
211
|
SPECTRAL ─────── 9-band frequency decomposition (sub_low → air)
|
|
@@ -228,36 +228,43 @@ WARP ─────────── get / add / move / remove markers
|
|
|
228
228
|
|
|
229
229
|
<br>
|
|
230
230
|
|
|
231
|
-
### Device Atlas —
|
|
231
|
+
### Device Atlas — 10 tools
|
|
232
232
|
|
|
233
233
|
The atlas is an in-memory indexed database of Ableton's entire device library.
|
|
234
234
|
|
|
235
235
|
```
|
|
236
236
|
1305 devices total
|
|
237
237
|
120 enriched with sonic intelligence (mood, genre, texture, chains)
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
47 with aesthetic-tagged signature_techniques
|
|
239
|
+
683 drum kits mapped with note assignments
|
|
240
|
+
7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack
|
|
241
|
+
146 technique cross-references across 58 devices (reverse-index)
|
|
240
242
|
```
|
|
241
243
|
|
|
242
244
|
```
|
|
243
|
-
atlas_search
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
atlas_search Search devices by name, category, or tag
|
|
246
|
+
atlas_device_info Full enriched profile for a single device
|
|
247
|
+
atlas_suggest Suggest devices for a musical intent (e.g., "warm pad")
|
|
248
|
+
atlas_chain_suggest Build a device chain from a genre, artist, or purpose
|
|
249
|
+
atlas_compare Compare two devices side-by-side
|
|
250
|
+
atlas_describe_chain Free-text describe-a-chain ("granular pad like Tim Hecker")
|
|
251
|
+
atlas_techniques_for_device Reverse-lookup: what techniques reference this device?
|
|
252
|
+
atlas_pack_info Inspect a single Ableton pack — devices + enrichment coverage
|
|
253
|
+
scan_full_library Scan what's actually installed on this machine
|
|
254
|
+
reload_atlas Hot-reload the atlas after adding enrichments
|
|
249
255
|
```
|
|
250
256
|
|
|
251
257
|
<br>
|
|
252
258
|
|
|
253
|
-
### Sample Engine —
|
|
259
|
+
### Sample Engine — 23 tools
|
|
254
260
|
|
|
255
|
-
Three-source sample intelligence with critic-driven fitness scoring.
|
|
261
|
+
Three-source sample intelligence with critic-driven fitness scoring, plus deep Splice integration (catalog search, preview, collections, preset downloads).
|
|
256
262
|
|
|
257
263
|
```
|
|
258
264
|
SOURCES ─────────── BrowserSource (Ableton's built-in library)
|
|
259
265
|
SpliceSource (local Splice catalog via SQLite)
|
|
260
266
|
FilesystemSource (user-specified directories)
|
|
267
|
+
Splice LIVE (gRPC + GraphQL for the full catalog)
|
|
261
268
|
|
|
262
269
|
CRITICS ─────────── key fitness · tempo fitness · spectral match
|
|
263
270
|
genre alignment · mood alignment · technical quality
|
|
@@ -265,15 +272,35 @@ CRITICS ─────────── key fitness · tempo fitness · spectr
|
|
|
265
272
|
TECHNIQUES ─────── 29 processing recipes:
|
|
266
273
|
Surgeon (precise, transparent) vs.
|
|
267
274
|
Alchemist (experimental, transformative)
|
|
275
|
+
|
|
276
|
+
PLAN-AWARE ─────── Ableton Live plan 100 samples/day (no credit drain)
|
|
277
|
+
Sounds+/Creator CREDIT_HARD_FLOOR=5 safety gate
|
|
278
|
+
Free samples bypass both gates
|
|
268
279
|
```
|
|
269
280
|
|
|
270
281
|
```
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
Sample analysis & planning
|
|
283
|
+
analyze_sample Build complete SampleProfile (material, key, BPM, spectral)
|
|
284
|
+
search_samples Multi-source search with critic scoring
|
|
285
|
+
evaluate_sample_fit Score a candidate sample against session context
|
|
286
|
+
suggest_sample_technique Recommend processing technique for a sample
|
|
287
|
+
plan_sample_workflow Full processing pipeline: warp + slice + effects
|
|
288
|
+
plan_slice_workflow Slice-specific workflow for breaks / drum loops
|
|
289
|
+
get_sample_opportunities Surface sample-friendly spots in the session
|
|
290
|
+
|
|
291
|
+
Splice LIVE (catalog, collections, presets)
|
|
292
|
+
get_splice_credits Plan + remaining credits + daily quota state
|
|
293
|
+
splice_catalog_hunt Query the full Splice catalog (gRPC)
|
|
294
|
+
splice_download_sample Plan-aware download (credit floor + quota check)
|
|
295
|
+
splice_preview_sample Zero-cost audition via PreviewURL
|
|
296
|
+
splice_describe_sound Natural-language search via Splice GraphQL
|
|
297
|
+
splice_generate_variation Find catalog samples similar to a given UUID
|
|
298
|
+
splice_list_collections Enumerate user's Likes / bass / keys folders
|
|
299
|
+
splice_search_in_collection / add_to_collection / remove_from_collection / create_collection
|
|
300
|
+
splice_list_presets Purchased instrument presets
|
|
301
|
+
splice_preset_info · splice_download_preset
|
|
302
|
+
splice_pack_info Per-pack metadata
|
|
303
|
+
splice_http_diagnose Debug the Splice HTTPS bridge
|
|
277
304
|
```
|
|
278
305
|
|
|
279
306
|
<br>
|
|
@@ -362,7 +389,7 @@ The V2 intelligence layer. These tools analyze, diagnose, plan, evaluate, and le
|
|
|
362
389
|
| Creative Constraints | 5 | constraint activation, reference-inspired variants |
|
|
363
390
|
| Preview Studio | 5 | variant creation, preview rendering, comparison, commit |
|
|
364
391
|
|
|
365
|
-
> **[View all
|
|
392
|
+
> **[View all 430 tools →](docs/manual/tool-catalog.md)**
|
|
366
393
|
|
|
367
394
|
<br>
|
|
368
395
|
|
|
@@ -461,12 +488,12 @@ livepilot --install
|
|
|
461
488
|
</details>
|
|
462
489
|
|
|
463
490
|
<details>
|
|
464
|
-
<summary><strong>3. M4L Analyzer (optional — adds
|
|
491
|
+
<summary><strong>3. M4L Analyzer (optional — adds 38 tools)</strong></summary>
|
|
465
492
|
|
|
466
493
|
Drag `LivePilot_Analyzer.amxd` onto the master track for real-time spectral analysis.
|
|
467
|
-
The `--setup` wizard and Desktop Extension do this automatically.
|
|
494
|
+
The `--setup` wizard and Desktop Extension do this automatically. From v1.20.3, your AI client can also call `ensure_analyzer_on_master` — an idempotent pre-flight that loads the device if missing and no-ops otherwise. The Creative Director skill does this on every session's Phase 1 ground read so you can't forget.
|
|
468
495
|
|
|
469
|
-
> **Important:** The Analyzer must be the LAST device on the master track — after all effects (EQ, Compressor, Utility) so it reads the final output signal.
|
|
496
|
+
> **Important:** The Analyzer must be the LAST device on the master track — after all effects (EQ, Compressor, Utility) so it reads the final output signal. The pre-flight tool reports `is_last_on_master: bool` and warns if the invariant is broken.
|
|
470
497
|
|
|
471
498
|
</details>
|
|
472
499
|
|
|
@@ -589,7 +616,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details, code guidelines
|
|
|
589
616
|
|
|
590
617
|
| Document | What's inside |
|
|
591
618
|
|----------|---------------|
|
|
592
|
-
| [Manual](docs/manual/index.md) | Complete reference: architecture, all
|
|
619
|
+
| [Manual](docs/manual/index.md) | Complete reference: architecture, all 430 tools, workflows |
|
|
593
620
|
| [Intelligence Layer](docs/manual/intelligence.md) | How the 12 engines connect — conductor, moves, preview, evaluation |
|
|
594
621
|
| [Device Atlas](docs/manual/device-atlas.md) | 1305 devices indexed — search, suggest, chain building |
|
|
595
622
|
| [Samples & Slicing](docs/manual/samples.md) | 3-source search, fitness critics, slice workflows |
|
|
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.
|
|
37
|
+
var VERSION = "1.21.0";
|
|
38
38
|
|
|
39
39
|
// ── State ──────────────────────────────────────────────────────────────────
|
|
40
40
|
|
package/mcp_server/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.21.0"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Affordance-preset library — named presets per Ableton device class.
|
|
2
|
+
|
|
3
|
+
v1.21 (minimal): infrastructure + 3 seed presets proving the resolver
|
|
4
|
+
pattern:
|
|
5
|
+
- reverb.yaml → preset: dub-cathedral
|
|
6
|
+
- delay.yaml → preset: ping-pong-dub
|
|
7
|
+
- auto-filter.yaml → preset: slow-sweep
|
|
8
|
+
|
|
9
|
+
v1.22 (planned): full catalog coverage, class_name → slug inference,
|
|
10
|
+
taste-graph integration for preset ranking.
|
|
11
|
+
|
|
12
|
+
Public API (re-exported for ``from mcp_server.affordances import ...``):
|
|
13
|
+
resolve_preset(device_slug, preset_name) -> dict[str, number|bool] | None
|
|
14
|
+
list_devices() -> list[str]
|
|
15
|
+
list_presets(device_slug) -> list[str]
|
|
16
|
+
get_preset_metadata(device_slug, preset_name) -> dict | None
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .presets import (
|
|
20
|
+
resolve_preset,
|
|
21
|
+
list_devices,
|
|
22
|
+
list_presets,
|
|
23
|
+
get_preset_metadata,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"resolve_preset",
|
|
28
|
+
"list_devices",
|
|
29
|
+
"list_presets",
|
|
30
|
+
"get_preset_metadata",
|
|
31
|
+
]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Schema validator for affordance-device preset YAML files.
|
|
2
|
+
|
|
3
|
+
Enforces at test-time that every seed preset file is well-formed. The
|
|
4
|
+
runtime loader (``presets.py``) is tolerant of missing optional fields
|
|
5
|
+
so the product never crashes on a bad preset; this validator is the
|
|
6
|
+
pre-ship gate run by ``tests/test_affordance_presets.py``.
|
|
7
|
+
|
|
8
|
+
Schema:
|
|
9
|
+
device_slug: str # REQUIRED — must match filename stem
|
|
10
|
+
device_class_name: str # REQUIRED — Ableton's class_name
|
|
11
|
+
presets: # REQUIRED — dict[name → PresetRecord]
|
|
12
|
+
<name>:
|
|
13
|
+
description: str # REQUIRED — human-readable
|
|
14
|
+
param_overrides: dict # REQUIRED — {name: number|bool|int}, ≥1 entry
|
|
15
|
+
risk_notes: str # optional
|
|
16
|
+
suggested_pairings: list[str] # optional
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_REQUIRED_TOPLEVEL = {"device_slug", "device_class_name", "presets"}
|
|
28
|
+
_REQUIRED_PER_PRESET = {"description", "param_overrides"}
|
|
29
|
+
_OPTIONAL_PER_PRESET = {"risk_notes", "suggested_pairings"}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def validate_preset_file(path: Path) -> list[str]:
|
|
33
|
+
"""Return a list of validation errors. Empty list = valid.
|
|
34
|
+
|
|
35
|
+
Errors are human-readable one-liners suitable for test failure output.
|
|
36
|
+
"""
|
|
37
|
+
errors: list[str] = []
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
41
|
+
except yaml.YAMLError as exc:
|
|
42
|
+
return [f"{path.name}: YAML parse error — {exc}"]
|
|
43
|
+
|
|
44
|
+
if not isinstance(data, dict):
|
|
45
|
+
return [
|
|
46
|
+
f"{path.name}: top-level must be a mapping, "
|
|
47
|
+
f"got {type(data).__name__}"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
missing = _REQUIRED_TOPLEVEL - set(data.keys())
|
|
51
|
+
if missing:
|
|
52
|
+
errors.append(
|
|
53
|
+
f"{path.name}: missing required top-level fields {sorted(missing)}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
slug = data.get("device_slug")
|
|
57
|
+
if slug is not None and not isinstance(slug, str):
|
|
58
|
+
errors.append(f"{path.name}: device_slug must be a string")
|
|
59
|
+
elif slug and slug != path.stem:
|
|
60
|
+
errors.append(
|
|
61
|
+
f"{path.name}: device_slug {slug!r} must match "
|
|
62
|
+
f"filename stem {path.stem!r}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
class_name = data.get("device_class_name")
|
|
66
|
+
if class_name is not None and not isinstance(class_name, str):
|
|
67
|
+
errors.append(f"{path.name}: device_class_name must be a string")
|
|
68
|
+
|
|
69
|
+
presets = data.get("presets")
|
|
70
|
+
if presets is not None:
|
|
71
|
+
if not isinstance(presets, dict):
|
|
72
|
+
errors.append(f"{path.name}: presets must be a mapping")
|
|
73
|
+
elif not presets:
|
|
74
|
+
errors.append(f"{path.name}: presets dict is empty")
|
|
75
|
+
else:
|
|
76
|
+
for preset_name, preset in presets.items():
|
|
77
|
+
errors.extend(_validate_preset(path.name, preset_name, preset))
|
|
78
|
+
|
|
79
|
+
return errors
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _validate_preset(filename: str, name: str, preset: Any) -> list[str]:
|
|
83
|
+
errors: list[str] = []
|
|
84
|
+
if not isinstance(preset, dict):
|
|
85
|
+
return [f"{filename}: preset {name!r} must be a mapping"]
|
|
86
|
+
|
|
87
|
+
missing = _REQUIRED_PER_PRESET - set(preset.keys())
|
|
88
|
+
if missing:
|
|
89
|
+
errors.append(
|
|
90
|
+
f"{filename}: preset {name!r} missing required fields {sorted(missing)}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
description = preset.get("description")
|
|
94
|
+
if description is not None and not isinstance(description, str):
|
|
95
|
+
errors.append(f"{filename}: preset {name!r} description must be a string")
|
|
96
|
+
|
|
97
|
+
overrides = preset.get("param_overrides")
|
|
98
|
+
if overrides is not None:
|
|
99
|
+
if not isinstance(overrides, dict):
|
|
100
|
+
errors.append(
|
|
101
|
+
f"{filename}: preset {name!r} param_overrides must be a dict"
|
|
102
|
+
)
|
|
103
|
+
elif not overrides:
|
|
104
|
+
errors.append(
|
|
105
|
+
f"{filename}: preset {name!r} param_overrides is empty"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
for k, v in overrides.items():
|
|
109
|
+
if not isinstance(k, str):
|
|
110
|
+
errors.append(
|
|
111
|
+
f"{filename}: preset {name!r} param_overrides key "
|
|
112
|
+
f"{k!r} must be a string"
|
|
113
|
+
)
|
|
114
|
+
if not isinstance(v, (int, float, bool)):
|
|
115
|
+
errors.append(
|
|
116
|
+
f"{filename}: preset {name!r} param_overrides value for "
|
|
117
|
+
f"{k!r} must be number|bool, got {type(v).__name__}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
pairings = preset.get("suggested_pairings")
|
|
121
|
+
if pairings is not None:
|
|
122
|
+
if not isinstance(pairings, list):
|
|
123
|
+
errors.append(
|
|
124
|
+
f"{filename}: preset {name!r} suggested_pairings must be a list"
|
|
125
|
+
)
|
|
126
|
+
elif not all(isinstance(p, str) for p in pairings):
|
|
127
|
+
errors.append(
|
|
128
|
+
f"{filename}: preset {name!r} suggested_pairings entries "
|
|
129
|
+
f"must be strings"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
risk_notes = preset.get("risk_notes")
|
|
133
|
+
if risk_notes is not None and not isinstance(risk_notes, str):
|
|
134
|
+
errors.append(f"{filename}: preset {name!r} risk_notes must be a string")
|
|
135
|
+
|
|
136
|
+
extras = set(preset.keys()) - _REQUIRED_PER_PRESET - _OPTIONAL_PER_PRESET
|
|
137
|
+
if extras:
|
|
138
|
+
errors.append(
|
|
139
|
+
f"{filename}: preset {name!r} has unknown fields {sorted(extras)} "
|
|
140
|
+
f"(allowed: {sorted(_REQUIRED_PER_PRESET | _OPTIONAL_PER_PRESET)})"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return errors
|