mirrorneuron-cli 1.2.6__tar.gz → 1.2.7__tar.gz

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 (87) hide show
  1. {mirrorneuron_cli-1.2.6/mirrorneuron_cli.egg-info → mirrorneuron_cli-1.2.7}/PKG-INFO +1 -1
  2. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7/mirrorneuron_cli.egg-info}/PKG-INFO +1 -1
  3. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mirrorneuron_cli.egg-info/SOURCES.txt +2 -0
  4. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/blueprint_cmds.py +3 -16
  5. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/blueprint_observability.py +14 -44
  6. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/event_relay.py +2 -18
  7. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/run_cmds.py +117 -21
  8. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/run_manifest.py +283 -39
  9. mirrorneuron_cli-1.2.7/mn_cli/libs/skill_dependencies.py +102 -0
  10. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/skill_runtime.py +162 -15
  11. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/sys_cmds.py +42 -0
  12. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/main.py +2 -0
  13. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/schemas/workflow_manifest.schema.json +24 -0
  14. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/server_cmds.py +210 -0
  15. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_blueprint_cmds.py +2 -14
  16. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_main.py +1 -0
  17. mirrorneuron_cli-1.2.7/tests/test_repo_hygiene.py +25 -0
  18. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_run_cmds.py +139 -15
  19. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_run_helpers.py +283 -16
  20. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_server_cmds.py +51 -0
  21. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/.github/workflows/ci.yml +0 -0
  22. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/.github/workflows/release.yml +0 -0
  23. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/.gitignore +0 -0
  24. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/.python-version +0 -0
  25. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/AGENTS.md +0 -0
  26. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/LICENSE +0 -0
  27. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/README.md +0 -0
  28. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/RELEASE.md +0 -0
  29. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mirrorneuron_cli.egg-info/dependency_links.txt +0 -0
  30. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mirrorneuron_cli.egg-info/entry_points.txt +0 -0
  31. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mirrorneuron_cli.egg-info/requires.txt +0 -0
  32. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mirrorneuron_cli.egg-info/top_level.txt +0 -0
  33. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/__init__.py +0 -0
  34. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/banner.py +0 -0
  35. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/config.py +0 -0
  36. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/error_handler.py +0 -0
  37. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/__init__.py +0 -0
  38. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/artifacts.py +0 -0
  39. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/backup_cmds.py +0 -0
  40. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/blueprint_models.py +0 -0
  41. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/blueprint_repository.py +0 -0
  42. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/blueprint_resources.py +0 -0
  43. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/bundles.py +0 -0
  44. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/deployment_cmds.py +0 -0
  45. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/job_cmds.py +0 -0
  46. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/model_cmds.py +0 -0
  47. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/resource_cmds.py +0 -0
  48. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/run_logs.py +0 -0
  49. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/runtime_health.py +0 -0
  50. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/schedule_cmds.py +0 -0
  51. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/service_cmds.py +0 -0
  52. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/ui.py +0 -0
  53. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/workflow_progress.py +0 -0
  54. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/libs/workflow_validation.py +0 -0
  55. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/logging_config.py +0 -0
  56. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/runtime_mode.py +0 -0
  57. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/runtime_state.py +0 -0
  58. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/sdk_path.py +0 -0
  59. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/shared.py +0 -0
  60. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/terminal.py +0 -0
  61. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/mn_cli/update_cmds.py +0 -0
  62. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/pyproject.toml +0 -0
  63. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/scripts/check-release-artifacts.sh +0 -0
  64. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/scripts/make-release-zip.sh +0 -0
  65. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/scripts/validate-version-tag.sh +0 -0
  66. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/setup.cfg +0 -0
  67. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/conftest.py +0 -0
  68. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_backup_cmds.py +0 -0
  69. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_blueprint_repository.py +0 -0
  70. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_blueprint_resources.py +0 -0
  71. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_deployment_cmds.py +0 -0
  72. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_docker_network_integration.py +0 -0
  73. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_job_cmds.py +0 -0
  74. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_model_cmds.py +0 -0
  75. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_resource_cmds.py +0 -0
  76. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_runtime_health.py +0 -0
  77. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_runtime_mode.py +0 -0
  78. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_runtime_state.py +0 -0
  79. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_schedule_cmds.py +0 -0
  80. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_service_cmds.py +0 -0
  81. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_shared.py +0 -0
  82. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_sys_cmds.py +0 -0
  83. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_terminal.py +0 -0
  84. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_ui.py +0 -0
  85. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_update_cmds.py +0 -0
  86. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/tests/test_workflow_validation.py +0 -0
  87. {mirrorneuron_cli-1.2.6 → mirrorneuron_cli-1.2.7}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mirrorneuron-cli
3
- Version: 1.2.6
3
+ Version: 1.2.7
4
4
  Summary: MirrorNeuron CLI
5
5
  License-Expression: MIT
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mirrorneuron-cli
3
- Version: 1.2.6
3
+ Version: 1.2.7
4
4
  Summary: MirrorNeuron CLI
5
5
  License-Expression: MIT
6
6
  Classifier: Programming Language :: Python :: 3
@@ -47,6 +47,7 @@ mn_cli/libs/run_manifest.py
47
47
  mn_cli/libs/runtime_health.py
48
48
  mn_cli/libs/schedule_cmds.py
49
49
  mn_cli/libs/service_cmds.py
50
+ mn_cli/libs/skill_dependencies.py
50
51
  mn_cli/libs/skill_runtime.py
51
52
  mn_cli/libs/sys_cmds.py
52
53
  mn_cli/libs/ui.py
@@ -66,6 +67,7 @@ tests/test_docker_network_integration.py
66
67
  tests/test_job_cmds.py
67
68
  tests/test_main.py
68
69
  tests/test_model_cmds.py
70
+ tests/test_repo_hygiene.py
69
71
  tests/test_resource_cmds.py
70
72
  tests/test_run_cmds.py
71
73
  tests/test_run_helpers.py
@@ -3,7 +3,6 @@ import copy
3
3
  import json
4
4
  import shutil
5
5
  import subprocess
6
- import sys
7
6
  import time
8
7
  from pathlib import Path
9
8
  from typing import Annotated, Any, Optional
@@ -59,6 +58,9 @@ from mn_cli.libs.model_cmds import (
59
58
  remove_model_ref as _remove_model_ref,
60
59
  )
61
60
  from mn_sdk.runtime_config import resolve_mn_home
61
+ from mn_sdk.blueprint_support.python_workflow_bundle import (
62
+ generate_python_workflow_bundle_from_blueprint_dir,
63
+ )
62
64
  from mn_sdk import (
63
65
  cluster_provided_model as _cluster_provided_model,
64
66
  docker_model_name as _docker_model_name,
@@ -81,16 +83,6 @@ human_app = typer.Typer(
81
83
  _PATCH_COMPAT = (subprocess, _git_checkout, _git_fetch)
82
84
 
83
85
 
84
- def _inject_local_blueprint_support_path() -> None:
85
- repo_root = Path(
86
- os.getenv("MN_WORKSPACE_ROOT")
87
- or Path(__file__).resolve().parents[3]
88
- ).expanduser()
89
- candidate = repo_root / "mn-skills" / "blueprint_support_skill" / "src"
90
- if candidate.is_dir() and str(candidate) not in sys.path:
91
- sys.path.insert(0, str(candidate))
92
-
93
-
94
86
  @blueprint_app.callback()
95
87
  def blueprint_callback(
96
88
  ctx: typer.Context,
@@ -147,11 +139,6 @@ def _prepare_blueprint_bundle_for_run(
147
139
 
148
140
  def _generate_python_source_bundle(blueprint_dir: Path, output_dir: Path) -> Path:
149
141
  _load_observability_api()
150
- _inject_local_blueprint_support_path()
151
- from mn_blueprint_support.python_workflow_bundle import (
152
- generate_python_workflow_bundle_from_blueprint_dir,
153
- )
154
-
155
142
  return generate_python_workflow_bundle_from_blueprint_dir(
156
143
  blueprint_dir,
157
144
  output_dir,
@@ -1,47 +1,33 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- from pathlib import Path
5
- import sys
6
4
  import time
7
5
  from typing import Any, Callable, Optional
8
6
 
9
7
  import typer
8
+ from mn_sdk.blueprint_support import make_run_id
9
+ from mn_sdk.blueprint_support.observability import (
10
+ acknowledge_human_notice,
11
+ list_pending_human_requests,
12
+ list_runs,
13
+ load_run,
14
+ read_human_events,
15
+ read_run_events,
16
+ read_run_logs,
17
+ read_run_resources,
18
+ read_run_stream_records,
19
+ record_human_response,
20
+ )
21
+ from mn_sdk.blueprint_support.web_ui import write_static_run_report
10
22
 
11
23
  from mn_cli.shared import console
12
24
 
13
25
 
14
26
  def load_observability_api() -> tuple[Callable[..., list[dict[str, Any]]], Callable[..., dict[str, Any]], Callable[..., list[dict[str, Any]]]]:
15
- _ensure_blueprint_support_path()
16
- try:
17
- from mn_blueprint_support.observability import list_runs, load_run, read_run_events
18
- except ModuleNotFoundError:
19
- console.print(
20
- "[red]Blueprint observability support is unavailable. "
21
- "Install the blueprint support package or run from the monorepo checkout.[/red]"
22
- )
23
- raise typer.Exit(1)
24
27
  return list_runs, load_run, read_run_events
25
28
 
26
29
 
27
30
  def load_observability_tools() -> dict[str, Callable[..., Any]]:
28
- _ensure_blueprint_support_path()
29
- try:
30
- from mn_blueprint_support.observability import (
31
- acknowledge_human_notice,
32
- list_pending_human_requests,
33
- read_human_events,
34
- read_run_logs,
35
- read_run_resources,
36
- read_run_stream_records,
37
- record_human_response,
38
- )
39
- except ModuleNotFoundError:
40
- console.print(
41
- "[red]Blueprint observability support is unavailable. "
42
- "Install the blueprint support package or run from the monorepo checkout.[/red]"
43
- )
44
- raise typer.Exit(1)
45
31
  return {
46
32
  "acknowledge_human_notice": acknowledge_human_notice,
47
33
  "list_pending_human_requests": list_pending_human_requests,
@@ -53,28 +39,12 @@ def load_observability_tools() -> dict[str, Callable[..., Any]]:
53
39
  }
54
40
 
55
41
 
56
- def _ensure_blueprint_support_path() -> None:
57
- repo_root = Path(__file__).resolve().parents[3]
58
- support_src = repo_root / "mn-skills" / "blueprint_support_skill" / "src"
59
- if support_src.exists() and str(support_src) not in sys.path:
60
- sys.path.insert(0, str(support_src))
61
-
62
-
63
42
  def load_web_ui_api() -> Callable[..., Any]:
64
- load_observability_api()
65
- try:
66
- from mn_blueprint_support.web_ui import write_static_run_report
67
- except ModuleNotFoundError:
68
- console.print("[red]Blueprint web UI support is unavailable.[/red]")
69
- raise typer.Exit(1)
70
43
  return write_static_run_report
71
44
 
72
45
 
73
46
  def make_blueprint_run_id(blueprint_id: str) -> str:
74
47
  try:
75
- load_observability_api()
76
- from mn_blueprint_support import make_run_id
77
-
78
48
  return make_run_id(blueprint_id)
79
49
  except Exception:
80
50
  import uuid
@@ -2,25 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from mn_cli.shared import client
6
-
7
-
8
- def _ensure_blueprint_support_path() -> None:
9
- import os
10
- import sys
11
-
12
- repo_root = Path(
13
- os.getenv("MN_WORKSPACE_ROOT")
14
- or Path(__file__).resolve().parents[3]
15
- ).expanduser()
16
- support_src = repo_root / "mn-skills" / "blueprint_support_skill" / "src"
17
- if support_src.is_dir() and str(support_src) not in sys.path:
18
- sys.path.insert(0, str(support_src))
5
+ from mn_sdk.blueprint_support.event_relay import build_parser, run_event_relay
19
6
 
20
-
21
- _ensure_blueprint_support_path()
22
-
23
- from mn_blueprint_support.event_relay import build_parser, run_event_relay
7
+ from mn_cli.shared import client
24
8
 
25
9
 
26
10
  def main(argv: list[str] | None = None) -> int:
@@ -8,6 +8,7 @@ import signal
8
8
  import socket
9
9
  import subprocess
10
10
  import sys
11
+ import tempfile
11
12
  import time
12
13
  import urllib.parse
13
14
  from pathlib import Path
@@ -43,12 +44,14 @@ from mn_cli.libs.run_manifest import (
43
44
  runtime_web_ui_support_payloads_for_manifest,
44
45
  run_mode_label as _run_mode_label,
45
46
  stage_blueprint_support_payloads_for_manifest,
47
+ stage_skill_dependency_payloads_for_manifest,
46
48
  stage_skill_runtime_support_payloads_for_manifest,
47
49
  stage_local_input_payloads_for_manifest,
48
50
  stage_upload_path_payloads_for_manifest,
49
51
  with_shared_run_store_config as _with_shared_run_store_config,
50
52
  )
51
53
  from mn_cli.libs.skill_runtime import validate_skill_runtime_requirements
54
+ from mn_cli.libs.skill_dependencies import gar_requirements_text
52
55
  from mn_cli.libs.workflow_validation import (
53
56
  _is_workflow_manifest,
54
57
  _manifest_workflow_id,
@@ -59,6 +62,7 @@ from mn_cli.libs.blueprint_observability import (
59
62
  make_blueprint_run_id as _make_blueprint_run_id,
60
63
  )
61
64
  from mn_cli.libs.blueprint_resources import cleanup_blueprint_host_hooks
65
+ from mn_cli.server_cmds import ensure_context_engine_runtime
62
66
  from mn_cli.shared import console, client, logger
63
67
  from mn_cli.terminal import use_progress
64
68
  from mn_cli.error_handler import handle_cli_error
@@ -75,6 +79,7 @@ from mn_sdk import (
75
79
  validate_service_spec_issues,
76
80
  workflow_progress_snapshot,
77
81
  )
82
+ from mn_sdk.context_engine import blueprint_requires_context_engine
78
83
  from mn_sdk.runtime_config import default_runs_root
79
84
 
80
85
  FINAL_STATUSES = {"completed", "failed", "cancelled"}
@@ -84,6 +89,10 @@ POST_LAUNCH_SCRIPT = Path("scripts/post-launch.sh")
84
89
  DEFAULT_BLUEPRINT_WEB_UI_PORT_START = 61000
85
90
  DEFAULT_BLUEPRINT_WEB_UI_PORT_END = 61049
86
91
  DETACHED_AFTER_INTERRUPT_MESSAGE = "Detached from workflow UI. Job is still running."
92
+ CONTEXT_ENGINE_EXPECTATION = (
93
+ "This blueprint uses context memory. First launch may download the context model "
94
+ "and start the Membrane context engine; keep Docker running and be patient."
95
+ )
87
96
  _HELPER_COMPAT = (
88
97
  _add_mn_llm_aliases,
89
98
  _blueprint_runtime_environment,
@@ -93,6 +102,13 @@ _HELPER_COMPAT = (
93
102
  )
94
103
 
95
104
 
105
+ def _print_launch_progress(label: str, detail: str | None = None) -> None:
106
+ if detail:
107
+ console.print(f"[cyan]Launch:[/cyan] {label} - {detail}")
108
+ else:
109
+ console.print(f"[cyan]Launch:[/cyan] {label}")
110
+
111
+
96
112
  def fetch_and_save_results(job_id: str, data: dict = None):
97
113
  log_dir = Path(f"/tmp/mn_{job_id}")
98
114
  log_dir.mkdir(parents=True, exist_ok=True)
@@ -218,6 +234,48 @@ def _manifest_config(manifest: dict[str, Any]) -> dict[str, Any]:
218
234
  return {}
219
235
 
220
236
 
237
+ def _ensure_context_engine_for_run_if_needed(
238
+ bundle_dir: Path,
239
+ manifest: dict[str, Any],
240
+ *,
241
+ env_overrides: Optional[dict[str, str]] = None,
242
+ config_overrides: Optional[dict[str, Any]] = None,
243
+ force: bool = False,
244
+ ) -> dict[str, str] | None:
245
+ config = load_blueprint_config(bundle_dir, config_overrides=config_overrides)
246
+ effective_env = os.environ.copy()
247
+ effective_env.update(
248
+ {
249
+ str(key): str(value)
250
+ for key, value in (env_overrides or {}).items()
251
+ if value is not None
252
+ }
253
+ )
254
+ if not blueprint_requires_context_engine(manifest, config, env=effective_env):
255
+ return None
256
+
257
+ console.print(f"[cyan]{CONTEXT_ENGINE_EXPECTATION}[/cyan]")
258
+ with Progress(
259
+ SpinnerColumn(),
260
+ TextColumn("[progress.description]{task.description}"),
261
+ TimeElapsedColumn(),
262
+ console=console,
263
+ disable=not use_progress(),
264
+ ) as progress:
265
+ task = progress.add_task(
266
+ "[cyan]Preparing context memory: checking Membrane and Docker Model Runner...",
267
+ total=None,
268
+ )
269
+ summary = ensure_context_engine_runtime(force=force)
270
+ progress.update(task, description="[green]Context memory is ready.")
271
+ console.print(
272
+ f"[green]Context memory ready:[/green] {summary.get('service', 'membrane-context-engine')} "
273
+ f"using {summary.get('model', 'configured model')}"
274
+ )
275
+ logger.info("Context engine runtime ensured for %s: %s", bundle_dir, summary)
276
+ return summary
277
+
278
+
221
279
  def _expand_user_output_path(value: str) -> Path:
222
280
  text = str(value or "").strip()
223
281
  home = (
@@ -1423,6 +1481,7 @@ def _stage_bundle_payloads(
1423
1481
  payloads.update(runtime_web_ui_support_payloads_for_manifest(manifest_dict))
1424
1482
  stage_blueprint_support_payloads_for_manifest(manifest_dict, payloads, bundle_dir=bundle_dir)
1425
1483
  stage_skill_runtime_support_payloads_for_manifest(manifest_dict, payloads, bundle_dir=bundle_dir)
1484
+ stage_skill_dependency_payloads_for_manifest(manifest_dict, payloads, bundle_dir=bundle_dir)
1426
1485
  return payloads
1427
1486
 
1428
1487
 
@@ -1514,6 +1573,10 @@ def run_bundle(
1514
1573
  submission_metadata,
1515
1574
  config_overrides=config_overrides,
1516
1575
  )
1576
+ _print_launch_progress(
1577
+ "Check runtime resources",
1578
+ "confirming the runtime can satisfy this blueprint before submission.",
1579
+ )
1517
1580
  _validate_manifest_hardware_or_exit(
1518
1581
  manifest_dict,
1519
1582
  force=force,
@@ -1536,6 +1599,10 @@ def run_bundle(
1536
1599
  config_overrides=config_overrides,
1537
1600
  )
1538
1601
  if not force:
1602
+ _print_launch_progress(
1603
+ "Validate inputs and dependencies",
1604
+ "checking services, models, local inputs, and non-hard requirements.",
1605
+ )
1539
1606
  _validate_manifest_services_or_exit(
1540
1607
  bundle_dir,
1541
1608
  manifest_dict,
@@ -1558,6 +1625,10 @@ def run_bundle(
1558
1625
  console.print(
1559
1626
  "[yellow]Validation skipped because --force was provided; service checks, model checks, input checks, and non-hard runtime requirements will be bypassed for this run.[/yellow]"
1560
1627
  )
1628
+ _print_launch_progress(
1629
+ "Package workflow",
1630
+ "staging workflow files, local inputs, runtime helpers, and output wiring.",
1631
+ )
1561
1632
  manifest_dict = prepare_manifest_for_submission(
1562
1633
  bundle_dir,
1563
1634
  manifest_dict,
@@ -1583,6 +1654,14 @@ def run_bundle(
1583
1654
  )
1584
1655
  return
1585
1656
 
1657
+ _ensure_context_engine_for_run_if_needed(
1658
+ bundle_dir,
1659
+ manifest_dict,
1660
+ env_overrides=env_overrides,
1661
+ config_overrides=config_overrides,
1662
+ force=force,
1663
+ )
1664
+
1586
1665
  prepared_submission = prepare_job_submission(
1587
1666
  manifest_dict,
1588
1667
  payloads,
@@ -1599,6 +1678,10 @@ def run_bundle(
1599
1678
  else None
1600
1679
  )
1601
1680
  submitted_run_dir = blueprint_run_dir
1681
+ _print_launch_progress(
1682
+ "Submit runtime job",
1683
+ "handing the prepared bundle to MirrorNeuron core.",
1684
+ )
1602
1685
  job_id = client.submit_job(manifest, payloads, force=force)
1603
1686
  submitted_job_id = job_id
1604
1687
  log_writer = JobLogWriter(job_id, run_dir=blueprint_run_dir)
@@ -1883,9 +1966,14 @@ def _prepare_openshell_custom_images(
1883
1966
  if source_path is None:
1884
1967
  continue
1885
1968
 
1886
- config["from"] = _build_openshell_from_image(
1887
- source_path, node.get("node_id") or "openshell"
1888
- )
1969
+ build_source = _openshell_skill_dependency_context(source_path, manifest_dict)
1970
+ try:
1971
+ config["from"] = _build_openshell_from_image(
1972
+ build_source, node.get("node_id") or "openshell"
1973
+ )
1974
+ finally:
1975
+ if build_source != source_path:
1976
+ shutil.rmtree(build_source, ignore_errors=True)
1889
1977
 
1890
1978
 
1891
1979
  def _openshell_gateway_endpoint() -> str:
@@ -1981,6 +2069,30 @@ def _openshell_local_from_path(bundle_dir: Path, source: Any) -> Path | None:
1981
2069
  return None
1982
2070
 
1983
2071
 
2072
+ def _openshell_skill_dependency_context(source_path: Path, manifest: dict[str, Any]) -> Path:
2073
+ requirements_text = gar_requirements_text(manifest)
2074
+ if not requirements_text:
2075
+ return source_path
2076
+
2077
+ source_root = source_path.parent if source_path.is_file() else source_path
2078
+ temp_context = Path(tempfile.mkdtemp(prefix=f"mn-openshell-skill-deps-{source_root.name}."))
2079
+ shutil.copytree(source_root, temp_context, dirs_exist_ok=True)
2080
+ dockerfile = temp_context / "Dockerfile"
2081
+ requirements = temp_context / "__mn_skill_dependencies" / "requirements.txt"
2082
+ requirements.parent.mkdir(parents=True, exist_ok=True)
2083
+ requirements.write_text(requirements_text, encoding="utf-8")
2084
+ dockerfile.write_text(
2085
+ dockerfile.read_text(encoding="utf-8").rstrip()
2086
+ + "\n\n"
2087
+ + "COPY __mn_skill_dependencies/requirements.txt /tmp/mn-skill-dependencies/requirements.txt\n"
2088
+ + "RUN if [ -s /tmp/mn-skill-dependencies/requirements.txt ]; then \\\n"
2089
+ + " python3 -m pip install --break-system-packages --no-cache-dir -r /tmp/mn-skill-dependencies/requirements.txt; \\\n"
2090
+ + " fi\n",
2091
+ encoding="utf-8",
2092
+ )
2093
+ return temp_context
2094
+
2095
+
1984
2096
  def _build_openshell_from_image(source_path: Path, node_id: Any) -> str:
1985
2097
  console.print(
1986
2098
  f"[yellow]Building OpenShell sandbox image for {node_id} from {source_path}...[/yellow]"
@@ -2451,7 +2563,7 @@ def _start_background_event_relay_if_needed(
2451
2563
  command = [
2452
2564
  sys.executable,
2453
2565
  "-m",
2454
- "mn_blueprint_support.event_relay",
2566
+ "mn_sdk.blueprint_support.event_relay",
2455
2567
  "--job-id",
2456
2568
  job_id,
2457
2569
  "--run-dir",
@@ -2464,7 +2576,6 @@ def _start_background_event_relay_if_needed(
2464
2576
 
2465
2577
  env = os.environ.copy()
2466
2578
  env["MN_RUN_EVENT_RELAY_CHILD"] = "1"
2467
- _inject_local_blueprint_support_pythonpath(env)
2468
2579
  with open(log_path, "a", encoding="utf-8") as relay_log:
2469
2580
  process = subprocess.Popen(
2470
2581
  command,
@@ -2598,7 +2709,7 @@ def _web_ui_registration_module(
2598
2709
 
2599
2710
  adapter = str(output.get("adapter") or web_ui.get("kind") or "").lower()
2600
2711
  if adapter == "gradio" and output.get("auto_generate", True) is not False:
2601
- return "mn_blueprint_support.gradio_dashboard"
2712
+ return "mn_sdk.blueprint_support.gradio_dashboard"
2602
2713
  return None
2603
2714
 
2604
2715
 
@@ -2764,21 +2875,6 @@ def _launch_blueprint_web_ui_command(
2764
2875
  )
2765
2876
 
2766
2877
 
2767
- def _inject_local_blueprint_support_pythonpath(env: dict[str, str]) -> None:
2768
- repo_root = Path(
2769
- os.getenv("MN_WORKSPACE_ROOT")
2770
- or Path(__file__).resolve().parents[3]
2771
- ).expanduser()
2772
- support_src = repo_root / "mn-skills" / "blueprint_support_skill" / "src"
2773
- if not support_src.is_dir():
2774
- return
2775
- current = env.get("PYTHONPATH")
2776
- paths = [str(support_src)]
2777
- if current:
2778
- paths.append(current)
2779
- env["PYTHONPATH"] = os.pathsep.join(paths)
2780
-
2781
-
2782
2878
  def _web_ui_bind_host(output: dict[str, Any], env_overrides: dict[str, str]) -> str:
2783
2879
  for value in (
2784
2880
  env_overrides.get("MN_BLUEPRINT_WEB_UI_BIND_HOST"),