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,339 +0,0 @@
1
- """
2
- Nucleus Runtime - Depth Tracking Operations
3
- ===========================================
4
- Core logic for depth tracking (Rabbit hole protection).
5
- """
6
-
7
- import json
8
- import time
9
- import logging
10
- from typing import Dict, Any
11
- from pathlib import Path
12
-
13
- # Relative imports assuming this is in mcp_server_nucleus.runtime
14
- from .common import get_brain_path
15
- from .event_ops import _emit_event
16
-
17
- logger = logging.getLogger("nucleus.depth_ops")
18
-
19
- def _get_depth_path() -> Path:
20
- """Get the path to the depth tracking file."""
21
- brain = get_brain_path()
22
- session_dir = brain / "session"
23
- session_dir.mkdir(parents=True, exist_ok=True)
24
- return session_dir / "depth.json"
25
-
26
- def _get_depth_state() -> Dict[str, Any]:
27
- """Get current depth tracking state."""
28
- try:
29
- depth_path = _get_depth_path()
30
-
31
- if not depth_path.exists():
32
- # Initialize with default state
33
- default_state = {
34
- "session_id": f"session-{time.strftime('%Y%m%d')}",
35
- "current_depth": 0,
36
- "max_safe_depth": 5,
37
- "levels": [],
38
- "created_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
39
- "updated_at": time.strftime("%Y-%m-%dT%H:%M:%S%z")
40
- }
41
- with open(depth_path, "w") as f:
42
- json.dump(default_state, f, indent=2)
43
- return default_state
44
-
45
- with open(depth_path, "r") as f:
46
- return json.load(f)
47
- except Exception as e:
48
- logger.error(f"Error getting depth state: {e}")
49
- return {"current_depth": 0, "levels": [], "max_safe_depth": 5}
50
-
51
- def _save_depth_state(state: Dict[str, Any]) -> str:
52
- """Save depth tracking state."""
53
- try:
54
- depth_path = _get_depth_path()
55
- state["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
56
- with open(depth_path, "w") as f:
57
- json.dump(state, f, indent=2)
58
- return "Depth state saved"
59
- except Exception as e:
60
- return f"Error saving depth state: {str(e)}"
61
-
62
- def _depth_push(topic: str) -> Dict[str, Any]:
63
- """Go deeper into a subtopic. Returns current state with warnings."""
64
- try:
65
- state = _get_depth_state()
66
-
67
- new_depth = state.get("current_depth", 0) + 1
68
- max_safe = state.get("max_safe_depth", 5)
69
-
70
- # Create new level entry
71
- new_level = {
72
- "depth": new_depth,
73
- "topic": topic,
74
- "started_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
75
- "status": "current"
76
- }
77
-
78
- # Update previous level status
79
- levels = state.get("levels", [])
80
- for level in levels:
81
- if level.get("status") == "current":
82
- level["status"] = "active"
83
-
84
- levels.append(new_level)
85
-
86
- state["current_depth"] = new_depth
87
- state["levels"] = levels
88
- _save_depth_state(state)
89
-
90
- # Generate warning based on depth
91
- warning = None
92
- warning_level = "safe"
93
-
94
- if new_depth >= max_safe:
95
- warning = f"🔴🔴 RABBIT HOLE! You're at level {new_depth}/{max_safe}. Consider resurfacing."
96
- warning_level = "danger"
97
- elif new_depth >= max_safe - 1:
98
- warning = f"🔴 DEEP DIVE ALERT! Level {new_depth}/{max_safe}. You're in the danger zone."
99
- warning_level = "danger"
100
- elif new_depth >= 3:
101
- warning = f"🟡 CAUTION: Level {new_depth}/{max_safe}. Getting deep - just so you know."
102
- warning_level = "caution"
103
-
104
- # Build breadcrumb path
105
- breadcrumbs = " → ".join([lv["topic"] for lv in levels])
106
-
107
- # Emit event
108
- _emit_event("depth_increased", "depth_tracker", {
109
- "new_depth": new_depth,
110
- "topic": topic,
111
- "warning_level": warning_level
112
- })
113
-
114
- return {
115
- "current_depth": new_depth,
116
- "max_safe_depth": max_safe,
117
- "topic": topic,
118
- "breadcrumbs": breadcrumbs,
119
- "warning": warning,
120
- "warning_level": warning_level,
121
- "indicator": _format_depth_indicator(new_depth, max_safe)
122
- }
123
- except Exception as e:
124
- return {"error": str(e)}
125
-
126
- def _depth_pop() -> Dict[str, Any]:
127
- """Come back up one level. Returns new state."""
128
- try:
129
- state = _get_depth_state()
130
- levels = state.get("levels", [])
131
-
132
- if not levels:
133
- return {
134
- "current_depth": 0,
135
- "message": "Already at root level (depth 0). Nothing to pop.",
136
- "indicator": _format_depth_indicator(0, state.get("max_safe_depth", 5))
137
- }
138
-
139
- # Pop the current level
140
- popped = levels.pop()
141
-
142
- # Set new current
143
- if levels:
144
- levels[-1]["status"] = "current"
145
-
146
- new_depth = len(levels)
147
- state["current_depth"] = new_depth
148
- state["levels"] = levels
149
- _save_depth_state(state)
150
-
151
- # Build breadcrumb path
152
- breadcrumbs = " → ".join([lv["topic"] for lv in levels]) if levels else "(root)"
153
- returned_to = levels[-1]["topic"] if levels else "root"
154
-
155
- # Emit event
156
- _emit_event("depth_decreased", "depth_tracker", {
157
- "new_depth": new_depth,
158
- "returned_to": returned_to,
159
- "popped_topic": popped["topic"]
160
- })
161
-
162
- return {
163
- "current_depth": new_depth,
164
- "returned_to": returned_to,
165
- "popped_topic": popped["topic"],
166
- "breadcrumbs": breadcrumbs,
167
- "message": f"✅ Resurfaced! Now at level {new_depth}: {returned_to}",
168
- "indicator": _format_depth_indicator(new_depth, state.get("max_safe_depth", 5))
169
- }
170
- except Exception as e:
171
- return {"error": str(e)}
172
-
173
- def _depth_show() -> Dict[str, Any]:
174
- """Show current depth state with visual indicator."""
175
- try:
176
- state = _get_depth_state()
177
- current_depth = state.get("current_depth", 0)
178
- max_safe = state.get("max_safe_depth", 5)
179
- levels = state.get("levels", [])
180
-
181
- # Build breadcrumb path
182
- breadcrumbs = " → ".join([lv["topic"] for lv in levels]) if levels else "(root)"
183
-
184
- # Build tree visualization
185
- tree_lines = []
186
- for i, level in enumerate(levels):
187
- indent = " " * i
188
- prefix = "└─ " if i > 0 else ""
189
- marker = " ← YOU ARE HERE" if level.get("status") == "current" else ""
190
- tree_lines.append(f"{indent}{prefix}{i}: {level['topic']}{marker}")
191
-
192
- tree = "\n".join(tree_lines) if tree_lines else "(At root level)"
193
-
194
- # Generate status
195
- if current_depth >= max_safe:
196
- status = "🔴 RABBIT HOLE"
197
- elif current_depth >= max_safe - 1:
198
- status = "🔴 DANGER ZONE"
199
- elif current_depth >= 3:
200
- status = "🟡 CAUTION"
201
- else:
202
- status = "🟢 SAFE"
203
-
204
- return {
205
- "current_depth": current_depth,
206
- "max_safe_depth": max_safe,
207
- "status": status,
208
- "breadcrumbs": breadcrumbs,
209
- "tree": tree,
210
- "indicator": _format_depth_indicator(current_depth, max_safe),
211
- "levels": levels,
212
- "session_id": state.get("session_id"),
213
- "help": "Commands: brain_depth_push(topic), brain_depth_pop(), brain_depth_reset(), brain_depth_set_max(n)"
214
- }
215
- except Exception as e:
216
- return {"error": str(e)}
217
-
218
- def _depth_reset() -> Dict[str, Any]:
219
- """Reset depth to 0 (root level). Clears all levels."""
220
- try:
221
- state = _get_depth_state()
222
- state["current_depth"] = 0
223
- state["levels"] = []
224
- _save_depth_state(state)
225
-
226
- return {
227
- "current_depth": 0,
228
- "message": "✅ Depth reset to root level.",
229
- "indicator": _format_depth_indicator(0, state.get("max_safe_depth", 5))
230
- }
231
- except Exception as e:
232
- return {"error": str(e)}
233
-
234
- def _depth_set_max(max_depth: int) -> Dict[str, Any]:
235
- """Set the maximum safe depth threshold."""
236
- try:
237
- # Validate range
238
- if max_depth < 1 or max_depth > 10:
239
- return {
240
- "error": "max_depth must be between 1 and 10",
241
- "current_max": None
242
- }
243
-
244
- state = _get_depth_state()
245
- old_max = state.get("max_safe_depth", 5)
246
- state["max_safe_depth"] = max_depth
247
- _save_depth_state(state)
248
-
249
- current = state.get("current_depth", 0)
250
- return {
251
- "old_max": old_max,
252
- "new_max": max_depth,
253
- "current_depth": current,
254
- "indicator": _format_depth_indicator(current, max_depth),
255
- "message": f"✅ Max depth updated: {old_max} → {max_depth}"
256
- }
257
- except Exception as e:
258
- return {"error": str(e)}
259
-
260
- def _format_depth_indicator(current: int, max_safe: int) -> str:
261
- """Returns a visual progress-style indicator of depth."""
262
- indicator = "["
263
- for i in range(max_safe):
264
- if i < current:
265
- indicator += "█"
266
- else:
267
- indicator += "░"
268
- indicator += "]"
269
-
270
- if current >= max_safe:
271
- indicator += " ⚠️"
272
-
273
- return indicator
274
-
275
- def _generate_depth_map() -> Dict[str, Any]:
276
- """Generate a Mermaid diagram of the current exploration path."""
277
- try:
278
- state = _get_depth_state()
279
- levels = state.get("levels", [])
280
- max_safe = state.get("max_safe_depth", 5)
281
-
282
- if not levels:
283
- return {
284
- "mermaid": "```mermaid\ngraph TD\n ROOT((🏠 START))\n style ROOT fill:#ccffcc,stroke:#0a0\n```",
285
- "message": "You're at the root level. No exploration path yet.",
286
- "node_count": 0
287
- }
288
-
289
- # Build Mermaid graph
290
- lines = ["graph TD"]
291
- lines.append(" ROOT((🏠 START))")
292
-
293
- prev_id = "ROOT"
294
- for i, level in enumerate(levels):
295
- node_id = f"L{i}"
296
- topic = level.get("topic", f"Level {i+1}")
297
- # Escape quotes and special chars
298
- topic_safe = topic.replace('"', "'").replace("[", "(").replace("]", ")")
299
-
300
- # Determine node style based on depth
301
- depth = i + 1
302
- if depth >= max_safe:
303
- style = "fill:#ffcccc,stroke:#f00" # Red - rabbit hole
304
- node_shape = f'{node_id}[["🔴 {topic_safe}"]]'
305
- elif depth >= max_safe - 1:
306
- style = "fill:#ffddcc,stroke:#f60" # Orange - danger
307
- node_shape = f'{node_id}[["🔴 {topic_safe}"]]'
308
- elif depth >= 3:
309
- style = "fill:#ffffcc,stroke:#cc0" # Yellow - caution
310
- node_shape = f'{node_id}["🟡 {topic_safe}"]'
311
- else:
312
- style = "fill:#ccffcc,stroke:#0a0" # Green - safe
313
- node_shape = f'{node_id}["🟢 {topic_safe}"]'
314
-
315
- lines.append(f" {prev_id} --> {node_shape}")
316
- lines.append(f" style {node_id} {style}")
317
- prev_id = node_id
318
-
319
- # Mark the last node as current
320
- if levels:
321
- lines.append(f" style {prev_id} stroke-width:3px")
322
-
323
- # Wrap in code block
324
- mermaid_code = "```mermaid\n" + "\n".join(lines) + "\n```"
325
-
326
- # Build path summary
327
- path = " → ".join([lv.get("topic", "?") for lv in levels])
328
-
329
- return {
330
- "mermaid": mermaid_code,
331
- "path": f"🏠 → {path}",
332
- "current_depth": len(levels),
333
- "max_safe_depth": max_safe,
334
- "node_count": len(levels),
335
- "message": f"Exploration map with {len(levels)} nodes. Current depth: {len(levels)}/{max_safe}"
336
- }
337
- except Exception as e:
338
- return {"error": str(e)}
339
-
@@ -1,103 +0,0 @@
1
- """
2
- Nucleus Runtime - Event Operations
3
- ==================================
4
- Core logic for event stream management.
5
- """
6
-
7
- import json
8
- import time
9
- import uuid
10
- from typing import Dict, Any, List
11
- from datetime import datetime, timezone
12
-
13
- from .common import get_brain_path, logger
14
-
15
- def _log_interaction(emitter: str, event_type: str, data: Dict[str, Any]) -> None:
16
- """Log a cryptographic hash of the interaction for user trust (V9 Security)."""
17
- try:
18
- import hashlib
19
- brain = get_brain_path()
20
- log_path = brain / "ledger" / "interaction_log.jsonl"
21
-
22
- # Create a stable string representation for hashing
23
- payload = json.dumps({"type": event_type, "emitter": emitter, "data": data}, sort_keys=True)
24
- h = hashlib.sha256(payload.encode()).hexdigest()
25
-
26
- entry = {
27
- "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
28
- "emitter": emitter,
29
- "type": event_type,
30
- "hash": h,
31
- "alg": "sha256"
32
- }
33
-
34
- with open(log_path, "a") as f:
35
- f.write(json.dumps(entry) + "\n")
36
- except Exception as e:
37
- logger.warning(f"Failed to log interaction trust signal: {e}")
38
-
39
- def _emit_event(event_type: str, emitter: str, data: Dict[str, Any], description: str = "") -> str:
40
- """Core logic for emitting an event."""
41
- try:
42
- brain = get_brain_path()
43
- events_path = brain / "ledger" / "events.jsonl"
44
-
45
- event_id = f"evt-{int(time.time())}-{str(uuid.uuid4())[:8]}"
46
- timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
47
-
48
- event = {
49
- "event_id": event_id,
50
- "timestamp": timestamp,
51
- "type": event_type,
52
- "emitter": emitter,
53
- "data": data,
54
- "description": description
55
- }
56
-
57
- with open(events_path, "a") as f:
58
- f.write(json.dumps(event) + "\n")
59
-
60
- # Log interaction for security audit (Trust Signal)
61
- _log_interaction(emitter, event_type, data)
62
-
63
- # Update activity summary for fast satellite view (Tier 2 precomputation)
64
- try:
65
- summary_path = brain / "ledger" / "activity_summary.json"
66
- summary = {}
67
- if summary_path.exists():
68
- with open(summary_path, "r") as f:
69
- summary = json.load(f)
70
-
71
- summary["last_event"] = event
72
- summary["updated_at"] = timestamp
73
- summary["event_count"] = summary.get("event_count", 0) + 1
74
-
75
- with open(summary_path, "w") as f:
76
- json.dump(summary, f, indent=2)
77
- except Exception:
78
- pass # Don't fail event emit if summary update fails
79
-
80
- return event_id
81
- except Exception as e:
82
- return f"Error emitting event: {str(e)}"
83
-
84
- def _read_events(limit: int = 10) -> List[Dict[str, Any]]:
85
- """Core logic for reading events."""
86
- try:
87
- brain = get_brain_path()
88
- events_path = brain / "ledger" / "events.jsonl"
89
-
90
- if not events_path.exists():
91
- return []
92
-
93
- events = []
94
- with open(events_path, "r") as f:
95
- for line in f:
96
- if line.strip():
97
- events.append(json.loads(line))
98
-
99
- return events[-limit:]
100
- except Exception as e:
101
- # Avoid logger dependency here if possible, or print
102
- print(f"Error reading events: {e}")
103
- return []
@@ -1,213 +0,0 @@
1
- """
2
- Nucleus Agent Runtime - Event Stream
3
- =====================================
4
- The nervous system of the Agent Control Plane.
5
- Events flow through here, triggering agent activation.
6
-
7
- Location: mcp_server_nucleus/runtime/event_stream.py
8
- """
9
-
10
- import json
11
- from datetime import datetime, timezone
12
- from enum import Enum
13
- from pathlib import Path
14
- from typing import Dict, List, Any, Optional
15
- import uuid
16
-
17
-
18
- class EventSeverity(Enum):
19
- """Event severity levels per synthesizer.md protocol"""
20
- ROUTINE = "ROUTINE" # Auto-approve, log only
21
- NOTABLE = "NOTABLE" # Include in Daily Digest
22
- CRITICAL = "CRITICAL" # IMMEDIATE escalation to founder
23
-
24
-
25
- def get_events_path(brain_path: Path) -> Path:
26
- """Get path to event stream"""
27
- return brain_path / "ledger" / "events.jsonl"
28
-
29
-
30
- def read_events(brain_path: Path, limit: int = 50) -> List[Dict]:
31
- """
32
- Read the last N events from the stream.
33
- Returns events in reverse chronological order (newest first).
34
- """
35
- events_path = get_events_path(brain_path)
36
-
37
- if not events_path.exists():
38
- return []
39
-
40
- # Read all events
41
- events = []
42
- with open(events_path, 'r') as f:
43
- for line in f:
44
- line = line.strip()
45
- if line:
46
- try:
47
- events.append(json.loads(line))
48
- except json.JSONDecodeError:
49
- continue
50
-
51
- # Return last N, reversed (newest first)
52
- return events[-limit:][::-1]
53
-
54
-
55
- def emit_event(
56
- brain_path: Path,
57
- event_type: str,
58
- emitter: str,
59
- payload: Dict[str, Any],
60
- severity: EventSeverity = EventSeverity.ROUTINE,
61
- metadata: Optional[Dict] = None
62
- ) -> Dict:
63
- """
64
- Emit an event to the stream.
65
- Returns the created event.
66
- """
67
- events_path = get_events_path(brain_path)
68
- events_path.parent.mkdir(parents=True, exist_ok=True)
69
-
70
- event = {
71
- "event_id": f"{emitter[:3]}-{uuid.uuid4().hex[:8]}",
72
- "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
73
- "emitter": emitter,
74
- "event_type": event_type,
75
- "severity": severity.value,
76
- "payload": payload,
77
- "metadata": metadata or {}
78
- }
79
-
80
- # Append to stream
81
- with open(events_path, 'a') as f:
82
- f.write(json.dumps(event) + '\n')
83
-
84
- # Bridge to Cloud (Fire & Forget)
85
- try:
86
- from mcp_server_nucleus.runtime.firestore_bridge import get_bridge
87
- get_bridge().push_event(event)
88
- except Exception:
89
- # Never block local op on cloud fail
90
- pass
91
-
92
- return event
93
-
94
-
95
- def get_unprocessed_events(
96
- brain_path: Path,
97
- last_processed_id: Optional[str] = None,
98
- severity_filter: Optional[EventSeverity] = None
99
- ) -> List[Dict]:
100
- """
101
- Get events that haven't been processed yet.
102
- Optionally filter by severity.
103
- """
104
- events = read_events(brain_path, limit=100)
105
-
106
- # If we have a last processed ID, skip events until we find it
107
- if last_processed_id:
108
- found = False
109
- filtered = []
110
- for event in events:
111
- if found:
112
- filtered.append(event)
113
- elif event.get('event_id') == last_processed_id:
114
- found = True
115
- events = filtered if found else events
116
-
117
- # Filter by severity if specified
118
- if severity_filter:
119
- events = [e for e in events if e.get('severity') == severity_filter.value]
120
-
121
- return events
122
-
123
-
124
- def get_critical_events(brain_path: Path, limit: int = 10) -> List[Dict]:
125
- """Get CRITICAL events that need immediate attention"""
126
- events = read_events(brain_path, limit=limit * 5) # Read more to find criticals
127
- return [e for e in events if e.get('severity') == 'CRITICAL'][:limit]
128
-
129
-
130
- def rotate_events(brain_path: Path, keep_count: int = 1000) -> int:
131
- """
132
- Rotate event stream to prevent unbounded growth.
133
- Keeps the last N events, archives the rest.
134
- Returns count of archived events.
135
- """
136
- events_path = get_events_path(brain_path)
137
- archive_path = brain_path / "ledger" / "archive" / f"events_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jsonl"
138
-
139
- if not events_path.exists():
140
- return 0
141
-
142
- # Read all events
143
- events = []
144
- with open(events_path, 'r') as f:
145
- for line in f:
146
- line = line.strip()
147
- if line:
148
- events.append(line)
149
-
150
- if len(events) <= keep_count:
151
- return 0
152
-
153
- # Split into keep and archive
154
- to_archive = events[:-keep_count]
155
- to_keep = events[-keep_count:]
156
-
157
- # Write archive
158
- archive_path.parent.mkdir(parents=True, exist_ok=True)
159
- with open(archive_path, 'w') as f:
160
- f.write('\n'.join(to_archive) + '\n')
161
-
162
- # Overwrite main file with kept events
163
- with open(events_path, 'w') as f:
164
- f.write('\n'.join(to_keep) + '\n')
165
-
166
- return len(to_archive)
167
-
168
-
169
- # ============================================================
170
- # COMMON EVENT TYPES (for type safety)
171
- # ============================================================
172
-
173
- class EventTypes:
174
- """Standard event types in the system"""
175
- # Task lifecycle
176
- TASK_ASSIGNED = "task_assigned"
177
- TASK_COMPLETED = "task_completed"
178
- TASK_FAILED = "task_failed"
179
-
180
- # Sprint lifecycle
181
- SPRINT_STARTED = "sprint_started"
182
- SPRINT_COMPLETED = "sprint_completed"
183
-
184
- # Agent lifecycle
185
- AGENT_ACTIVATED = "agent_activated"
186
- AGENT_TERMINATED = "agent_terminated"
187
-
188
- # Review lifecycle
189
- IMPLEMENTATION_COMPLETE = "implementation_complete"
190
- REVIEW_APPROVED = "review_approved"
191
- REVIEW_BLOCKED = "review_blocked"
192
-
193
- # Escalation
194
- FOUNDER_DECISION_NEEDED = "founder_decision_needed"
195
-
196
- # System
197
- BRAIN_INITIALIZED = "brain_initialized"
198
- DAILY_DIGEST_GENERATED = "daily_digest_generated"
199
- META_OPTIMIZATION_COMPLETE = "meta_optimization_complete"
200
-
201
- # v0.6.0 DSoR - Federation Events
202
- FEDERATION_PEER_JOINED = "federation_peer_joined"
203
- FEDERATION_PEER_LEFT = "federation_peer_left"
204
- FEDERATION_PEER_SUSPECT = "federation_peer_suspect"
205
- FEDERATION_LEADER_ELECTED = "federation_leader_elected"
206
- FEDERATION_TASK_ROUTED = "federation_task_routed"
207
- FEDERATION_STATE_SYNCED = "federation_state_synced"
208
-
209
- # v0.6.0 DSoR - Decision Provenance
210
- DECISION_MADE = "decision_made"
211
- CONTEXT_SNAPSHOT = "context_snapshot"
212
- IPC_TOKEN_ISSUED = "ipc_token_issued"
213
- IPC_TOKEN_CONSUMED = "ipc_token_consumed"