Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fe7a7de
spec: openspec init
TomCC7 Jun 4, 2026
76158b2
chore: revert change to doc folder
TomCC7 Jun 8, 2026
35c8b14
Merge branch 'main' into cc/feat/openspec
TomCC7 Jun 8, 2026
12d4346
Merge branch 'main' into cc/feat/openspec
TomCC7 Jun 10, 2026
6cd2fd3
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jun 12, 2026
45f7f73
Merge branch 'main' into cc/feat/openspec
TomCC7 Jun 15, 2026
86a600d
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jun 18, 2026
43fd853
Merge branch 'main' into cc/feat/openspec
TomCC7 Jun 20, 2026
8394a61
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jun 20, 2026
bae46c4
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jun 23, 2026
4cf815e
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jun 30, 2026
2c80dab
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 7, 2026
bc381cb
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 11, 2026
3a976da
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 14, 2026
4e25297
add mattskill
TomCC7 Jul 20, 2026
11f0d7f
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 23, 2026
9ffcd58
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 23, 2026
f873ddf
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 24, 2026
e87e93e
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 27, 2026
e689348
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 28, 2026
794e585
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Jul 29, 2026
cfa3e3a
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Aug 1, 2026
d221a4f
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Aug 4, 2026
df82a32
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Aug 5, 2026
9cac56d
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Aug 8, 2026
1d2b2d2
Merge remote-tracking branch 'origin/main' into cc/feat/openspec
TomCC7 Aug 11, 2026
7f11caa
feat: add Python native module runtime
TomCC7 Aug 15, 2026
294867d
spec: remove
TomCC7 Aug 15, 2026
927c171
chore: lock external Python example
TomCC7 Aug 15, 2026
dffb4df
feat: add isolated LeRobot policy module
TomCC7 Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# DimOS Module Runtime

This context names the parts that let DimOS compose modules while their work runs in different processes or environments.

## Language

**Module**:
A typed, independently managed capability that participates in a Blueprint through streams and RPCs.
_Avoid_: Service, node

**Native Module**:
A Module whose computation runs in a managed external process while DimOS retains its composition and lifecycle identity.
_Avoid_: Foreign module, binary wrapper

**Python-Native Module**:
A Native Module whose concrete implementation runs in an isolated Python environment.
_Avoid_: External Python module, external-python deployment

**Contract**:
The host-visible Module class that defines a Python-Native Module's streams, configuration, RPCs, and skills.
_Avoid_: Declaration, interface module

**Runtime Subclass**:
The concrete subclass that implements a Contract inside its isolated Python environment.
_Avoid_: Worker class, external implementation

**Internal RPC Endpoint**:
The private identity through which a Contract reaches its Runtime Subclass without changing the Module's public identity.
_Avoid_: Public child RPC, socket bridge
10 changes: 8 additions & 2 deletions dimos/core/native_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,10 @@ def stop(self) -> None:
module=self._module_label,
pid=proc.pid,
)
proc.send_signal(signal.SIGTERM)
try:
os.killpg(proc.pid, signal.SIGTERM)
except ProcessLookupError:
pass
try:
proc.wait(timeout=self.config.shutdown_timeout)
except subprocess.TimeoutExpired:
Expand All @@ -324,7 +327,10 @@ def stop(self) -> None:
module=self._module_label,
pid=proc.pid,
)
proc.kill()
try:
os.killpg(proc.pid, signal.SIGKILL)
except ProcessLookupError:
pass
try:
proc.wait(timeout=self.config.shutdown_timeout)
except subprocess.TimeoutExpired:
Expand Down
121 changes: 121 additions & 0 deletions dimos/core/python_native_bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright 2026 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Bootstrap an isolated PythonNativeModule runtime subclass."""

from __future__ import annotations

import inspect
import os
import pickle
import signal
import sys
import threading
from typing import Any

import typer

from dimos.core.python_native_module import PythonNativeModule, contract_rpc_names
from dimos.spec.utils import _signatures_compatible


def load_class(reference: str) -> type[Any]:
module_name, separator, class_name = reference.partition(":")
if not separator:
module_name, _, class_name = reference.rpartition(".")
if not module_name or not class_name:
raise ValueError(f"Invalid import reference {reference!r}; use module:Class")
module = __import__(module_name, fromlist=[class_name])
value = getattr(module, class_name)
if not isinstance(value, type):
raise TypeError(f"Import reference {reference!r} does not resolve to a class")
return value


def _method_owner(module_class: type[Any], name: str) -> type[Any] | None:
return next((base for base in module_class.__mro__ if name in base.__dict__), None)


def validate_runtime(
declaration: type[PythonNativeModule], runtime: type[PythonNativeModule]
) -> None:
if not issubclass(declaration, PythonNativeModule):
raise TypeError(f"{declaration.__name__} is not a PythonNativeModule contract")
if not issubclass(runtime, declaration):
raise TypeError(f"{runtime.__name__} must subclass {declaration.__name__}")

for name in contract_rpc_names(declaration):
owner = _method_owner(runtime, name)
if owner is None or owner is declaration or owner in declaration.__mro__[1:]:
raise TypeError(f"{runtime.__name__} must override contract RPC {name!r}")
contract_method = getattr(declaration, name)
runtime_method = getattr(runtime, name)
if not hasattr(runtime_method, "__rpc__"):
raise TypeError(f"{runtime.__name__}.{name} must retain @rpc or @skill")
if bool(hasattr(contract_method, "__skill__")) != bool(
hasattr(runtime_method, "__skill__")
):
raise TypeError(f"{runtime.__name__}.{name} must preserve skill classification")
if not _signatures_compatible(
inspect.signature(contract_method, eval_str=True),
inspect.signature(runtime_method, eval_str=True),
):
raise TypeError(f"{runtime.__name__}.{name} has an incompatible signature")


def main(
declaration: str = typer.Option(..., "--declaration"),
implementation: str = typer.Option(..., "--implementation"),
instance_name: str = typer.Option(..., "--instance-name"),
handshake_fd: int = typer.Option(..., "--handshake-fd"),
) -> None:
module: PythonNativeModule | None = None
try:
declaration_class = load_class(declaration)
runtime_class = load_class(implementation)
if not issubclass(declaration_class, PythonNativeModule):
raise TypeError(f"{declaration!r} is not a PythonNativeModule contract")
if not issubclass(runtime_class, PythonNativeModule):
raise TypeError(f"{implementation!r} is not a PythonNativeModule subclass")
validate_runtime(declaration_class, runtime_class)
kwargs = pickle.load(sys.stdin.buffer)
kwargs["instance_name"] = instance_name
module = runtime_class(_python_native_runtime=True, **kwargs)
os.write(handshake_fd, b"READY\n")
except Exception as error:
try:
os.write(handshake_fd, f"ERROR {type(error).__name__}: {error}\n".encode())
except OSError:
pass
raise
finally:
try:
os.close(handshake_fd)
except OSError:
pass

stopping = threading.Event()

def request_stop(_signum: int, _frame: object) -> None:
stopping.set()

signal.signal(signal.SIGTERM, request_stop)
signal.signal(signal.SIGINT, request_stop)
stopping.wait()
if module is not None:
module.stop()


if __name__ == "__main__":
typer.run(main)
Loading
Loading