threadkeeper 0.6.0__tar.gz → 0.6.1__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.
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/PKG-INFO +2 -1
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/README.md +1 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/pyproject.toml +1 -1
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_memory_guard.py +37 -1
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/config.py +3 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/memory_guard.py +4 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/memory_guard.py +1 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/PKG-INFO +2 -1
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/LICENSE +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/setup.cfg +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_adapters.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_brief_sections.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_candidate_reviewer.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_core_memory.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_curator.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_delegated_search.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_dialectic.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_dialectic_tier.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_error_paths.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_extract_daemon.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_i18n_multilang.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_identity.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_lessons.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_missed_spawns.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_nudges.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_process_health.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_shadow_review.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_skill_hint.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_skill_tier.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_skill_use_parser.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_skill_watcher.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_skills.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_spawn_budget.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_spawn_config.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_spawn_hint.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_spawn_slim.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_threads.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_tools_smoke.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_validate_threads.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/tests/test_vec_search.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/__init__.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/_mcp.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/_setup.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/__init__.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/_hook_helpers.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/base.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/claude_code.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/claude_desktop.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/codex.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/copilot.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/gemini.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/adapters/vscode.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/brief.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/candidate_reviewer.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/curator.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/db.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/embeddings.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/extract_daemon.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/helpers.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/i18n.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/identity.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/ingest.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/lessons.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/nudges.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/process_health.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/review_prompts.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/search_proxy.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/server.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/shadow_review.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/skill_watcher.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/spawn_budget.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/spawn_config.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/__init__.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/candidate_reviewer.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/concepts.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/consolidate.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/core_memory.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/correlation.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/curator.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/dialectic.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/dialog.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/distill.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/extract.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/graph.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/invariants.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/lessons.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/missed_spawns.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/peers.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/pickup.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/probes.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/process_health.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/session.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/shadow_review.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/skills.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/spawn.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/style.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/threads.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper/tools/validate.py +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/SOURCES.txt +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/dependency_links.txt +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/entry_points.txt +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/requires.txt +0 -0
- {threadkeeper-0.6.0 → threadkeeper-0.6.1}/threadkeeper.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: threadkeeper
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
|
|
5
5
|
Author: thread-keeper contributors
|
|
6
6
|
License: MIT
|
|
@@ -432,6 +432,7 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
|
|
|
432
432
|
| `THREADKEEPER_MEMORY_GUARD_RECLAIM_MB` | 1024 | local RSS floor before warn-triggered self trim |
|
|
433
433
|
| `THREADKEEPER_MEMORY_GUARD_TARGET_SERVERS` | 1 | aggregate-pressure target after retiring stale idle servers |
|
|
434
434
|
| `THREADKEEPER_MEMORY_GUARD_RETIRE_IDLE_S` | 900 | heartbeat age before a non-self server is retireable |
|
|
435
|
+
| `THREADKEEPER_MEMORY_GUARD_RETIRE_LIVE` | "" (off) | allow retiring parent-alive MCP servers; off protects live clients |
|
|
435
436
|
| `THREADKEEPER_MEMORY_GUARD_NOTIFY` | "1" | send macOS desktop notification when possible |
|
|
436
437
|
| `THREADKEEPER_INGEST_INTERVAL_S` | 3 | transcript ingest tick (s) |
|
|
437
438
|
| `THREADKEEPER_NO_EMBEDDINGS` | "" | force-disable sentence-transformers |
|
|
@@ -398,6 +398,7 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
|
|
|
398
398
|
| `THREADKEEPER_MEMORY_GUARD_RECLAIM_MB` | 1024 | local RSS floor before warn-triggered self trim |
|
|
399
399
|
| `THREADKEEPER_MEMORY_GUARD_TARGET_SERVERS` | 1 | aggregate-pressure target after retiring stale idle servers |
|
|
400
400
|
| `THREADKEEPER_MEMORY_GUARD_RETIRE_IDLE_S` | 900 | heartbeat age before a non-self server is retireable |
|
|
401
|
+
| `THREADKEEPER_MEMORY_GUARD_RETIRE_LIVE` | "" (off) | allow retiring parent-alive MCP servers; off protects live clients |
|
|
401
402
|
| `THREADKEEPER_MEMORY_GUARD_NOTIFY` | "1" | send macOS desktop notification when possible |
|
|
402
403
|
| `THREADKEEPER_INGEST_INTERVAL_S` | 3 | transcript ingest tick (s) |
|
|
403
404
|
| `THREADKEEPER_NO_EMBEDDINGS` | "" | force-disable sentence-transformers |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "threadkeeper"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.1"
|
|
8
8
|
description = "Multi-agent shared brain across Claude Code/Desktop, Codex, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server."
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
authors = [{ name = "thread-keeper contributors" }]
|
|
@@ -195,6 +195,7 @@ def test_check_apply_retires_idle_candidate_on_aggregate_pressure(mp_with_cid, m
|
|
|
195
195
|
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_AGG_KILL_MB", 3000)
|
|
196
196
|
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_TARGET_SERVERS", 1)
|
|
197
197
|
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_RETIRE_IDLE_S", 900)
|
|
198
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_RETIRE_LIVE", False)
|
|
198
199
|
monkeypatch.setattr(memory_guard, "reclaim_memory", lambda reason="": {
|
|
199
200
|
"before_mb": 900, "after_mb": 800, "freed_mb": 100,
|
|
200
201
|
"pid": os.getpid(), "actions": ["fake"],
|
|
@@ -203,7 +204,12 @@ def test_check_apply_retires_idle_candidate_on_aggregate_pressure(mp_with_cid, m
|
|
|
203
204
|
def scan():
|
|
204
205
|
return [
|
|
205
206
|
_proc(os.getpid(), 900),
|
|
206
|
-
_proc(1001, 1200, ppid=
|
|
207
|
+
_proc(1001, 1200, ppid=1) | {
|
|
208
|
+
"heartbeat_age_s": None,
|
|
209
|
+
"parent_alive": False,
|
|
210
|
+
"is_orphaned": True,
|
|
211
|
+
"orphan_reason": "parent_gone + no_heartbeat",
|
|
212
|
+
},
|
|
207
213
|
_proc(1002, 800, ppid=os.getpid()) | {"heartbeat_age_s": 5},
|
|
208
214
|
]
|
|
209
215
|
|
|
@@ -217,3 +223,33 @@ def test_check_apply_retires_idle_candidate_on_aggregate_pressure(mp_with_cid, m
|
|
|
217
223
|
out = memory_guard.check_once(dry_run=False, notify=False)
|
|
218
224
|
assert out["retired"] == [1001]
|
|
219
225
|
assert calls == [(1001, _sig.SIGTERM)]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_aggregate_retire_skips_live_parent_without_opt_in(mp_with_cid, monkeypatch):
|
|
229
|
+
mp_with_cid(_FAKE_CID)
|
|
230
|
+
from threadkeeper import memory_guard, process_health
|
|
231
|
+
|
|
232
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_WARN_MB", 5000)
|
|
233
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_KILL_MB", 6000)
|
|
234
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_AGG_WARN_MB", 2000)
|
|
235
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_AGG_KILL_MB", 3000)
|
|
236
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_TARGET_SERVERS", 1)
|
|
237
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_RETIRE_IDLE_S", 900)
|
|
238
|
+
monkeypatch.setattr(memory_guard, "MEMORY_GUARD_RETIRE_LIVE", False)
|
|
239
|
+
monkeypatch.setattr(memory_guard, "reclaim_memory", lambda reason="": {
|
|
240
|
+
"before_mb": 900, "after_mb": 800, "freed_mb": 100,
|
|
241
|
+
"pid": os.getpid(), "actions": ["fake"],
|
|
242
|
+
})
|
|
243
|
+
monkeypatch.setattr(process_health, "scan", lambda: [
|
|
244
|
+
_proc(os.getpid(), 900),
|
|
245
|
+
_proc(1001, 1200, ppid=os.getpid()) | {"heartbeat_age_s": None},
|
|
246
|
+
])
|
|
247
|
+
calls: list[tuple[int, int]] = []
|
|
248
|
+
monkeypatch.setattr(
|
|
249
|
+
"os.kill",
|
|
250
|
+
lambda pid, sig: calls.append((pid, sig)) if sig != 0 else None,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
out = memory_guard.check_once(dry_run=False, notify=False)
|
|
254
|
+
assert out["retired"] == []
|
|
255
|
+
assert calls == []
|
|
@@ -183,6 +183,9 @@ MEMORY_GUARD_TARGET_SERVERS: int = int(
|
|
|
183
183
|
MEMORY_GUARD_RETIRE_IDLE_S: int = int(
|
|
184
184
|
os.environ.get("THREADKEEPER_MEMORY_GUARD_RETIRE_IDLE_S", "900")
|
|
185
185
|
)
|
|
186
|
+
MEMORY_GUARD_RETIRE_LIVE: bool = os.environ.get(
|
|
187
|
+
"THREADKEEPER_MEMORY_GUARD_RETIRE_LIVE", ""
|
|
188
|
+
).lower() in {"1", "true", "yes", "on"}
|
|
186
189
|
MEMORY_GUARD_NOTIFY: bool = os.environ.get(
|
|
187
190
|
"THREADKEEPER_MEMORY_GUARD_NOTIFY", "1"
|
|
188
191
|
).lower() in {"1", "true", "yes", "on"}
|
|
@@ -28,6 +28,7 @@ from .config import (
|
|
|
28
28
|
MEMORY_GUARD_POLL_S,
|
|
29
29
|
MEMORY_GUARD_RECLAIM_MB,
|
|
30
30
|
MEMORY_GUARD_RETIRE_IDLE_S,
|
|
31
|
+
MEMORY_GUARD_RETIRE_LIVE,
|
|
31
32
|
MEMORY_GUARD_TARGET_SERVERS,
|
|
32
33
|
MEMORY_GUARD_WARN_MB,
|
|
33
34
|
TASK_LOG_DIR,
|
|
@@ -288,6 +289,7 @@ def _aggregate_state(procs: list[dict]) -> dict:
|
|
|
288
289
|
"kill_mb": MEMORY_GUARD_AGG_KILL_MB,
|
|
289
290
|
"target_servers": MEMORY_GUARD_TARGET_SERVERS,
|
|
290
291
|
"retire_idle_s": MEMORY_GUARD_RETIRE_IDLE_S,
|
|
292
|
+
"retire_live": MEMORY_GUARD_RETIRE_LIVE,
|
|
291
293
|
}
|
|
292
294
|
|
|
293
295
|
|
|
@@ -296,6 +298,8 @@ def _idle_retire_candidates(procs: list[dict]) -> list[dict]:
|
|
|
296
298
|
for p in procs:
|
|
297
299
|
if p.get("is_self"):
|
|
298
300
|
continue
|
|
301
|
+
if p.get("parent_alive") and not MEMORY_GUARD_RETIRE_LIVE:
|
|
302
|
+
continue
|
|
299
303
|
hb = p.get("heartbeat_age_s")
|
|
300
304
|
if hb is None or hb >= MEMORY_GUARD_RETIRE_IDLE_S:
|
|
301
305
|
candidates.append(dict(p, rss_mb=_rss_mb(p)))
|
|
@@ -32,6 +32,7 @@ def memory_guard_status() -> str:
|
|
|
32
32
|
f"warn_mb={result['warn_mb']} kill_mb={result['kill_mb']} "
|
|
33
33
|
f"agg_warn_mb={agg['warn_mb']} agg_kill_mb={agg['kill_mb']} "
|
|
34
34
|
f"target_servers={agg['target_servers']} "
|
|
35
|
+
f"retire_live={'on' if agg['retire_live'] else 'off'} "
|
|
35
36
|
f"notify={'on' if result['notify'] else 'off'}",
|
|
36
37
|
f"processes={len(procs)} rss_total={total_mb}MB aggregate={agg_marker}",
|
|
37
38
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: threadkeeper
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
|
|
5
5
|
Author: thread-keeper contributors
|
|
6
6
|
License: MIT
|
|
@@ -432,6 +432,7 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
|
|
|
432
432
|
| `THREADKEEPER_MEMORY_GUARD_RECLAIM_MB` | 1024 | local RSS floor before warn-triggered self trim |
|
|
433
433
|
| `THREADKEEPER_MEMORY_GUARD_TARGET_SERVERS` | 1 | aggregate-pressure target after retiring stale idle servers |
|
|
434
434
|
| `THREADKEEPER_MEMORY_GUARD_RETIRE_IDLE_S` | 900 | heartbeat age before a non-self server is retireable |
|
|
435
|
+
| `THREADKEEPER_MEMORY_GUARD_RETIRE_LIVE` | "" (off) | allow retiring parent-alive MCP servers; off protects live clients |
|
|
435
436
|
| `THREADKEEPER_MEMORY_GUARD_NOTIFY` | "1" | send macOS desktop notification when possible |
|
|
436
437
|
| `THREADKEEPER_INGEST_INTERVAL_S` | 3 | transcript ingest tick (s) |
|
|
437
438
|
| `THREADKEEPER_NO_EMBEDDINGS` | "" | force-disable sentence-transformers |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|