@mcoda/mswarm 0.1.135-codali.4 → 0.1.135-codali.7

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 (38) hide show
  1. package/dist/vendor/codali/cli/TerminalCommand.d.ts.map +1 -1
  2. package/dist/vendor/codali/cli/TerminalCommand.js +4 -2
  3. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts +1 -1
  4. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.js +2 -2
  5. package/dist/vendor/codali/eval/fixtures/MixedMediaRecoveryJourney.d.ts +6 -0
  6. package/dist/vendor/codali/eval/fixtures/MixedMediaRecoveryJourney.d.ts.map +1 -0
  7. package/dist/vendor/codali/eval/fixtures/MixedMediaRecoveryJourney.js +141 -0
  8. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts +1 -0
  9. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts.map +1 -1
  10. package/dist/vendor/codali/providers/ConfiguredModelScope.js +4 -0
  11. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts +2 -0
  12. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts.map +1 -1
  13. package/dist/vendor/codali/providers/ConfiguredProvider.js +16 -3
  14. package/dist/vendor/codali/providers/ProviderTypes.d.ts +2 -0
  15. package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
  16. package/dist/vendor/codali/providers/RequestUsage.d.ts +11 -0
  17. package/dist/vendor/codali/providers/RequestUsage.d.ts.map +1 -0
  18. package/dist/vendor/codali/providers/RequestUsage.js +33 -0
  19. package/dist/vendor/codali/runtime/ReviewSource.d.ts +3 -0
  20. package/dist/vendor/codali/runtime/ReviewSource.d.ts.map +1 -1
  21. package/dist/vendor/codali/runtime/ReviewSource.js +17 -1
  22. package/dist/vendor/codali/runtime/SessionController.d.ts +2 -0
  23. package/dist/vendor/codali/runtime/SessionController.d.ts.map +1 -1
  24. package/dist/vendor/codali/runtime/SessionController.js +138 -24
  25. package/dist/vendor/codali/runtime/TaskReview.d.ts +23 -0
  26. package/dist/vendor/codali/runtime/TaskReview.d.ts.map +1 -0
  27. package/dist/vendor/codali/runtime/TaskReview.js +66 -0
  28. package/dist/vendor/codali/runtime/UsageReconciliation.d.ts +28 -0
  29. package/dist/vendor/codali/runtime/UsageReconciliation.d.ts.map +1 -0
  30. package/dist/vendor/codali/runtime/UsageReconciliation.js +68 -0
  31. package/dist/vendor/codali/runtime/VerificationReceiptSelection.d.ts +15 -0
  32. package/dist/vendor/codali/runtime/VerificationReceiptSelection.d.ts.map +1 -0
  33. package/dist/vendor/codali/runtime/VerificationReceiptSelection.js +30 -0
  34. package/dist/vendor/codali/session/SessionStoragePath.d.ts.map +1 -1
  35. package/dist/vendor/codali/session/SessionStoragePath.js +28 -16
  36. package/package.json +3 -3
  37. package/scripts/media_gpu_lock.py +2 -2
  38. package/scripts/native_load_guard.py +257 -0
@@ -2,15 +2,23 @@ import { constants, promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
3
  /** Existing state files must not alias another file or an external endpoint. */
4
4
  export async function assertRegularStateFile(file) {
5
- try {
6
- const stat = await fs.lstat(file);
7
- if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1)
8
- throw new Error(`Unsafe session state file: ${path.basename(file)}`);
9
- }
10
- catch (error) {
11
- if (error.code !== "ENOENT")
5
+ for (let attempt = 0; attempt < 8; attempt++) {
6
+ try {
7
+ const stat = await fs.lstat(file);
8
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink > 1)
9
+ throw new Error(`Unsafe session state file: ${path.basename(file)}`);
10
+ if (stat.nlink === 1)
11
+ return;
12
+ // An atomic rename may unlink the old inode while the asynchronous stat
13
+ // is completing. Validate the new named file; never accept the old one.
14
+ }
15
+ catch (error) {
16
+ if (error.code === "ENOENT")
17
+ return;
12
18
  throw error;
19
+ }
13
20
  }
21
+ throw new Error(`Session state file changed during validation: ${path.basename(file)}`);
14
22
  }
15
23
  /** Resolve trusted outer aliases (e.g. macOS /var) once, then reject symlinks
16
24
  * beneath the storage boundary. The terminal sandbox prevents model processes
@@ -76,14 +84,18 @@ export async function sessionStoragePath(options) {
76
84
  return resolved;
77
85
  }
78
86
  export async function readStateFile(file) {
79
- const handle = await fs.open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
80
- try {
81
- const stat = await handle.stat();
82
- if (!stat.isFile() || stat.nlink !== 1)
83
- throw new Error("Unsafe session state file");
84
- return await handle.readFile("utf8");
85
- }
86
- finally {
87
- await handle.close();
87
+ for (let attempt = 0; attempt < 8; attempt++) {
88
+ const handle = await fs.open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
89
+ try {
90
+ const stat = await handle.stat();
91
+ if (!stat.isFile() || stat.nlink > 1)
92
+ throw new Error("Unsafe session state file");
93
+ if (stat.nlink === 1)
94
+ return await handle.readFile("utf8");
95
+ }
96
+ finally {
97
+ await handle.close();
98
+ }
88
99
  }
100
+ throw new Error(`Session state file changed during validation: ${path.basename(file)}`);
89
101
  }
package/package.json CHANGED
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/bekirdag/mcoda/issues"
9
9
  },
10
10
  "dependencies": {
11
- "@mcoda/db": "0.1.135-codali.4",
12
- "@mcoda/shared": "0.1.135-codali.4",
11
+ "@mcoda/db": "0.1.135-codali.7",
12
+ "@mcoda/shared": "0.1.135-codali.7",
13
13
  "fastify": "^5.7.4",
14
14
  "undici": "^6.28.0"
15
15
  },
@@ -57,5 +57,5 @@
57
57
  },
58
58
  "type": "module",
59
59
  "types": "dist/server.d.ts",
60
- "version": "0.1.135-codali.4"
60
+ "version": "0.1.135-codali.7"
61
61
  }
@@ -34,9 +34,9 @@ class MediaGpuRecoveryRequired(MediaGpuError):
34
34
  code = "media_gpu_recovery_required"
35
35
 
36
36
 
37
- def _open_scheduler() -> int:
37
+ def _open_scheduler(lock_path: str | None = None) -> int:
38
38
  runtime_dir = Path(os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"))
39
- path = os.environ.get("MCODA_MEDIA_GPU_LOCK_PATH", str(runtime_dir / "mcoda-media-gpu1.lock"))
39
+ path = lock_path or os.environ.get("MCODA_MEDIA_GPU_LOCK_PATH", str(runtime_dir / "mcoda-media-gpu1.lock"))
40
40
  fd = -1
41
41
  try:
42
42
  fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC | os.O_NOFOLLOW, 0o600)
@@ -0,0 +1,257 @@
1
+ """Opt-in Linux host RAM admission for native model loading and recovery.
2
+
3
+ The embedding runtime must confirm model readiness or confirmed process death
4
+ before leaving the context. Unknown outcomes retain the shared media fence.
5
+ This module never dispatches or replays inference.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import math
12
+ import os
13
+ import re
14
+ import stat
15
+ import subprocess
16
+ import sys
17
+ import threading
18
+ import time
19
+ import urllib.request
20
+ from urllib.parse import urlsplit
21
+ from contextlib import contextmanager
22
+ from pathlib import Path
23
+
24
+ try:
25
+ from .media_gpu_lock import _open_scheduler, _read_lease, _write_lease, MediaGpuLease, MediaGpuError
26
+ except ImportError:
27
+ from media_gpu_lock import _open_scheduler, _read_lease, _write_lease, MediaGpuLease, MediaGpuError
28
+
29
+
30
+ class NativeLoadError(RuntimeError):
31
+ pass
32
+
33
+
34
+ _local = threading.local()
35
+
36
+
37
+ def read_policy(path: str) -> dict:
38
+ if not isinstance(path, str) or not os.path.isabs(path):
39
+ raise NativeLoadError("native load policy must be an absolute file path")
40
+ fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC)
41
+ try:
42
+ info = os.fstat(fd)
43
+ if (not stat.S_ISREG(info.st_mode) or info.st_uid != os.geteuid()
44
+ or info.st_nlink != 1 or stat.S_IMODE(info.st_mode) & 0o077):
45
+ raise NativeLoadError("native load policy must be a private, owner-owned regular file")
46
+ data = os.read(fd, 32769)
47
+ if len(data) > 32768:
48
+ raise NativeLoadError("native load policy is too large")
49
+ policy = json.loads(data)
50
+ finally:
51
+ os.close(fd)
52
+ fields = {"schemaVersion", "lockPath", "waitSeconds", "minAvailableMiB",
53
+ "restoreReserveMiB", "serviceTimeoutSeconds", "services"}
54
+ if not isinstance(policy, dict) or set(policy) != fields or type(policy["schemaVersion"]) is not int or policy["schemaVersion"] != 1:
55
+ raise NativeLoadError("unsupported native load policy fields or version")
56
+ if not isinstance(policy["lockPath"], str) or not os.path.isabs(policy["lockPath"]):
57
+ raise NativeLoadError("native load policy requires an absolute shared lock path")
58
+ for key in ("waitSeconds", "minAvailableMiB", "restoreReserveMiB", "serviceTimeoutSeconds"):
59
+ value = policy[key]
60
+ if type(value) not in (int, float) or not math.isfinite(value) or value <= 0:
61
+ raise NativeLoadError(f"native load policy {key} must be finite and positive")
62
+ if key.endswith("Seconds") and value > 3600:
63
+ raise NativeLoadError(f"native load policy {key} exceeds one hour")
64
+ peers = policy["services"]
65
+ if not isinstance(peers, list) or len(peers) > 16:
66
+ raise NativeLoadError("native load policy services must be a bounded array")
67
+ seen = set()
68
+ for peer in peers:
69
+ if (not isinstance(peer, dict) or set(peer) != {"unit", "restoreMiB", "readyUrl"}
70
+ or not isinstance(peer["unit"], str)
71
+ or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.@-]{0,240}\.service", peer["unit"])
72
+ or peer["unit"] in seen or type(peer["restoreMiB"]) is not int or peer["restoreMiB"] <= 0):
73
+ raise NativeLoadError("invalid or duplicate native load peer")
74
+ url = urlsplit(peer["readyUrl"]) if isinstance(peer["readyUrl"], str) else None
75
+ if (url is None or url.scheme != "http" or url.hostname not in ("127.0.0.1", "::1")
76
+ or url.username or url.password or url.fragment):
77
+ raise NativeLoadError("native load peer readiness must use credential-free loopback HTTP")
78
+ seen.add(peer["unit"])
79
+ return policy
80
+
81
+
82
+ def available_mib() -> int:
83
+ try:
84
+ for line in Path("/proc/meminfo").read_text().splitlines():
85
+ if line.startswith("MemAvailable:"):
86
+ fields = line.split()
87
+ if len(fields) == 3 and fields[2] == "kB":
88
+ return int(fields[1]) // 1024
89
+ except (OSError, ValueError):
90
+ pass
91
+ raise NativeLoadError("available host RAM cannot be measured")
92
+
93
+
94
+ def service_state(unit: str, timeout: float) -> str:
95
+ try:
96
+ result = subprocess.run(["/usr/bin/systemctl", "--user", "show", unit,
97
+ "--property=LoadState", "--property=ActiveState"],
98
+ check=True, capture_output=True, text=True, timeout=timeout)
99
+ except (OSError, subprocess.SubprocessError) as error:
100
+ raise NativeLoadError(f"cannot inspect native load peer: {unit}") from error
101
+ values = dict(line.split("=", 1) for line in result.stdout.splitlines() if "=" in line)
102
+ if values.get("LoadState") != "loaded":
103
+ raise NativeLoadError(f"configured native load peer is not loaded: {unit}")
104
+ return values.get("ActiveState", "unknown")
105
+
106
+
107
+ def service_action(action: str, unit: str, timeout: float) -> None:
108
+ try:
109
+ subprocess.run(["/usr/bin/systemctl", "--user", action, unit],
110
+ check=True, stdin=subprocess.DEVNULL, capture_output=True, timeout=timeout)
111
+ except (OSError, subprocess.SubprocessError) as error:
112
+ raise NativeLoadError(f"cannot {action} native load peer: {unit}") from error
113
+
114
+
115
+ def wait_ready(peer: dict, timeout: float) -> None:
116
+ class NoRedirect(urllib.request.HTTPRedirectHandler):
117
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
118
+ return None
119
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect())
120
+ deadline = time.monotonic() + timeout
121
+ while time.monotonic() < deadline:
122
+ try:
123
+ with opener.open(peer["readyUrl"], timeout=min(2, max(.01, deadline - time.monotonic()))) as response:
124
+ if response.status == 200:
125
+ return
126
+ except (OSError, ValueError):
127
+ pass
128
+ if service_state(peer["unit"], min(5, timeout)) not in ("active", "activating"):
129
+ raise NativeLoadError(f"native load peer stopped during restoration: {peer['unit']}")
130
+ time.sleep(min(.1, max(0, deadline - time.monotonic())))
131
+ raise NativeLoadError(f"native load peer readiness timed out: {peer['unit']}")
132
+
133
+
134
+ class NativeLoadLease:
135
+ def __init__(self, record: MediaGpuLease):
136
+ self.record = record
137
+ self.outcome = None
138
+
139
+ def confirm_ready(self) -> None:
140
+ self.outcome = "ready"
141
+
142
+ def confirm_stopped(self) -> None:
143
+ self.outcome = "stopped"
144
+
145
+
146
+ def _restore(policy: dict, parked: list[dict]) -> None:
147
+ errors = []
148
+ for peer in parked:
149
+ try:
150
+ needed = policy["restoreReserveMiB"] + peer["restoreMiB"]
151
+ if available_mib() < needed:
152
+ raise NativeLoadError(f"insufficient host RAM to restore {peer['unit']}")
153
+ service_action("start", peer["unit"], policy["serviceTimeoutSeconds"])
154
+ if service_state(peer["unit"], policy["serviceTimeoutSeconds"]) != "active":
155
+ raise NativeLoadError(f"native load peer did not become active: {peer['unit']}")
156
+ wait_ready(peer, policy["serviceTimeoutSeconds"])
157
+ except Exception as error:
158
+ # Preserve the other original peers when one cannot restart.
159
+ errors.append(error)
160
+ if errors:
161
+ raise NativeLoadError("native load peer restoration failed; retain the admission fence") from errors[0]
162
+
163
+
164
+ @contextmanager
165
+ def native_load_guard(env: dict | None):
166
+ """Use MCODA_NATIVE_LOAD_POLICY from trusted host configuration only.
167
+
168
+ The caller must have stopped its old model process before entering. Nested
169
+ retries in the same thread retain one lease; other threads/processes wait.
170
+ No configured policy means no lock, filesystem, systemctl or RAM probes.
171
+ """
172
+ path = (env or {}).get("MCODA_NATIVE_LOAD_POLICY")
173
+ if path is None:
174
+ yield None
175
+ return
176
+ active = getattr(_local, "active", None)
177
+ if active is not None:
178
+ if active[0] != path:
179
+ raise NativeLoadError("cannot nest different native load policies")
180
+ active[1].outcome = None
181
+ yield active[1]
182
+ return
183
+ import fcntl
184
+ try:
185
+ policy = read_policy(path)
186
+ fd = _open_scheduler(policy["lockPath"])
187
+ except (OSError, ValueError, MediaGpuError) as error:
188
+ raise NativeLoadError("cannot open the native load policy or shared lease") from error
189
+ record = None
190
+ parked = []
191
+ entered_model = False
192
+ completed = False
193
+ try:
194
+ deadline = time.monotonic() + policy["waitSeconds"]
195
+ while True:
196
+ try:
197
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
198
+ break
199
+ except BlockingIOError as error:
200
+ if time.monotonic() >= deadline:
201
+ raise NativeLoadError("active media still owns the native load lease") from error
202
+ time.sleep(min(0.1, max(0, deadline - time.monotonic())))
203
+ try:
204
+ previous = _read_lease(fd)
205
+ except MediaGpuError as error:
206
+ raise NativeLoadError("shared native/media lease is unreadable; reconciliation is required") from error
207
+ if previous is not None:
208
+ raise NativeLoadError("an earlier native/media outcome must be reconciled before model loading")
209
+ record = MediaGpuLease(fd, "native-model-load")
210
+ lease = NativeLoadLease(record)
211
+ _local.active = (path, lease)
212
+ for peer in policy["services"]:
213
+ state = service_state(peer["unit"], policy["serviceTimeoutSeconds"])
214
+ if state in ("inactive", "failed"):
215
+ continue
216
+ if state != "active":
217
+ raise NativeLoadError(f"native load peer is changing state: {peer['unit']}")
218
+ # Record before stop: a timed-out stop may still complete.
219
+ parked.append(peer)
220
+ record.record["parked_units"] = [item["unit"] for item in parked]
221
+ _write_lease(fd, record.record)
222
+ service_action("stop", peer["unit"], policy["serviceTimeoutSeconds"])
223
+ if service_state(peer["unit"], policy["serviceTimeoutSeconds"]) != "inactive":
224
+ raise NativeLoadError(f"native load peer has not stopped: {peer['unit']}")
225
+ free = available_mib()
226
+ if free < policy["minAvailableMiB"]:
227
+ raise NativeLoadError(f"native model needs {policy['minAvailableMiB']} MiB available RAM; measured {free}")
228
+ logging.info("native_model_load_admitted available_mib=%d parked_peers=%d", free, len(parked))
229
+ entered_model = True
230
+ try:
231
+ yield lease
232
+ finally:
233
+ # A dead caller or an unconfirmed model process must never admit
234
+ # concurrent media. Keep a durable receipt for native reconciliation.
235
+ if lease.outcome is not None:
236
+ record.mark_uncertain("native_model_peer_restore_pending")
237
+ _restore(policy, parked)
238
+ completed = True
239
+ _write_lease(fd, None)
240
+ else:
241
+ record.mark_uncertain("native_model_load_outcome_unknown")
242
+ if sys.exc_info()[0] is None:
243
+ raise NativeLoadError("native model load returned without readiness or process-death proof")
244
+ finally:
245
+ try:
246
+ if record is not None and not entered_model:
247
+ record.mark_uncertain("native_model_peer_restore_pending")
248
+ _restore(policy, parked)
249
+ completed = True
250
+ _write_lease(fd, None)
251
+ if record is not None and not completed:
252
+ record.mark_uncertain("native_model_load_or_restore_unconfirmed")
253
+ finally:
254
+ if record is not None:
255
+ record._closed = True
256
+ _local.active = None
257
+ os.close(fd)