livepilot 1.23.3 → 1.23.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +106 -8
  3. package/m4l_device/LivePilot_Analyzer.amxd +0 -0
  4. package/m4l_device/livepilot_bridge.js +1 -1
  5. package/mcp_server/__init__.py +1 -1
  6. package/mcp_server/atlas/cross_pack_chain.py +658 -0
  7. package/mcp_server/atlas/demo_story.py +700 -0
  8. package/mcp_server/atlas/extract_chain.py +786 -0
  9. package/mcp_server/atlas/macro_fingerprint.py +554 -0
  10. package/mcp_server/atlas/overlays.py +95 -3
  11. package/mcp_server/atlas/pack_aware_compose.py +1255 -0
  12. package/mcp_server/atlas/preset_resolver.py +238 -0
  13. package/mcp_server/atlas/tools.py +1001 -31
  14. package/mcp_server/atlas/transplant.py +1177 -0
  15. package/mcp_server/mix_engine/state_builder.py +44 -1
  16. package/mcp_server/runtime/capability_state.py +34 -3
  17. package/mcp_server/server.py +45 -24
  18. package/mcp_server/tools/agent_os.py +33 -9
  19. package/mcp_server/tools/analyzer.py +38 -7
  20. package/mcp_server/tools/browser.py +20 -1
  21. package/mcp_server/tools/devices.py +78 -11
  22. package/mcp_server/tools/perception.py +5 -1
  23. package/mcp_server/tools/tracks.py +39 -2
  24. package/mcp_server/user_corpus/__init__.py +48 -0
  25. package/mcp_server/user_corpus/manifest.py +142 -0
  26. package/mcp_server/user_corpus/plugin_engine/__init__.py +39 -0
  27. package/mcp_server/user_corpus/plugin_engine/detector.py +579 -0
  28. package/mcp_server/user_corpus/plugin_engine/manual.py +347 -0
  29. package/mcp_server/user_corpus/plugin_engine/research.py +247 -0
  30. package/mcp_server/user_corpus/runner.py +261 -0
  31. package/mcp_server/user_corpus/scanner.py +115 -0
  32. package/mcp_server/user_corpus/scanners/__init__.py +18 -0
  33. package/mcp_server/user_corpus/scanners/adg.py +79 -0
  34. package/mcp_server/user_corpus/scanners/als.py +144 -0
  35. package/mcp_server/user_corpus/scanners/amxd.py +374 -0
  36. package/mcp_server/user_corpus/scanners/plugin_preset.py +202 -0
  37. package/mcp_server/user_corpus/tools.py +904 -0
  38. package/mcp_server/user_corpus/wizard.py +224 -0
  39. package/package.json +2 -2
  40. package/remote_script/LivePilot/__init__.py +1 -1
  41. package/remote_script/LivePilot/browser.py +7 -2
  42. package/requirements.txt +3 -3
  43. package/server.json +2 -2
@@ -0,0 +1,48 @@
1
+ """User-corpus builder — scan your own .als / .adg / .amxd / plugin presets / samples
2
+ into queryable atlas overlays.
3
+
4
+ See docs/USER_CORPUS_GUIDE.md for the full architecture + user guide.
5
+
6
+ Public API:
7
+ from mcp_server.user_corpus import (
8
+ Scanner, register_scanner, get_scanner, list_scanners,
9
+ Manifest, Source, load_manifest, save_manifest,
10
+ run_scan, ScanResult,
11
+ )
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from .scanner import Scanner, register_scanner, get_scanner, list_scanners
16
+ from .manifest import (
17
+ Manifest,
18
+ Source,
19
+ load_manifest,
20
+ save_manifest,
21
+ init_default_manifest,
22
+ DEFAULT_MANIFEST_PATH,
23
+ DEFAULT_OUTPUT_ROOT,
24
+ )
25
+ from .runner import run_scan, ScanResult, SourceScanResult
26
+
27
+ # Eager-import the built-in scanners so their @register_scanner decorators fire.
28
+ from .scanners import als as _als # noqa: F401
29
+ from .scanners import adg as _adg # noqa: F401
30
+ from .scanners import amxd as _amxd # noqa: F401
31
+ from .scanners import plugin_preset as _pp # noqa: F401
32
+
33
+ __all__ = [
34
+ "Scanner",
35
+ "register_scanner",
36
+ "get_scanner",
37
+ "list_scanners",
38
+ "Manifest",
39
+ "Source",
40
+ "load_manifest",
41
+ "save_manifest",
42
+ "init_default_manifest",
43
+ "DEFAULT_MANIFEST_PATH",
44
+ "DEFAULT_OUTPUT_ROOT",
45
+ "run_scan",
46
+ "ScanResult",
47
+ "SourceScanResult",
48
+ ]
@@ -0,0 +1,142 @@
1
+ """Manifest — declarative source registry for user corpus scans.
2
+
3
+ Schema lives in the YAML; this module is just (de)serialization + dataclasses.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ from dataclasses import dataclass, field, asdict
10
+ from datetime import datetime, timezone
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import yaml
15
+
16
+
17
+ # ─── Default paths ───────────────────────────────────────────────────────────
18
+
19
+ DEFAULT_OUTPUT_ROOT = Path.home() / ".livepilot" / "atlas-overlays" / "user"
20
+ DEFAULT_MANIFEST_PATH = DEFAULT_OUTPUT_ROOT / "manifest.yaml"
21
+
22
+ CURRENT_SCHEMA_VERSION = 1
23
+
24
+
25
+ # ─── Data classes ────────────────────────────────────────────────────────────
26
+
27
+
28
+ @dataclass
29
+ class Source:
30
+ """One declared scan source."""
31
+ id: str
32
+ type: str # scanner type_id
33
+ path: str # filesystem path (may contain ~)
34
+ recursive: bool = True
35
+ exclude_globs: list[str] = field(default_factory=list)
36
+ last_scanned: str | None = None # ISO 8601 UTC, set by the runner
37
+ file_count: int | None = None # set by the runner after scan
38
+ options: dict[str, Any] = field(default_factory=dict)
39
+
40
+ @property
41
+ def resolved_path(self) -> Path:
42
+ return Path(os.path.expanduser(self.path)).resolve()
43
+
44
+ def mark_scanned(self, file_count: int) -> None:
45
+ self.last_scanned = datetime.now(timezone.utc).isoformat(timespec="seconds")
46
+ self.file_count = file_count
47
+
48
+
49
+ @dataclass
50
+ class Manifest:
51
+ """Top-level manifest — all scan sources + global options."""
52
+ schema_version: int = CURRENT_SCHEMA_VERSION
53
+ sources: list[Source] = field(default_factory=list)
54
+ output: dict[str, Any] = field(default_factory=lambda: {
55
+ "root": str(DEFAULT_OUTPUT_ROOT),
56
+ "schema_version": CURRENT_SCHEMA_VERSION,
57
+ })
58
+ options: dict[str, Any] = field(default_factory=lambda: {
59
+ "parallel_workers": 4,
60
+ "skip_unchanged": True,
61
+ "log_level": "info",
62
+ "on_error": "continue",
63
+ })
64
+ ai_annotation: dict[str, Any] = field(default_factory=lambda: {
65
+ "enabled": False,
66
+ "model": "sonnet",
67
+ "fields": [
68
+ "sonic_fingerprint",
69
+ "tags",
70
+ "reach_for",
71
+ "avoid",
72
+ "cross_references",
73
+ ],
74
+ })
75
+
76
+ @property
77
+ def output_root(self) -> Path:
78
+ return Path(os.path.expanduser(self.output.get("root", str(DEFAULT_OUTPUT_ROOT))))
79
+
80
+ def find_source(self, source_id: str) -> Source | None:
81
+ for s in self.sources:
82
+ if s.id == source_id:
83
+ return s
84
+ return None
85
+
86
+ def add_source(self, source: Source) -> None:
87
+ if self.find_source(source.id):
88
+ raise ValueError(f"Source id '{source.id}' already exists; remove or rename first")
89
+ self.sources.append(source)
90
+
91
+ def remove_source(self, source_id: str) -> Source | None:
92
+ match = self.find_source(source_id)
93
+ if match:
94
+ self.sources.remove(match)
95
+ return match
96
+
97
+
98
+ # ─── (De)serialization ───────────────────────────────────────────────────────
99
+
100
+
101
+ def load_manifest(path: Path = DEFAULT_MANIFEST_PATH) -> Manifest:
102
+ """Load a manifest YAML. Returns a default Manifest if the file is missing."""
103
+ if not path.exists():
104
+ return Manifest()
105
+ raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
106
+ sources = [Source(**s) for s in (raw.get("sources") or [])]
107
+ return Manifest(
108
+ schema_version=raw.get("schema_version", CURRENT_SCHEMA_VERSION),
109
+ sources=sources,
110
+ output=raw.get("output") or Manifest().output,
111
+ options=raw.get("options") or Manifest().options,
112
+ ai_annotation=raw.get("ai_annotation") or Manifest().ai_annotation,
113
+ )
114
+
115
+
116
+ def save_manifest(manifest: Manifest, path: Path = DEFAULT_MANIFEST_PATH) -> None:
117
+ """Persist a manifest as YAML. Creates parent dirs if needed."""
118
+ path.parent.mkdir(parents=True, exist_ok=True)
119
+ data = {
120
+ "schema_version": manifest.schema_version,
121
+ "sources": [
122
+ {k: v for k, v in asdict(s).items() if v is not None and v != [] and v != {}}
123
+ for s in manifest.sources
124
+ ],
125
+ "output": manifest.output,
126
+ "options": manifest.options,
127
+ "ai_annotation": manifest.ai_annotation,
128
+ }
129
+ path.write_text(
130
+ yaml.dump(data, sort_keys=False, default_flow_style=False, width=200, allow_unicode=True),
131
+ encoding="utf-8",
132
+ )
133
+
134
+
135
+ def init_default_manifest(path: Path = DEFAULT_MANIFEST_PATH) -> Manifest:
136
+ """Create a fresh manifest at the default path, ensuring directory exists."""
137
+ path.parent.mkdir(parents=True, exist_ok=True)
138
+ if path.exists():
139
+ return load_manifest(path)
140
+ m = Manifest()
141
+ save_manifest(m, path)
142
+ return m
@@ -0,0 +1,39 @@
1
+ """Plugin Knowledge Engine — detects installed plugins, extracts identity from
2
+ their bundles, finds + extracts manuals, and emits research / synthesis briefs
3
+ for the agent to fulfill via WebSearch + sonnet subagents.
4
+
5
+ See docs/PLUGIN_KNOWLEDGE_ENGINE.md for the full architecture.
6
+
7
+ Public API:
8
+ from mcp_server.user_corpus.plugin_engine import (
9
+ detect_installed_plugins, discover_manuals_for_plugin,
10
+ extract_manual_text, build_research_targets,
11
+ build_synthesis_brief, default_plugin_dir,
12
+ )
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from .detector import (
17
+ detect_installed_plugins,
18
+ default_plugin_dir,
19
+ DetectedPlugin,
20
+ )
21
+ from .manual import (
22
+ discover_manuals_for_plugin,
23
+ extract_manual_text,
24
+ ManualCandidate,
25
+ ManualExtraction,
26
+ )
27
+ from .research import build_research_targets, build_synthesis_brief
28
+
29
+ __all__ = [
30
+ "detect_installed_plugins",
31
+ "default_plugin_dir",
32
+ "DetectedPlugin",
33
+ "discover_manuals_for_plugin",
34
+ "extract_manual_text",
35
+ "ManualCandidate",
36
+ "ManualExtraction",
37
+ "build_research_targets",
38
+ "build_synthesis_brief",
39
+ ]