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.
- package/package.json +2 -4
- package/.github/workflows/ci.yml +0 -72
- package/.smithery/shttp/module.js +0 -3
- package/.smithery/shttp/module.js.map +0 -7
- package/.smithery/stdio/index.cjs +0 -74
- package/.smithery/stdio/index.cjs.map +0 -7
- package/CHANGELOG.md +0 -65
- package/CODE_OF_CONDUCT.md +0 -34
- package/COMPARISON.md +0 -20
- package/CONTRIBUTING.md +0 -137
- package/DEVELOPMENT.md +0 -109
- package/LICENSE +0 -21
- package/README.md +0 -319
- package/ROADMAP.md +0 -24
- package/SECURITY.md +0 -88
- package/docs/COMMUNITY_FAQ.md +0 -48
- package/docs/COMPETITIVE_ANALYSIS.md +0 -90
- package/docs/DEMO_SCRIPT.md +0 -86
- package/docs/ENTERPRISE.md +0 -169
- package/docs/FAQ.md +0 -228
- package/docs/QUICK_START.md +0 -155
- package/docs/README.md +0 -67
- package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
- package/docs/social-preview-hq.png +0 -0
- package/docs/social-preview.png +0 -0
- package/examples/basic_usage.py +0 -84
- package/package.json.real +0 -24
- package/pyproject.toml +0 -50
- package/registry-server.json +0 -20
- package/scripts/demo_governance.py +0 -109
- package/scripts/sse_bridge.py +0 -87
- package/scripts/verify_windows_paths.py +0 -48
- package/server.json +0 -20
- package/src/mcp_server_nucleus/__init__.py +0 -8583
- package/src/mcp_server_nucleus/__main__.py +0 -9
- package/src/mcp_server_nucleus/cli.py +0 -1225
- package/src/mcp_server_nucleus/commitment_core.py +0 -146
- package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
- package/src/mcp_server_nucleus/core/__init__.py +0 -6
- package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
- package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
- package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
- package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
- package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
- package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
- package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
- package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
- package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
- package/src/mcp_server_nucleus/runtime/agent.py +0 -517
- package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
- package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
- package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
- package/src/mcp_server_nucleus/runtime/auth.py +0 -98
- package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
- package/src/mcp_server_nucleus/runtime/broker.py +0 -126
- package/src/mcp_server_nucleus/runtime/budget.py +0 -153
- package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
- package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
- package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
- package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
- package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
- package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
- package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
- package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
- package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
- package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
- package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
- package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
- package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
- package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
- package/src/mcp_server_nucleus/runtime/common.py +0 -134
- package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
- package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
- package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
- package/src/mcp_server_nucleus/runtime/critic.py +0 -150
- package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
- package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
- package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
- package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
- package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
- package/src/mcp_server_nucleus/runtime/export.py +0 -64
- package/src/mcp_server_nucleus/runtime/factory.py +0 -594
- package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
- package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
- package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
- package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
- package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
- package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
- package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
- package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
- package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
- package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
- package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
- package/src/mcp_server_nucleus/runtime/installer.py +0 -81
- package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
- package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
- package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
- package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
- package/src/mcp_server_nucleus/runtime/locking.py +0 -228
- package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
- package/src/mcp_server_nucleus/runtime/memory.py +0 -158
- package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
- package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
- package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
- package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
- package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
- package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
- package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
- package/src/mcp_server_nucleus/runtime/policy.py +0 -117
- package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
- package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
- package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
- package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
- package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
- package/src/mcp_server_nucleus/runtime/registry.py +0 -82
- package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
- package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
- package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
- package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
- package/src/mcp_server_nucleus/runtime/storage.py +0 -83
- package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
- package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
- package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
- package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
- package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
- package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
- package/src/mcp_server_nucleus/runtime/team.py +0 -59
- package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
- package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
- package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
- package/src/mcp_server_nucleus/tool_tiers.py +0 -243
- package/tests/__init__.py +0 -1
- package/tests/conftest.py +0 -42
- package/tests/test_core.py +0 -235
- 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()
|