dsh-open-dream-rsi 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -40,8 +40,8 @@ directory holding your `tasks.json`:
40
40
 
41
41
  ```bash
42
42
  # from the npm registry, a tarball, a checkout, or straight from GitHub — pick one:
43
- dsh plugin --profile <name> add dsh-open-dream-rsi # or pin: dsh-open-dream-rsi@0.2.0
44
- dsh plugin --profile <name> add /path/to/dsh-open-dream-rsi-0.2.0.tgz
43
+ dsh plugin --profile <name> add dsh-open-dream-rsi # or pin: dsh-open-dream-rsi@0.2.1
44
+ dsh plugin --profile <name> add /path/to/dsh-open-dream-rsi-0.2.1.tgz
45
45
  dsh plugin --profile <name> add /path/to/open-dream-rsi/plugins/deepseek_harness
46
46
  dsh plugin --profile <name> add github:patrykorwat/open-dream-rsi#path:plugins/deepseek_harness
47
47
 
@@ -46,7 +46,7 @@ from open_dream_rsi.bench_policy import (
46
46
  run_policy_benchmark,
47
47
  )
48
48
 
49
- __version__ = "0.2.0"
49
+ __version__ = "0.2.1"
50
50
 
51
51
  __all__ = [
52
52
  "DiscoveryTree",
@@ -302,6 +302,10 @@ class PolicySandbox:
302
302
  )
303
303
  except subprocess.TimeoutExpired:
304
304
  return PolicyRun(None, "policy execution timed out")
305
+ except subprocess.SubprocessError as exc:
306
+ # A failed spawn (e.g. "Exception occurred in preexec_fn" under a
307
+ # low hard rlimit) must not kill the whole cycle.
308
+ return PolicyRun(None, f"sandbox spawn failed: {exc}")
305
309
  finally:
306
310
  for p in (src_path, fr_path, harness_path):
307
311
  p.unlink(missing_ok=True)
@@ -339,6 +343,10 @@ class PolicySandbox:
339
343
  )
340
344
  except subprocess.TimeoutExpired:
341
345
  return None, [], 0, "policy rollout timed out"
346
+ except subprocess.SubprocessError as exc:
347
+ # See choose(): a failed spawn is an errored rollout, not a
348
+ # crashed cycle.
349
+ return None, [], 0, f"sandbox spawn failed: {exc}"
342
350
  finally:
343
351
  for p in (src_path, world_path, harness_path):
344
352
  p.unlink(missing_ok=True)
@@ -57,21 +57,51 @@ def _memory_mb() -> int:
57
57
 
58
58
 
59
59
  def _make_preexec(timeout: float):
60
- """preexec_fn: RLIMIT_AS / RLIMIT_CPU / RLIMIT_NPROC for the child."""
60
+ """preexec_fn: RLIMIT_AS / RLIMIT_CPU / RLIMIT_NPROC for the child.
61
+
62
+ Best-effort by design: in restrictive environments (a hard rlimit below
63
+ the request — common for daemons and desktop sandboxes) ``setrlimit``
64
+ would raise and kill the spawn with ``Exception occurred in
65
+ preexec_fn``, so each limit is clamped to the hard ceiling and applied
66
+ only when it can be.
67
+ """
61
68
  import resource
62
69
 
63
70
  mem_bytes = _memory_mb() * 1024 * 1024
64
71
  cpu_soft = max(2, int(timeout) + 1)
65
72
 
73
+ def _apply(limit: int, soft: int, hard_extra: Optional[int]) -> bool:
74
+ """Set one rlimit, clamped to the environment's hard ceiling."""
75
+ try:
76
+ _, hard = resource.getrlimit(limit)
77
+ except (ValueError, OSError):
78
+ hard = resource.RLIM_INFINITY
79
+ if hard == 0:
80
+ # The environment forbids this resource entirely; applying
81
+ # soft=0 would insta-kill the child, so skip it.
82
+ return False
83
+ if hard != resource.RLIM_INFINITY:
84
+ soft = min(soft, hard)
85
+ if hard_extra is not None:
86
+ hard = (soft + hard_extra) if hard == resource.RLIM_INFINITY \
87
+ else min(hard, soft + hard_extra)
88
+ try:
89
+ resource.setrlimit(limit, (soft, hard))
90
+ except (ValueError, OSError):
91
+ return False
92
+ return True
93
+
66
94
  def _limits() -> None: # runs in the forked child only
67
- resource.setrlimit(resource.RLIMIT_AS, (mem_bytes, mem_bytes))
68
- resource.setrlimit(resource.RLIMIT_CPU, (cpu_soft, cpu_soft + 1))
95
+ # Each limit is best-effort: degrade to fewer enforced limits
96
+ # rather than kill the spawn.
97
+ _apply(resource.RLIMIT_AS, mem_bytes, None)
98
+ _apply(resource.RLIMIT_CPU, cpu_soft, 1)
99
+ # RLIMIT_NPROC counts THREADS of this uid (shared with the parent),
100
+ # so the floor must sit ABOVE the uid's current task count or every
101
+ # fork fails with EAGAIN. Count threads via /proc, fall back to the
102
+ # static floor where /proc is absent.
103
+ existing = 0
69
104
  try:
70
- # RLIMIT_NPROC counts THREADS of this uid (shared with the
71
- # parent), so the floor must sit ABOVE the uid's current task
72
- # count or every fork fails with EAGAIN. Count threads via
73
- # /proc, fall back to the static floor where /proc is absent.
74
- existing = 0
75
105
  for entry in os.listdir("/proc"):
76
106
  if not entry.isdigit():
77
107
  continue
@@ -87,10 +117,7 @@ def _make_preexec(timeout: float):
87
117
  except OSError:
88
118
  existing = 0
89
119
  cap = max(MAX_PROCS, existing + 64)
90
- try:
91
- resource.setrlimit(resource.RLIMIT_NPROC, (cap, cap))
92
- except (ValueError, OSError):
93
- pass # platform without NPROC limits
120
+ _apply(resource.RLIMIT_NPROC, cap, None)
94
121
 
95
122
  return _limits
96
123
 
@@ -101,6 +101,12 @@ class CodeVerifier:
101
101
  except subprocess.TimeoutExpired:
102
102
  return {"ok": False, "error": "execution timed out",
103
103
  "defined": [], "stdout": ""}
104
+ except subprocess.SubprocessError as exc:
105
+ # Restrictive environments can fail the spawn itself (e.g.
106
+ # "Exception occurred in preexec_fn" under a low hard rlimit):
107
+ # report sandbox evidence instead of killing the whole cycle.
108
+ return {"ok": False, "error": f"sandbox spawn failed: {exc}",
109
+ "defined": [], "stdout": ""}
104
110
  finally:
105
111
  Path(path).unlink(missing_ok=True)
106
112
  line = proc.stdout.strip().splitlines()[-1] if proc.stdout.strip() else ""
@@ -129,6 +135,10 @@ class CodeVerifier:
129
135
  )
130
136
  except subprocess.TimeoutExpired:
131
137
  return ToolResult(False, 0.0, "execution timed out")
138
+ except subprocess.SubprocessError as exc:
139
+ # See smoke_run: a failed spawn is sandbox evidence, not a
140
+ # crashed cycle.
141
+ return ToolResult(False, 0.0, f"sandbox spawn failed: {exc}")
132
142
  finally:
133
143
  Path(path).unlink(missing_ok=True)
134
144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-open-dream-rsi",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Let your coding agent dream: strategies must survive counterfactual replay before they earn trust. An offline self-improvement loop over an evolving world model — no fine-tuning, the LLM stays frozen. Speaks MCP. Ships its Python server inside the package — the only requirement is a system python3 (3.10+) on PATH — and the plugin's bridge serves the dreamer's LLM calls from the model your harness is already configured with (no API keys, no env, no extra endpoints).",
5
5
  "type": "module",
6
6
  "license": "MIT",