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,64 +0,0 @@
1
-
2
- """
3
- DataExporter: The Anti-Sherlock "Eject Button".
4
- Ensures Sovereign Data Portability.
5
-
6
- Strategic Role:
7
- - Exports entire Brain state (Ledger, Memory, Config).
8
- - Formats: JSON (Standard), SQLite (Future).
9
- - Guarantees: "You can always leave with your data."
10
- """
11
-
12
- import json
13
- import time
14
- from pathlib import Path
15
- from zipfile import ZipFile
16
- from typing import Dict
17
-
18
-
19
- class DataExporter:
20
- def __init__(self, brain_path: Path):
21
- self.brain_path = brain_path
22
- self.export_dir = brain_path / "exports"
23
- self.export_dir.mkdir(parents=True, exist_ok=True)
24
-
25
- def export_full_state(self) -> Dict[str, str]:
26
- """
27
- Create a full snapshot of the Sovereign Brain.
28
- Returns path to the export artifact.
29
- """
30
- timestamp = int(time.time())
31
- export_name = f"sovereign_dump_{timestamp}"
32
- zip_path = self.export_dir / f"{export_name}.zip"
33
-
34
- # We need a shared 'top level' read lock on critical subsystems if possible
35
- # Or just best-effort copy. For "Eject", consistency is slightly less critical
36
- # than just "getting the files".
37
-
38
- # Files to include
39
- include_dirs = ["ledger", "memory", "swarms", "agents", "checkpoints"]
40
-
41
- with ZipFile(zip_path, 'w') as zipf:
42
- # Metadata
43
- meta = {
44
- "version": "1.0",
45
- "timestamp": timestamp,
46
- "system": "Nucleus Daemon Phase 59"
47
- }
48
- zipf.writestr("metadata.json", json.dumps(meta, indent=2))
49
-
50
- # Recursive add
51
- for d in include_dirs:
52
- src_dir = self.brain_path / d
53
- if src_dir.exists():
54
- for file in src_dir.rglob("*"):
55
- if file.is_file():
56
- # Rel path inside zip
57
- rel_path = file.relative_to(self.brain_path)
58
- zipf.write(file, arcname=str(rel_path))
59
-
60
- return {
61
- "status": "success",
62
- "path": str(zip_path),
63
- "size_bytes": zip_path.stat().st_size
64
- }
@@ -1,594 +0,0 @@
1
- """
2
- Nucleus Agent Runtime (NAR) - Context Factory
3
- ==============================================
4
- MDR_004 Compliant: Full Agent-Tool Fit routing with persona-based tool loading
5
- MDR Third-Pass: Now loads rich agent definitions from .brain/agents/*.md
6
-
7
- Supports both:
8
- - Ephemeral agents (fast, minimal prompts) - DevOps, Librarian
9
- - Nuanced agents (rich, 400-line prompts) - Critic, Researcher, Synthesizer
10
- """
11
-
12
- import os
13
- import re
14
- from pathlib import Path
15
- from typing import List, Dict, Any, Optional
16
- from .capabilities.base import Capability
17
- from .capabilities.render_ops import RenderOps
18
- from .capabilities.brain_ops import BrainOps
19
- from .capabilities.depth_tracker import DepthTracker
20
- from .capabilities.feature_map import FeatureMap
21
- from .capabilities.proof_system import ProofSystem
22
- from .capabilities.render_poller_cap import RenderPolling
23
- from .capabilities.code_ops import CodeOps
24
- from .capabilities.web_ops import WebOps
25
- from .capabilities.memory_ops import MemoryOps
26
- from .capabilities.self_healing import SelfHealingOps
27
- from .storage import read_brain_file, brain_file_exists
28
-
29
- from .plugin_loader import PluginLoader
30
- from .budget import BudgetAuditor
31
-
32
-
33
-
34
- # ============================================================
35
- # CONFIGURATION
36
- # ============================================================
37
-
38
- # Path to brain (can be overridden via env var)
39
- # NOTE: Default changed to absolute path to fix "Read-only file system" error
40
- # when MCP server runs from a different working directory.
41
- BRAIN_PATH = Path(os.environ.get("NUCLEUS_BRAIN_PATH", "/Users/lokeshgarg/ai-mvp-backend/.brain"))
42
-
43
- # ============================================================
44
- # INTENT-BASED TIER ESCALATION (Enterprise Feature)
45
- # ============================================================
46
- # Keywords that trigger automatic escalation to CRITICAL job_type (premium tier)
47
- ESCALATION_KEYWORDS = ["CRITICAL", "SECURITY", "URGENT", "EMERGENCY", "HIGH-PRIORITY", "AUDIT"]
48
-
49
- def _should_escalate_tier(intent: str) -> bool:
50
- """
51
- Check if intent warrants tier escalation to CRITICAL.
52
- This ensures high-stakes tasks always use premium models.
53
- """
54
- if not intent:
55
- return False
56
- intent_upper = intent.upper()
57
- return any(keyword in intent_upper for keyword in ESCALATION_KEYWORDS)
58
-
59
-
60
-
61
- # ============================================================
62
- # PERSONA REGISTRY: 8 Personas (4 Fast + 4 Rich)
63
- # Each persona has a job_type for LLM tier routing
64
- # ============================================================
65
-
66
- PERSONAS = {
67
- # --- FAST PERSONAS (Ephemeral, Directive Prompts) ---
68
- "librarian": {
69
- "name": "Librarian",
70
- "mode": "rich",
71
- "description": "Information Architect. Guardian of the Memory Bank.",
72
- "capabilities": ["brain_ops", "memory_ops", "code_ops"],
73
- "system_prompt_fragment": None,
74
- "agent_file": "librarian.md",
75
- "job_type": "BACKGROUND" # Low-cost operations
76
- },
77
-
78
- "devops": {
79
- "name": "DevOps",
80
- "mode": "fast",
81
- "description": "Operator. Deployment and infrastructure actions.",
82
- "capabilities": ["render_ops", "render_poller"],
83
- "system_prompt_fragment": """You are a DevOps Specialist (Infrastructure Operator).
84
-
85
- CRITICAL RULES (MDR_002 DIRECTIVE):
86
- 1. Your ONLY output should be tool calls.
87
- 2. Do NOT chat, explain, or give status updates as text.
88
- 3. Execute infrastructure tasks using ONLY the provided tools.
89
- 4. Output format: JSON tool call blocks.
90
-
91
- If you output text instead of tool calls, you have FAILED.
92
- """,
93
- "agent_file": None,
94
- "job_type": "ORCHESTRATION" # Standard operations
95
- },
96
-
97
- # --- RICH PERSONAS (Nuanced, Load from .brain/agents/*.md) ---
98
- "synthesizer": {
99
- "name": "Synthesizer",
100
- "mode": "rich",
101
- "description": "Meta-orchestrator. Sees across all domains. Founder's force multiplier.",
102
- "capabilities": ["brain_ops", "depth_tracker", "memory_ops"],
103
- "system_prompt_fragment": None,
104
- "agent_file": "synthesizer.md",
105
- "job_type": "ORCHESTRATION" # Standard tier
106
- },
107
-
108
- "critic": {
109
- "name": "Critic",
110
- "mode": "rich",
111
- "description": "Quality gate. Code review, security audit, strategy validation.",
112
- "capabilities": ["brain_ops", "proof_system", "memory_ops", "code_ops"],
113
- "system_prompt_fragment": None,
114
- "agent_file": "critic.md",
115
- "job_type": "CRITICAL" # Premium tier - needs high quality
116
- },
117
-
118
- "developer": {
119
- "name": "Developer",
120
- "mode": "rich",
121
- "description": "Implementation specialist. Code execution, testing.",
122
- "capabilities": ["brain_ops", "code_ops", "proof_system"],
123
- "system_prompt_fragment": None,
124
- "agent_file": "developer.md",
125
- "job_type": "ORCHESTRATION" # Standard tier
126
- },
127
-
128
- "researcher": {
129
- "name": "Researcher",
130
- "mode": "rich",
131
- "description": "Information gatherer. Search, analysis, documentation.",
132
- "capabilities": ["brain_ops", "web_ops", "memory_ops"],
133
- "system_prompt_fragment": None,
134
- "agent_file": "researcher.md",
135
- "job_type": "RESEARCH" # Standard tier
136
- },
137
-
138
- "architect": {
139
- "name": "Architect",
140
- "mode": "rich",
141
- "description": "System designer. Specs, architecture, depth tracking.",
142
- "capabilities": ["brain_ops", "depth_tracker", "memory_ops"],
143
- "system_prompt_fragment": None,
144
- "agent_file": "architect.md",
145
- "job_type": "CRITICAL" # Premium tier - needs deep reasoning
146
- },
147
-
148
- "product_manager": {
149
- "name": "Product Manager",
150
- "mode": "rich",
151
- "description": "User intent translator. Backlog grooming, spec creation.",
152
- "capabilities": ["brain_ops", "memory_ops"],
153
- "system_prompt_fragment": None,
154
- "agent_file": "product_manager.md",
155
- "job_type": "ORCHESTRATION" # Standard tier
156
- },
157
-
158
- "strategist": {
159
- "name": "Strategist",
160
- "mode": "rich",
161
- "description": "Business strategy. Decisions, roadmap, positioning.",
162
- "capabilities": ["brain_ops", "memory_ops"],
163
- "system_prompt_fragment": None,
164
- "agent_file": "strategist.md",
165
- "job_type": "CRITICAL" # Premium tier - important decisions
166
- },
167
- "tech_lead": {
168
- "name": "Tech Lead",
169
- "mode": "rich",
170
- "description": "Operational Commander. Execution Swarm Lead. Task breakdown and code review.",
171
- "capabilities": ["brain_ops", "code_ops", "proof_system", "depth_tracker"],
172
- "system_prompt_fragment": None,
173
- "agent_file": "tech_lead.md",
174
- "job_type": "CRITICAL" # Premium tier
175
- },
176
-
177
- "product_owner": {
178
- "name": "Product Owner",
179
- "mode": "rich",
180
- "description": "Value Maximizer. User Stories and Acceptance Criteria.",
181
- "capabilities": ["brain_ops", "memory_ops"],
182
- "system_prompt_fragment": None,
183
- "agent_file": "product_owner.md",
184
- "job_type": "ORCHESTRATION" # Standard tier
185
- },
186
- }
187
-
188
-
189
-
190
- # ============================================================
191
- # INTENT CLASSIFICATION (MDR_004)
192
- # ============================================================
193
-
194
- INTENT_KEYWORDS = {
195
- "orchestrate": ["start sprint", "digest", "optimize", "meta", "daily", "weekly"],
196
- "review": ["review", "audit", "check", "validate", "security", "quality"],
197
- "implement": ["implement", "code", "build", "fix", "develop", "create component"],
198
- "research": ["research", "analyze", "search", "find", "investigate", "learn"],
199
- "design": ["design", "architect", "spec", "plan", "structure", "depth"],
200
- "strategy": ["strategy", "decide", "pivot", "position", "roadmap", "business"],
201
- "deploy": ["deploy", "render", "service", "smoke", "poll", "infra"],
202
- "admin": ["task", "loop", "commitment", "feature", "proof", "add", "close", "scan"],
203
- "groom": ["groom", "backlog", "user story", "intent", "triage", "prioritize"],
204
- "retrieve": ["search memory", "recall", "find history", "what did we decide", "why"],
205
- }
206
-
207
-
208
- def classify_intent(message: str) -> str:
209
- """
210
- Classify user intent into a category.
211
- Extended for 8 personas.
212
- """
213
- message_lower = message.lower()
214
-
215
- scores = {intent: 0 for intent in INTENT_KEYWORDS}
216
-
217
- for intent, keywords in INTENT_KEYWORDS.items():
218
- for kw in keywords:
219
- if kw in message_lower:
220
- scores[intent] += 1
221
-
222
- # Default to 'admin' if no matches
223
- if all(s == 0 for s in scores.values()):
224
- return "admin"
225
-
226
- return max(scores, key=scores.get)
227
-
228
-
229
- def get_persona_for_intent(intent: str) -> Dict:
230
- """
231
- Map classified intent to persona (MDR_004 Extended).
232
- """
233
- mapping = {
234
- "orchestrate": PERSONAS["synthesizer"],
235
- "review": PERSONAS["critic"],
236
- "implement": PERSONAS["developer"],
237
- "research": PERSONAS["researcher"],
238
- "design": PERSONAS["architect"],
239
- "strategy": PERSONAS["strategist"],
240
- "deploy": PERSONAS["devops"],
241
- "admin": PERSONAS["librarian"], # Admin uses librarian for DB ops
242
- "retrieve": PERSONAS["librarian"],
243
- "groom": PERSONAS["product_manager"],
244
- }
245
- return mapping.get(intent, PERSONAS["librarian"])
246
-
247
-
248
- # ============================================================
249
- # CONTEXT FACTORY (MDR_004 + Third-Pass Enhanced)
250
- # ============================================================
251
-
252
- class ContextFactory:
253
- """
254
- The Compiler.
255
- Maps Intent -> Persona -> Toolset.
256
-
257
- Supports:
258
- - Fast personas: Inline directive prompts (5-10 lines)
259
- - Rich personas: Load full protocols from .brain/agents/*.md (400+ lines)
260
- - External agent definitions: Load from any path (marketplace-ready)
261
- """
262
-
263
- def __init__(self, brain_path: Optional[Path] = None):
264
- self._registry: Dict[str, Capability] = {}
265
- self._brain_path = brain_path or BRAIN_PATH
266
- self._auditor = BudgetAuditor(self._brain_path)
267
- self._plugin_loader = PluginLoader(self._brain_path, self._auditor)
268
- self._agent_cache: Dict[str, str] = {} # Cache loaded agent prompts
269
- self._register_defaults()
270
-
271
- def _register_defaults(self):
272
- """Register all available capabilities"""
273
- self.register(RenderOps())
274
- self.register(BrainOps())
275
- self.register(DepthTracker())
276
- self.register(FeatureMap())
277
- self.register(ProofSystem())
278
- self.register(RenderPolling())
279
- self.register(CodeOps())
280
- self.register(WebOps())
281
- self.register(MemoryOps())
282
- self.register(SelfHealingOps())
283
-
284
- # Load Plugins (Marketplace - Phase 57 Update)
285
- # We no longer strictly scan all tools for global registration.
286
- # Tools are now loaded per-context via the Manifest.
287
- # However, for backward compatibility or "SysAdmin" tools, we could load a specific set here.
288
- # For now, we leave the Global Registry clean of user scripts to enforce Air Gap.
289
- pass
290
- # plugins = load_plugins(self._brain_path)
291
- # for p in plugins:
292
- # self.register(p)
293
-
294
- def register(self, capability: Capability):
295
- """Register a capability for use by agents"""
296
- self._registry[capability.name] = capability
297
-
298
- def load_agent_prompt(self, agent_file: str) -> Optional[str]:
299
- """
300
- Load an agent prompt from .brain/agents/{agent_file}.
301
- Extracts key sections: IDENTITY, CORE FUNCTIONS, CONSTRAINTS.
302
- Caches for performance.
303
- """
304
- if agent_file in self._agent_cache:
305
- return self._agent_cache[agent_file]
306
-
307
- agent_path = self._brain_path / "agents" / agent_file
308
-
309
- if not brain_file_exists(agent_path):
310
- return None
311
-
312
- try:
313
- full_content = read_brain_file(agent_path)
314
-
315
- # Extract key sections using regex
316
- sections = []
317
-
318
- # Get IDENTITY section
319
- identity_match = re.search(
320
- r'## IDENTITY\s*(.*?)(?=## |\Z)',
321
- full_content,
322
- re.DOTALL
323
- )
324
- if identity_match:
325
- sections.append(identity_match.group(1).strip())
326
-
327
- # Get CORE FUNCTIONS or PERMISSIONS section
328
- core_match = re.search(
329
- r'## (?:CORE FUNCTIONS|PERMISSIONS)\s*(.*?)(?=## |\Z)',
330
- full_content,
331
- re.DOTALL
332
- )
333
- if core_match:
334
- sections.append(core_match.group(1).strip())
335
-
336
- # Get CONSTRAINTS section
337
- constraints_match = re.search(
338
- r'## CONSTRAINTS\s*(.*?)(?=## |\Z)',
339
- full_content,
340
- re.DOTALL
341
- )
342
- if constraints_match:
343
- sections.append(constraints_match.group(1).strip())
344
-
345
- # Combine extracted sections
346
- prompt = "\n\n".join(sections) if sections else full_content[:2000]
347
-
348
- # Cache it
349
- self._agent_cache[agent_file] = prompt
350
- return prompt
351
-
352
- except Exception as e:
353
- print(f"Warning: Could not load agent prompt {agent_file}: {e}")
354
- return None
355
-
356
- def load_external_agent(self, path: str) -> Optional[str]:
357
- """
358
- Load an agent definition from any external path.
359
- Supports marketplace-style agent loading.
360
- """
361
- try:
362
- ext_path = Path(path)
363
- if brain_file_exists(ext_path):
364
- return read_brain_file(ext_path)
365
- except Exception:
366
- pass
367
- return None
368
-
369
- def get_persona(self, persona_name: str) -> Optional[Dict]:
370
- """Get a persona by name (case-insensitive)"""
371
- return PERSONAS.get(persona_name.lower())
372
-
373
- def _resolve_dynamic_context(self, intent: str) -> str:
374
- """
375
- Resolve dynamic context based on intent keywords.
376
- Returns a formatted string of injected documentation.
377
- MDR_004: Dynamic RAG-lite.
378
- """
379
- import json
380
-
381
- # Load rules
382
- rules_path = Path(__file__).parent / "context_rules.json"
383
- if not rules_path.exists():
384
- return ""
385
-
386
- try:
387
- # Context rules are likely config code, not brain data, but for consistency we specificy check
388
- # Actually, context_rules.json is in the package directory, not brain.
389
- # We should probably KEEP it local as it's part of the codebase logic.
390
- # But let's check if the file exists using standard pathlib first as it is package data.
391
- rules = json.loads(rules_path.read_text())
392
- except Exception as e:
393
- print(f"Error loading context rules: {e}")
394
- return ""
395
-
396
- intent_lower = intent.lower()
397
- injected_docs = []
398
-
399
- for rule in rules:
400
- # Check for keyword matches
401
- if any(kw in intent_lower for kw in rule["keywords"]):
402
- for doc_path in rule["inject"]:
403
- # Try to resolve path relative to project root
404
- # Assuming CWD is usually project root, or try logical locations
405
- full_path = Path(doc_path)
406
-
407
- if not full_path.exists():
408
- # Try relative to BRAIN_PATH parent (project root usually)
409
- full_path = self._brain_path.parent / doc_path
410
-
411
- if full_path.exists():
412
- try:
413
- content = full_path.read_text()
414
- # Truncate if too long (simple safety)
415
- if len(content) > 10000:
416
- content = content[:10000] + "\n...[TRUNCATED]..."
417
-
418
- injected_docs.append(f"## Context: {doc_path}\n{content}\n")
419
- except Exception:
420
- pass
421
-
422
- if not injected_docs:
423
- return ""
424
-
425
- return "\n\n".join(injected_docs)
426
-
427
- def create_context(self, session_id: str, intent: str) -> Dict[str, Any]:
428
- """
429
- Create an execution context for an agent.
430
- MDR_004 Enhanced + Third-Pass: Supports rich agent prompts.
431
- Phase 2 Upgrade: Dynamic Context Injection.
432
- """
433
- # Step 1: Classify intent
434
- intent_category = classify_intent(intent)
435
-
436
- # Step 2: Get persona
437
- persona = get_persona_for_intent(intent_category)
438
-
439
- # Step 3: Load capabilities
440
- tools = []
441
- active_caps_names = []
442
- active_caps_instances = []
443
-
444
- for cap_name in persona.get("capabilities", []):
445
- cap = self._registry.get(cap_name)
446
- if cap:
447
- tools.extend(cap.get_tools())
448
- active_caps_names.append(cap.name)
449
- active_caps_instances.append(cap)
450
-
451
- # Step 4: Generate system prompt (fast or rich)
452
- system_prompt = self._generate_system_prompt(intent, persona, active_caps_names)
453
-
454
- # Step 5: Inject Dynamic Context (Phase 2)
455
- dynamic_context = self._resolve_dynamic_context(intent)
456
- if dynamic_context:
457
- system_prompt += f"\n\n# DYNAMIC CONTEXT INJECTION\nThe following documentation was automatically retrieved based on your intent:\n\n{dynamic_context}"
458
-
459
- return {
460
- "session_id": session_id,
461
- "intent": intent,
462
- "intent_category": intent_category,
463
- "persona": persona["name"],
464
- "persona_mode": persona.get("mode", "fast"),
465
- "job_type": persona.get("job_type", "ORCHESTRATION"), # For LLM tier routing
466
- "capabilities": active_caps_names,
467
- "capability_instances": active_caps_instances,
468
- "tools": tools,
469
- "tool_count": len(tools),
470
- "system_prompt": system_prompt
471
- }
472
-
473
-
474
- def create_context_for_persona(
475
- self,
476
- session_id: str,
477
- persona_name: str,
478
- intent: str,
479
- external_prompt: Optional[str] = None
480
- ) -> Dict[str, Any]:
481
- """
482
- Create context for a specific persona (skip intent classification).
483
- Useful for event-driven orchestration and direct delegation.
484
-
485
- Args:
486
- external_prompt: Optional external agent definition (marketplace)
487
- """
488
- persona = self.get_persona(persona_name)
489
- if not persona:
490
- # Fallback to librarian
491
- persona = PERSONAS["librarian"]
492
-
493
- # Load capabilities
494
- tools = []
495
- active_caps_names = []
496
- active_caps_instances = []
497
-
498
- for cap_name in persona.get("capabilities", []):
499
- cap = self._registry.get(cap_name)
500
- if cap:
501
- tools.extend(cap.get_tools())
502
- active_caps_names.append(cap.name)
503
- active_caps_instances.append(cap)
504
-
505
- # Generate prompt (external overrides everything)
506
- if external_prompt:
507
- system_prompt = f"{external_prompt}\n\nIntent: {intent}"
508
- else:
509
- system_prompt = self._generate_system_prompt(intent, persona, active_caps_names)
510
-
511
- return {
512
- "session_id": session_id,
513
- "intent": intent,
514
- "intent_category": "direct",
515
- "persona": persona["name"],
516
- "persona_mode": persona.get("mode", "fast"),
517
- # Intent-based escalation: CRITICAL keywords override persona's default job_type
518
- "job_type": "CRITICAL" if _should_escalate_tier(intent) else persona.get("job_type", "ORCHESTRATION"),
519
- "capabilities": active_caps_names,
520
- "capability_instances": active_caps_instances,
521
- "tools": tools,
522
- "tool_count": len(tools),
523
- "system_prompt": system_prompt
524
- }
525
-
526
- def _generate_system_prompt(
527
- self,
528
- intent: str,
529
- persona: Dict,
530
- caps: List[str]
531
- ) -> str:
532
- """
533
- Generate persona-specific system prompt.
534
- - Fast mode: Use inline fragment
535
- - Rich mode: Load from .brain/agents/*.md
536
- """
537
- mode = persona.get("mode", "fast")
538
- agent_file = persona.get("agent_file")
539
-
540
- # Try to load rich prompt
541
- if mode == "rich" and agent_file:
542
- loaded_prompt = self.load_agent_prompt(agent_file)
543
- if loaded_prompt:
544
- return f"""
545
- # {persona['name']} Agent
546
-
547
- {loaded_prompt}
548
-
549
- ---
550
- ## Current Task
551
-
552
- Intent: {intent}
553
- Active Capabilities: {', '.join(caps) if caps else 'None (Files Only)'}
554
- Tool Count: {len(caps)}
555
-
556
- Execute the intent. Once complete, output 'TERMINATE'.
557
- """
558
-
559
- # Fallback to inline fragment (fast mode)
560
- fragment = persona.get("system_prompt_fragment", "Execute the user's request.")
561
-
562
- return f"""
563
- You are an Ephemeral Agent: {persona['name']}.
564
-
565
- {fragment}
566
-
567
- Intent: {intent}
568
- Active Capabilities: {', '.join(caps) if caps else 'None (Files Only)'}
569
- Tool Count: {len(caps)}
570
-
571
- Your Goal: Execute the intent using the provided tools.
572
- Once the task is done, output 'TERMINATE'.
573
- """
574
-
575
- def list_personas(self) -> List[Dict]:
576
- """List all available personas"""
577
- return [
578
- {
579
- "name": p["name"],
580
- "mode": p.get("mode", "fast"),
581
- "description": p["description"],
582
- "capabilities": p.get("capabilities", []),
583
- "has_rich_prompt": p.get("agent_file") is not None
584
- }
585
- for p in PERSONAS.values()
586
- ]
587
-
588
- def list_capabilities(self) -> List[str]:
589
- """List all registered capabilities"""
590
- return list(self._registry.keys())
591
-
592
- def clear_cache(self):
593
- """Clear the agent prompt cache"""
594
- self._agent_cache.clear()