nucleus-mcp 1.0.7 → 1.2.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.
Files changed (135) hide show
  1. package/package.json +2 -4
  2. package/.github/workflows/ci.yml +0 -72
  3. package/.smithery/shttp/module.js +0 -3
  4. package/.smithery/shttp/module.js.map +0 -7
  5. package/.smithery/stdio/index.cjs +0 -74
  6. package/.smithery/stdio/index.cjs.map +0 -7
  7. package/CHANGELOG.md +0 -65
  8. package/CODE_OF_CONDUCT.md +0 -34
  9. package/COMPARISON.md +0 -20
  10. package/CONTRIBUTING.md +0 -137
  11. package/DEVELOPMENT.md +0 -109
  12. package/LICENSE +0 -21
  13. package/README.md +0 -319
  14. package/ROADMAP.md +0 -24
  15. package/SECURITY.md +0 -88
  16. package/docs/COMMUNITY_FAQ.md +0 -48
  17. package/docs/COMPETITIVE_ANALYSIS.md +0 -90
  18. package/docs/DEMO_SCRIPT.md +0 -86
  19. package/docs/ENTERPRISE.md +0 -169
  20. package/docs/FAQ.md +0 -228
  21. package/docs/QUICK_START.md +0 -155
  22. package/docs/README.md +0 -67
  23. package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
  24. package/docs/social-preview-hq.png +0 -0
  25. package/docs/social-preview.png +0 -0
  26. package/examples/basic_usage.py +0 -84
  27. package/package.json.real +0 -24
  28. package/pyproject.toml +0 -50
  29. package/registry-server.json +0 -20
  30. package/scripts/demo_governance.py +0 -109
  31. package/scripts/sse_bridge.py +0 -87
  32. package/scripts/verify_windows_paths.py +0 -48
  33. package/server.json +0 -20
  34. package/src/mcp_server_nucleus/__init__.py +0 -8583
  35. package/src/mcp_server_nucleus/__main__.py +0 -9
  36. package/src/mcp_server_nucleus/cli.py +0 -1225
  37. package/src/mcp_server_nucleus/commitment_core.py +0 -146
  38. package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
  39. package/src/mcp_server_nucleus/core/__init__.py +0 -6
  40. package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
  41. package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
  42. package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
  43. package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
  44. package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
  45. package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
  46. package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
  47. package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
  48. package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
  49. package/src/mcp_server_nucleus/runtime/agent.py +0 -517
  50. package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
  51. package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
  52. package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
  53. package/src/mcp_server_nucleus/runtime/auth.py +0 -98
  54. package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
  55. package/src/mcp_server_nucleus/runtime/broker.py +0 -126
  56. package/src/mcp_server_nucleus/runtime/budget.py +0 -153
  57. package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
  58. package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
  59. package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
  60. package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
  61. package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
  62. package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
  63. package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
  64. package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
  65. package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
  66. package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
  67. package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
  68. package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
  69. package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
  70. package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
  71. package/src/mcp_server_nucleus/runtime/common.py +0 -134
  72. package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
  73. package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
  74. package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
  75. package/src/mcp_server_nucleus/runtime/critic.py +0 -150
  76. package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
  77. package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
  78. package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
  79. package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
  80. package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
  81. package/src/mcp_server_nucleus/runtime/export.py +0 -64
  82. package/src/mcp_server_nucleus/runtime/factory.py +0 -594
  83. package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
  84. package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
  85. package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
  86. package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
  87. package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
  88. package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
  89. package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
  90. package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
  91. package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
  92. package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
  93. package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
  94. package/src/mcp_server_nucleus/runtime/installer.py +0 -81
  95. package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
  96. package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
  97. package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
  98. package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
  99. package/src/mcp_server_nucleus/runtime/locking.py +0 -228
  100. package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
  101. package/src/mcp_server_nucleus/runtime/memory.py +0 -158
  102. package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
  103. package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
  104. package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
  105. package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
  106. package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
  107. package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
  108. package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
  109. package/src/mcp_server_nucleus/runtime/policy.py +0 -117
  110. package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
  111. package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
  112. package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
  113. package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
  114. package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
  115. package/src/mcp_server_nucleus/runtime/registry.py +0 -82
  116. package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
  117. package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
  118. package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
  119. package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
  120. package/src/mcp_server_nucleus/runtime/storage.py +0 -83
  121. package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
  122. package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
  123. package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
  124. package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
  125. package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
  126. package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
  127. package/src/mcp_server_nucleus/runtime/team.py +0 -59
  128. package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
  129. package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
  130. package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
  131. package/src/mcp_server_nucleus/tool_tiers.py +0 -243
  132. package/tests/__init__.py +0 -1
  133. package/tests/conftest.py +0 -42
  134. package/tests/test_core.py +0 -235
  135. package/tests/test_search.py +0 -69
@@ -1,94 +0,0 @@
1
- from typing import List, Dict, Any
2
- from .base import Capability
3
-
4
- class WebOps(Capability):
5
- @property
6
- def name(self) -> str:
7
- return "web_ops"
8
-
9
- @property
10
- def description(self) -> str:
11
- return "Tools for browsing the web (simulated via requests/search)."
12
-
13
- def get_tools(self) -> List[Dict[str, Any]]:
14
- return [
15
- {
16
- "name": "web_search",
17
- "description": "Search Google for a query.",
18
- "parameters": {
19
- "type": "object",
20
- "properties": {
21
- "query": {"type": "string"},
22
- "num_results": {"type": "integer", "default": 5}
23
- },
24
- "required": ["query"]
25
- }
26
- },
27
- {
28
- "name": "web_read_page",
29
- "description": "Read text content from a URL.",
30
- "parameters": {
31
- "type": "object",
32
- "properties": {
33
- "url": {"type": "string"}
34
- },
35
- "required": ["url"]
36
- }
37
- }
38
- ]
39
-
40
- def execute_tool(self, tool_name: str, args: Dict) -> str:
41
- """Execute web operations."""
42
- if tool_name == "web_search":
43
- query = args['query']
44
- num_results = args.get('num_results', 5)
45
- try:
46
- from duckduckgo_search import DDGS
47
- with DDGS() as ddgs:
48
- results = list(ddgs.text(query, max_results=num_results))
49
-
50
- if not results:
51
- return f"No results found for '{query}'."
52
-
53
- formatted = []
54
- for r in results:
55
- formatted.append(f"- [{r['title']}]({r['href']})\n {r['body']}")
56
-
57
- return "\n\n".join(formatted)
58
- except ImportError:
59
- return "Error: duckduckgo-search not installed. Please install it to use this feature."
60
- except Exception as e:
61
- return f"Search error: {e}"
62
-
63
- elif tool_name == "web_read_page":
64
- url = args['url']
65
- try:
66
- import urllib.request
67
- from bs4 import BeautifulSoup
68
-
69
- headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
70
- req = urllib.request.Request(url, headers=headers)
71
-
72
- with urllib.request.urlopen(req, timeout=10) as response:
73
- html_content = response.read().decode('utf-8')
74
-
75
- soup = BeautifulSoup(html_content, 'html.parser')
76
-
77
- # Remove script and style elements
78
- for script in soup(['script', 'style', 'nav', 'footer', 'header']):
79
- script.decompose()
80
-
81
- text = soup.get_text()
82
-
83
- # Clean up whitespace
84
- lines = (line.strip() for line in text.splitlines())
85
- chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
86
- text = '\n'.join(chunk for chunk in chunks if chunk)
87
-
88
- return f"URL: {url}\n\n{text[:8000]}..." # Truncate to 8k chars
89
- except ImportError:
90
- return "Error: beautifulsoup4 not installed. Please install it to use this feature."
91
- except Exception as e:
92
- return f"Error reading {url}: {e}"
93
-
94
- return f"Tool {tool_name} not found in WebOps."
@@ -1,134 +0,0 @@
1
- """
2
- Nucleus Runtime - Common Utilities
3
- ==================================
4
- Shared utilities and constants for the Nucleus runtime.
5
- """
6
-
7
- import os
8
- import json
9
- import logging
10
- import sys
11
- from pathlib import Path
12
- from datetime import datetime, timezone
13
- from typing import Dict, Any, Optional
14
-
15
- # ============================================================
16
- # STRUCTURED LOGGING SYSTEM (AG-010)
17
- # ============================================================
18
-
19
- class JSONFormatter(logging.Formatter):
20
- """Formats log records as JSON objects for machine readability."""
21
- def format(self, record):
22
- log_entry = {
23
- "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
24
- "level": record.levelname,
25
- "logger": record.name,
26
- "message": record.getMessage(),
27
- "module": record.module,
28
- "line": record.lineno
29
- }
30
- if record.exc_info:
31
- log_entry["exception"] = self.formatException(record.exc_info)
32
- return json.dumps(log_entry)
33
-
34
- def setup_nucleus_logging(name: str = "nucleus", level: int = logging.INFO):
35
- """Setup structured logging for a component."""
36
- logger = logging.getLogger(name)
37
- logger.setLevel(level)
38
-
39
- # Avoid duplicate handlers
40
- if not logger.handlers:
41
- handler = logging.StreamHandler(sys.stdout)
42
-
43
- # Use JSON if specified via env
44
- if os.environ.get("NUCLEUS_LOG_JSON", "false").lower() == "true":
45
- handler.setFormatter(JSONFormatter())
46
- else:
47
- handler.setFormatter(logging.Formatter(
48
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
49
- ))
50
-
51
- logger.addHandler(handler)
52
- return logger
53
-
54
- # Common logger
55
- logger = setup_nucleus_logging()
56
-
57
- def get_brain_path() -> Path:
58
- """Get the brain path from environment variable (read dynamically for testing)."""
59
- brain_path = os.environ.get("NUCLEAR_BRAIN_PATH")
60
- if not brain_path:
61
- raise ValueError("NUCLEAR_BRAIN_PATH environment variable not set")
62
- path = Path(brain_path)
63
- if not path.exists():
64
- raise ValueError(f"Brain path does not exist: {brain_path}")
65
- return path
66
-
67
- def make_response(success: bool, data=None, error=None, error_code=None):
68
- """Standardized API response formatter.
69
-
70
- Args:
71
- success: Whether the operation succeeded
72
- data: Successful payload (dict, list, string)
73
- error: Error message if failed
74
- error_code: Optional short code for error (e.g. ERR_NOT_FOUND)
75
-
76
- Returns:
77
- JSON string matching Nucleus Standard Response Schema
78
- """
79
- return json.dumps({
80
- "success": success,
81
- "data": data,
82
- "error": error,
83
- "error_code": error_code,
84
- "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
85
- }, indent=2)
86
-
87
- def _get_state(path: Optional[str] = None) -> Dict:
88
- """Core logic for getting state."""
89
- try:
90
- brain = get_brain_path()
91
- state_path = brain / "ledger" / "state.json"
92
-
93
- if not state_path.exists():
94
- return {}
95
-
96
- from .sync_ops import sync_lock
97
- with sync_lock(brain, timeout=2):
98
- with open(state_path, "r") as f:
99
- state = json.load(f)
100
-
101
- if path:
102
- keys = path.split('.')
103
- val = state
104
- for k in keys:
105
- val = val.get(k, {})
106
- return val
107
-
108
- return state
109
- except Exception as e:
110
- logger.error(f"Error reading state: {e}")
111
- return {}
112
-
113
- def _update_state(updates: Dict[str, Any]) -> str:
114
- """Core logic for updating state."""
115
- try:
116
- brain = get_brain_path()
117
- state_path = brain / "ledger" / "state.json"
118
- state_path.parent.mkdir(parents=True, exist_ok=True)
119
-
120
- from .sync_ops import sync_lock
121
- with sync_lock(brain, timeout=5):
122
- current_state = {}
123
- if state_path.exists():
124
- with open(state_path, "r") as f:
125
- current_state = json.load(f)
126
-
127
- current_state.update(updates)
128
-
129
- with open(state_path, "w") as f:
130
- json.dump(current_state, f, indent=2)
131
-
132
- return "State updated successfully"
133
- except Exception as e:
134
- return f"Error updating state: {str(e)}"
@@ -1,342 +0,0 @@
1
- """
2
- v0.6.0 DSoR: Context Manager
3
-
4
- Provides stateless hashing of the current world-state for decision provenance.
5
- This module enables verification of state before/after agent turns to ensure
6
- integrity and detect unauthorized mutations.
7
-
8
- Part of the Decision System of Record (DSoR) initiative.
9
- """
10
-
11
- import hashlib
12
- import json
13
- import os
14
- from datetime import datetime, timezone
15
- from pathlib import Path
16
- from typing import Dict, Any, List, Optional, Tuple
17
- from dataclasses import dataclass, field, asdict
18
-
19
-
20
- def get_brain_path() -> Path:
21
- """Get the brain path from environment."""
22
- return Path(os.getenv("NUCLEAR_BRAIN_PATH", "/Users/lokeshgarg/ai-mvp-backend/.brain"))
23
-
24
-
25
- @dataclass
26
- class ContextSnapshot:
27
- """
28
- Immutable snapshot of world-state at a point in time.
29
- Used for before/after comparisons in agent turns.
30
- """
31
- snapshot_id: str
32
- timestamp: str
33
- state_hash: str
34
- component_hashes: Dict[str, str] = field(default_factory=dict)
35
- metadata: Dict[str, Any] = field(default_factory=dict)
36
-
37
- def to_dict(self) -> Dict[str, Any]:
38
- return asdict(self)
39
-
40
- @classmethod
41
- def from_dict(cls, data: Dict[str, Any]) -> "ContextSnapshot":
42
- return cls(**data)
43
-
44
-
45
- @dataclass
46
- class StateVerificationResult:
47
- """Result of comparing two context snapshots."""
48
- is_valid: bool
49
- before_hash: str
50
- after_hash: str
51
- mutations_detected: List[str] = field(default_factory=list)
52
- drift_components: Dict[str, Tuple[str, str]] = field(default_factory=dict)
53
- verification_timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
54
-
55
-
56
- class ContextManager:
57
- """
58
- v0.6.0 DSoR: Manages context state hashing and verification.
59
-
60
- Provides:
61
- 1. Stateless hashing of current world-state
62
- 2. Before/after verification for agent turns
63
- 3. Drift detection for unauthorized mutations
64
- """
65
-
66
- def __init__(self, brain_path: Optional[Path] = None):
67
- self.brain_path = brain_path or get_brain_path()
68
- self._snapshot_counter = 0
69
-
70
- def _hash_file(self, file_path: Path) -> Optional[str]:
71
- """Compute SHA-256 hash of a file's contents."""
72
- try:
73
- if file_path.exists() and file_path.is_file():
74
- content = file_path.read_bytes()
75
- return hashlib.sha256(content).hexdigest()[:16]
76
- except Exception:
77
- pass
78
- return None
79
-
80
- def _hash_dict(self, data: Dict[str, Any]) -> str:
81
- """Compute deterministic hash of a dictionary."""
82
- serialized = json.dumps(data, sort_keys=True, default=str)
83
- return hashlib.sha256(serialized.encode()).hexdigest()[:16]
84
-
85
- def _collect_ledger_state(self) -> Dict[str, Any]:
86
- """Collect state from the ledger directory."""
87
- ledger_path = self.brain_path / "ledger"
88
- state = {
89
- "engrams_hash": None,
90
- "events_hash": None,
91
- "tasks_hash": None,
92
- "decisions_hash": None,
93
- }
94
-
95
- if ledger_path.exists():
96
- # Hash key ledger files
97
- engrams_file = ledger_path / "engrams.jsonl"
98
- events_file = ledger_path / "events.jsonl"
99
- tasks_file = ledger_path / "tasks.json"
100
- decisions_dir = ledger_path / "decisions"
101
-
102
- state["engrams_hash"] = self._hash_file(engrams_file)
103
- state["events_hash"] = self._hash_file(events_file)
104
- state["tasks_hash"] = self._hash_file(tasks_file)
105
-
106
- if decisions_dir.exists():
107
- decisions_file = decisions_dir / "decisions.jsonl"
108
- state["decisions_hash"] = self._hash_file(decisions_file)
109
-
110
- return state
111
-
112
- def _collect_slots_state(self) -> Dict[str, Any]:
113
- """Collect state from the slots directory."""
114
- slots_path = self.brain_path / "slots"
115
- state = {"registry_hash": None, "slot_count": 0}
116
-
117
- if slots_path.exists():
118
- registry_file = slots_path / "registry.json"
119
- state["registry_hash"] = self._hash_file(registry_file)
120
- state["slot_count"] = len(list(slots_path.glob("slot_*.json")))
121
-
122
- return state
123
-
124
- def _collect_mounts_state(self) -> Dict[str, Any]:
125
- """Collect state from mounted servers."""
126
- mounts_file = self.brain_path / "ledger" / "mounts.json"
127
- state = {"mounts_hash": None, "mount_count": 0}
128
-
129
- if mounts_file.exists():
130
- state["mounts_hash"] = self._hash_file(mounts_file)
131
- try:
132
- with open(mounts_file) as f:
133
- mounts = json.load(f)
134
- state["mount_count"] = len(mounts.get("servers", []))
135
- except Exception:
136
- pass
137
-
138
- return state
139
-
140
- def compute_world_state_hash(self, include_docs: bool = False,
141
- recent_events: Optional[List[str]] = None) -> Tuple[str, Dict[str, str]]:
142
- """
143
- Compute a comprehensive hash of the current world-state.
144
-
145
- Args:
146
- include_docs: Whether to include document checksums
147
- recent_events: Optional list of recent event strings to include
148
-
149
- Returns:
150
- Tuple of (overall_hash, component_hashes)
151
- """
152
- components = {}
153
-
154
- # 1. Ledger state
155
- ledger_state = self._collect_ledger_state()
156
- components["ledger"] = self._hash_dict(ledger_state)
157
-
158
- # 2. Slots state
159
- slots_state = self._collect_slots_state()
160
- components["slots"] = self._hash_dict(slots_state)
161
-
162
- # 3. Mounts state
163
- mounts_state = self._collect_mounts_state()
164
- components["mounts"] = self._hash_dict(mounts_state)
165
-
166
- # 4. Recent events (if provided)
167
- if recent_events:
168
- events_blob = json.dumps(recent_events[-10:], sort_keys=True)
169
- components["recent_events"] = hashlib.sha256(events_blob.encode()).hexdigest()[:16]
170
-
171
- # 5. Optional: Include docs checksums
172
- if include_docs:
173
- docs_path = self.brain_path / "artifacts"
174
- if docs_path.exists():
175
- doc_hashes = []
176
- for md_file in docs_path.rglob("*.md"):
177
- h = self._hash_file(md_file)
178
- if h:
179
- doc_hashes.append(f"{md_file.name}:{h}")
180
- if doc_hashes:
181
- components["docs"] = hashlib.sha256(
182
- "|".join(sorted(doc_hashes)).encode()
183
- ).hexdigest()[:16]
184
-
185
- # Compute overall hash from components
186
- overall = self._hash_dict(components)
187
-
188
- return overall, components
189
-
190
- def take_snapshot(self, include_docs: bool = False,
191
- recent_events: Optional[List[str]] = None,
192
- metadata: Optional[Dict[str, Any]] = None) -> ContextSnapshot:
193
- """
194
- Take an immutable snapshot of the current world-state.
195
-
196
- Args:
197
- include_docs: Whether to include document checksums
198
- recent_events: Optional list of recent event strings
199
- metadata: Optional metadata to attach to snapshot
200
-
201
- Returns:
202
- ContextSnapshot instance
203
- """
204
- self._snapshot_counter += 1
205
- state_hash, component_hashes = self.compute_world_state_hash(
206
- include_docs=include_docs,
207
- recent_events=recent_events
208
- )
209
-
210
- snapshot = ContextSnapshot(
211
- snapshot_id=f"snap-{self._snapshot_counter:06d}",
212
- timestamp=datetime.now(timezone.utc).isoformat(),
213
- state_hash=state_hash,
214
- component_hashes=component_hashes,
215
- metadata=metadata or {}
216
- )
217
-
218
- return snapshot
219
-
220
- def verify_state_integrity(self, before: ContextSnapshot,
221
- after: ContextSnapshot,
222
- allowed_mutations: Optional[List[str]] = None) -> StateVerificationResult:
223
- """
224
- Verify state integrity by comparing before/after snapshots.
225
-
226
- Args:
227
- before: Snapshot taken before agent turn
228
- after: Snapshot taken after agent turn
229
- allowed_mutations: List of component names that are allowed to change
230
-
231
- Returns:
232
- StateVerificationResult with drift analysis
233
- """
234
- allowed = set(allowed_mutations or [])
235
- mutations = []
236
- drift_components = {}
237
-
238
- # Compare component hashes
239
- all_components = set(before.component_hashes.keys()) | set(after.component_hashes.keys())
240
-
241
- for component in all_components:
242
- before_hash = before.component_hashes.get(component)
243
- after_hash = after.component_hashes.get(component)
244
-
245
- if before_hash != after_hash:
246
- mutations.append(component)
247
- drift_components[component] = (before_hash or "none", after_hash or "none")
248
-
249
- # Check if mutations are within allowed set
250
- unauthorized_mutations = [m for m in mutations if m not in allowed]
251
- is_valid = len(unauthorized_mutations) == 0
252
-
253
- return StateVerificationResult(
254
- is_valid=is_valid,
255
- before_hash=before.state_hash,
256
- after_hash=after.state_hash,
257
- mutations_detected=mutations,
258
- drift_components=drift_components
259
- )
260
-
261
- def persist_snapshot(self, snapshot: ContextSnapshot) -> Path:
262
- """
263
- Persist a snapshot to the ledger for audit purposes.
264
-
265
- Args:
266
- snapshot: The snapshot to persist
267
-
268
- Returns:
269
- Path to the persisted snapshot file
270
- """
271
- snapshots_dir = self.brain_path / "ledger" / "snapshots"
272
- snapshots_dir.mkdir(parents=True, exist_ok=True)
273
-
274
- snapshot_file = snapshots_dir / f"{snapshot.snapshot_id}.json"
275
- snapshot_file.write_text(json.dumps(snapshot.to_dict(), indent=2))
276
-
277
- return snapshot_file
278
-
279
- def load_snapshot(self, snapshot_id: str) -> Optional[ContextSnapshot]:
280
- """
281
- Load a previously persisted snapshot.
282
-
283
- Args:
284
- snapshot_id: The ID of the snapshot to load
285
-
286
- Returns:
287
- ContextSnapshot if found, None otherwise
288
- """
289
- snapshots_dir = self.brain_path / "ledger" / "snapshots"
290
- snapshot_file = snapshots_dir / f"{snapshot_id}.json"
291
-
292
- if snapshot_file.exists():
293
- try:
294
- data = json.loads(snapshot_file.read_text())
295
- return ContextSnapshot.from_dict(data)
296
- except Exception:
297
- pass
298
-
299
- return None
300
-
301
-
302
- # Singleton instance for convenience
303
- _context_manager: Optional[ContextManager] = None
304
-
305
-
306
- def get_context_manager() -> ContextManager:
307
- """Get or create the singleton ContextManager instance."""
308
- global _context_manager
309
- if _context_manager is None:
310
- _context_manager = ContextManager()
311
- return _context_manager
312
-
313
-
314
- def compute_context_hash(recent_events: Optional[List[str]] = None) -> str:
315
- """
316
- Convenience function to compute the current context hash.
317
-
318
- Args:
319
- recent_events: Optional list of recent event strings
320
-
321
- Returns:
322
- SHA-256 hash (truncated to 16 chars) of current world-state
323
- """
324
- cm = get_context_manager()
325
- state_hash, _ = cm.compute_world_state_hash(recent_events=recent_events)
326
- return state_hash
327
-
328
-
329
- def verify_turn_integrity(before_hash: str, after_hash: str,
330
- allowed_drift: float = 0.0) -> bool:
331
- """
332
- Simple verification that state hasn't drifted unexpectedly.
333
-
334
- Args:
335
- before_hash: Hash from before agent turn
336
- after_hash: Hash from after agent turn
337
- allowed_drift: Currently unused, reserved for fuzzy matching
338
-
339
- Returns:
340
- True if hashes match (no drift), False otherwise
341
- """
342
- return before_hash == after_hash
@@ -1,86 +0,0 @@
1
- [
2
- {
3
- "keywords": [
4
- "deploy",
5
- "cloud run",
6
- "build",
7
- "ship",
8
- "production"
9
- ],
10
- "inject": [
11
- "cloudbuild.yaml",
12
- "docs/infra/DEPLOYMENT.md",
13
- "docs/infra/RELEASE_PROTOCOL.md"
14
- ],
15
- "description": "Deployment and Infrastructure Context"
16
- },
17
- {
18
- "keywords": [
19
- "database",
20
- "schema",
21
- "sql",
22
- "postgres",
23
- "migration"
24
- ],
25
- "inject": [
26
- "docs/architecture/DATABASE_SCHEMA.md",
27
- "alembic.ini"
28
- ],
29
- "description": "Database and Storage Context"
30
- },
31
- {
32
- "keywords": [
33
- "brain",
34
- "memory",
35
- "context",
36
- "recall",
37
- "store"
38
- ],
39
- "inject": [
40
- "mcp-server-nucleus/src/mcp_server_nucleus/runtime/factory.py",
41
- "docs/architecture/BRAIN_DESIGN.md"
42
- ],
43
- "description": "Nucleus Brain Architecture Context"
44
- },
45
- {
46
- "keywords": [
47
- "api",
48
- "endpoint",
49
- "route",
50
- "http",
51
- "rest"
52
- ],
53
- "inject": [
54
- "docs/api/OPENAPI_SPEC.md",
55
- "app.py"
56
- ],
57
- "description": "API Surface Area Context"
58
- },
59
- {
60
- "keywords": [
61
- "ui",
62
- "frontend",
63
- "component",
64
- "react",
65
- "tailwind"
66
- ],
67
- "inject": [
68
- "tools/nucleus-hud/tailwind.config.ts",
69
- "tools/nucleus-hud/app/globals.css"
70
- ],
71
- "description": "Frontend Design System Context"
72
- },
73
- {
74
- "keywords": [
75
- "test",
76
- "verify",
77
- "qa",
78
- "check"
79
- ],
80
- "inject": [
81
- "tests/README.md",
82
- "NUCLEUS_GRAND_VERIFICATION_PLAN.md"
83
- ],
84
- "description": "Testing and Verification Context"
85
- }
86
- ]