aether-context 0.3.1__tar.gz → 0.5.0__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 (107) hide show
  1. {aether_context-0.3.1/aether_context.egg-info → aether_context-0.5.0}/PKG-INFO +29 -2
  2. {aether_context-0.3.1 → aether_context-0.5.0}/README.md +24 -1
  3. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/__init__.py +1 -1
  4. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/cli.py +7 -0
  5. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/config.py +2 -0
  6. aether_context-0.5.0/aether_context/contracts/__init__.py +534 -0
  7. aether_context-0.5.0/aether_context/contracts/schema-v1.json +2201 -0
  8. aether_context-0.5.0/aether_context/crypto.py +105 -0
  9. aether_context-0.5.0/aether_context/engine.py +1637 -0
  10. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/local_llm.py +90 -16
  11. aether_context-0.5.0/aether_context/policy/__init__.py +66 -0
  12. aether_context-0.5.0/aether_context/retention.py +647 -0
  13. aether_context-0.5.0/aether_context/scale.py +298 -0
  14. aether_context-0.5.0/aether_context/service/__init__.py +279 -0
  15. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/session.py +61 -8
  16. aether_context-0.5.0/aether_context/storage_v2/__init__.py +125 -0
  17. {aether_context-0.3.1 → aether_context-0.5.0/aether_context.egg-info}/PKG-INFO +29 -2
  18. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context.egg-info/SOURCES.txt +14 -0
  19. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context.egg-info/entry_points.txt +1 -0
  20. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context.egg-info/requires.txt +5 -0
  21. {aether_context-0.3.1 → aether_context-0.5.0}/pyproject.toml +6 -3
  22. aether_context-0.5.0/tests/test_context_ipc.py +65 -0
  23. aether_context-0.5.0/tests/test_hosted_context.py +423 -0
  24. aether_context-0.5.0/tests/test_local_llm_lmstudio.py +189 -0
  25. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_release_parity.py +16 -4
  26. aether_context-0.5.0/tests/test_retention_scale.py +1601 -0
  27. aether_context-0.5.0/tests/test_run_recall.py +93 -0
  28. {aether_context-0.3.1 → aether_context-0.5.0}/LICENSE +0 -0
  29. {aether_context-0.3.1 → aether_context-0.5.0}/NOTICE.md +0 -0
  30. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/_log.py +0 -0
  31. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/context_pool.py +0 -0
  32. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/encoder.py +0 -0
  33. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/errors.py +0 -0
  34. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/mpo.py +0 -0
  35. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/py.typed +0 -0
  36. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/quantize.py +0 -0
  37. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/slice_loader.py +0 -0
  38. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/tokenizer.py +0 -0
  39. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/ui.py +0 -0
  40. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context/witness.py +0 -0
  41. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context.egg-info/dependency_links.txt +0 -0
  42. {aether_context-0.3.1 → aether_context-0.5.0}/aether_context.egg-info/top_level.txt +0 -0
  43. {aether_context-0.3.1 → aether_context-0.5.0}/setup.cfg +0 -0
  44. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_aether_agent.py +0 -0
  45. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_commands.py +0 -0
  46. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_config.py +0 -0
  47. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_library.py +0 -0
  48. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_profile.py +0 -0
  49. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_profile_commands.py +0 -0
  50. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_runner.py +0 -0
  51. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_runner_define_command.py +0 -0
  52. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_runner_lock.py +0 -0
  53. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_slash.py +0 -0
  54. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_slash_agents_columns.py +0 -0
  55. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_slash_cmd.py +0 -0
  56. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agent_store.py +0 -0
  57. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_agents_view.py +0 -0
  58. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_api_eval.py +0 -0
  59. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_auth.py +0 -0
  60. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_brains.py +0 -0
  61. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_bridge.py +0 -0
  62. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli.py +0 -0
  63. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli_disk.py +0 -0
  64. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli_dispatch.py +0 -0
  65. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli_doctor.py +0 -0
  66. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli_setup.py +0 -0
  67. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_cli_surface.py +0 -0
  68. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_config.py +0 -0
  69. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_context_pool.py +0 -0
  70. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_encoder.py +0 -0
  71. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_end_to_end.py +0 -0
  72. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_errors.py +0 -0
  73. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_hardware.py +0 -0
  74. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_install_security.py +0 -0
  75. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_local_llm.py +0 -0
  76. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_local_llm_openai.py +0 -0
  77. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_moat_seal.py +0 -0
  78. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_mpo.py +0 -0
  79. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_multi_runner.py +0 -0
  80. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_ollama_ctl.py +0 -0
  81. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_ollama_integration.py +0 -0
  82. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_onboarding.py +0 -0
  83. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_pool_quantize.py +0 -0
  84. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_profiles.py +0 -0
  85. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_progress.py +0 -0
  86. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_protocol_lockstep.py +0 -0
  87. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_quantize.py +0 -0
  88. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_repl_agents.py +0 -0
  89. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_repl_multi.py +0 -0
  90. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_repl_preflight.py +0 -0
  91. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_run_agent_events_system.py +0 -0
  92. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_safety.py +0 -0
  93. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_session.py +0 -0
  94. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_session_fallback.py +0 -0
  95. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_slash.py +0 -0
  96. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_slash_agents.py +0 -0
  97. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_slash_custom_invoke.py +0 -0
  98. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_slash_ollama.py +0 -0
  99. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_slice_loader.py +0 -0
  100. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_smoke.py +0 -0
  101. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_tokenizer.py +0 -0
  102. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_toolparse.py +0 -0
  103. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_transport.py +0 -0
  104. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_ui.py +0 -0
  105. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_web_tools.py +0 -0
  106. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_witness.py +0 -0
  107. {aether_context-0.3.1 → aether_context-0.5.0}/tests/test_workflow_yaml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-context
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: Unlimited Context — virtual memory for an LLM's attention. Local-first, numpy-only core.
5
5
  Author: Aether AI
6
6
  Maintainer: Aether AI
@@ -34,6 +34,9 @@ Requires-Dist: transformers>=4.40; extra == "hf"
34
34
  Requires-Dist: torch>=2.2; extra == "hf"
35
35
  Provides-Extra: fast
36
36
  Requires-Dist: hnswlib>=0.8; extra == "fast"
37
+ Provides-Extra: hosted
38
+ Requires-Dist: pydantic<3,>=2.10; extra == "hosted"
39
+ Requires-Dist: cryptography<51,>=44; extra == "hosted"
37
40
  Provides-Extra: all
38
41
  Requires-Dist: aether-context[fast,hf,llamacpp]; extra == "all"
39
42
  Provides-Extra: dev
@@ -42,6 +45,7 @@ Requires-Dist: pytest-cov; extra == "dev"
42
45
  Requires-Dist: ruff>=0.5; extra == "dev"
43
46
  Requires-Dist: mypy>=1.10; extra == "dev"
44
47
  Requires-Dist: pyyaml>=6; extra == "dev"
48
+ Requires-Dist: aether-context[hosted]; extra == "dev"
45
49
  Dynamic: license-file
46
50
 
47
51
  <div align="center">
@@ -86,6 +90,27 @@ aether-context setup
86
90
  `setup` sizes the pool, checks for a local model, and verifies the engine end to end. It works
87
91
  with no daemon, no network and no model pulled — the check runs against the built-in mock model.
88
92
 
93
+ Two `run` commands can use the same pool directory. The first task is stored as user-authored
94
+ memory; the second retrieves it before generation, even with the default `separate` pool mode:
95
+
96
+ ```bash
97
+ aether-context run "remember: the deploy key rotates on Fridays" --dir ./pool
98
+ aether-context run "when does the deploy key rotate?" --dir ./pool
99
+ ```
100
+
101
+ With the default mock model, the status lines from a fresh pool are:
102
+
103
+ ```text
104
+ [pool 5 GB | slices 2/2275390 | reach 1.17B tok | hit 0% | recalled 0 | mode separate | index flat]
105
+ [pool 5 GB | slices 4/2275390 | reach 1.17B tok | hit 0% | recalled 1 | mode separate | index flat]
106
+ ```
107
+
108
+ The mock prints synthetic text, so use a real local model for an answer to the question.
109
+ `recalled` counts slices supplied before generation; `hit` remains the pager's warm-cache rate.
110
+ Only user and tool memory is supplied by default. `run --include-model-memory` also supplies
111
+ model-authored spill. Retrieved context is capped at 20% of the model window, with room reserved
112
+ for the task and output; a slice too large to fit is skipped.
113
+
89
114
  ```python
90
115
  from aether_context import Session
91
116
 
@@ -242,7 +267,9 @@ session its own pool and index — fully isolated and private, but you pay one i
242
267
  RAM scales with `N × pool` (that's the last column above). `--pool-mode shared` pays for the index
243
268
  **once**; each extra session adds only ~30 MB, so 50–70+ sessions fit and CPU becomes the limit
244
269
  instead of memory. The trade-off is that sessions can see each other's context. Use shared for
245
- related work on one project, separate for unrelated tasks.
270
+ related work on one project, separate for unrelated tasks. A `run` invocation is a special case:
271
+ it recalls prior user/tool facts from the same pool directory on reopen, even in separate mode.
272
+ Use different directories when those tasks must not share memory.
246
273
 
247
274
  **How much building is that?** A ~128K window fills after well under an hour of active agent work,
248
275
  then starts compacting and forgetting. Assuming a busy coding agent encodes ~300K–1M keep-worthy
@@ -40,6 +40,27 @@ aether-context setup
40
40
  `setup` sizes the pool, checks for a local model, and verifies the engine end to end. It works
41
41
  with no daemon, no network and no model pulled — the check runs against the built-in mock model.
42
42
 
43
+ Two `run` commands can use the same pool directory. The first task is stored as user-authored
44
+ memory; the second retrieves it before generation, even with the default `separate` pool mode:
45
+
46
+ ```bash
47
+ aether-context run "remember: the deploy key rotates on Fridays" --dir ./pool
48
+ aether-context run "when does the deploy key rotate?" --dir ./pool
49
+ ```
50
+
51
+ With the default mock model, the status lines from a fresh pool are:
52
+
53
+ ```text
54
+ [pool 5 GB | slices 2/2275390 | reach 1.17B tok | hit 0% | recalled 0 | mode separate | index flat]
55
+ [pool 5 GB | slices 4/2275390 | reach 1.17B tok | hit 0% | recalled 1 | mode separate | index flat]
56
+ ```
57
+
58
+ The mock prints synthetic text, so use a real local model for an answer to the question.
59
+ `recalled` counts slices supplied before generation; `hit` remains the pager's warm-cache rate.
60
+ Only user and tool memory is supplied by default. `run --include-model-memory` also supplies
61
+ model-authored spill. Retrieved context is capped at 20% of the model window, with room reserved
62
+ for the task and output; a slice too large to fit is skipped.
63
+
43
64
  ```python
44
65
  from aether_context import Session
45
66
 
@@ -196,7 +217,9 @@ session its own pool and index — fully isolated and private, but you pay one i
196
217
  RAM scales with `N × pool` (that's the last column above). `--pool-mode shared` pays for the index
197
218
  **once**; each extra session adds only ~30 MB, so 50–70+ sessions fit and CPU becomes the limit
198
219
  instead of memory. The trade-off is that sessions can see each other's context. Use shared for
199
- related work on one project, separate for unrelated tasks.
220
+ related work on one project, separate for unrelated tasks. A `run` invocation is a special case:
221
+ it recalls prior user/tool facts from the same pool directory on reopen, even in separate mode.
222
+ Use different directories when those tasks must not share memory.
200
223
 
201
224
  **How much building is that?** A ~128K window fills after well under an hour of active agent work,
202
225
  then starts compacting and forgetting. Assuming a busy coding agent encodes ~300K–1M keep-worthy
@@ -24,6 +24,6 @@ Public surface (intentionally tiny):
24
24
  from aether_context.local_llm import load_model
25
25
  from aether_context.session import Session
26
26
 
27
- __version__ = "0.3.1"
27
+ __version__ = "0.5.0"
28
28
 
29
29
  __all__ = ["Session", "load_model", "__version__"]
@@ -158,6 +158,10 @@ def build_parser() -> argparse.ArgumentParser:
158
158
  )
159
159
  p_run.add_argument("task", type=str, help="the task/prompt to run.")
160
160
  _add_session_flags(p_run)
161
+ p_run.add_argument(
162
+ "--include-model-memory", action="store_true",
163
+ help="also supply model-authored spill as retrieved context (excluded by default).",
164
+ )
161
165
 
162
166
  p_chat = subparsers.add_parser(
163
167
  "chat", help="interactive REPL with slash-commands (/clear /new /status /quit ...)."
@@ -539,6 +543,7 @@ def _build_session(args: argparse.Namespace) -> Session:
539
543
  pool_index=args.index,
540
544
  pool_dir=pool_dir,
541
545
  mpo_chain=getattr(args, "mpo_chain", True),
546
+ include_model_memory=getattr(args, "include_model_memory", False),
542
547
  fallback_to_mock=True,
543
548
  )
544
549
 
@@ -564,6 +569,7 @@ def _status_line(s: dict[str, Any]) -> str:
564
569
  return (
565
570
  f"[pool {s['pool_gb']} GB | slices {s['slices_used']}/{s['capacity']} | "
566
571
  f"reach {int(s['reach_tokens']) / 1e9:.2f}B tok | hit {float(s['hit_rate']):.0%} | "
572
+ f"recalled {s['recalled']} | "
567
573
  f"mode {s['pool_mode']} | index {s['index']}]"
568
574
  )
569
575
 
@@ -885,6 +891,7 @@ def _status_lines(s: dict[str, Any]) -> list[str]:
885
891
  f" slices: {s['slices_used']} / {s['capacity']}",
886
892
  f" reach: {reach:,} tokens",
887
893
  f" hit rate: {float(s['hit_rate']):.0%}",
894
+ f" recalled: {s['recalled']} slices (last run)",
888
895
  f" resident RAM ~= {s['resident_ram_mb']} MB (estimate)",
889
896
  f" pool-mode: {s['pool_mode']}",
890
897
  f" index: {s['index']}",
@@ -174,12 +174,14 @@ class SessionConfig:
174
174
  verbatim_fraction: float = VERBATIM_FRACTION
175
175
  trigger_fraction: float = TRIGGER_FRACTION
176
176
  target_fraction: float = TARGET_FRACTION
177
+ recall_fraction: float = 0.20
177
178
 
178
179
  def __post_init__(self) -> None:
179
180
  for name, value in (
180
181
  ("verbatim_fraction", self.verbatim_fraction),
181
182
  ("trigger_fraction", self.trigger_fraction),
182
183
  ("target_fraction", self.target_fraction),
184
+ ("recall_fraction", self.recall_fraction),
183
185
  ):
184
186
  if not (0.0 <= float(value) <= 1.0):
185
187
  raise PoolBudgetError(