livepilot 1.26.2 → 1.27.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 +44 -0
- package/README.md +9 -8
- package/livepilot/.Codex-plugin/plugin.json +2 -2
- package/livepilot/.claude-plugin/plugin.json +2 -2
- package/livepilot/skills/livepilot-core/SKILL.md +5 -5
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-space.md +2 -1
- package/livepilot/skills/livepilot-core/references/overview.md +5 -3
- package/livepilot/skills/livepilot-core/references/sound-design.md +5 -2
- package/livepilot/skills/livepilot-creative-director/SKILL.md +36 -0
- package/livepilot/skills/livepilot-creative-director/references/move-family-diversity-rule.md +1 -1
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +15 -9
- package/livepilot/skills/livepilot-release/SKILL.md +7 -5
- 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/runtime/capability_probe.py +1 -1
- package/mcp_server/runtime/capability_state.py +66 -9
- package/mcp_server/runtime/live_version.py +18 -0
- package/mcp_server/runtime/session_kernel.py +7 -0
- package/mcp_server/runtime/tools.py +324 -22
- package/mcp_server/tools/analyzer.py +38 -1
- package/package.json +2 -2
- package/remote_script/LivePilot/__init__.py +1 -1
- package/remote_script/LivePilot/version_detect.py +3 -0
- package/requirements.txt +3 -3
- package/server.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.27.0 — 2026-06-16
|
|
4
|
+
|
|
5
|
+
Probe-first Live 12.4 capability release.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added read-only `probe_link_audio()` and `probe_stem_workflow()` runtime tools. They report observed capability modes and reasons without invoking UI scripting, menu automation, or destructive stem operations.
|
|
10
|
+
- Added `link_audio` and `stem_workflow` capability domains to `get_capability_state()` and `get_session_kernel()`. Both default to `manual_only` unless a real probe supplies routable/callable evidence.
|
|
11
|
+
- Added Live 12.4 version flags for Link Audio, selected-time stem separation, and merge-selected-stems while keeping workflow support probe-gated.
|
|
12
|
+
- Added `operation_profile` to the session kernel with `studio_deep` as the legacy default and profile names for `safe_live`, `arrangement_build`, `sound_design_deep`, and `release_audit`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `replace_simpler_sample()` and `load_sample_to_simpler()` now report `native_attempted`, `bridge_attempted`, and `fallback_reason` so native-vs-bridge behavior is observable during Live 12.4 sample workflows.
|
|
17
|
+
- Creative Director guidance now includes a Producer Decision Center: library hunt before loading, inspect enriched atlas hits, avoid Analog/Poli/Drift filler unless explicitly requested, and require instrument/source-level programming before effects-only polish.
|
|
18
|
+
- Capability-mode docs now describe the new Link Audio and stem workflow probe domains instead of treating them as undocumented future work.
|
|
19
|
+
|
|
20
|
+
### Tests
|
|
21
|
+
|
|
22
|
+
- Added coverage for Live 12.4 version flags, Link/stem capability domains and probe tools, session-kernel operation profiles, sample fallback reporting, and Producer Decision Center contract text.
|
|
23
|
+
|
|
24
|
+
## v1.26.3 — 2026-06-16
|
|
25
|
+
|
|
26
|
+
Truth/knowledge patch for Live 12.4.2, local Codex plugin sync, and runtime capability reporting.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Runtime FluCoMa capability probing now checks the Max/FluCoMa package and live M4L streams instead of a nonexistent Python `flucoma` module, so installed-but-bridge-blocked systems report `flucoma_bridge_unavailable` or `flucoma_no_streams` instead of the misleading `flucoma_not_installed`.
|
|
31
|
+
- Metadata drift checks now cover `AGENTS.md` bridge-command claims and the runtime capability probe's analyzer-tool unavailable message.
|
|
32
|
+
- README compatibility docs now advertise all four Live 12 capability tiers, including the Live 12.4+ Collaborative tier for native Simpler sample replacement.
|
|
33
|
+
- M4L bridge docs and operating contracts now distinguish the M4L `replace_sample` empty-Simpler limitation from the Live 12.4+ native `replace_sample_native` route.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Added `scripts/verify_codex_plugin_sync.py` to verify the local Codex plugin active dir, versioned cache dir, mirrored manifests, `.mcp.json`, payload directories, and Local Plugins marketplace entry.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Refreshed Live 12.4.2 knowledge notes for Link Audio, stem-selection workflows, Erosion, Chorus-Ensemble, Delay LFOs, Max 9.1.4, and `SimplerDevice.replace_sample`, while keeping unprobed Link Audio/stem workflows marked as future LivePilot work.
|
|
42
|
+
|
|
43
|
+
### Tests
|
|
44
|
+
|
|
45
|
+
- Added drift guards for AGENTS bridge-command claims, analyzer-tool capability probe text, README Live tier docs, and Codex plugin sync verification.
|
|
46
|
+
|
|
3
47
|
## v1.26.2 — 2026-05-27
|
|
4
48
|
|
|
5
49
|
Patch release for Claude/Codex plugin instruction correctness and local install reliability.
|
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
|
+
467 tools. 56 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>
|
|
@@ -59,7 +59,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
59
59
|
|
|
60
60
|
## Two Ways to Talk to LivePilot
|
|
61
61
|
|
|
62
|
-
Pick whichever is faster for the idea in your head — both reach the same
|
|
62
|
+
Pick whichever is faster for the idea in your head — both reach the same 467-tool surface.
|
|
63
63
|
|
|
64
64
|
### Route A — Artist / aesthetic shorthand
|
|
65
65
|
|
|
@@ -112,7 +112,7 @@ Most sessions do both. Lead with shorthand to anchor the aesthetic, then refine
|
|
|
112
112
|
│ └─────────────────┼──────────────────┘ │
|
|
113
113
|
│ ▼ │
|
|
114
114
|
│ ┌─────────────────┐ │
|
|
115
|
-
│ │
|
|
115
|
+
│ │ 467 MCP Tools │ │
|
|
116
116
|
│ │ 56 domains │ │
|
|
117
117
|
│ └────────┬────────┘ │
|
|
118
118
|
│ │ │
|
|
@@ -129,7 +129,7 @@ Most sessions do both. Lead with shorthand to anchor the aesthetic, then refine
|
|
|
129
129
|
|
|
130
130
|
### How the pieces connect
|
|
131
131
|
|
|
132
|
-
**Remote Script** (`remote_script/LivePilot/`) — A Python ControlSurface that runs inside Ableton's process. Listens on TCP 9878. All Live Object Model calls execute on Ableton's main thread via `schedule_message`. Detects Ableton version at startup and enables
|
|
132
|
+
**Remote Script** (`remote_script/LivePilot/`) — A Python ControlSurface that runs inside Ableton's process. Listens on TCP 9878. All Live Object Model calls execute on Ableton's main thread via `schedule_message`. Detects Ableton version at startup and enables four capability tiers: Core (12.0+), Enhanced Arrangement (12.1.10+), Full Intelligence (12.3+), Collaborative (12.4+).
|
|
133
133
|
|
|
134
134
|
**MCP Server** (`mcp_server/`) — Python FastMCP server. Validates inputs, routes commands to the Remote Script over TCP, manages the M4L bridge, runs the atlas, sample engine, composer, and all intelligence engines. This is what your AI client connects to.
|
|
135
135
|
|
|
@@ -153,7 +153,7 @@ Most sessions do both. Lead with shorthand to anchor the aesthetic, then refine
|
|
|
153
153
|
|
|
154
154
|
## The Intelligence Layer
|
|
155
155
|
|
|
156
|
-
12 engines sit on top of the
|
|
156
|
+
12 engines sit on top of the 467 tools. They give the AI musical judgment, not just musical execution.
|
|
157
157
|
|
|
158
158
|
### SongBrain — What the Song Is
|
|
159
159
|
|
|
@@ -205,7 +205,7 @@ Every engine follows: **measure before → act → measure after → compare**.
|
|
|
205
205
|
|
|
206
206
|
## Tools
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
467 tools across 56 domains. Highlights below — [full catalog here](docs/manual/tool-catalog.md).
|
|
209
209
|
|
|
210
210
|
<br>
|
|
211
211
|
|
|
@@ -540,7 +540,7 @@ The V2 intelligence layer. These tools analyze, diagnose, plan, evaluate, and le
|
|
|
540
540
|
| Creative Constraints | 5 | constraint activation, reference-inspired variants |
|
|
541
541
|
| Preview Studio | 5 | variant creation, preview rendering, comparison, commit |
|
|
542
542
|
|
|
543
|
-
> **[View all
|
|
543
|
+
> **[View all 467 tools →](docs/manual/tool-catalog.md)**
|
|
544
544
|
|
|
545
545
|
<br>
|
|
546
546
|
|
|
@@ -741,6 +741,7 @@ npx livepilot --version # Show version
|
|
|
741
741
|
- **Core (12.0+):** All session tools, mixing, devices, MIDI, theory, generative, memory
|
|
742
742
|
- **Enhanced Arrangement (12.1.10+):** Native arrangement clips, arrangement automation
|
|
743
743
|
- **Full Intelligence (12.3+):** `insert_device_native`, complete device insertion pipeline
|
|
744
|
+
- **Collaborative (12.4+):** `replace_sample_native` and newer sample-editing routes that bypass the M4L fallback when Live exposes a native LOM path
|
|
744
745
|
|
|
745
746
|
<br>
|
|
746
747
|
|
|
@@ -767,7 +768,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details, code guidelines
|
|
|
767
768
|
|
|
768
769
|
| Document | What's inside |
|
|
769
770
|
|----------|---------------|
|
|
770
|
-
| [Manual](docs/manual/index.md) | Complete reference: architecture, all
|
|
771
|
+
| [Manual](docs/manual/index.md) | Complete reference: architecture, all 467 tools, workflows |
|
|
771
772
|
| [Intelligence Layer](docs/manual/intelligence.md) | How the 12 engines connect — conductor, moves, preview, evaluation |
|
|
772
773
|
| [Device Atlas](docs/manual/device-atlas.md) | 5264 devices indexed — search, suggest, chain building |
|
|
773
774
|
| [Samples & Slicing](docs/manual/samples.md) | 3-source search, fitness critics, slice workflows |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Agentic production system for Ableton Live 12 \u2014
|
|
3
|
+
"version": "1.27.0",
|
|
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"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Agentic production system for Ableton Live 12 \u2014
|
|
3
|
+
"version": "1.27.0",
|
|
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"
|
|
7
7
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: livepilot-core
|
|
3
|
-
description: Core discipline for LivePilot — agentic production system for Ableton Live 12.
|
|
3
|
+
description: Core discipline for LivePilot — agentic production system for Ableton Live 12. 467 tools across 56 domains. This skill should be used whenever working with Ableton Live through MCP tools. Provides golden rules, tool speed tiers, error handling protocol, and pointers to domain and engine skills.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# LivePilot Core — Ableton Live 12
|
|
7
7
|
|
|
8
|
-
Agentic production system for Ableton Live 12.
|
|
8
|
+
Agentic production system for Ableton Live 12. 467 tools across 56 domains, three layers:
|
|
9
9
|
|
|
10
10
|
- **Device Atlas** — 5264 devices indexed (120 enriched with sonic intelligence, 683 drum kits). Consult `atlas_search` or `atlas_suggest` before loading any device. Never guess a device name.
|
|
11
11
|
- **M4L Analyzer** — Real-time audio analysis on the master bus (9-band spectrum sub_low → air, RMS/peak, key detection). Optional — all core tools work without it.
|
|
@@ -52,7 +52,7 @@ Agentic production system for Ableton Live 12. 465 tools across 56 domains, thre
|
|
|
52
52
|
## Tool Speed Tiers
|
|
53
53
|
|
|
54
54
|
### Instant (<1s) — Use freely
|
|
55
|
-
All
|
|
55
|
+
All 467 tools plus M4L perception tools.
|
|
56
56
|
|
|
57
57
|
### Fast (1-5s) — Use freely
|
|
58
58
|
`analyze_loudness` · `analyze_mix` · `analyze_sound_design`
|
|
@@ -142,7 +142,7 @@ Deep production knowledge in `references/`:
|
|
|
142
142
|
|
|
143
143
|
| File | Content |
|
|
144
144
|
|------|---------|
|
|
145
|
-
| `references/overview.md` | All
|
|
145
|
+
| `references/overview.md` | All 467 tools with params and ranges |
|
|
146
146
|
| `references/device-atlas/` | 280+ device corpus with URIs and presets |
|
|
147
147
|
| `references/device-knowledge/` | Per-device parameter + technique knowledge |
|
|
148
148
|
| `references/pack-knowledge.md` | All 44 installed packs scored for aesthetic fit (Tier S / A / B / C), with Top / Use-when guidance |
|
|
@@ -198,7 +198,7 @@ Use when the user wants options, variants, or is stuck ("surprise me", "try some
|
|
|
198
198
|
**Rule of thumb**: if the user asked for a specific fix, Flow A. If they asked "what would you do?" or mentioned feel/vibe without parameters, Flow B.
|
|
199
199
|
|
|
200
200
|
### Semantic Moves
|
|
201
|
-
High-level musical intents that compile to deterministic tool sequences. 7 families (44 moves as of v1.
|
|
201
|
+
High-level musical intents that compile to deterministic tool sequences. 7 families (44 moves as of v1.27.0):
|
|
202
202
|
- **mix** — `tighten_low_end`, `widen_stereo`, `make_punchier`, `darken_without_losing_width`, `reduce_repetition_fatigue`, `make_kick_bass_lock`, `reduce_foreground_competition`
|
|
203
203
|
- **arrangement** — `refresh_repeated_section`, plus structural moves defined alongside mix
|
|
204
204
|
- **transition** — `create_buildup_tension`, `smooth_scene_handoff`, `increase_contrast_before_payoff`, `bridge_sections`, `increase_forward_motion`, `open_chorus`, `create_breakdown`
|
|
@@ -63,7 +63,7 @@ Combines convolution (early reflections from real spaces) with algorithmic (cust
|
|
|
63
63
|
|
|
64
64
|
## Delay
|
|
65
65
|
|
|
66
|
-
Updated in 12.4 with
|
|
66
|
+
Updated in Live 12.4 with a deeper LFO section: rate can be set in Hz, ms, or tempo-synced divisions, with seven waveforms and Morph shaping. The most creative delay in Live.
|
|
67
67
|
|
|
68
68
|
### Key Parameters
|
|
69
69
|
|
|
@@ -77,6 +77,7 @@ Updated in 12.4 with new LFO modes. The most creative delay in Live.
|
|
|
77
77
|
**Mod Freq / Dly < Mod / Filter < Mod:** The modulation section.
|
|
78
78
|
- **Dly < Mod:** Modulates delay time — creates pitch wobble on echoes. At 5-15%, subtle tape-like wow. At 20-40%, obvious pitch warping — dub character. At 50%+, extreme — pitch spirals.
|
|
79
79
|
- **Filter < Mod:** Modulates the filter cutoff with the same LFO — echoes alternately brighten and darken. Combined with Dly < Mod, this creates echoes that are never the same twice.
|
|
80
|
+
- **Morph:** Live 12.4+ waveform shaping. Use small Morph values for stable repeat motion; push Morph when the delay tail should feel handmade, unstable, or visibly animated.
|
|
80
81
|
|
|
81
82
|
**Filter Freq / Width:** Bandpass filter in the feedback loop. This shapes how each echo changes:
|
|
82
83
|
- Freq 500-1000 Hz, Width 4-6: Dark, telephone-like echoes that thin out over time (classic dub)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# LivePilot v1.
|
|
1
|
+
# LivePilot v1.27.0 — Architecture & Tool Reference
|
|
2
2
|
|
|
3
|
-
Agentic production system for Ableton Live 12.
|
|
3
|
+
Agentic production system for Ableton Live 12. 467 tools across 56 domains. Device atlas (5264 devices, 120 enriched, 47 with aesthetic-tagged `signature_techniques`), spectral perception (M4L analyzer with 9-band FFT — sub_low / sub / low / low_mid / mid / high_mid / high / presence / air), technique memory, automation intelligence (16 curve types, 15 recipes), music theory (Krumhansl-Schmuckler, species counterpoint), generative algorithms (Euclidean rhythm, tintinnabuli, phase shift, additive process), neo-Riemannian harmony (PRL transforms, Tonnetz), MIDI file I/O, **LIVE Splice describe-a-sound + variations via captured GraphQL endpoints (v1.17)**, drum-rack pad-by-pad construction, live dead-device detection via meter sampling, role-aware Simpler defaults, session-record arrangement-automation workaround.
|
|
4
4
|
|
|
5
5
|
**Concept surface (v1.17):** `artist-vocabularies.md` and `genre-vocabularies.md` in `references/` provide structured translation from the LLM's training (producers like Villalobos, Hawtin, Basic Channel, Gas, Basinski, Hecker, Aphex, Dilla, Burial, Henke; genres like microhouse, dub_techno, ambient, idm) into LivePilot's device surface. New MCP tools `atlas_describe_chain` (free-text → chain proposal) and `atlas_techniques_for_device` (reverse-lookup: 146 technique cross-references across 58 devices).
|
|
6
6
|
|
|
@@ -12,6 +12,8 @@ Agentic production system for Ableton Live 12. 465 tools across 56 domains. Devi
|
|
|
12
12
|
|
|
13
13
|
**Compose framework rebuild (v1.25.0):** three modes share a shared Applier substrate (bridge handshake retry + monitoring=Auto postflight + back_to_arranger). `compose_fast_apply` — quick loop in session view, curated-.adg-first hunt order, drum-role pitch repair. `compose_full_apply` — full song form (intro/verse/hook/breakdown/outro), per-section MIDI variants, native arrangement clips via `create_native_arrangement_clip`, zombie-track cleanup. `develop_apply` — extends an existing 8-bar seed, introspects tracks by name+content, pulls references from prompt. `KnowledgePack` scaffolding: `event_lexicon` (42 events), `genre_context` (15 genres), `artist_context` (~25 producers). Known gap: `atlas_candidates_per_role` is an empty stub in v1.25.0 — device lookup still falls back to `search_browser` filename matching. Resolved in v1.25 (BUG-FULL-MODE-24).
|
|
14
14
|
|
|
15
|
+
**Live 12.4.2 knowledge refresh (2026-06-16):** Ableton's current stable line is Live 12.4.2 (June 11, 2026). The 12.4 baseline added Link Audio, selected-time stem separation and stem merge, updated Erosion, refined Chorus mode in Chorus-Ensemble, expanded Delay LFO modulation, Max 9.1.4, and `SimplerDevice.replace_sample` in the Live API. LivePilot exploits the native Simpler replacement path on Live 12.4+ and ships read-only `probe_link_audio()` / `probe_stem_workflow()` checks; Link Audio routing and stem write workflows remain unavailable unless a real probe proves a stable non-UI-scripted API path.
|
|
16
|
+
|
|
15
17
|
## Architecture
|
|
16
18
|
|
|
17
19
|
```
|
|
@@ -42,7 +44,7 @@ A flat tool list lets the AI press buttons. LivePilot's three layers give it con
|
|
|
42
44
|
|
|
43
45
|
This turns "set EQ band 3 to -4 dB" into "cut 400 Hz by 4 dB, then read the spectrum to confirm the mud is actually reduced."
|
|
44
46
|
|
|
45
|
-
## The
|
|
47
|
+
## The 467 Tools — What Each One Does
|
|
46
48
|
|
|
47
49
|
### Transport (12) — Playback, tempo, global state, diagnostics
|
|
48
50
|
|
|
@@ -165,6 +165,7 @@ Simple 3-band EQ for quick tonal shaping.
|
|
|
165
165
|
- `Filter` — LP/HP on feedback path
|
|
166
166
|
- `Dry/Wet` — Mix
|
|
167
167
|
- `Ping Pong` — Alternates between L/R
|
|
168
|
+
- **Live 12.4+ LFO** — Rate can be Hz, ms, or tempo-synced; seven LFO shapes plus Morph make Delay a stronger movement source before reaching for Echo.
|
|
168
169
|
- **Common sync values**: 1/4, 1/8, 3/16 (dotted eighth), 1/4T (triplet)
|
|
169
170
|
|
|
170
171
|
**Echo** (combines delay + reverb + modulation)
|
|
@@ -191,8 +192,9 @@ Simple 3-band EQ for quick tonal shaping.
|
|
|
191
192
|
- **Use for**: Guitar-amp style warmth, bass grit
|
|
192
193
|
|
|
193
194
|
**Erosion**
|
|
194
|
-
- Adds digital artifacts: noise,
|
|
195
|
-
- `Amount`, `Frequency`, `Width`
|
|
195
|
+
- Adds digital artifacts: noise/sine modulation, edge, air, bitcrush-like abrasion
|
|
196
|
+
- `Amount`, `Frequency`, `Noise Blend`, `Stereo Width`
|
|
197
|
+
- **Live 12.4+** — refreshed Erosion blends sine/noise continuously, scales stereo modulation precisely, and has lower algorithmic latency. Older Sets load as Erosion Legacy.
|
|
196
198
|
- **Use for**: Lo-fi, bitcrushed textures, digital degradation
|
|
197
199
|
|
|
198
200
|
**Redux**
|
|
@@ -206,6 +208,7 @@ Simple 3-band EQ for quick tonal shaping.
|
|
|
206
208
|
- `Rate 1/2` — LFO speeds
|
|
207
209
|
- `Amount 1/2` — Modulation depths
|
|
208
210
|
- `Feedback` — Flanging intensity
|
|
211
|
+
- `Time` / `Taps` — Live 12.4+ Chorus mode controls for fixed delay-line times and one- vs two-tap chorus textures
|
|
209
212
|
- `Dry/Wet` — Mix
|
|
210
213
|
- **Use for**: Thickening, stereo width, classic chorus
|
|
211
214
|
|
|
@@ -113,6 +113,42 @@ For open-ended quality requests, treat timbre and spectral character as the main
|
|
|
113
113
|
|
|
114
114
|
The `mix` family is dominant only when the user asks for balance, loudness, headroom, masking, stereo translation, send levels, or an explicit mix pass. Otherwise use mix analysis as safety/evidence and keep it out of the main creative slot.
|
|
115
115
|
|
|
116
|
+
## Producer Decision Center
|
|
117
|
+
|
|
118
|
+
Before choosing any instrument, preset, sample, or dominant move, run a
|
|
119
|
+
producer decision center pass. This pass exists to prevent generic AI synth
|
|
120
|
+
choices and effects-only "sound design".
|
|
121
|
+
|
|
122
|
+
**Library hunt order:**
|
|
123
|
+
|
|
124
|
+
1. `search_browser(path="sounds", name_filter="<role or sonic target>")`
|
|
125
|
+
for curated Ableton `.adg` chains.
|
|
126
|
+
2. `atlas_search(query="<sonic description>", category="instruments")`
|
|
127
|
+
for scored character matches.
|
|
128
|
+
3. If `atlas_search` returns `enriched: true`, read the surfaced fields and
|
|
129
|
+
call `atlas_device_info(device_id)` when the device is borderline or the
|
|
130
|
+
plan depends on gotchas, self-contained status, or signature techniques.
|
|
131
|
+
4. `search_browser(path="instruments", name_filter="<specific candidate>")`
|
|
132
|
+
only after the curated/preset search has been exhausted.
|
|
133
|
+
|
|
134
|
+
**Generic-default guardrail:** do not reach for Analog/Poli/Drift as a
|
|
135
|
+
default bass, pad, or lead unless the user explicitly asked for analog
|
|
136
|
+
subtractive synthesis or that exact instrument. Those defaults read as a
|
|
137
|
+
generic AI synth in this project. If one of them is still the right choice,
|
|
138
|
+
state the reason and program the source immediately.
|
|
139
|
+
|
|
140
|
+
**Instrument/source-level first:** real sound design changes what the sound
|
|
141
|
+
is before effects-only polish. For every melodic, harmonic, bass, drum, or
|
|
142
|
+
texture layer, plan at least one instrument/source-level move before adding
|
|
143
|
+
effects: envelopes, LFO routing, filter envelope, oscillator/wavetable/FM
|
|
144
|
+
position, pitch modulation, sample start, slice selection, velocity mapping,
|
|
145
|
+
spread, detune, or sampler playback mode.
|
|
146
|
+
|
|
147
|
+
**Layer precision gate:** before declaring a plan or execution complete,
|
|
148
|
+
each active layer needs a role and an evidence path: timbre/spectrum,
|
|
149
|
+
sequence feel, stereo placement, movement/automation, and parameter
|
|
150
|
+
programming. Low-volume buried layers do not count as solved layers.
|
|
151
|
+
|
|
116
152
|
### Phase 1 — Ground
|
|
117
153
|
|
|
118
154
|
Read in parallel (all are fast). All of these are REQUIRED, not
|
package/livepilot/skills/livepilot-creative-director/references/move-family-diversity-rule.md
CHANGED
|
@@ -44,7 +44,7 @@ Never invent an eighth family at the director level.
|
|
|
44
44
|
|
|
45
45
|
**Discovery:** always call `list_semantic_moves(domain=<family>)` at
|
|
46
46
|
runtime to enumerate — do not hardcode move IDs. Families are stable;
|
|
47
|
-
the move catalog grows. As of v1.26.
|
|
47
|
+
the move catalog grows. As of v1.26.3 the runtime returns 44 moves
|
|
48
48
|
across all 7 domains.
|
|
49
49
|
|
|
50
50
|
**Why the director never invents an eighth `rhythmic` family:** the
|
|
@@ -111,7 +111,9 @@ Call `get_capability_state` at the start of any evaluation session. The response
|
|
|
111
111
|
"memory": {"name": "memory", "available": true, "confidence": 1.0, "mode": "available", "reasons": []},
|
|
112
112
|
"web": {"name": "web", "available": true, "confidence": 0.7, "mode": "available", "reasons": []},
|
|
113
113
|
"research": {"name": "research", "available": true, "confidence": 0.9, "mode": "available", "reasons": []},
|
|
114
|
-
"flucoma": {"name": "flucoma", "available": false, "confidence": 0.
|
|
114
|
+
"flucoma": {"name": "flucoma", "available": false, "confidence": 0.2, "mode": "unavailable", "reasons": ["flucoma_bridge_unavailable"], "device_loaded": true},
|
|
115
|
+
"link_audio": {"name": "link_audio", "available": false, "confidence": 0.2, "mode": "manual_only", "reasons": ["link_audio_unprobed"]},
|
|
116
|
+
"stem_workflow": {"name": "stem_workflow", "available": false, "confidence": 0.2, "mode": "manual_only", "reasons": ["stem_workflow_unprobed"]}
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
}
|
|
@@ -127,11 +129,11 @@ Call `get_capability_state` at the start of any evaluation session. The response
|
|
|
127
129
|
|
|
128
130
|
Every entry in `domains` has the same shape:
|
|
129
131
|
|
|
130
|
-
- `name`: the domain key (`"session_access"`, `"analyzer"`, `"memory"`, `"web"`, `"research"`, `"flucoma"`).
|
|
132
|
+
- `name`: the domain key (`"session_access"`, `"analyzer"`, `"memory"`, `"web"`, `"research"`, `"flucoma"`, `"link_audio"`, `"stem_workflow"`).
|
|
131
133
|
- `available`: boolean — is this capability ready to use right now?
|
|
132
134
|
- `confidence`: 0.0–1.0 — how much to trust the `available` flag (e.g. stale analyzer data lowers confidence).
|
|
133
135
|
- `mode`: short human label specific to the domain (`"healthy"`, `"available"`, `"measured"`, `"stale"`, `"targeted_only"`, `"full"`, `"unavailable"`).
|
|
134
|
-
- `reasons`: list of short machine-readable tokens explaining why the domain is in its current state (`"analyzer_offline"`, `"web_unavailable"`, `"flucoma_not_installed"`, …). Empty when healthy.
|
|
136
|
+
- `reasons`: list of short machine-readable tokens explaining why the domain is in its current state (`"analyzer_offline"`, `"web_unavailable"`, `"flucoma_bridge_unavailable"`, `"flucoma_no_streams"`, `"flucoma_not_installed"`, …). Empty when healthy.
|
|
135
137
|
- `freshness_ms`: optional — milliseconds since the domain last received fresh data (currently only the analyzer domain populates this).
|
|
136
138
|
|
|
137
139
|
### Domain definitions
|
|
@@ -141,7 +143,9 @@ Every entry in `domains` has the same shape:
|
|
|
141
143
|
- **memory** — the local technique-store / taste memory. `available=true` means the persistent stores can be read and written.
|
|
142
144
|
- **web** — server-side outbound HTTP capability. True when the MCP host can reach an arbitrary public URL (probed by a 500 ms HEAD request to `https://api.github.com`). Does NOT imply curated research corpora are installed — see the `research` domain for that.
|
|
143
145
|
- **research** — composite over `session_access`, `memory`, and `web`. `mode="full"` when all three are available; `"targeted_only"` when at least one source is up; `"unavailable"` when nothing is reachable.
|
|
144
|
-
- **flucoma** —
|
|
146
|
+
- **flucoma** — Max/FluCoMa real-time stream readiness. `device_loaded=true` means the FluidCorpusManipulation Max package is installed, or active streams prove a frozen analyzer is working. `available=true` requires at least one FluCoMa stream (`spectral_shape`, `mel_bands`, `chroma`, `onset`, `novelty`, or `loudness`) to have reached the M4L spectral cache. FluCoMa-backed tools (`check_flucoma`, `extract_timbre_fingerprint`, etc.) degrade gracefully when this domain is unavailable.
|
|
147
|
+
- **link_audio** — Live 12.4 Link Audio capability. `probe_link_audio` is read-only and reports `mode="manual_only"` until peer/input/routing evidence proves the surface is readable or routable. Version number alone never makes this domain available.
|
|
148
|
+
- **stem_workflow** — Live 12.4 selected-time stem separation / merge capability. `probe_stem_workflow` is read-only and reports `mode="manual_only"` until a stable callable path is observed. Stem tools must not run on full tracks/clips by default.
|
|
145
149
|
|
|
146
150
|
## Collaborative Mode (Live 12.4+)
|
|
147
151
|
|
|
@@ -169,8 +173,10 @@ Live 12.4 introduces a new capability tier that unlocks native LOM access for sa
|
|
|
169
173
|
**Tool signatures:** unchanged. Callers do not need to detect the tier —
|
|
170
174
|
routing is transparent.
|
|
171
175
|
|
|
172
|
-
**
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
-
|
|
176
|
-
|
|
176
|
+
**Probe-first 12.4 surfaces in v1.27.0:**
|
|
177
|
+
- `probe_link_audio()` reports observed peer/input/routing visibility and
|
|
178
|
+
returns `manual_only`, `readable`, `routable`, or `unavailable`.
|
|
179
|
+
- `probe_stem_workflow()` reports observed callable paths and returns
|
|
180
|
+
`manual_only`, `callable`, or `unavailable`.
|
|
181
|
+
- Write workflows are intentionally absent unless a real Live probe proves
|
|
182
|
+
a stable non-UI-scripted route.
|
|
@@ -28,20 +28,20 @@ Run this checklist EVERY time the user says "update everything", "push", "releas
|
|
|
28
28
|
|
|
29
29
|
## 2. Tool Count (must ALL match)
|
|
30
30
|
|
|
31
|
-
Current: **
|
|
31
|
+
Current: **467 tools across 56 domains**.
|
|
32
32
|
Spectral/analyzer (bridge-only): **38**. The remaining tool surface works without the bridge or degrades gracefully. Backed by 32 bridge commands.
|
|
33
33
|
|
|
34
34
|
Verify: `grep -rc "@mcp.tool" mcp_server/tools/ | grep -v ":0" | awk -F: '{sum+=$2} END{print sum}'`
|
|
35
35
|
|
|
36
36
|
Files that reference tool count:
|
|
37
|
-
- [ ] `README.md` — header ("
|
|
37
|
+
- [ ] `README.md` — header ("467 tools. 56 domains"), bridge section ("38 spectral/analyzer tools require bridge")
|
|
38
38
|
- [ ] `package.json` → `"description"`
|
|
39
39
|
- [ ] `server.json` → `"description"`
|
|
40
40
|
- [ ] `manifest.json` → `"description"`
|
|
41
41
|
- [ ] `livepilot/.Codex-plugin/plugin.json` → `"description"` (primary Codex manifest)
|
|
42
42
|
- [ ] `livepilot/.claude-plugin/plugin.json` → `"description"` (must match Codex plugin)
|
|
43
43
|
- [ ] `.claude-plugin/marketplace.json` → `"description"`
|
|
44
|
-
- [ ] `CLAUDE.md` → "
|
|
44
|
+
- [ ] `CLAUDE.md` → "467 tools across 56 domains"
|
|
45
45
|
- [ ] `CONTRIBUTING.md` → tool count in intro
|
|
46
46
|
- [ ] `livepilot/skills/livepilot-core/SKILL.md` — tool/domain count
|
|
47
47
|
- [ ] `livepilot/skills/livepilot-core/references/overview.md` — tool/domain count
|
|
@@ -85,6 +85,8 @@ Current: **56 domains**: transport, tracks, clips, notes, devices, scenes, mixin
|
|
|
85
85
|
|
|
86
86
|
## 6. Plugin Cache
|
|
87
87
|
|
|
88
|
+
- [ ] `python3 scripts/verify_codex_plugin_sync.py` passes after `npx livepilot --install-codex-plugin`
|
|
89
|
+
- [ ] `python3 scripts/verify_plugin_sync.py` passes after Claude plugin cache/mirror sync
|
|
88
90
|
- [ ] `~/.claude/plugins/cache/dreamrec-LivePilot/livepilot/` has current version directory
|
|
89
91
|
- [ ] Old version directories removed
|
|
90
92
|
- [ ] `~/.claude/plugins/installed_plugins.json` → `livepilot@dreamrec-LivePilot` entry: update `version`, `installPath`, `gitCommitSha`, `lastUpdated`
|
|
@@ -98,7 +100,7 @@ Current: **56 domains**: transport, tracks, clips, notes, devices, scenes, mixin
|
|
|
98
100
|
|
|
99
101
|
- [ ] `README.md` — features match current capabilities, "Coming" section is accurate
|
|
100
102
|
- [ ] `docs/manual/getting-started.md` — install instructions current
|
|
101
|
-
- [ ] `docs/manual/tool-reference.md` — all 56 domains listed, all
|
|
103
|
+
- [ ] `docs/manual/tool-reference.md` — all 56 domains listed, all 467 tools present
|
|
102
104
|
- [ ] `docs/M4L_BRIDGE.md` — architecture accurate, core/bridge counts correct
|
|
103
105
|
|
|
104
106
|
## 9. Derived Artifacts
|
|
@@ -147,5 +149,5 @@ Current: **56 domains**: transport, tracks, clips, notes, devices, scenes, mixin
|
|
|
147
149
|
## Quick Verify Command
|
|
148
150
|
|
|
149
151
|
```bash
|
|
150
|
-
echo "=== Versions ===" && grep -h '"version"' package.json server.json manifest.json livepilot/.Codex-plugin/plugin.json livepilot/.claude-plugin/plugin.json .claude-plugin/marketplace.json | head -8 && grep __version__ mcp_server/__init__.py remote_script/LivePilot/__init__.py && echo "===
|
|
152
|
+
echo "=== Versions ===" && grep -h '"version"' package.json server.json manifest.json livepilot/.Codex-plugin/plugin.json livepilot/.claude-plugin/plugin.json .claude-plugin/marketplace.json | head -8 && grep __version__ mcp_server/__init__.py remote_script/LivePilot/__init__.py && echo "=== Metadata ===" && python3 scripts/sync_metadata.py --check && echo "=== Plugin sync ===" && python3 scripts/verify_codex_plugin_sync.py --quiet && python3 scripts/verify_plugin_sync.py --quiet && echo "=== Tests ===" && python3 -m pytest tests/ -q 2>&1 | tail -1
|
|
151
153
|
```
|
|
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.27.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.27.0"
|
|
@@ -73,7 +73,7 @@ def probe_capabilities(
|
|
|
73
73
|
bridge_ok = bridge is not None and spectral is not None and getattr(spectral, "is_connected", False)
|
|
74
74
|
report["m4l_bridge"] = {
|
|
75
75
|
"status": "ok" if bridge_ok else "unavailable",
|
|
76
|
-
"detail": "UDP 9880 / OSC 9881 active" if bridge_ok else "Not connected —
|
|
76
|
+
"detail": "UDP 9880 / OSC 9881 active" if bridge_ok else "Not connected — 38 analyzer tools unavailable",
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
# 4. Offline perception
|
|
@@ -118,6 +118,12 @@ def build_capability_state(
|
|
|
118
118
|
memory_ok: bool = False,
|
|
119
119
|
web_ok: bool = False,
|
|
120
120
|
flucoma_ok: bool = False,
|
|
121
|
+
flucoma_device_loaded: Optional[bool] = None,
|
|
122
|
+
flucoma_reasons: Optional[list[str]] = None,
|
|
123
|
+
link_audio_mode: str = "manual_only",
|
|
124
|
+
link_audio_reasons: Optional[list[str]] = None,
|
|
125
|
+
stem_workflow_mode: str = "manual_only",
|
|
126
|
+
stem_workflow_reasons: Optional[list[str]] = None,
|
|
121
127
|
) -> CapabilityState:
|
|
122
128
|
"""Build a CapabilityState from simple boolean probes.
|
|
123
129
|
|
|
@@ -200,19 +206,70 @@ def build_capability_state(
|
|
|
200
206
|
)
|
|
201
207
|
|
|
202
208
|
# ── flucoma ──────────────────────────────────────────────────────
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
# Max/FluCoMa real-time streams. Emitted unconditionally so consumers
|
|
210
|
+
# can distinguish "not installed" from "installed but bridge/streams
|
|
211
|
+
# are currently unavailable".
|
|
212
|
+
resolved_flucoma_device_loaded = (
|
|
213
|
+
flucoma_ok if flucoma_device_loaded is None else flucoma_device_loaded
|
|
214
|
+
)
|
|
215
|
+
resolved_flucoma_reasons = list(flucoma_reasons or [])
|
|
216
|
+
if not flucoma_ok and not resolved_flucoma_reasons:
|
|
217
|
+
resolved_flucoma_reasons.append(
|
|
218
|
+
"flucoma_no_streams"
|
|
219
|
+
if resolved_flucoma_device_loaded
|
|
220
|
+
else "flucoma_not_installed"
|
|
221
|
+
)
|
|
209
222
|
domains["flucoma"] = CapabilityDomain(
|
|
210
223
|
name="flucoma",
|
|
211
224
|
available=flucoma_ok,
|
|
212
|
-
confidence=0.9 if flucoma_ok else 0.0,
|
|
225
|
+
confidence=0.9 if flucoma_ok else (0.2 if resolved_flucoma_device_loaded else 0.0),
|
|
213
226
|
mode="available" if flucoma_ok else "unavailable",
|
|
214
|
-
reasons=
|
|
215
|
-
device_loaded=
|
|
227
|
+
reasons=resolved_flucoma_reasons,
|
|
228
|
+
device_loaded=resolved_flucoma_device_loaded,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# ── link_audio ────────────────────────────────────────────────────
|
|
232
|
+
# Live 12.4 exposes Link Audio in the product UX, but LivePilot must
|
|
233
|
+
# not claim automation support from the version number alone. This
|
|
234
|
+
# domain only becomes available when runtime probing observes a stable
|
|
235
|
+
# readable/routable surface.
|
|
236
|
+
link_mode = link_audio_mode if session_ok else "unavailable"
|
|
237
|
+
link_reasons = list(link_audio_reasons or [])
|
|
238
|
+
if not link_reasons:
|
|
239
|
+
if not session_ok:
|
|
240
|
+
link_reasons.append("session_unavailable")
|
|
241
|
+
elif link_mode == "manual_only":
|
|
242
|
+
link_reasons.append("link_audio_unprobed")
|
|
243
|
+
elif link_mode == "unavailable":
|
|
244
|
+
link_reasons.append("link_audio_not_exposed")
|
|
245
|
+
link_available = link_mode in {"readable", "routable"}
|
|
246
|
+
domains["link_audio"] = CapabilityDomain(
|
|
247
|
+
name="link_audio",
|
|
248
|
+
available=link_available,
|
|
249
|
+
confidence=0.8 if link_available else (0.2 if session_ok else 0.0),
|
|
250
|
+
mode=link_mode,
|
|
251
|
+
reasons=link_reasons,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# ── stem_workflow ─────────────────────────────────────────────────
|
|
255
|
+
# Selected-time stem separation / merge are also probe-first. The
|
|
256
|
+
# safe default is manual_only; callable only after concrete evidence.
|
|
257
|
+
stem_mode = stem_workflow_mode if session_ok else "unavailable"
|
|
258
|
+
stem_reasons = list(stem_workflow_reasons or [])
|
|
259
|
+
if not stem_reasons:
|
|
260
|
+
if not session_ok:
|
|
261
|
+
stem_reasons.append("session_unavailable")
|
|
262
|
+
elif stem_mode == "manual_only":
|
|
263
|
+
stem_reasons.append("stem_workflow_unprobed")
|
|
264
|
+
elif stem_mode == "unavailable":
|
|
265
|
+
stem_reasons.append("stem_workflow_not_exposed")
|
|
266
|
+
stem_available = stem_mode == "callable"
|
|
267
|
+
domains["stem_workflow"] = CapabilityDomain(
|
|
268
|
+
name="stem_workflow",
|
|
269
|
+
available=stem_available,
|
|
270
|
+
confidence=0.8 if stem_available else (0.2 if session_ok else 0.0),
|
|
271
|
+
mode=stem_mode,
|
|
272
|
+
reasons=stem_reasons,
|
|
216
273
|
)
|
|
217
274
|
|
|
218
275
|
# ── research (composite) ────────────────────────────────────────
|
|
@@ -72,6 +72,21 @@ class LiveVersionCapabilities:
|
|
|
72
72
|
"""Stem separation via MFL — 12.3+"""
|
|
73
73
|
return self._version_tuple >= (12, 3, 0)
|
|
74
74
|
|
|
75
|
+
@property
|
|
76
|
+
def has_link_audio(self) -> bool:
|
|
77
|
+
"""Live 12.4 Link Audio UX exists; MCP control remains probe-gated."""
|
|
78
|
+
return self._version_tuple >= (12, 4, 0)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def has_stem_time_selection(self) -> bool:
|
|
82
|
+
"""Live 12.4 selected-time stem separation UX exists; probe before use."""
|
|
83
|
+
return self._version_tuple >= (12, 4, 0)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def has_stem_merge_selected(self) -> bool:
|
|
87
|
+
"""Live 12.4 merge-selected-stems UX exists; probe before use."""
|
|
88
|
+
return self._version_tuple >= (12, 4, 0)
|
|
89
|
+
|
|
75
90
|
@property
|
|
76
91
|
def has_replace_sample_native(self) -> bool:
|
|
77
92
|
"""SimplerDevice.replace_sample(path) — 12.4+"""
|
|
@@ -100,5 +115,8 @@ class LiveVersionCapabilities:
|
|
|
100
115
|
"drum_rack_construction": self.has_drum_rack_construction,
|
|
101
116
|
"take_lanes": self.has_take_lanes,
|
|
102
117
|
"stem_separation": self.has_stem_separation,
|
|
118
|
+
"link_audio": self.has_link_audio,
|
|
119
|
+
"stem_time_selection": self.has_stem_time_selection,
|
|
120
|
+
"stem_merge_selected": self.has_stem_merge_selected,
|
|
103
121
|
"replace_sample_native": self.has_replace_sample_native,
|
|
104
122
|
}
|
|
@@ -60,6 +60,11 @@ class SessionKernel:
|
|
|
60
60
|
# may add "sculptor". Empty string = producer picks a default.
|
|
61
61
|
creativity_profile: str = ""
|
|
62
62
|
|
|
63
|
+
# v1.27 operation posture. This is intentionally descriptive at the
|
|
64
|
+
# kernel layer; engines can opt into stricter behavior without forcing
|
|
65
|
+
# every tool to know every profile immediately.
|
|
66
|
+
operation_profile: str = "studio_deep"
|
|
67
|
+
|
|
63
68
|
# Caller-asserted sacred elements. Normally sacred elements come from
|
|
64
69
|
# song_brain; this lets the user or a skill override. Shape matches
|
|
65
70
|
# song_brain.sacred_elements entries: {element_type, description, salience}.
|
|
@@ -94,6 +99,7 @@ def build_session_kernel(
|
|
|
94
99
|
# PR2 — creative controls. All optional; legacy callers unaffected.
|
|
95
100
|
freshness: float = 0.5,
|
|
96
101
|
creativity_profile: str = "",
|
|
102
|
+
operation_profile: str = "studio_deep",
|
|
97
103
|
sacred_elements: Optional[list] = None,
|
|
98
104
|
synth_hints: Optional[dict] = None,
|
|
99
105
|
) -> SessionKernel:
|
|
@@ -137,6 +143,7 @@ def build_session_kernel(
|
|
|
137
143
|
protected_dimensions=protected_dimensions or {},
|
|
138
144
|
freshness=freshness,
|
|
139
145
|
creativity_profile=creativity_profile,
|
|
146
|
+
operation_profile=operation_profile,
|
|
140
147
|
sacred_elements=sacred_elements or [],
|
|
141
148
|
synth_hints=synth_hints or {},
|
|
142
149
|
)
|
|
@@ -7,10 +7,11 @@ Tools:
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import importlib.util
|
|
11
10
|
import logging
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
12
13
|
import urllib.request
|
|
13
|
-
from typing import Optional
|
|
14
|
+
from typing import Any, Optional
|
|
14
15
|
|
|
15
16
|
from fastmcp import Context
|
|
16
17
|
|
|
@@ -21,6 +22,14 @@ from .capability_state import build_capability_state
|
|
|
21
22
|
logger = logging.getLogger(__name__)
|
|
22
23
|
|
|
23
24
|
_memory_store = TechniqueStore()
|
|
25
|
+
_FLUCOMA_STREAM_KEYS = (
|
|
26
|
+
"spectral_shape",
|
|
27
|
+
"mel_bands",
|
|
28
|
+
"chroma",
|
|
29
|
+
"onset",
|
|
30
|
+
"novelty",
|
|
31
|
+
"loudness",
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
# ── Capability probes ──────────────────────────────────────────────────
|
|
@@ -51,21 +60,296 @@ def _probe_web(timeout: float = 0.5) -> bool:
|
|
|
51
60
|
return False
|
|
52
61
|
|
|
53
62
|
|
|
54
|
-
def
|
|
55
|
-
"""
|
|
63
|
+
def _flucoma_package_dirs() -> list[Path]:
|
|
64
|
+
"""Return likely Max package locations for FluCoMa."""
|
|
65
|
+
home = Path.home()
|
|
66
|
+
if os.name == "nt":
|
|
67
|
+
docs = Path(os.environ.get("USERPROFILE", str(home))) / "Documents"
|
|
68
|
+
else:
|
|
69
|
+
docs = home / "Documents"
|
|
70
|
+
return [
|
|
71
|
+
docs / "Max 9" / "Packages" / "FluidCorpusManipulation",
|
|
72
|
+
docs / "Max 8" / "Packages" / "FluidCorpusManipulation",
|
|
73
|
+
]
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
|
|
76
|
+
def _probe_flucoma_package() -> bool:
|
|
77
|
+
"""Check whether the FluCoMa Max package exists on disk.
|
|
78
|
+
|
|
79
|
+
LivePilot's real-time FluCoMa support is Max-for-Live based. There is
|
|
80
|
+
no required Python package named ``flucoma``; probing importability
|
|
81
|
+
caused false ``flucoma_not_installed`` reports on healthy systems.
|
|
61
82
|
"""
|
|
62
83
|
try:
|
|
63
|
-
|
|
84
|
+
for package_dir in _flucoma_package_dirs():
|
|
85
|
+
if not package_dir.exists():
|
|
86
|
+
continue
|
|
87
|
+
if (package_dir / "package-info.json").exists():
|
|
88
|
+
return True
|
|
89
|
+
externals = package_dir / "externals"
|
|
90
|
+
if externals.exists() and any(externals.glob("fluid.*")):
|
|
91
|
+
return True
|
|
92
|
+
return True
|
|
93
|
+
return False
|
|
64
94
|
except Exception as exc: # noqa: BLE001
|
|
65
|
-
logger.debug("
|
|
95
|
+
logger.debug("_probe_flucoma_package failed: %s", exc)
|
|
66
96
|
return False
|
|
67
97
|
|
|
68
98
|
|
|
99
|
+
def _probe_flucoma(spectral=None) -> dict:
|
|
100
|
+
"""Probe the Max/bridge-backed FluCoMa runtime.
|
|
101
|
+
|
|
102
|
+
``available`` means at least one FluCoMa stream has reached the
|
|
103
|
+
spectral cache. ``device_loaded`` means the external package appears
|
|
104
|
+
installed (or streams prove it is working from a frozen analyzer).
|
|
105
|
+
"""
|
|
106
|
+
package_installed = _probe_flucoma_package()
|
|
107
|
+
bridge_connected = bool(
|
|
108
|
+
spectral is not None and getattr(spectral, "is_connected", False)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
streams: dict[str, bool] = {}
|
|
112
|
+
if bridge_connected:
|
|
113
|
+
for key in _FLUCOMA_STREAM_KEYS:
|
|
114
|
+
try:
|
|
115
|
+
streams[key] = spectral.get(key) is not None
|
|
116
|
+
except Exception as exc: # noqa: BLE001
|
|
117
|
+
logger.debug("_probe_flucoma stream %s failed: %s", key, exc)
|
|
118
|
+
streams[key] = False
|
|
119
|
+
|
|
120
|
+
active_streams = sum(1 for present in streams.values() if present)
|
|
121
|
+
available = active_streams > 0
|
|
122
|
+
device_loaded = package_installed or available
|
|
123
|
+
|
|
124
|
+
reasons: list[str] = []
|
|
125
|
+
if not device_loaded:
|
|
126
|
+
reasons.append("flucoma_not_installed")
|
|
127
|
+
if not bridge_connected:
|
|
128
|
+
reasons.append("flucoma_bridge_unavailable")
|
|
129
|
+
elif not available:
|
|
130
|
+
reasons.append("flucoma_no_streams")
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"available": available,
|
|
134
|
+
"device_loaded": device_loaded,
|
|
135
|
+
"bridge_connected": bridge_connected,
|
|
136
|
+
"active_streams": active_streams,
|
|
137
|
+
"streams": streams,
|
|
138
|
+
"reasons": reasons,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _normalize_flucoma_probe(raw) -> dict:
|
|
143
|
+
"""Back-compat shim for tests/extensions that monkeypatch a bool probe."""
|
|
144
|
+
if isinstance(raw, bool):
|
|
145
|
+
return {
|
|
146
|
+
"available": raw,
|
|
147
|
+
"device_loaded": raw,
|
|
148
|
+
"reasons": [] if raw else ["flucoma_not_installed"],
|
|
149
|
+
}
|
|
150
|
+
if isinstance(raw, dict):
|
|
151
|
+
available = bool(raw.get("available", False))
|
|
152
|
+
return {
|
|
153
|
+
**raw,
|
|
154
|
+
"available": available,
|
|
155
|
+
"device_loaded": bool(raw.get("device_loaded", available)),
|
|
156
|
+
"reasons": list(raw.get("reasons", [])),
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
"available": False,
|
|
160
|
+
"device_loaded": False,
|
|
161
|
+
"reasons": ["flucoma_probe_failed"],
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _run_flucoma_probe(spectral=None) -> dict:
|
|
166
|
+
try:
|
|
167
|
+
return _normalize_flucoma_probe(_probe_flucoma(spectral))
|
|
168
|
+
except TypeError:
|
|
169
|
+
# Older tests monkeypatch _probe_flucoma as a no-arg callable.
|
|
170
|
+
return _normalize_flucoma_probe(_probe_flucoma())
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _session_info(ableton) -> dict:
|
|
174
|
+
try:
|
|
175
|
+
info = ableton.send_command("get_session_info")
|
|
176
|
+
return info if isinstance(info, dict) else {}
|
|
177
|
+
except Exception as exc: # noqa: BLE001
|
|
178
|
+
logger.debug("_session_info probe failed: %s", exc)
|
|
179
|
+
return {}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _live_version_from_session(session_info: dict) -> str:
|
|
183
|
+
version = session_info.get("live_version")
|
|
184
|
+
return str(version) if version else "12.0.0"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _normalize_probe_surface(
|
|
188
|
+
raw: Any,
|
|
189
|
+
*,
|
|
190
|
+
default_reason: str,
|
|
191
|
+
default_mode: str = "manual_only",
|
|
192
|
+
) -> dict:
|
|
193
|
+
if isinstance(raw, dict):
|
|
194
|
+
mode = str(raw.get("mode") or default_mode)
|
|
195
|
+
available = bool(raw.get("available", mode in {"readable", "routable", "callable"}))
|
|
196
|
+
reasons = list(raw.get("reasons") or [])
|
|
197
|
+
if not available and not reasons:
|
|
198
|
+
reasons.append(default_reason)
|
|
199
|
+
return {
|
|
200
|
+
"mode": mode,
|
|
201
|
+
"available": available,
|
|
202
|
+
"reasons": reasons,
|
|
203
|
+
"observed": dict(raw.get("observed") or {}),
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
"mode": default_mode,
|
|
207
|
+
"available": False,
|
|
208
|
+
"reasons": [default_reason],
|
|
209
|
+
"observed": {},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _probe_link_audio_surface(ableton, session_info: Optional[dict] = None) -> dict:
|
|
214
|
+
"""Read-only Link Audio surface probe.
|
|
215
|
+
|
|
216
|
+
Current Live 12.4 builds expose Link Audio as product UX, but no stable
|
|
217
|
+
Remote Script/M4L routing contract has been proven. This helper looks for
|
|
218
|
+
explicit evidence if future Remote Scripts surface it; otherwise it reports
|
|
219
|
+
manual_only with a reason instead of claiming support from version alone.
|
|
220
|
+
"""
|
|
221
|
+
info = session_info or _session_info(ableton)
|
|
222
|
+
observed = {
|
|
223
|
+
"peers_visible": bool(info.get("link_audio_peers") or info.get("link_peers")),
|
|
224
|
+
"inputs_visible": bool(info.get("link_audio_inputs") or info.get("audio_inputs")),
|
|
225
|
+
"routing_visible": bool(info.get("link_audio_routing")),
|
|
226
|
+
}
|
|
227
|
+
if observed["peers_visible"] and observed["inputs_visible"] and observed["routing_visible"]:
|
|
228
|
+
return {
|
|
229
|
+
"mode": "routable",
|
|
230
|
+
"available": True,
|
|
231
|
+
"reasons": [],
|
|
232
|
+
"observed": observed,
|
|
233
|
+
}
|
|
234
|
+
if observed["peers_visible"] or observed["inputs_visible"]:
|
|
235
|
+
return {
|
|
236
|
+
"mode": "readable",
|
|
237
|
+
"available": True,
|
|
238
|
+
"reasons": [],
|
|
239
|
+
"observed": observed,
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
"mode": "manual_only",
|
|
243
|
+
"available": False,
|
|
244
|
+
"reasons": ["link_audio_not_exposed"],
|
|
245
|
+
"observed": observed,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _probe_stem_workflow_surface(ableton, session_info: Optional[dict] = None) -> dict:
|
|
250
|
+
"""Read-only selected-time stem workflow probe.
|
|
251
|
+
|
|
252
|
+
This intentionally does not invoke stem separation. It only reports
|
|
253
|
+
callable evidence if a future Remote Script/M4L surface advertises a
|
|
254
|
+
stable command path.
|
|
255
|
+
"""
|
|
256
|
+
info = session_info or _session_info(ableton)
|
|
257
|
+
callable_paths = list(info.get("stem_callable_paths") or [])
|
|
258
|
+
if callable_paths:
|
|
259
|
+
return {
|
|
260
|
+
"mode": "callable",
|
|
261
|
+
"available": True,
|
|
262
|
+
"reasons": [],
|
|
263
|
+
"observed": {"callable_paths": callable_paths},
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
"mode": "manual_only",
|
|
267
|
+
"available": False,
|
|
268
|
+
"reasons": ["stem_command_not_observable"],
|
|
269
|
+
"observed": {"callable_paths": []},
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _probe_live_12_4_domain(
|
|
274
|
+
*,
|
|
275
|
+
ableton,
|
|
276
|
+
session_info: dict,
|
|
277
|
+
feature_available: bool,
|
|
278
|
+
surface_probe,
|
|
279
|
+
default_reason: str,
|
|
280
|
+
) -> dict:
|
|
281
|
+
live_version = _live_version_from_session(session_info)
|
|
282
|
+
if not feature_available:
|
|
283
|
+
return {
|
|
284
|
+
"ok": True,
|
|
285
|
+
"live_version": live_version,
|
|
286
|
+
"mode": "unavailable",
|
|
287
|
+
"available": False,
|
|
288
|
+
"reasons": ["live_version_below_12_4"],
|
|
289
|
+
"observed": {},
|
|
290
|
+
}
|
|
291
|
+
try:
|
|
292
|
+
surface = _normalize_probe_surface(
|
|
293
|
+
surface_probe(ableton, session_info=session_info),
|
|
294
|
+
default_reason=default_reason,
|
|
295
|
+
)
|
|
296
|
+
except Exception as exc: # noqa: BLE001
|
|
297
|
+
logger.debug("Live 12.4 surface probe failed: %s", exc)
|
|
298
|
+
surface = {
|
|
299
|
+
"mode": "manual_only",
|
|
300
|
+
"available": False,
|
|
301
|
+
"reasons": [f"probe_failed: {type(exc).__name__}"],
|
|
302
|
+
"observed": {},
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
"ok": True,
|
|
306
|
+
"live_version": live_version,
|
|
307
|
+
**surface,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _probe_link_audio_domain(ableton, session_info: dict) -> dict:
|
|
312
|
+
from .live_version import LiveVersionCapabilities
|
|
313
|
+
|
|
314
|
+
caps = LiveVersionCapabilities.from_session_info(session_info)
|
|
315
|
+
return _probe_live_12_4_domain(
|
|
316
|
+
ableton=ableton,
|
|
317
|
+
session_info=session_info,
|
|
318
|
+
feature_available=caps.has_link_audio,
|
|
319
|
+
surface_probe=_probe_link_audio_surface,
|
|
320
|
+
default_reason="link_audio_not_exposed",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _probe_stem_workflow_domain(ableton, session_info: dict) -> dict:
|
|
325
|
+
from .live_version import LiveVersionCapabilities
|
|
326
|
+
|
|
327
|
+
caps = LiveVersionCapabilities.from_session_info(session_info)
|
|
328
|
+
return _probe_live_12_4_domain(
|
|
329
|
+
ableton=ableton,
|
|
330
|
+
session_info=session_info,
|
|
331
|
+
feature_available=caps.has_stem_time_selection,
|
|
332
|
+
surface_probe=_probe_stem_workflow_surface,
|
|
333
|
+
default_reason="stem_command_not_observable",
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@mcp.tool()
|
|
338
|
+
def probe_link_audio(ctx: Context) -> dict:
|
|
339
|
+
"""Read-only probe for Live 12.4 Link Audio MCP controllability."""
|
|
340
|
+
ableton = ctx.lifespan_context["ableton"]
|
|
341
|
+
session_info = _session_info(ableton)
|
|
342
|
+
return _probe_link_audio_domain(ableton, session_info)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@mcp.tool()
|
|
346
|
+
def probe_stem_workflow(ctx: Context) -> dict:
|
|
347
|
+
"""Read-only probe for Live 12.4 selected-time stem workflow support."""
|
|
348
|
+
ableton = ctx.lifespan_context["ableton"]
|
|
349
|
+
session_info = _session_info(ableton)
|
|
350
|
+
return _probe_stem_workflow_domain(ableton, session_info)
|
|
351
|
+
|
|
352
|
+
|
|
69
353
|
@mcp.tool()
|
|
70
354
|
def get_capability_state(ctx: Context) -> dict:
|
|
71
355
|
"""Probe the runtime and return a capability state snapshot.
|
|
@@ -77,13 +361,8 @@ def get_capability_state(ctx: Context) -> dict:
|
|
|
77
361
|
spectral = ctx.lifespan_context.get("spectral")
|
|
78
362
|
|
|
79
363
|
# ── Probe session ───────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
result = ableton.send_command("get_session_info")
|
|
83
|
-
session_ok = isinstance(result, dict) and "error" not in result
|
|
84
|
-
except Exception as exc:
|
|
85
|
-
logger.debug("get_capability_state failed: %s", exc)
|
|
86
|
-
session_ok = False
|
|
364
|
+
session_info = _session_info(ableton)
|
|
365
|
+
session_ok = bool(session_info) and "error" not in session_info
|
|
87
366
|
|
|
88
367
|
# ── Probe analyzer (M4L bridge) ─────────────────────────────────
|
|
89
368
|
analyzer_ok = False
|
|
@@ -109,8 +388,12 @@ def get_capability_state(ctx: Context) -> dict:
|
|
|
109
388
|
# a curated research corpus is installed (see ``research`` domain).
|
|
110
389
|
web_ok = _probe_web()
|
|
111
390
|
|
|
112
|
-
# ── FluCoMa —
|
|
113
|
-
|
|
391
|
+
# ── FluCoMa — Max package + live bridge streams ─────────────────
|
|
392
|
+
flucoma_probe = _run_flucoma_probe(spectral)
|
|
393
|
+
|
|
394
|
+
# ── Live 12.4 probe-first surfaces ──────────────────────────────
|
|
395
|
+
link_probe = _probe_link_audio_domain(ableton, session_info)
|
|
396
|
+
stem_probe = _probe_stem_workflow_domain(ableton, session_info)
|
|
114
397
|
|
|
115
398
|
state = build_capability_state(
|
|
116
399
|
session_ok=session_ok,
|
|
@@ -118,7 +401,13 @@ def get_capability_state(ctx: Context) -> dict:
|
|
|
118
401
|
analyzer_fresh=analyzer_fresh,
|
|
119
402
|
memory_ok=memory_ok,
|
|
120
403
|
web_ok=web_ok,
|
|
121
|
-
flucoma_ok=
|
|
404
|
+
flucoma_ok=flucoma_probe["available"],
|
|
405
|
+
flucoma_device_loaded=flucoma_probe["device_loaded"],
|
|
406
|
+
flucoma_reasons=flucoma_probe["reasons"],
|
|
407
|
+
link_audio_mode=link_probe["mode"],
|
|
408
|
+
link_audio_reasons=link_probe["reasons"],
|
|
409
|
+
stem_workflow_mode=stem_probe["mode"],
|
|
410
|
+
stem_workflow_reasons=stem_probe["reasons"],
|
|
122
411
|
)
|
|
123
412
|
|
|
124
413
|
return state.to_dict()
|
|
@@ -134,6 +423,7 @@ def get_session_kernel(
|
|
|
134
423
|
creativity_profile: str = "",
|
|
135
424
|
sacred_elements: Optional[list] = None,
|
|
136
425
|
synth_hints: Optional[dict] = None,
|
|
426
|
+
operation_profile: str = "studio_deep",
|
|
137
427
|
) -> dict:
|
|
138
428
|
"""Build the unified turn snapshot for V2 orchestration.
|
|
139
429
|
|
|
@@ -159,6 +449,9 @@ def get_session_kernel(
|
|
|
159
449
|
synth_hints: focus hints for synthesis_brain; shape is open in PR2
|
|
160
450
|
and firms up in PR9. Typical keys: track_indices, device_paths,
|
|
161
451
|
target_timbre, preferred_devices.
|
|
452
|
+
operation_profile: safety/intent posture for this turn. Known values:
|
|
453
|
+
safe_live, studio_deep, arrangement_build, sound_design_deep,
|
|
454
|
+
release_audit.
|
|
162
455
|
|
|
163
456
|
Returns: SessionKernel dict with kernel_id, session topology, capabilities,
|
|
164
457
|
memory context, routing hints, and (if provided) creative controls.
|
|
@@ -183,7 +476,9 @@ def get_session_kernel(
|
|
|
183
476
|
# does, and propagate through. Without this the kernel's capability view
|
|
184
477
|
# lies to orchestration planners.
|
|
185
478
|
web_ok = _probe_web()
|
|
186
|
-
|
|
479
|
+
flucoma_probe = _run_flucoma_probe(spectral)
|
|
480
|
+
link_probe = _probe_link_audio_domain(ableton, session_info if isinstance(session_info, dict) else {})
|
|
481
|
+
stem_probe = _probe_stem_workflow_domain(ableton, session_info if isinstance(session_info, dict) else {})
|
|
187
482
|
|
|
188
483
|
# v1.17.4: probe memory the same way too. Previously memory_ok=True was
|
|
189
484
|
# hardcoded — if the store raised, the kernel still reported memory
|
|
@@ -201,7 +496,13 @@ def get_session_kernel(
|
|
|
201
496
|
analyzer_fresh=analyzer_fresh,
|
|
202
497
|
memory_ok=memory_ok,
|
|
203
498
|
web_ok=web_ok,
|
|
204
|
-
flucoma_ok=
|
|
499
|
+
flucoma_ok=flucoma_probe["available"],
|
|
500
|
+
flucoma_device_loaded=flucoma_probe["device_loaded"],
|
|
501
|
+
flucoma_reasons=flucoma_probe["reasons"],
|
|
502
|
+
link_audio_mode=link_probe["mode"],
|
|
503
|
+
link_audio_reasons=link_probe["reasons"],
|
|
504
|
+
stem_workflow_mode=stem_probe["mode"],
|
|
505
|
+
stem_workflow_reasons=stem_probe["reasons"],
|
|
205
506
|
)
|
|
206
507
|
|
|
207
508
|
# Optional subcomponents — degrade gracefully, but reach into the SAME
|
|
@@ -286,6 +587,7 @@ def get_session_kernel(
|
|
|
286
587
|
anti_preferences=anti_prefs,
|
|
287
588
|
freshness=freshness,
|
|
288
589
|
creativity_profile=creativity_profile,
|
|
590
|
+
operation_profile=operation_profile,
|
|
289
591
|
sacred_elements=sacred_elements,
|
|
290
592
|
synth_hints=synth_hints,
|
|
291
593
|
)
|
|
@@ -171,6 +171,18 @@ async def _try_native_replace_sample(
|
|
|
171
171
|
return resp
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
def _normalize_native_fallback_reason(skip_reason: Optional[str]) -> Optional[str]:
|
|
175
|
+
if not skip_reason:
|
|
176
|
+
return None
|
|
177
|
+
if skip_reason.startswith("gate_closed:"):
|
|
178
|
+
return "live_version_below_12_4"
|
|
179
|
+
return skip_reason
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _native_dispatch_was_attempted(skip_reason: Optional[str]) -> bool:
|
|
183
|
+
return bool(skip_reason) and not skip_reason.startswith("gate_closed:")
|
|
184
|
+
|
|
185
|
+
|
|
174
186
|
@mcp.tool()
|
|
175
187
|
async def reconnect_bridge(ctx: Context) -> dict:
|
|
176
188
|
"""Attempt to reconnect the M4L UDP bridge (port 9880).
|
|
@@ -627,6 +639,9 @@ async def replace_simpler_sample(
|
|
|
627
639
|
result = dict(native)
|
|
628
640
|
result.update(hygiene)
|
|
629
641
|
result["method"] = "native_12_4" # preserved in case hygiene ever adds its own key
|
|
642
|
+
result["native_attempted"] = True
|
|
643
|
+
result["bridge_attempted"] = False
|
|
644
|
+
result["fallback_reason"] = None
|
|
630
645
|
return result
|
|
631
646
|
|
|
632
647
|
# Pre-12.4 fallback: M4L bridge path (unchanged behavior).
|
|
@@ -636,11 +651,17 @@ async def replace_simpler_sample(
|
|
|
636
651
|
)
|
|
637
652
|
|
|
638
653
|
if "error" in result:
|
|
654
|
+
result["native_attempted"] = _native_dispatch_was_attempted(skip_reason)
|
|
655
|
+
result["bridge_attempted"] = True
|
|
656
|
+
result["fallback_reason"] = _normalize_native_fallback_reason(skip_reason)
|
|
639
657
|
return result
|
|
640
658
|
if not result.get("sample_loaded"):
|
|
641
659
|
return {
|
|
642
660
|
"error": "Sample may not have loaded. Ensure the Simpler already "
|
|
643
|
-
"has a sample loaded — replace_sample silently fails on empty Simplers."
|
|
661
|
+
"has a sample loaded — replace_sample silently fails on empty Simplers.",
|
|
662
|
+
"native_attempted": _native_dispatch_was_attempted(skip_reason),
|
|
663
|
+
"bridge_attempted": True,
|
|
664
|
+
"fallback_reason": _normalize_native_fallback_reason(skip_reason),
|
|
644
665
|
}
|
|
645
666
|
|
|
646
667
|
hygiene = await _simpler_post_load_hygiene(
|
|
@@ -652,6 +673,9 @@ async def replace_simpler_sample(
|
|
|
652
673
|
|
|
653
674
|
result.update(hygiene)
|
|
654
675
|
result["method"] = "bridge_m4l"
|
|
676
|
+
result["native_attempted"] = _native_dispatch_was_attempted(skip_reason)
|
|
677
|
+
result["bridge_attempted"] = True
|
|
678
|
+
result["fallback_reason"] = _normalize_native_fallback_reason(skip_reason)
|
|
655
679
|
if skip_reason:
|
|
656
680
|
result["native_skip_reason"] = skip_reason
|
|
657
681
|
return result
|
|
@@ -692,7 +716,10 @@ async def load_sample_to_simpler(
|
|
|
692
716
|
# Live 12.4+: create an empty Simpler via insert_device, then use the
|
|
693
717
|
# native replace_sample path. Skips the dummy-sample bootstrap entirely.
|
|
694
718
|
caps = _live_caps(ctx)
|
|
719
|
+
native_attempted = False
|
|
720
|
+
fallback_reason = "live_version_below_12_4"
|
|
695
721
|
if caps.has_replace_sample_native:
|
|
722
|
+
fallback_reason = "native_insert_device_unavailable"
|
|
696
723
|
try:
|
|
697
724
|
ins = ableton.send_command("insert_device", {
|
|
698
725
|
"track_index": track_index,
|
|
@@ -701,6 +728,7 @@ async def load_sample_to_simpler(
|
|
|
701
728
|
except Exception:
|
|
702
729
|
ins = None
|
|
703
730
|
if isinstance(ins, dict) and "error" not in ins:
|
|
731
|
+
native_attempted = True
|
|
704
732
|
actual_device_index = ins.get("device_index", device_index)
|
|
705
733
|
native = await _try_native_replace_sample(
|
|
706
734
|
ctx, track_index, actual_device_index, file_path
|
|
@@ -717,7 +745,13 @@ async def load_sample_to_simpler(
|
|
|
717
745
|
result["method"] = "native_12_4"
|
|
718
746
|
result["device_index"] = actual_device_index
|
|
719
747
|
result["track_index"] = track_index
|
|
748
|
+
result["native_attempted"] = True
|
|
749
|
+
result["bridge_attempted"] = False
|
|
750
|
+
result["fallback_reason"] = None
|
|
720
751
|
return result
|
|
752
|
+
fallback_reason = _normalize_native_fallback_reason(
|
|
753
|
+
ctx.lifespan_context.get("_native_replace_skip_reason")
|
|
754
|
+
) or "native_replace_failed"
|
|
721
755
|
# Fall through to the legacy bootstrap path below on any failure.
|
|
722
756
|
|
|
723
757
|
# Step 1: Load a sample from the browser to create Simpler with content
|
|
@@ -774,6 +808,9 @@ async def load_sample_to_simpler(
|
|
|
774
808
|
result["method"] = "bootstrap_and_replace"
|
|
775
809
|
result["device_index"] = actual_device_index # additive — for step-result binding
|
|
776
810
|
result["track_index"] = track_index
|
|
811
|
+
result["native_attempted"] = native_attempted
|
|
812
|
+
result["bridge_attempted"] = True
|
|
813
|
+
result["fallback_reason"] = fallback_reason
|
|
777
814
|
return result
|
|
778
815
|
|
|
779
816
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"mcpName": "io.github.dreamrec/livepilot",
|
|
5
|
-
"description": "Agentic production system for Ableton Live 12 —
|
|
5
|
+
"description": "Agentic production system for Ableton Live 12 — 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, 12 creative intelligence engines",
|
|
6
6
|
"author": "Pilot Studio",
|
|
7
7
|
"license": "BSL-1.1",
|
|
8
8
|
"type": "commonjs",
|
|
@@ -5,7 +5,7 @@ Entry point for the ControlSurface. Ableton calls create_instance(c_instance)
|
|
|
5
5
|
when this script is selected in Preferences > Link, Tempo & MIDI.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "1.
|
|
8
|
+
__version__ = "1.27.0"
|
|
9
9
|
|
|
10
10
|
from _Framework.ControlSurface import ControlSurface
|
|
11
11
|
from . import router
|
|
@@ -25,6 +25,9 @@ FEATURES = {
|
|
|
25
25
|
"drum_chain_in_note": (12, 3, 0),
|
|
26
26
|
"stem_separation": (12, 3, 0),
|
|
27
27
|
"device_ab_compare": (12, 3, 0),
|
|
28
|
+
"link_audio": (12, 4, 0),
|
|
29
|
+
"stem_time_selection": (12, 4, 0),
|
|
30
|
+
"stem_merge_selected": (12, 4, 0),
|
|
28
31
|
"replace_sample_native": (12, 4, 0),
|
|
29
32
|
"groove_pool_api": (11, 0, 0),
|
|
30
33
|
"rack_variations_api": (11, 0, 0),
|
package/requirements.txt
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# LivePilot MCP Server dependencies
|
|
2
2
|
numpy>=2.4.6
|
|
3
|
-
fastmcp>=3.
|
|
3
|
+
fastmcp>=3.4.2,<3.5.0 # pinned upper bound — _get_all_tools() accesses private internals
|
|
4
4
|
midiutil>=1.2.1
|
|
5
5
|
pretty_midi>=0.2.11
|
|
6
6
|
# v1.8 Perception Layer (offline analysis)
|
|
7
7
|
pyloudnorm>=0.2.0
|
|
8
|
-
soundfile>=0.
|
|
8
|
+
soundfile>=0.14.0
|
|
9
9
|
scipy>=1.17.1
|
|
10
10
|
mutagen>=1.47.0
|
|
11
11
|
# v1.10.5 Splice online catalog integration — required, not optional.
|
|
12
12
|
# Without these, SpliceGRPCClient silently disables itself and search_samples
|
|
13
13
|
# falls back to the SQLite sounds.db which only returns locally downloaded
|
|
14
14
|
# samples (see docs/2026-04-14-bugs-discovered.md — P0-2).
|
|
15
|
-
grpcio>=1.
|
|
15
|
+
grpcio>=1.81.1
|
|
16
16
|
protobuf>=7.35.0
|
|
17
17
|
|
|
18
18
|
# Known benign warning during install:
|
package/server.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.dreamrec/livepilot",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "467-tool agentic MCP production system for Ableton Live 12 \u2014 56 domains, 44 semantic moves, device atlas (5264 devices), Splice intelligence (gRPC + GraphQL), 9-band spectral perception auto-loaded, Creative Director skill, technique memory, 12 creative engines",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/dreamrec/LivePilot",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.27.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "livepilot",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.27.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|