superlocalmemory 3.7.8 → 3.8.0
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/.claude-plugin/marketplace.json +1 -1
- package/ATTRIBUTION.md +1 -3
- package/CHANGELOG.md +69 -0
- package/README.md +199 -29
- package/package.json +4 -2
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/CLAUDE.md +8 -8
- package/plugin/agents/slm-governance-advisor.md +80 -0
- package/plugin/agents/slm-loop-runner.md +71 -0
- package/plugin/agents/slm-memory-advisor.md +10 -5
- package/plugin/agents/slm-optimize-advisor.md +9 -3
- package/plugin/commands/slm-loop.md +31 -0
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/requirements.txt +1 -1
- package/plugin/scripts/slm-launch +46 -7
- package/plugin/settings.json +9 -0
- package/plugin/skills/slm-cache/SKILL.md +9 -1
- package/plugin/skills/slm-compress/SKILL.md +8 -1
- package/plugin/skills/slm-governance/SKILL.md +248 -0
- package/plugin/skills/slm-graph/SKILL.md +17 -3
- package/plugin/skills/slm-loop/SKILL.md +99 -0
- package/plugin/skills/slm-mesh/SKILL.md +282 -0
- package/plugin/skills/slm-profile/SKILL.md +148 -0
- package/plugin/skills/slm-recall/SKILL.md +46 -10
- package/plugin/skills/slm-remember/SKILL.md +48 -1
- package/plugin/skills/slm-scope/SKILL.md +176 -0
- package/plugin/skills/slm-session/SKILL.md +24 -1
- package/plugin/skills/slm-status/SKILL.md +18 -1
- package/plugin-src/agents/slm-governance-advisor.md +80 -0
- package/plugin-src/agents/slm-loop-runner.md +71 -0
- package/plugin-src/agents/slm-memory-advisor.md +10 -5
- package/plugin-src/agents/slm-optimize-advisor.md +9 -3
- package/plugin-src/commands/slm-loop.md +31 -0
- package/plugin-src/hooks/hooks.json +79 -0
- package/plugin-src/manifest.json +7 -2
- package/plugin-src/requirements.txt +1 -1
- package/plugin-src/rules/AGENTS.md +57 -18
- package/plugin-src/rules/CLAUDE.md.fragment +8 -8
- package/plugin-src/scripts/slm-launch +46 -7
- package/plugin-src/settings.json +9 -0
- package/plugin-src/skills/slm-cache/SKILL.md +9 -1
- package/plugin-src/skills/slm-compress/SKILL.md +8 -1
- package/plugin-src/skills/slm-governance/SKILL.md +248 -0
- package/plugin-src/skills/slm-graph/SKILL.md +17 -3
- package/plugin-src/skills/slm-loop/SKILL.md +99 -0
- package/plugin-src/skills/slm-mesh/SKILL.md +282 -0
- package/plugin-src/skills/slm-profile/SKILL.md +148 -0
- package/plugin-src/skills/slm-recall/SKILL.md +46 -10
- package/plugin-src/skills/slm-remember/SKILL.md +48 -1
- package/plugin-src/skills/slm-scope/SKILL.md +176 -0
- package/plugin-src/skills/slm-session/SKILL.md +24 -1
- package/plugin-src/skills/slm-status/SKILL.md +18 -1
- package/pyproject.toml +1 -1
- package/scripts/postinstall/validation.js +2 -0
- package/scripts/postinstall-interactive.js +74 -2
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/access/__init__.py +3 -0
- package/src/superlocalmemory/access/rbac.py +477 -0
- package/src/superlocalmemory/cli/commands.py +94 -10
- package/src/superlocalmemory/cli/compress_cmd.py +17 -7
- package/src/superlocalmemory/cli/loop_cmd.py +192 -0
- package/src/superlocalmemory/cli/main.py +39 -4
- package/src/superlocalmemory/cli/mesh_cmd.py +38 -0
- package/src/superlocalmemory/cli/optimize_cmd.py +3 -0
- package/src/superlocalmemory/cli/pending_store.py +49 -13
- package/src/superlocalmemory/cli/proxy_cmd.py +4 -0
- package/src/superlocalmemory/cli/scale_engine_cmd.py +6 -0
- package/src/superlocalmemory/cli/setup_wizard.py +22 -13
- package/src/superlocalmemory/compliance/audit.py +6 -0
- package/src/superlocalmemory/compliance/gdpr.py +128 -138
- package/src/superlocalmemory/compliance/retention.py +176 -45
- package/src/superlocalmemory/core/backend_orchestrator.py +5 -43
- package/src/superlocalmemory/core/community_summary.py +267 -0
- package/src/superlocalmemory/core/config.py +216 -3
- package/src/superlocalmemory/core/consolidation_engine.py +95 -22
- package/src/superlocalmemory/core/context_cache.py +61 -18
- package/src/superlocalmemory/core/embedding_worker.py +17 -2
- package/src/superlocalmemory/core/embeddings.py +12 -1
- package/src/superlocalmemory/core/engine.py +17 -1
- package/src/superlocalmemory/core/engine_ingestion.py +29 -0
- package/src/superlocalmemory/core/engine_wiring.py +13 -0
- package/src/superlocalmemory/core/entity_community.py +178 -0
- package/src/superlocalmemory/core/graph_analyzer.py +39 -2
- package/src/superlocalmemory/core/graph_pruner.py +13 -8
- package/src/superlocalmemory/core/key_expander.py +138 -0
- package/src/superlocalmemory/core/maintenance.py +23 -0
- package/src/superlocalmemory/core/modes.py +1 -1
- package/src/superlocalmemory/core/mutations.py +2 -2
- package/src/superlocalmemory/core/pii.py +105 -0
- package/src/superlocalmemory/core/progressive_abstraction.py +208 -0
- package/src/superlocalmemory/core/recall_pipeline.py +2 -0
- package/src/superlocalmemory/core/recall_worker.py +20 -6
- package/src/superlocalmemory/core/scale_engine.py +60 -1
- package/src/superlocalmemory/core/security_primitives.py +40 -2
- package/src/superlocalmemory/core/store_pipeline.py +35 -11
- package/src/superlocalmemory/core/worker_pool.py +21 -6
- package/src/superlocalmemory/encoding/entity_reflexion.py +200 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +34 -24
- package/src/superlocalmemory/encoding/fact_extractor.py +26 -1
- package/src/superlocalmemory/encoding/temporal_validator.py +64 -1
- package/src/superlocalmemory/evolution/evolution_store.py +122 -45
- package/src/superlocalmemory/evolution/llm_dispatch.py +12 -1
- package/src/superlocalmemory/evolution/model_selection.py +160 -0
- package/src/superlocalmemory/evolution/mutation_generator.py +16 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +127 -42
- package/src/superlocalmemory/evolution/triggers.py +22 -13
- package/src/superlocalmemory/graph/cozo_backend.py +43 -20
- package/src/superlocalmemory/hooks/adapter_base.py +5 -1
- package/src/superlocalmemory/hooks/auto_recall.py +13 -1
- package/src/superlocalmemory/hooks/claude_code_hooks.py +11 -0
- package/src/superlocalmemory/hooks/codex_assets.py +64 -5
- package/src/superlocalmemory/hooks/hook_daemon.py +20 -3
- package/src/superlocalmemory/hooks/memory_protocol.py +54 -0
- package/src/superlocalmemory/hooks/portable_kit.py +114 -1
- package/src/superlocalmemory/infra/backup.py +12 -1
- package/src/superlocalmemory/infra/daemon_identity.py +40 -4
- package/src/superlocalmemory/infra/data_root.py +43 -4
- package/src/superlocalmemory/infra/event_bus.py +107 -24
- package/src/superlocalmemory/infra/rate_limiter.py +93 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +4 -1
- package/src/superlocalmemory/ingestion/credentials.py +1 -1
- package/src/superlocalmemory/learning/cross_project.py +28 -19
- package/src/superlocalmemory/learning/reward_proxy.py +42 -9
- package/src/superlocalmemory/loops/__init__.py +56 -0
- package/src/superlocalmemory/loops/budget.py +58 -0
- package/src/superlocalmemory/loops/engine.py +164 -0
- package/src/superlocalmemory/loops/ledger.py +243 -0
- package/src/superlocalmemory/loops/models.py +152 -0
- package/src/superlocalmemory/loops/rules.py +52 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +3 -0
- package/src/superlocalmemory/mcp/_pool_adapter.py +2 -0
- package/src/superlocalmemory/mcp/profiles.py +103 -0
- package/src/superlocalmemory/mcp/server.py +21 -49
- package/src/superlocalmemory/mcp/tools_active.py +4 -7
- package/src/superlocalmemory/mcp/tools_code_graph.py +51 -5
- package/src/superlocalmemory/mcp/tools_core.py +8 -1
- package/src/superlocalmemory/mcp/tools_evolution.py +6 -3
- package/src/superlocalmemory/mcp/tools_loops.py +300 -0
- package/src/superlocalmemory/mcp/tools_mesh.py +140 -4
- package/src/superlocalmemory/mcp/tools_optimize.py +15 -8
- package/src/superlocalmemory/mesh/broker.py +237 -129
- package/src/superlocalmemory/mesh/remote_sync.py +50 -8
- package/src/superlocalmemory/optimize/NOTICE +1 -6
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +1 -4
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +1 -4
- package/src/superlocalmemory/optimize/cache/semantic.py +27 -19
- package/src/superlocalmemory/optimize/compress/align.py +32 -26
- package/src/superlocalmemory/optimize/compress/ccr.py +14 -71
- package/src/superlocalmemory/optimize/compress/router.py +105 -22
- package/src/superlocalmemory/optimize/config/defaults.py +1 -1
- package/src/superlocalmemory/optimize/config/schema.py +87 -4
- package/src/superlocalmemory/optimize/metrics/counters.py +13 -4
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -3
- package/src/superlocalmemory/optimize/proxy/_helpers.py +31 -4
- package/src/superlocalmemory/optimize/storage/db.py +38 -9
- package/src/superlocalmemory/optimize/storage/schema.py +10 -0
- package/src/superlocalmemory/parameterization/pattern_extractor.py +6 -3
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +68 -10
- package/src/superlocalmemory/retrieval/engine.py +168 -26
- package/src/superlocalmemory/retrieval/entity_channel.py +7 -5
- package/src/superlocalmemory/retrieval/hopfield_channel.py +9 -2
- package/src/superlocalmemory/retrieval/semantic_channel.py +114 -21
- package/src/superlocalmemory/retrieval/spreading_activation.py +11 -2
- package/src/superlocalmemory/retrieval/temporal_channel.py +48 -9
- package/src/superlocalmemory/retrieval/temporal_frame.py +102 -0
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +135 -0
- package/src/superlocalmemory/retrieval/time_window.py +181 -0
- package/src/superlocalmemory/server/api.py +4 -4
- package/src/superlocalmemory/server/profile_runtime.py +125 -8
- package/src/superlocalmemory/server/rbac_enforce.py +142 -0
- package/src/superlocalmemory/server/recall_health.py +24 -3
- package/src/superlocalmemory/server/recall_serializer.py +19 -1
- package/src/superlocalmemory/server/routes/abstraction.py +115 -0
- package/src/superlocalmemory/server/routes/agents.py +128 -38
- package/src/superlocalmemory/server/routes/backup.py +34 -10
- package/src/superlocalmemory/server/routes/behavioral.py +13 -12
- package/src/superlocalmemory/server/routes/brain.py +21 -5
- package/src/superlocalmemory/server/routes/chat.py +10 -5
- package/src/superlocalmemory/server/routes/compliance.py +171 -21
- package/src/superlocalmemory/server/routes/config_api.py +436 -0
- package/src/superlocalmemory/server/routes/data_io.py +30 -8
- package/src/superlocalmemory/server/routes/entity.py +9 -4
- package/src/superlocalmemory/server/routes/events.py +24 -8
- package/src/superlocalmemory/server/routes/evolution.py +135 -17
- package/src/superlocalmemory/server/routes/helpers.py +16 -1
- package/src/superlocalmemory/server/routes/ingest.py +7 -4
- package/src/superlocalmemory/server/routes/insights.py +3 -3
- package/src/superlocalmemory/server/routes/learning.py +14 -14
- package/src/superlocalmemory/server/routes/lifecycle.py +59 -8
- package/src/superlocalmemory/server/routes/memories.py +182 -57
- package/src/superlocalmemory/server/routes/mesh.py +95 -15
- package/src/superlocalmemory/server/routes/optimize.py +33 -1
- package/src/superlocalmemory/server/routes/prewarm.py +2 -0
- package/src/superlocalmemory/server/routes/profiles.py +63 -17
- package/src/superlocalmemory/server/routes/ratelimit.py +124 -0
- package/src/superlocalmemory/server/routes/rbac.py +367 -0
- package/src/superlocalmemory/server/routes/stats.py +13 -6
- package/src/superlocalmemory/server/routes/tiers.py +11 -9
- package/src/superlocalmemory/server/routes/v3_api.py +183 -69
- package/src/superlocalmemory/server/routes/ws.py +5 -2
- package/src/superlocalmemory/server/security_middleware.py +12 -5
- package/src/superlocalmemory/server/ui.py +20 -5
- package/src/superlocalmemory/server/unified_daemon.py +384 -56
- package/src/superlocalmemory/server/write_identity.py +38 -8
- package/src/superlocalmemory/storage/database.py +265 -53
- package/src/superlocalmemory/storage/migration_runner.py +53 -0
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +108 -0
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +86 -0
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +194 -0
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +87 -0
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +90 -0
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +136 -0
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +163 -0
- package/src/superlocalmemory/storage/models.py +4 -0
- package/src/superlocalmemory/storage/schema.py +87 -0
- package/src/superlocalmemory/storage/schema_v343.py +24 -12
- package/src/superlocalmemory/trust/gate.py +49 -8
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +64 -0
- package/src/superlocalmemory/ui/assets/slm-icon.svg +36 -0
- package/src/superlocalmemory/ui/css/design-system.css +621 -0
- package/src/superlocalmemory/ui/css/neural-glass.css +6 -0
- package/src/superlocalmemory/ui/css/od-bridge.css +158 -0
- package/src/superlocalmemory/ui/favicon.svg +35 -4
- package/src/superlocalmemory/ui/index.html +306 -173
- package/src/superlocalmemory/ui/js/brain.js +5 -20
- package/src/superlocalmemory/ui/js/core.js +47 -31
- package/src/superlocalmemory/ui/js/dashboard.js +314 -63
- package/src/superlocalmemory/ui/js/event-delegation.js +102 -0
- package/src/superlocalmemory/ui/js/knowledge-graph.js +11 -11
- package/src/superlocalmemory/ui/js/math-health.js +1 -1
- package/src/superlocalmemory/ui/js/memories.js +15 -4
- package/src/superlocalmemory/ui/js/memory-chat.js +7 -7
- package/src/superlocalmemory/ui/js/ng-entities.js +6 -8
- package/src/superlocalmemory/ui/js/ng-ingestion.js +4 -4
- package/src/superlocalmemory/ui/js/ng-mesh.js +4 -9
- package/src/superlocalmemory/ui/js/ng-shell.js +8 -8
- package/src/superlocalmemory/ui/js/ng-skills.js +54 -2
- package/src/superlocalmemory/ui/js/od-agents.js +544 -0
- package/src/superlocalmemory/ui/js/od-auth-gate.js +257 -0
- package/src/superlocalmemory/ui/js/od-backup.js +780 -0
- package/src/superlocalmemory/ui/js/od-brain.js +779 -0
- package/src/superlocalmemory/ui/js/od-entities.js +579 -0
- package/src/superlocalmemory/ui/js/od-graph.js +593 -0
- package/src/superlocalmemory/ui/js/od-health.js +539 -0
- package/src/superlocalmemory/ui/js/od-mcp.js +508 -0
- package/src/superlocalmemory/ui/js/od-memories.js +887 -0
- package/src/superlocalmemory/ui/js/od-mesh.js +539 -0
- package/src/superlocalmemory/ui/js/od-operations.js +1250 -0
- package/src/superlocalmemory/ui/js/od-optimize.js +787 -0
- package/src/superlocalmemory/ui/js/od-settings.js +1053 -0
- package/src/superlocalmemory/ui/js/od-shell.js +593 -0
- package/src/superlocalmemory/ui/js/od-skills.js +573 -0
- package/src/superlocalmemory/ui/js/od-team.js +258 -0
- package/src/superlocalmemory/ui/js/profiles.js +159 -46
- package/src/superlocalmemory/ui/js/settings.js +2 -2
- package/src/superlocalmemory/ui/js/timeline.js +34 -5
- package/src/superlocalmemory/ui/js/trust-dashboard.js +2 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +8 -6
- package/src/superlocalmemory/learning/behavioral_listener.py +0 -94
|
@@ -28,6 +28,103 @@ from mcp.types import ToolAnnotations
|
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# M03: Simple circuit breaker for mesh_send
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# Prevents runaway retries (~30s stall) when the daemon is dead.
|
|
36
|
+
# State machine: CLOSED → OPEN (after 3 consecutive failures)
|
|
37
|
+
# OPEN → HALF_OPEN (after 60s cooldown)
|
|
38
|
+
# HALF_OPEN → CLOSED (on one successful probe)
|
|
39
|
+
# HALF_OPEN → OPEN (on probe failure)
|
|
40
|
+
|
|
41
|
+
_CB_FAILURE_THRESHOLD = 3
|
|
42
|
+
_CB_COOLDOWN_SECONDS = 60
|
|
43
|
+
|
|
44
|
+
# Client-side mesh message cap (mirrors the broker's MAX_MESSAGE_SIZE).
|
|
45
|
+
MAX_MESSAGE_SIZE = 4096
|
|
46
|
+
|
|
47
|
+
_CB_STATE_CLOSED = "closed"
|
|
48
|
+
_CB_STATE_OPEN = "open"
|
|
49
|
+
_CB_STATE_HALF_OPEN = "half_open"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _SendCircuitBreaker:
|
|
53
|
+
"""Thread-safe circuit breaker scoped to mesh_send daemon calls."""
|
|
54
|
+
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
self._lock = threading.Lock()
|
|
57
|
+
self._state: str = _CB_STATE_CLOSED
|
|
58
|
+
self._failure_count: int = 0
|
|
59
|
+
self._opened_at: float = 0.0
|
|
60
|
+
|
|
61
|
+
def reset(self) -> None:
|
|
62
|
+
"""Reset to CLOSED; used by tests for isolation."""
|
|
63
|
+
with self._lock:
|
|
64
|
+
self._state = _CB_STATE_CLOSED
|
|
65
|
+
self._failure_count = 0
|
|
66
|
+
self._opened_at = 0.0
|
|
67
|
+
|
|
68
|
+
def is_open(self) -> bool:
|
|
69
|
+
with self._lock:
|
|
70
|
+
if self._state == _CB_STATE_OPEN:
|
|
71
|
+
if time.monotonic() - self._opened_at >= _CB_COOLDOWN_SECONDS:
|
|
72
|
+
self._state = _CB_STATE_HALF_OPEN
|
|
73
|
+
return False # allow one probe
|
|
74
|
+
return True
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
def allow_request(self) -> bool:
|
|
78
|
+
"""Return True if the call should proceed; False if circuit is OPEN.
|
|
79
|
+
|
|
80
|
+
State transitions inside the lock prevent concurrent probe races:
|
|
81
|
+
- CLOSED → allow
|
|
82
|
+
- OPEN (cooldown not elapsed) → block
|
|
83
|
+
- OPEN (cooldown elapsed) → transition to HALF_OPEN, allow one probe,
|
|
84
|
+
and immediately re-enter OPEN so any concurrent call is blocked until
|
|
85
|
+
the probe result comes in via record_success / record_failure.
|
|
86
|
+
- HALF_OPEN → block (probe already dispatched and not yet resolved)
|
|
87
|
+
"""
|
|
88
|
+
with self._lock:
|
|
89
|
+
if self._state == _CB_STATE_CLOSED:
|
|
90
|
+
return True
|
|
91
|
+
if self._state == _CB_STATE_OPEN:
|
|
92
|
+
if time.monotonic() - self._opened_at >= _CB_COOLDOWN_SECONDS:
|
|
93
|
+
# Grant exactly one probe by briefly entering HALF_OPEN then
|
|
94
|
+
# going back to OPEN. Subsequent callers are blocked until
|
|
95
|
+
# record_success or record_failure resolves the probe.
|
|
96
|
+
self._state = _CB_STATE_HALF_OPEN
|
|
97
|
+
return True
|
|
98
|
+
return False
|
|
99
|
+
# HALF_OPEN: probe is in flight — block until resolved
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def record_success(self) -> None:
|
|
103
|
+
with self._lock:
|
|
104
|
+
self._state = _CB_STATE_CLOSED
|
|
105
|
+
self._failure_count = 0
|
|
106
|
+
|
|
107
|
+
def record_failure(self) -> None:
|
|
108
|
+
with self._lock:
|
|
109
|
+
if self._state == _CB_STATE_HALF_OPEN:
|
|
110
|
+
# Probe failed — reopen immediately
|
|
111
|
+
self._state = _CB_STATE_OPEN
|
|
112
|
+
self._opened_at = time.monotonic()
|
|
113
|
+
return
|
|
114
|
+
self._failure_count += 1
|
|
115
|
+
if self._failure_count >= _CB_FAILURE_THRESHOLD:
|
|
116
|
+
self._state = _CB_STATE_OPEN
|
|
117
|
+
self._opened_at = time.monotonic()
|
|
118
|
+
logger.warning(
|
|
119
|
+
"mesh_send circuit breaker OPEN after %d consecutive failures; "
|
|
120
|
+
"fast-failing for %ds",
|
|
121
|
+
self._failure_count,
|
|
122
|
+
_CB_COOLDOWN_SECONDS,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_SEND_CIRCUIT = _SendCircuitBreaker()
|
|
127
|
+
|
|
31
128
|
# Unique peer ID for this MCP server session
|
|
32
129
|
_PEER_ID = str(uuid.uuid4())[:12]
|
|
33
130
|
_SESSION_SUMMARY = ""
|
|
@@ -69,7 +166,15 @@ def _ensure_registered() -> None:
|
|
|
69
166
|
"session_id": os.environ.get("CLAUDE_SESSION_ID", _PEER_ID),
|
|
70
167
|
"summary": _SESSION_SUMMARY or "SLM MCP session",
|
|
71
168
|
"project_path": _PROJECT_PATH,
|
|
72
|
-
|
|
169
|
+
# Peer identity is the canonical SLM_AGENT_ID (same var memory
|
|
170
|
+
# attribution uses), so Antigravity/Hermes/Cursor/etc. show as
|
|
171
|
+
# themselves instead of collapsing to "claude_code". Fall back to the
|
|
172
|
+
# legacy CLAUDE_AGENT_TYPE, then a generic default.
|
|
173
|
+
"agent_type": (
|
|
174
|
+
os.environ.get("SLM_AGENT_ID")
|
|
175
|
+
or os.environ.get("CLAUDE_AGENT_TYPE")
|
|
176
|
+
or "claude_code"
|
|
177
|
+
),
|
|
73
178
|
})
|
|
74
179
|
if result:
|
|
75
180
|
# v3.6.12 (mesh-1): the broker mints its OWN peer_id (RegisterRequest has
|
|
@@ -174,12 +279,34 @@ def register_mesh_tools(server, get_engine: Callable) -> None:
|
|
|
174
279
|
- "project:/path/to/dir" (all sessions in that project directory)
|
|
175
280
|
message: The message content (max 4KB — use file paths for large data)
|
|
176
281
|
"""
|
|
282
|
+
# Enforce the documented 4KB notification cap client-side too (the
|
|
283
|
+
# broker also caps, but fail fast without a round-trip).
|
|
284
|
+
if len(message.encode("utf-8")) > MAX_MESSAGE_SIZE:
|
|
285
|
+
return {"ok": False, "error": (
|
|
286
|
+
f"message too large (max {MAX_MESSAGE_SIZE} bytes) — "
|
|
287
|
+
"reference a file path instead")}
|
|
288
|
+
# M03: circuit breaker — fast-fail if daemon is repeatedly unreachable
|
|
289
|
+
if not _SEND_CIRCUIT.allow_request():
|
|
290
|
+
return {
|
|
291
|
+
"ok": False,
|
|
292
|
+
"error": (
|
|
293
|
+
"mesh_send circuit open: daemon unreachable after repeated failures. "
|
|
294
|
+
f"Retrying in {_CB_COOLDOWN_SECONDS}s."
|
|
295
|
+
),
|
|
296
|
+
}
|
|
297
|
+
|
|
177
298
|
await asyncio.to_thread(_ensure_registered)
|
|
178
299
|
result = await asyncio.to_thread(
|
|
179
300
|
_mesh_request, "POST", "/send",
|
|
180
301
|
{"from_peer": _PEER_ID, "to_peer": to, "content": message},
|
|
181
302
|
)
|
|
182
|
-
|
|
303
|
+
# Circuit tracks daemon-unreachable (None) only — not valid broker errors
|
|
304
|
+
# like "recipient not found", which are application-level, not failures.
|
|
305
|
+
if result is None:
|
|
306
|
+
_SEND_CIRCUIT.record_failure()
|
|
307
|
+
return {"ok": False, "error": "Failed to send message"}
|
|
308
|
+
_SEND_CIRCUIT.record_success()
|
|
309
|
+
return result
|
|
183
310
|
|
|
184
311
|
@server.tool()
|
|
185
312
|
async def mesh_inbox() -> dict:
|
|
@@ -190,9 +317,11 @@ def register_mesh_tools(server, get_engine: Callable) -> None:
|
|
|
190
317
|
Messages auto-expire after 48 hours.
|
|
191
318
|
"""
|
|
192
319
|
await asyncio.to_thread(_ensure_registered)
|
|
320
|
+
from urllib.parse import quote
|
|
193
321
|
project = _PROJECT_PATH or _detect_project_path()
|
|
194
322
|
messages = await asyncio.to_thread(
|
|
195
|
-
_mesh_request, "GET",
|
|
323
|
+
_mesh_request, "GET",
|
|
324
|
+
f"/inbox/{_PEER_ID}?project_path={quote(project, safe='')}",
|
|
196
325
|
)
|
|
197
326
|
msg_list = (messages or {}).get("messages", [])
|
|
198
327
|
# Auto-mark unread messages as read. v3.6.12 (failopen-2): use .get("id")
|
|
@@ -250,9 +379,16 @@ def register_mesh_tools(server, get_engine: Callable) -> None:
|
|
|
250
379
|
Before editing a shared file, check if another session has it locked.
|
|
251
380
|
|
|
252
381
|
Args:
|
|
253
|
-
file_path:
|
|
382
|
+
file_path: Absolute path to the file
|
|
254
383
|
action: "query" (check lock), "acquire" (lock file), "release" (unlock)
|
|
255
384
|
"""
|
|
385
|
+
# Require a non-empty absolute path; a relative/blank path is ambiguous
|
|
386
|
+
# and lets a caller probe arbitrary strings via the coordination store.
|
|
387
|
+
if not file_path or not (
|
|
388
|
+
file_path.startswith("/")
|
|
389
|
+
or (len(file_path) >= 3 and file_path[1] == ":")
|
|
390
|
+
):
|
|
391
|
+
return {"ok": False, "error": "file_path must be a non-empty absolute path"}
|
|
256
392
|
await asyncio.to_thread(_ensure_registered)
|
|
257
393
|
result = await asyncio.to_thread(
|
|
258
394
|
_mesh_request, "POST", "/lock",
|
|
@@ -170,7 +170,7 @@ def register_optimize_tools(server) -> None:
|
|
|
170
170
|
"ok": False, "content": None, "size_bytes": 0,
|
|
171
171
|
"error": "ccr_id must be a UUID4",
|
|
172
172
|
}
|
|
173
|
-
original = CCRStore.get_instance().retrieve(ccr_id)
|
|
173
|
+
original = CCRStore.get_instance().retrieve(ccr_id, tenant_id=_tenant())
|
|
174
174
|
if original is None:
|
|
175
175
|
return {
|
|
176
176
|
"ok": False, "content": None, "size_bytes": 0,
|
|
@@ -257,13 +257,16 @@ def register_optimize_tools(server) -> None:
|
|
|
257
257
|
cache_key = hashlib.sha256(f"mcpkv:{tenant}:{key}".encode()).hexdigest()
|
|
258
258
|
norm_tid = _normalize_tenant_id(tenant)
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
db = CacheDB.get_default()
|
|
261
|
+
blob = db.get_value(cache_key, norm_tid)
|
|
261
262
|
if blob is None:
|
|
262
263
|
with _kv_lock:
|
|
263
264
|
_kv_misses += 1
|
|
265
|
+
db.kv_counter_incr("kv_misses") # M2: durable across restarts
|
|
264
266
|
return {"ok": True, "hit": False, "value": None, "note": None}
|
|
265
267
|
with _kv_lock:
|
|
266
268
|
_kv_hits += 1
|
|
269
|
+
db.kv_counter_incr("kv_hits") # M2: durable across restarts
|
|
267
270
|
return {"ok": True, "hit": True, "value": blob.decode("utf-8"), "note": None}
|
|
268
271
|
|
|
269
272
|
except Exception as exc:
|
|
@@ -277,14 +280,18 @@ def register_optimize_tools(server) -> None:
|
|
|
277
280
|
async def slm_optimize_stats() -> dict:
|
|
278
281
|
"""Return compression and cache statistics.
|
|
279
282
|
|
|
280
|
-
Proxy/compress stats are daemon-persisted (accurate across
|
|
281
|
-
KV
|
|
283
|
+
Proxy/compress AND KV stats are daemon-persisted (accurate across
|
|
284
|
+
restarts). KV counters fall back to this session's in-memory tally if
|
|
285
|
+
the persisted counters can't be read.
|
|
282
286
|
"""
|
|
283
287
|
try:
|
|
284
|
-
|
|
288
|
+
db = CacheDB.get_default()
|
|
289
|
+
snap = db.metrics_load()
|
|
290
|
+
# M2: prefer the durable KV counters; fall back to session counters.
|
|
291
|
+
persisted = db.kv_counters_load()
|
|
285
292
|
with _kv_lock:
|
|
286
|
-
kv_h = _kv_hits
|
|
287
|
-
kv_m = _kv_misses
|
|
293
|
+
kv_h = persisted.get("kv_hits", _kv_hits)
|
|
294
|
+
kv_m = persisted.get("kv_misses", _kv_misses)
|
|
288
295
|
return {
|
|
289
296
|
"ok": True,
|
|
290
297
|
"compress_runs": snap.compress_runs,
|
|
@@ -297,7 +304,7 @@ def register_optimize_tools(server) -> None:
|
|
|
297
304
|
"CCR entry count not tracked per-session; "
|
|
298
305
|
"see daemon /api/v1/metrics"
|
|
299
306
|
),
|
|
300
|
-
"note": "proxy stats are daemon-persisted
|
|
307
|
+
"note": "proxy and kv stats are daemon-persisted across restarts",
|
|
301
308
|
}
|
|
302
309
|
except Exception as exc:
|
|
303
310
|
logger.error("slm_optimize_stats failed (fail-open): %s", exc)
|