@ponythewhite/base-context 1.0.7 → 1.0.8
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/CHANGELOG.md +19 -0
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/src/rlm/__init__.py +3 -0
- package/dist/base-context-runtime/src/rlm/harness.py +86 -48
- package/dist/base-context-runtime/test/test_harness.py +215 -140
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/amazon-bedrock.js +3 -3
- package/dist/bundle/{anthropic-UV5GZDGU.js → anthropic-5PSCZEER.js} +2 -7
- package/dist/bundle/{azure-openai-responses-WSNPXLYC.js → azure-openai-responses-AYTLJR33.js} +2 -2
- package/dist/bundle/{bundled-modules-PS66L3IT.js → bundled-modules-IGMJH2MU.js} +5 -5
- package/dist/bundle/{chunk-UPVDERMD.js → chunk-2IPDSTDR.js} +11 -9
- package/dist/bundle/chunk-D3KOF57W.js +22040 -0
- package/dist/bundle/{chunk-VCW3YDFF.js → chunk-EXEFSSF4.js} +1475 -562
- package/dist/bundle/{chunk-VSIYFWYI.js → chunk-KT2BNEPE.js} +21 -8
- package/dist/bundle/{chunk-KTNETTHI.js → chunk-Q3MBSBEB.js} +3 -101
- package/dist/bundle/{chunk-LFPWHI42.js → chunk-QKIFBA7N.js} +2 -2
- package/dist/bundle/{chunk-PFROC4EV.js → chunk-RVJVXZCS.js} +12 -9
- package/dist/bundle/{cli-main-MMHTK7EB.js → cli-main-BUK7QTHO.js} +4 -4
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{google-T32FMD4G.js → google-HTMCUJ7X.js} +2 -2
- package/dist/bundle/{google-vertex-56KP6I3B.js → google-vertex-ZQO65AXF.js} +2 -2
- package/dist/bundle/{main-XAIVH4Y7.js → main-2FWHT2WZ.js} +5 -5
- package/dist/bundle/{mistral-WGBMYIVU.js → mistral-SB7LU56G.js} +1 -1
- package/dist/bundle/{openai-codex-responses-PIV2RQ3G.js → openai-codex-responses-5CEW4VN3.js} +53 -27
- package/dist/bundle/{openai-completions-64C3JPR4.js → openai-completions-BAZJQSS2.js} +1 -1
- package/dist/bundle/{openai-responses-UI7RSTDV.js → openai-responses-VHOE427P.js} +48 -5
- package/dist/core/agent-session.d.ts +6 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +177 -24
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/canonical-context.d.ts +8 -0
- package/dist/core/canonical-context.d.ts.map +1 -1
- package/dist/core/canonical-context.js +37 -5
- package/dist/core/canonical-context.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +31 -13
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context-epoch.d.ts +1 -1
- package/dist/core/context-epoch.d.ts.map +1 -1
- package/dist/core/context-epoch.js +2 -1
- package/dist/core/context-epoch.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/history-index-worker.d.ts.map +1 -1
- package/dist/core/history-index-worker.js +29 -8
- package/dist/core/history-index-worker.js.map +1 -1
- package/dist/core/history-index.d.ts +11 -1
- package/dist/core/history-index.d.ts.map +1 -1
- package/dist/core/history-index.js +2 -1
- package/dist/core/history-index.js.map +1 -1
- package/dist/core/inference-coordinator.d.ts.map +1 -1
- package/dist/core/inference-coordinator.js +108 -13
- package/dist/core/inference-coordinator.js.map +1 -1
- package/dist/core/kernel/bootstrap.d.ts.map +1 -1
- package/dist/core/kernel/bootstrap.js +97 -107
- package/dist/core/kernel/bootstrap.js.map +1 -1
- package/dist/core/kernel/repl-manager.d.ts.map +1 -1
- package/dist/core/kernel/repl-manager.js +11 -0
- package/dist/core/kernel/repl-manager.js.map +1 -1
- package/dist/core/kernel/shared.d.ts +2 -0
- package/dist/core/kernel/shared.d.ts.map +1 -1
- package/dist/core/kernel/shared.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +4 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/prompts/rlm.d.ts.map +1 -1
- package/dist/core/prompts/rlm.js +4 -2
- package/dist/core/prompts/rlm.js.map +1 -1
- package/dist/core/public-context.d.ts.map +1 -1
- package/dist/core/public-context.js +16 -0
- package/dist/core/public-context.js.map +1 -1
- package/dist/core/refinement/refinement.d.ts +19 -5
- package/dist/core/refinement/refinement.d.ts.map +1 -1
- package/dist/core/refinement/refinement.js +90 -35
- package/dist/core/refinement/refinement.js.map +1 -1
- package/dist/core/request-view-selection.d.ts +10 -2
- package/dist/core/request-view-selection.d.ts.map +1 -1
- package/dist/core/request-view-selection.js +235 -100
- package/dist/core/request-view-selection.js.map +1 -1
- package/dist/core/retained-tool-output.d.ts +28 -0
- package/dist/core/retained-tool-output.d.ts.map +1 -0
- package/dist/core/retained-tool-output.js +102 -0
- package/dist/core/retained-tool-output.js.map +1 -0
- package/dist/core/selective-recovery.d.ts +47 -3
- package/dist/core/selective-recovery.d.ts.map +1 -1
- package/dist/core/selective-recovery.js +428 -109
- package/dist/core/selective-recovery.js.map +1 -1
- package/dist/core/session-history-index.d.ts +3 -1
- package/dist/core/session-history-index.d.ts.map +1 -1
- package/dist/core/session-history-index.js +1 -1
- package/dist/core/session-history-index.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +3 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/task-frame.d.ts +1 -1
- package/dist/core/task-frame.d.ts.map +1 -1
- package/dist/core/task-frame.js +21 -1
- package/dist/core/task-frame.js.map +1 -1
- package/dist/core/task-state-reader.d.ts +10 -2
- package/dist/core/task-state-reader.d.ts.map +1 -1
- package/dist/core/task-state-reader.js +75 -9
- package/dist/core/task-state-reader.js.map +1 -1
- package/dist/core/tools/ipython.d.ts +5 -0
- package/dist/core/tools/ipython.d.ts.map +1 -1
- package/dist/core/tools/ipython.js +59 -17
- package/dist/core/tools/ipython.js.map +1 -1
- package/dist/core/tools/prime-context.d.ts.map +1 -1
- package/dist/core/tools/prime-context.js +1 -1
- package/dist/core/tools/prime-context.js.map +1 -1
- package/dist/core/view-units.d.ts +4 -0
- package/dist/core/view-units.d.ts.map +1 -1
- package/dist/core/view-units.js +28 -19
- package/dist/core/view-units.js.map +1 -1
- package/dist/installer.mjs +98 -77
- package/dist/modes/interactive/components/refinement-outcome-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/refinement-outcome-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +5 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/skills/skill-creator/SKILL.md +18 -1
- package/docs/settings.md +45 -0
- package/docs/skills.md +77 -2
- package/package.json +4 -4
- package/skills/skill-creator/SKILL.md +18 -1
- package/dist/bundle/chunk-W46VZFI6.js +0 -21681
|
@@ -7,6 +7,7 @@ import sys
|
|
|
7
7
|
import tempfile
|
|
8
8
|
import unittest
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from unittest.mock import Mock, patch
|
|
10
11
|
|
|
11
12
|
from rlm import harness as package_harness
|
|
12
13
|
from rlm import rlm as callable_rlm
|
|
@@ -21,6 +22,54 @@ PYTHON_REFERENCE = {
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class HarnessStateTest(unittest.TestCase):
|
|
25
|
+
def test_workspace_lessons_survive_sessions_and_route_explicit_crud(self) -> None:
|
|
26
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
27
|
+
root = Path(temp_dir)
|
|
28
|
+
workspace_dir = root / "project" / ".base-context"
|
|
29
|
+
env = {
|
|
30
|
+
"BASE_CONTEXT_HARNESS_STATE_DIR": str(root / "session-one"),
|
|
31
|
+
"BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR": str(root / "global"),
|
|
32
|
+
"BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR": str(workspace_dir),
|
|
33
|
+
}
|
|
34
|
+
with patch.dict(os.environ, env):
|
|
35
|
+
workspace = callable_rlm.get_harness_state(scope="workspace")
|
|
36
|
+
self.assertIs(workspace, get_harness_state(scope="workspace"))
|
|
37
|
+
workspace.upsert("memory", "Project tests", "Problem: missing dependency. Fix: use .venv/bin/python.",
|
|
38
|
+
id="error-fix:project-tests", metadata={"tool": "ipython", "condition": "project tests"})
|
|
39
|
+
workspace.upsert("memory", "Project tests", "Problem: missing dependency. Fix: use project .venv.",
|
|
40
|
+
id="error-fix:project-tests")
|
|
41
|
+
self.assertEqual(len(workspace.list("memory")), 1)
|
|
42
|
+
self.assertEqual(workspace.file_path, workspace_dir / "harness-state.json")
|
|
43
|
+
# A new session reads the ordinary persisted workspace image, not the old store object.
|
|
44
|
+
with patch.dict(os.environ, {"BASE_CONTEXT_HARNESS_STATE_DIR": str(root / "session-two")}):
|
|
45
|
+
later = HarnessState(workspace.file_path, scope="workspace")
|
|
46
|
+
self.assertEqual(later.get("memory", "error-fix:project-tests").version, 2)
|
|
47
|
+
session = get_harness_state()
|
|
48
|
+
self.assertEqual(session.get("memory", "workspace:error-fix:project-tests").scope, "workspace")
|
|
49
|
+
session.update_memory("workspace:error-fix:project-tests", "Project tests", "Problem: x. Fix: y.")
|
|
50
|
+
self.assertIn("workspace:error-fix:project-tests", workspace.overview())
|
|
51
|
+
workspace.create_memory("Explicit global", "global only", id="global-note", global_=True)
|
|
52
|
+
self.assertIsNone(workspace.get("memory", "global-note"))
|
|
53
|
+
self.assertEqual(get_harness_state(global_=True).get("memory", "global-note").scope, "global")
|
|
54
|
+
self.assertTrue(session.delete_memory("workspace:error-fix:project-tests"))
|
|
55
|
+
self.assertIsNone(later.get("memory", "error-fix:project-tests"))
|
|
56
|
+
with self.assertRaisesRegex(ValueError, "conflicts"):
|
|
57
|
+
get_harness_state(scope="workspace", global_=True)
|
|
58
|
+
with self.assertRaisesRegex(ValueError, "conflicts"):
|
|
59
|
+
workspace.get("memory", "workspace:note", global_=True)
|
|
60
|
+
|
|
61
|
+
def test_workspace_never_falls_back_to_another_project_or_mutable_cwd(self) -> None:
|
|
62
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
63
|
+
first = Path(temp_dir) / "first" / ".base-context"
|
|
64
|
+
second = Path(temp_dir) / "second" / ".base-context"
|
|
65
|
+
with patch.dict(os.environ, {"BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR": str(first)}):
|
|
66
|
+
get_harness_state(scope="workspace").create_memory("First project", "Only here", id="error-fix:tests")
|
|
67
|
+
with patch.dict(os.environ, {"BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR": str(second)}):
|
|
68
|
+
self.assertEqual(get_harness_state(scope="workspace").list("memory"), [])
|
|
69
|
+
with patch.dict(os.environ, {"BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR": ""}):
|
|
70
|
+
with self.assertRaisesRegex(RuntimeError, "Workspace harness state requires"):
|
|
71
|
+
get_harness_state(scope="workspace")
|
|
72
|
+
|
|
24
73
|
def test_crud_for_all_entry_kinds(self) -> None:
|
|
25
74
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
26
75
|
state = HarnessState(Path(temp_dir) / "harness_state.json")
|
|
@@ -292,19 +341,45 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
292
341
|
arguments={},
|
|
293
342
|
)
|
|
294
343
|
|
|
295
|
-
def
|
|
344
|
+
def test_load_rejects_corrupt_or_non_object_state(self) -> None:
|
|
296
345
|
for payload in ("not json at all", "null", "[]", '"a string"', "123"):
|
|
297
346
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
298
347
|
state_path = Path(temp_dir) / "harness_state.json"
|
|
348
|
+
state = HarnessState(state_path)
|
|
299
349
|
state_path.write_text(payload, encoding="utf-8")
|
|
300
350
|
|
|
301
|
-
|
|
351
|
+
with self.assertRaisesRegex(ValueError, "Invalid harness state"):
|
|
352
|
+
HarnessState(state_path)
|
|
353
|
+
with self.assertRaisesRegex(ValueError, "Invalid harness state"):
|
|
354
|
+
state.create_memory("New", "Must not overwrite corruption.")
|
|
355
|
+
self.assertEqual(state_path.read_text(encoding="utf-8"), payload)
|
|
302
356
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
357
|
+
def test_failed_temporary_write_preserves_file_and_mutations(self) -> None:
|
|
358
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
359
|
+
state_path = Path(temp_dir) / "harness_state.json"
|
|
360
|
+
state = HarnessState(state_path)
|
|
361
|
+
current = state.create_memory("Existing", "Original", id="existing")
|
|
362
|
+
image = state_path.read_bytes()
|
|
363
|
+
before = state.snapshot()
|
|
364
|
+
|
|
365
|
+
def failing_temporary_file(*args, **kwargs):
|
|
366
|
+
temporary = tempfile.NamedTemporaryFile(*args, **kwargs)
|
|
367
|
+
temporary.write = Mock(side_effect=OSError("temporary write failed"))
|
|
368
|
+
return temporary
|
|
369
|
+
|
|
370
|
+
with patch("rlm.harness.NamedTemporaryFile", side_effect=failing_temporary_file):
|
|
371
|
+
for mutation in (
|
|
372
|
+
lambda: state.update_memory("existing", "Changed", "Replacement"),
|
|
373
|
+
lambda: state.create_memory("New", "Not published", id="new"),
|
|
374
|
+
lambda: state.delete_memory("existing"),
|
|
375
|
+
lambda: state.record_refinement("failed save", []),
|
|
376
|
+
):
|
|
377
|
+
with self.assertRaisesRegex(OSError, "temporary write failed"):
|
|
378
|
+
mutation()
|
|
379
|
+
self.assertEqual(state_path.read_bytes(), image)
|
|
380
|
+
self.assertEqual(state.snapshot(), before)
|
|
381
|
+
self.assertIs(state.get("memory", "existing"), current)
|
|
382
|
+
self.assertEqual(list(Path(temp_dir).iterdir()), [state_path])
|
|
308
383
|
|
|
309
384
|
def test_update_skill_preserves_omitted_arguments(self) -> None:
|
|
310
385
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
@@ -359,11 +434,11 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
359
434
|
self.assertEqual(state.get("memory", "grouped").path, "repo/other")
|
|
360
435
|
|
|
361
436
|
def test_in_memory_state_never_touches_disk(self) -> None:
|
|
362
|
-
previous = os.environ.get("
|
|
363
|
-
previous_global = os.environ.get("
|
|
437
|
+
previous = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
438
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
364
439
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
365
|
-
os.environ["
|
|
366
|
-
os.environ.pop("
|
|
440
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = temp_dir
|
|
441
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
367
442
|
try:
|
|
368
443
|
state = HarnessState(in_memory=True)
|
|
369
444
|
created = state.create_memory("Volatile", "in memory only", id="volatile")
|
|
@@ -376,27 +451,27 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
376
451
|
self.assertEqual(list(Path(temp_dir).iterdir()), [])
|
|
377
452
|
finally:
|
|
378
453
|
if previous is None:
|
|
379
|
-
os.environ.pop("
|
|
454
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
380
455
|
else:
|
|
381
|
-
os.environ["
|
|
456
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous
|
|
382
457
|
if previous_global is None:
|
|
383
|
-
os.environ.pop("
|
|
458
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
384
459
|
else:
|
|
385
|
-
os.environ["
|
|
460
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
386
461
|
|
|
387
462
|
def test_in_memory_state_global_flag_uses_global_env_store(self) -> None:
|
|
388
|
-
previous_global = os.environ.get("
|
|
463
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
389
464
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
390
465
|
global_dir = Path(temp_dir) / "global"
|
|
391
|
-
os.environ["
|
|
466
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
392
467
|
try:
|
|
393
468
|
state = HarnessState(in_memory=True)
|
|
394
469
|
global_entry = state.create_memory("Global note", "persisted", id="global_note", global_=True)
|
|
395
470
|
finally:
|
|
396
471
|
if previous_global is None:
|
|
397
|
-
os.environ.pop("
|
|
472
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
398
473
|
else:
|
|
399
|
-
os.environ["
|
|
474
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
400
475
|
|
|
401
476
|
self.assertIsNone(state.file_path)
|
|
402
477
|
self.assertEqual(global_entry.scope, "global")
|
|
@@ -408,24 +483,24 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
408
483
|
)
|
|
409
484
|
|
|
410
485
|
def test_in_memory_state_global_flag_uses_default_global_store(self) -> None:
|
|
411
|
-
previous_agent_dir = os.environ.get("
|
|
412
|
-
previous_global = os.environ.get("
|
|
486
|
+
previous_agent_dir = os.environ.get("BASE_CONTEXT_HOME")
|
|
487
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
413
488
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
414
489
|
agent_dir = Path(temp_dir) / "agent"
|
|
415
|
-
os.environ["
|
|
416
|
-
os.environ.pop("
|
|
490
|
+
os.environ["BASE_CONTEXT_HOME"] = str(agent_dir)
|
|
491
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
417
492
|
try:
|
|
418
493
|
state = HarnessState(in_memory=True)
|
|
419
494
|
global_entry = state.create_memory("Default global", "persisted", id="default_global", global_=True)
|
|
420
495
|
finally:
|
|
421
496
|
if previous_agent_dir is None:
|
|
422
|
-
os.environ.pop("
|
|
497
|
+
os.environ.pop("BASE_CONTEXT_HOME", None)
|
|
423
498
|
else:
|
|
424
|
-
os.environ["
|
|
499
|
+
os.environ["BASE_CONTEXT_HOME"] = previous_agent_dir
|
|
425
500
|
if previous_global is None:
|
|
426
|
-
os.environ.pop("
|
|
501
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
427
502
|
else:
|
|
428
|
-
os.environ["
|
|
503
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
429
504
|
|
|
430
505
|
self.assertIsNone(state.file_path)
|
|
431
506
|
self.assertEqual(global_entry.scope, "global")
|
|
@@ -503,19 +578,19 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
503
578
|
self.assertEqual(state.file_path, Path(temp_dir).resolve() / "harness_state.json")
|
|
504
579
|
|
|
505
580
|
def test_explicit_state_dir_global_flag_uses_matching_state_file(self) -> None:
|
|
506
|
-
previous_global = os.environ.get("
|
|
581
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
507
582
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
508
583
|
explicit_dir = Path(temp_dir) / "explicit"
|
|
509
584
|
env_global_dir = Path(temp_dir) / "env-global"
|
|
510
|
-
os.environ["
|
|
585
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(env_global_dir)
|
|
511
586
|
try:
|
|
512
587
|
state = get_harness_state(explicit_dir)
|
|
513
588
|
global_entry = state.create_memory("Scoped global", "custom dir", id="scoped_global", global_=True)
|
|
514
589
|
finally:
|
|
515
590
|
if previous_global is None:
|
|
516
|
-
os.environ.pop("
|
|
591
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
517
592
|
else:
|
|
518
|
-
os.environ["
|
|
593
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
519
594
|
|
|
520
595
|
self.assertEqual(global_entry.scope, "global")
|
|
521
596
|
self.assertIsNotNone(
|
|
@@ -524,13 +599,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
524
599
|
self.assertFalse((env_global_dir / "harness_state.json").exists())
|
|
525
600
|
|
|
526
601
|
def test_env_default_state_keeps_env_global_target_after_explicit_dir_cache_hit(self) -> None:
|
|
527
|
-
previous_local = os.environ.get("
|
|
528
|
-
previous_global = os.environ.get("
|
|
602
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
603
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
529
604
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
530
605
|
local_dir = Path(temp_dir) / "local"
|
|
531
606
|
env_global_dir = Path(temp_dir) / "env-global"
|
|
532
|
-
os.environ["
|
|
533
|
-
os.environ["
|
|
607
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
608
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(env_global_dir)
|
|
534
609
|
try:
|
|
535
610
|
cached_from_env = get_harness_state()
|
|
536
611
|
# An explicit state_dir that aliases the env local dir must not
|
|
@@ -544,13 +619,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
544
619
|
)
|
|
545
620
|
finally:
|
|
546
621
|
if previous_local is None:
|
|
547
|
-
os.environ.pop("
|
|
622
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
548
623
|
else:
|
|
549
|
-
os.environ["
|
|
624
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
550
625
|
if previous_global is None:
|
|
551
|
-
os.environ.pop("
|
|
626
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
552
627
|
else:
|
|
553
|
-
os.environ["
|
|
628
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
554
629
|
|
|
555
630
|
self.assertIs(cached_from_env, cached_from_explicit)
|
|
556
631
|
self.assertEqual(global_entry.scope, "global")
|
|
@@ -564,68 +639,68 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
564
639
|
)
|
|
565
640
|
|
|
566
641
|
def test_local_state_requires_local_path(self) -> None:
|
|
567
|
-
previous_local = os.environ.get("
|
|
568
|
-
previous_session = os.environ.get("
|
|
642
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
643
|
+
previous_session = os.environ.get("BASE_CONTEXT_KERNEL_SESSION_DIR")
|
|
569
644
|
try:
|
|
570
|
-
os.environ.pop("
|
|
571
|
-
os.environ.pop("
|
|
645
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
646
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
572
647
|
with self.assertRaisesRegex(RuntimeError, "Local harness state requires"):
|
|
573
648
|
HarnessState()
|
|
574
649
|
finally:
|
|
575
650
|
if previous_local is None:
|
|
576
|
-
os.environ.pop("
|
|
651
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
577
652
|
else:
|
|
578
|
-
os.environ["
|
|
653
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
579
654
|
if previous_session is None:
|
|
580
|
-
os.environ.pop("
|
|
655
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
581
656
|
else:
|
|
582
|
-
os.environ["
|
|
657
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = previous_session
|
|
583
658
|
|
|
584
659
|
def test_default_state_uses_global_harness_env_dir(self) -> None:
|
|
585
|
-
previous = os.environ.get("
|
|
660
|
+
previous = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
586
661
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
587
|
-
os.environ["
|
|
662
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = temp_dir
|
|
588
663
|
try:
|
|
589
664
|
state = HarnessState()
|
|
590
665
|
finally:
|
|
591
666
|
if previous is None:
|
|
592
|
-
os.environ.pop("
|
|
667
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
593
668
|
else:
|
|
594
|
-
os.environ["
|
|
669
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous
|
|
595
670
|
|
|
596
671
|
self.assertEqual(state.file_path, Path(temp_dir).resolve() / "harness_state.json")
|
|
597
672
|
|
|
598
673
|
def test_global_scope_default_state_uses_global_harness_env_dir(self) -> None:
|
|
599
|
-
previous_local = os.environ.get("
|
|
600
|
-
previous_global = os.environ.get("
|
|
674
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
675
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
601
676
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
602
677
|
local_dir = Path(temp_dir) / "local"
|
|
603
678
|
global_dir = Path(temp_dir) / "global"
|
|
604
|
-
os.environ["
|
|
605
|
-
os.environ["
|
|
679
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
680
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
606
681
|
try:
|
|
607
682
|
state = HarnessState(scope="global")
|
|
608
683
|
finally:
|
|
609
684
|
if previous_local is None:
|
|
610
|
-
os.environ.pop("
|
|
685
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
611
686
|
else:
|
|
612
|
-
os.environ["
|
|
687
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
613
688
|
if previous_global is None:
|
|
614
|
-
os.environ.pop("
|
|
689
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
615
690
|
else:
|
|
616
|
-
os.environ["
|
|
691
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
617
692
|
|
|
618
693
|
self.assertEqual(state.scope, "global")
|
|
619
694
|
self.assertEqual(state.file_path, global_dir.resolve() / "harness_state.json")
|
|
620
695
|
|
|
621
696
|
def test_default_state_is_local_and_global_flag_targets_global_store(self) -> None:
|
|
622
|
-
previous_local = os.environ.get("
|
|
623
|
-
previous_global = os.environ.get("
|
|
697
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
698
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
624
699
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
625
700
|
local_dir = Path(temp_dir) / "local"
|
|
626
701
|
global_dir = Path(temp_dir) / "global"
|
|
627
|
-
os.environ["
|
|
628
|
-
os.environ["
|
|
702
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
703
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
629
704
|
try:
|
|
630
705
|
state = get_harness_state()
|
|
631
706
|
global_state = get_harness_state(global_=True)
|
|
@@ -639,13 +714,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
639
714
|
)
|
|
640
715
|
finally:
|
|
641
716
|
if previous_local is None:
|
|
642
|
-
os.environ.pop("
|
|
717
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
643
718
|
else:
|
|
644
|
-
os.environ["
|
|
719
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
645
720
|
if previous_global is None:
|
|
646
|
-
os.environ.pop("
|
|
721
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
647
722
|
else:
|
|
648
|
-
os.environ["
|
|
723
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
649
724
|
|
|
650
725
|
self.assertEqual(state.file_path, local_dir.resolve() / "harness_state.json")
|
|
651
726
|
self.assertEqual(global_state.file_path, global_dir.resolve() / "harness_state.json")
|
|
@@ -667,11 +742,11 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
667
742
|
state.create_memory("Bad global flag", "bad", id="bad_global", **{"global": "false"})
|
|
668
743
|
|
|
669
744
|
def test_state_cache_keeps_scope_distinct_when_local_and_global_share_a_file(self) -> None:
|
|
670
|
-
previous_local = os.environ.get("
|
|
671
|
-
previous_global = os.environ.get("
|
|
745
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
746
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
672
747
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
673
|
-
os.environ["
|
|
674
|
-
os.environ["
|
|
748
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = temp_dir
|
|
749
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = temp_dir
|
|
675
750
|
try:
|
|
676
751
|
state = get_harness_state()
|
|
677
752
|
global_state = get_harness_state(global_=True)
|
|
@@ -679,13 +754,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
679
754
|
global_entry = state.create_memory("Global note", "All sessions.", id="global_note", global_=True)
|
|
680
755
|
finally:
|
|
681
756
|
if previous_local is None:
|
|
682
|
-
os.environ.pop("
|
|
757
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
683
758
|
else:
|
|
684
|
-
os.environ["
|
|
759
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
685
760
|
if previous_global is None:
|
|
686
|
-
os.environ.pop("
|
|
761
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
687
762
|
else:
|
|
688
|
-
os.environ["
|
|
763
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
689
764
|
|
|
690
765
|
self.assertIsNot(state, global_state)
|
|
691
766
|
self.assertEqual(state.file_path, global_state.file_path)
|
|
@@ -698,13 +773,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
698
773
|
self.assertEqual(reloaded.get("memory", "global_note").scope, "global")
|
|
699
774
|
|
|
700
775
|
def test_scope_prefixed_ids_route_to_the_displayed_scope(self) -> None:
|
|
701
|
-
previous_local = os.environ.get("
|
|
702
|
-
previous_global = os.environ.get("
|
|
776
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
777
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
703
778
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
704
779
|
local_dir = Path(temp_dir) / "local"
|
|
705
780
|
global_dir = Path(temp_dir) / "global"
|
|
706
|
-
os.environ["
|
|
707
|
-
os.environ["
|
|
781
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
782
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
708
783
|
try:
|
|
709
784
|
state = get_harness_state()
|
|
710
785
|
state.create_memory("Global note", "v1", id="routed", global_=True)
|
|
@@ -722,13 +797,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
722
797
|
self.assertIsNone(state.get("memory", "local_note"))
|
|
723
798
|
finally:
|
|
724
799
|
if previous_local is None:
|
|
725
|
-
os.environ.pop("
|
|
800
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
726
801
|
else:
|
|
727
|
-
os.environ["
|
|
802
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
728
803
|
if previous_global is None:
|
|
729
|
-
os.environ.pop("
|
|
804
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
730
805
|
else:
|
|
731
|
-
os.environ["
|
|
806
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
732
807
|
|
|
733
808
|
self.assertEqual(
|
|
734
809
|
HarnessState(global_dir / "harness_state.json", scope="global").get("memory", "routed").content,
|
|
@@ -736,25 +811,25 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
736
811
|
)
|
|
737
812
|
|
|
738
813
|
def test_create_with_prefixed_id_does_not_mint_literal_id(self) -> None:
|
|
739
|
-
previous_local = os.environ.get("
|
|
740
|
-
previous_global = os.environ.get("
|
|
814
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
815
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
741
816
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
742
817
|
local_dir = Path(temp_dir) / "local"
|
|
743
818
|
global_dir = Path(temp_dir) / "global"
|
|
744
|
-
os.environ["
|
|
745
|
-
os.environ["
|
|
819
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
820
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
746
821
|
try:
|
|
747
822
|
state = get_harness_state()
|
|
748
823
|
entry = state.create_memory("Validation", "content", id="global:validation")
|
|
749
824
|
finally:
|
|
750
825
|
if previous_local is None:
|
|
751
|
-
os.environ.pop("
|
|
826
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
752
827
|
else:
|
|
753
|
-
os.environ["
|
|
828
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
754
829
|
if previous_global is None:
|
|
755
|
-
os.environ.pop("
|
|
830
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
756
831
|
else:
|
|
757
|
-
os.environ["
|
|
832
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
758
833
|
|
|
759
834
|
self.assertEqual(entry.id, "validation")
|
|
760
835
|
self.assertEqual(entry.scope, "global")
|
|
@@ -767,39 +842,39 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
767
842
|
# Forkserver scenario: rlm is imported in the template process without the
|
|
768
843
|
# per-session env; the child applies env after fork. rlm.harness must then
|
|
769
844
|
# resolve against the new env instead of a store frozen at import time.
|
|
770
|
-
previous_local = os.environ.get("
|
|
771
|
-
previous_session = os.environ.get("
|
|
845
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
846
|
+
previous_session = os.environ.get("BASE_CONTEXT_KERNEL_SESSION_DIR")
|
|
772
847
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
773
848
|
try:
|
|
774
|
-
os.environ.pop("
|
|
775
|
-
os.environ.pop("
|
|
849
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
850
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
776
851
|
# Without local env, local writes fail loudly instead of vanishing.
|
|
777
852
|
with self.assertRaisesRegex(RuntimeError, "global_=True"):
|
|
778
853
|
package_harness.create_memory("Volatile", "pre-env", id="pre_env")
|
|
779
854
|
|
|
780
|
-
os.environ["
|
|
855
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = temp_dir
|
|
781
856
|
entry = package_harness.create_memory("Session note", "persisted", id="session_note")
|
|
782
857
|
self.assertIsNone(package_harness.get("memory", "pre_env"))
|
|
783
858
|
finally:
|
|
784
859
|
if previous_local is None:
|
|
785
|
-
os.environ.pop("
|
|
860
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
786
861
|
else:
|
|
787
|
-
os.environ["
|
|
862
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
788
863
|
if previous_session is None:
|
|
789
|
-
os.environ.pop("
|
|
864
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
790
865
|
else:
|
|
791
|
-
os.environ["
|
|
866
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = previous_session
|
|
792
867
|
|
|
793
868
|
self.assertEqual(entry.scope, "local")
|
|
794
869
|
reloaded = HarnessState(Path(temp_dir) / "harness_state.json")
|
|
795
870
|
self.assertEqual(reloaded.get("memory", "session_note").content, "persisted")
|
|
796
871
|
|
|
797
872
|
def test_module_harness_without_env_raises_on_local_writes_and_reads_work(self) -> None:
|
|
798
|
-
previous_local = os.environ.get("
|
|
799
|
-
previous_session = os.environ.get("
|
|
873
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
874
|
+
previous_session = os.environ.get("BASE_CONTEXT_KERNEL_SESSION_DIR")
|
|
800
875
|
try:
|
|
801
|
-
os.environ.pop("
|
|
802
|
-
os.environ.pop("
|
|
876
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
877
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
803
878
|
|
|
804
879
|
for mutate in (
|
|
805
880
|
lambda: package_harness.create_memory("Lost", "content", id="lost"),
|
|
@@ -818,38 +893,38 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
818
893
|
self.assertEqual(package_harness.snapshot()["refinements"], [])
|
|
819
894
|
finally:
|
|
820
895
|
if previous_local is None:
|
|
821
|
-
os.environ.pop("
|
|
896
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
822
897
|
else:
|
|
823
|
-
os.environ["
|
|
898
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
824
899
|
if previous_session is None:
|
|
825
|
-
os.environ.pop("
|
|
900
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
826
901
|
else:
|
|
827
|
-
os.environ["
|
|
902
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = previous_session
|
|
828
903
|
|
|
829
904
|
def test_module_harness_without_env_still_routes_global_writes(self) -> None:
|
|
830
|
-
previous_local = os.environ.get("
|
|
831
|
-
previous_session = os.environ.get("
|
|
832
|
-
previous_global = os.environ.get("
|
|
905
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
906
|
+
previous_session = os.environ.get("BASE_CONTEXT_KERNEL_SESSION_DIR")
|
|
907
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
833
908
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
834
909
|
global_dir = Path(temp_dir) / "global"
|
|
835
910
|
try:
|
|
836
|
-
os.environ.pop("
|
|
837
|
-
os.environ.pop("
|
|
838
|
-
os.environ["
|
|
911
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
912
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
913
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(global_dir)
|
|
839
914
|
entry = package_harness.create_memory("Lesson", "keep me", id="no_session_lesson", global_=True)
|
|
840
915
|
finally:
|
|
841
916
|
if previous_local is None:
|
|
842
|
-
os.environ.pop("
|
|
917
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
843
918
|
else:
|
|
844
|
-
os.environ["
|
|
919
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
845
920
|
if previous_session is None:
|
|
846
|
-
os.environ.pop("
|
|
921
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
847
922
|
else:
|
|
848
|
-
os.environ["
|
|
923
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = previous_session
|
|
849
924
|
if previous_global is None:
|
|
850
|
-
os.environ.pop("
|
|
925
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
851
926
|
else:
|
|
852
|
-
os.environ["
|
|
927
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
853
928
|
|
|
854
929
|
self.assertEqual(entry.scope, "global")
|
|
855
930
|
self.assertEqual(
|
|
@@ -859,8 +934,8 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
859
934
|
|
|
860
935
|
def test_import_rlm_without_env_does_not_raise(self) -> None:
|
|
861
936
|
env = dict(os.environ)
|
|
862
|
-
env.pop("
|
|
863
|
-
env.pop("
|
|
937
|
+
env.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
938
|
+
env.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
864
939
|
env["PYTHONPATH"] = str(Path(__file__).resolve().parents[1] / "src")
|
|
865
940
|
result = subprocess.run(
|
|
866
941
|
[sys.executable, "-c", "import rlm; repr(rlm.harness); rlm.harness.overview(); rlm.harness.create_memory"],
|
|
@@ -871,43 +946,43 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
871
946
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
872
947
|
|
|
873
948
|
def test_empty_local_state_dir_env_is_treated_as_unset(self) -> None:
|
|
874
|
-
previous_local = os.environ.get("
|
|
875
|
-
previous_session = os.environ.get("
|
|
949
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
950
|
+
previous_session = os.environ.get("BASE_CONTEXT_KERNEL_SESSION_DIR")
|
|
876
951
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
877
952
|
try:
|
|
878
953
|
# Empty local dir must not fall through to the global agent-dir default.
|
|
879
|
-
os.environ["
|
|
880
|
-
os.environ.pop("
|
|
954
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = ""
|
|
955
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
881
956
|
with self.assertRaisesRegex(RuntimeError, "Local harness state requires"):
|
|
882
957
|
HarnessState()
|
|
883
958
|
|
|
884
959
|
# With a session dir it takes the session fallback instead.
|
|
885
|
-
os.environ["
|
|
960
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = temp_dir
|
|
886
961
|
state = HarnessState()
|
|
887
962
|
self.assertEqual(state.file_path, Path(temp_dir).resolve() / "harness" / "harness_state.json")
|
|
888
963
|
|
|
889
964
|
# A whitespace-only session dir is also unset.
|
|
890
|
-
os.environ["
|
|
965
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = " "
|
|
891
966
|
with self.assertRaisesRegex(RuntimeError, "Local harness state requires"):
|
|
892
967
|
HarnessState()
|
|
893
968
|
finally:
|
|
894
969
|
if previous_local is None:
|
|
895
|
-
os.environ.pop("
|
|
970
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
896
971
|
else:
|
|
897
|
-
os.environ["
|
|
972
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
898
973
|
if previous_session is None:
|
|
899
|
-
os.environ.pop("
|
|
974
|
+
os.environ.pop("BASE_CONTEXT_KERNEL_SESSION_DIR", None)
|
|
900
975
|
else:
|
|
901
|
-
os.environ["
|
|
976
|
+
os.environ["BASE_CONTEXT_KERNEL_SESSION_DIR"] = previous_session
|
|
902
977
|
|
|
903
978
|
def test_explicit_dir_aliasing_env_local_dir_keeps_env_global_target(self) -> None:
|
|
904
|
-
previous_local = os.environ.get("
|
|
905
|
-
previous_global = os.environ.get("
|
|
979
|
+
previous_local = os.environ.get("BASE_CONTEXT_HARNESS_STATE_DIR")
|
|
980
|
+
previous_global = os.environ.get("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR")
|
|
906
981
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
907
982
|
local_dir = Path(temp_dir) / "local"
|
|
908
983
|
env_global_dir = Path(temp_dir) / "env-global"
|
|
909
|
-
os.environ["
|
|
910
|
-
os.environ["
|
|
984
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = str(local_dir)
|
|
985
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = str(env_global_dir)
|
|
911
986
|
try:
|
|
912
987
|
# First construction happens via an explicit dir that merely aliases
|
|
913
988
|
# the env local dir; global writes must still hit the env global dir.
|
|
@@ -915,13 +990,13 @@ class HarnessStateTest(unittest.TestCase):
|
|
|
915
990
|
global_entry = state.create_memory("Aliased", "still global", id="alias_global", global_=True)
|
|
916
991
|
finally:
|
|
917
992
|
if previous_local is None:
|
|
918
|
-
os.environ.pop("
|
|
993
|
+
os.environ.pop("BASE_CONTEXT_HARNESS_STATE_DIR", None)
|
|
919
994
|
else:
|
|
920
|
-
os.environ["
|
|
995
|
+
os.environ["BASE_CONTEXT_HARNESS_STATE_DIR"] = previous_local
|
|
921
996
|
if previous_global is None:
|
|
922
|
-
os.environ.pop("
|
|
997
|
+
os.environ.pop("BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR", None)
|
|
923
998
|
else:
|
|
924
|
-
os.environ["
|
|
999
|
+
os.environ["BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR"] = previous_global
|
|
925
1000
|
|
|
926
1001
|
self.assertEqual(global_entry.scope, "global")
|
|
927
1002
|
self.assertIsNotNone(
|