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,126 +0,0 @@
1
- """
2
- ContextBroker: The Economy Engine.
3
- Facilitates the exchange of information (context) negotiation between agents.
4
-
5
- Roles:
6
- 1. MARKETPLACE: Where agents list what they know.
7
- 2. CLEARING HOUSE: Records transactions (who bought what).
8
- 3. SETTLEMENT: (Future) Handles credit transfers.
9
- """
10
-
11
- import json
12
- import logging
13
- import uuid
14
- from datetime import datetime
15
- from typing import List, Optional, Dict, Literal
16
- from pathlib import Path
17
- from pydantic import BaseModel
18
-
19
- from .locking import get_lock
20
-
21
- logger = logging.getLogger("BROKER")
22
-
23
- class ContextListing(BaseModel):
24
- id: str
25
- provider_id: str
26
- topic: str
27
- description: str
28
- content: str # In MVP, content is stored directly. In prod, this is a pointer.
29
- price: float = 0.0
30
- type: Literal["data", "service"] = "data"
31
- created_at: str
32
-
33
- class ContextTransaction(BaseModel):
34
- id: str
35
- listing_id: str
36
- buyer_id: str
37
- seller_id: str
38
- amount: float
39
- timestamp: str
40
- content: Optional[str] = None # The delivered goods
41
-
42
- class ContextBroker:
43
- def __init__(self, brain_path: Path):
44
- self.brain_path = brain_path
45
- self.listings_path = brain_path / "ledger" / "listings.json"
46
- self.ledger_path = brain_path / "ledger" / "transactions.jsonl"
47
-
48
- # Ensure dirs
49
- self.listings_path.parent.mkdir(parents=True, exist_ok=True)
50
-
51
- def _load_listings(self) -> Dict[str, ContextListing]:
52
- with get_lock("broker", self.brain_path).section():
53
- if not self.listings_path.exists():
54
- return {}
55
- try:
56
- data = json.loads(self.listings_path.read_text())
57
- return {k: ContextListing(**v) for k, v in data.items()}
58
- except Exception:
59
- return {}
60
-
61
- def _save_listings(self, listings: Dict[str, ContextListing]):
62
- with get_lock("broker", self.brain_path).section():
63
- data = {k: v.model_dump() for k, v in listings.items()}
64
- self.listings_path.write_text(json.dumps(data, indent=2))
65
-
66
- def publish_listing(self, provider_id: str, topic: str, description: str, content: str, price: float = 0.0, type: Literal["data", "service"] = "data") -> str:
67
- """Create a new context listing."""
68
- listing_id = f"list-{uuid.uuid4().hex[:8]}"
69
- listing = ContextListing(
70
- id=listing_id,
71
- provider_id=provider_id,
72
- topic=topic,
73
- description=description,
74
- content=content,
75
- price=price,
76
- type=type,
77
- created_at=datetime.now().isoformat()
78
- )
79
-
80
- listings = self._load_listings()
81
- listings[listing_id] = listing
82
- self._save_listings(listings)
83
-
84
- logger.info(f"📢 Listing Published: {topic} by {provider_id} ({listing_id})")
85
- return listing_id
86
-
87
- def search_listings(self, query: str) -> List[ContextListing]:
88
- """Find listings by topic or description."""
89
- listings = self._load_listings()
90
- query = query.lower()
91
- results = []
92
- for lst in listings.values():
93
- if query in lst.topic.lower() or query in lst.description.lower():
94
- results.append(lst)
95
- return results
96
-
97
- def buy_context(self, buyer_id: str, listing_id: str) -> Optional[ContextTransaction]:
98
- """Execute a context transaction."""
99
- listings = self._load_listings()
100
-
101
- if listing_id not in listings:
102
- logger.error(f"❌ Transaction Failed: Listing {listing_id} not found")
103
- return None
104
-
105
- listing = listings[listing_id]
106
-
107
- # In a real economy, check budget here.
108
-
109
- tx_id = f"tx-{uuid.uuid4().hex[:8]}"
110
- tx = ContextTransaction(
111
- id=tx_id,
112
- listing_id=listing.id,
113
- buyer_id=buyer_id,
114
- seller_id=listing.provider_id,
115
- amount=listing.price,
116
- timestamp=datetime.now().isoformat(),
117
- content=listing.content # Deliver the goods
118
- )
119
-
120
- # Append to ledger
121
- with get_lock("ledger", self.brain_path).section():
122
- with open(self.ledger_path, "a") as f:
123
- f.write(tx.model_dump_json() + "\n")
124
-
125
- logger.info(f"💰 Sold: {listing.topic} from {listing.provider_id} to {buyer_id}")
126
- return tx
@@ -1,153 +0,0 @@
1
- """
2
- BudgetAuditor: The Financial Fuse.
3
- Strictly enforces spending limits for Nucleus operations.
4
-
5
- Strategic Role:
6
- - Tracks cumulative spend across sessions.
7
- - "Hard Fuse": Cuts off execution if budget exceeded.
8
- - Uses BrainLock for accurate accounting.
9
- - Supports Per-Agent BudgetGuard.
10
- """
11
-
12
- import json
13
- import logging
14
- from datetime import datetime
15
- from pathlib import Path
16
- from typing import Dict, Any, List
17
- from .locking import get_lock
18
- from .capabilities.base import Capability
19
-
20
- logger = logging.getLogger("BUDGET_AUDITOR")
21
-
22
- class BudgetAuditor:
23
- def __init__(self, brain_path: Path):
24
- self.brain_path = brain_path
25
- self.ledger_path = brain_path / "ledger" / "budget_ledger.json"
26
-
27
- # Ensure ledger dir exists
28
- self.ledger_path.parent.mkdir(parents=True, exist_ok=True)
29
-
30
- def _load_ledger(self) -> Dict:
31
- if self.ledger_path.exists():
32
- try:
33
- return json.loads(self.ledger_path.read_text())
34
- except Exception:
35
- pass
36
- return {
37
- "total_spend_usd": 0.0,
38
- "daily_spend_usd": {}, # "YYYY-MM-DD": float
39
- "agent_spend_usd": {}, # "str(agent_id)": float (Cumulative)
40
- "transactions": []
41
- }
42
-
43
- def _save_ledger(self, ledger: Dict):
44
- self.ledger_path.write_text(json.dumps(ledger, indent=2))
45
-
46
- def check_authorization(self, completed_cost: float = 0.0) -> bool:
47
- """
48
- Global check (Not agent specific).
49
- For now, just a placeholder or daily limit check.
50
- """
51
- return True
52
-
53
- def record_expense(self, amount_usd: float, description: str, source: str = "llm"):
54
- """Record a completed expense globally and per-agent"""
55
- with get_lock("budget", self.brain_path).section():
56
- ledger = self._load_ledger()
57
- today = datetime.now().strftime("%Y-%m-%d")
58
-
59
- ledger["total_spend_usd"] += amount_usd
60
- ledger["daily_spend_usd"][today] = ledger["daily_spend_usd"].get(today, 0.0) + amount_usd
61
-
62
- # If source is an agent ID, track per agent
63
- if source:
64
- ledger["agent_spend_usd"][source] = ledger["agent_spend_usd"].get(source, 0.0) + amount_usd
65
-
66
- ledger["transactions"].append({
67
- "timestamp": datetime.now().isoformat(),
68
- "amount": amount_usd,
69
- "description": description,
70
- "source": source
71
- })
72
-
73
- # Trim transactions if too long
74
- if len(ledger["transactions"]) > 1000:
75
- ledger["transactions"] = ledger["transactions"][-1000:]
76
-
77
- self._save_ledger(ledger)
78
-
79
- def get_agent_spend(self, agent_id: str) -> float:
80
- """Get total spend for a specific agent"""
81
- ledger = self._load_ledger()
82
- return ledger.get("agent_spend_usd", {}).get(agent_id, 0.0)
83
-
84
-
85
- class BudgetGuard(Capability):
86
- """
87
- Wraps an imported capability to enforce strict budget limits for a specific agent.
88
- "Host Policy overrides Agent Policy".
89
- """
90
- def __init__(self, inner: Capability, auditor: BudgetAuditor, agent_id: str, max_budget_usd: float = 0.0):
91
- # Initialize Abstract Base Class
92
- # Note: We don't call super().__init__() because ABCs don't need it,
93
- # but we must ensure we don't violate property requirements.
94
- self.inner = inner
95
- self.auditor = auditor
96
- self.agent_id = agent_id
97
- self.max_budget_usd = max_budget_usd
98
-
99
- # Load initial spend state
100
- self.spent_usd = self.auditor.get_agent_spend(agent_id)
101
-
102
- @property
103
- def name(self) -> str:
104
- return self.inner.name
105
-
106
- @property
107
- def description(self) -> str:
108
- return f"{self.inner.description} (Budget: ${self.max_budget_usd})"
109
-
110
- def get_tools(self) -> List[Dict[str, Any]]:
111
- # We might want to wrap tool descriptions too, but for now we delegate
112
- return self.inner.get_tools()
113
-
114
- def execute(self, params: Dict[str, Any]) -> str:
115
- # Sync simple spending state (in case other tools updated it)
116
- self.spent_usd = self.auditor.get_agent_spend(self.agent_id)
117
-
118
- # 1. Pre-Check (Hard Fuse)
119
- if self.spent_usd >= self.max_budget_usd and self.max_budget_usd >= 0:
120
- # Note: We can treat max_budget_usd < 0 as infinite if we wanted,
121
- # but for now let's say >=0 is required.
122
- if self.max_budget_usd == 0:
123
- return "❌ SECURITY BLOCK: Zero Cost Budget Enforced."
124
-
125
- # Allow execution if we are extremely close (floating point epsilon)
126
- # OR if we want to strict block. Let's strict block.
127
- return f"❌ SECURITY BLOCK: Budget Exceeded (${self.spent_usd:.4f}/${self.max_budget_usd:.2f})"
128
-
129
- # 2. Execute Inner Tool
130
- logger.info(f"💰 BudgetGuard Allowing {self.agent_id} call (Spent: ${self.spent_usd})")
131
- try:
132
- result = self.inner.execute(params)
133
- except Exception as e:
134
- # Even if it fails, did it cost money?
135
- # For network tools/LLM, yes. For local function errors, maybe not.
136
- # We assume "Attempted Execution" costs standard rate for now.
137
- result = f"Error: {e}"
138
-
139
- # 3. Post-Check (Record Cost)
140
- # In a real system, we'd measure token usage or network bytes.
141
- # For simulation/Phase 57, we assume a flat rate per tool call.
142
- COST_PER_CALL = 0.01
143
-
144
- self.auditor.record_expense(
145
- amount_usd=COST_PER_CALL,
146
- description=f"Action: {self.inner.name}",
147
- source=self.agent_id
148
- )
149
-
150
- # Update local tracking
151
- self.spent_usd += COST_PER_CALL
152
-
153
- return result
@@ -1,20 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import List, Dict, Any
3
-
4
- class Capability(ABC):
5
- @property
6
- @abstractmethod
7
- def name(self) -> str:
8
- """Name of the capability cluster (e.g., 'render_ops')."""
9
- pass
10
-
11
- @property
12
- @abstractmethod
13
- def description(self) -> str:
14
- """Description for the Context Factory."""
15
- pass
16
-
17
- @abstractmethod
18
- def get_tools(self) -> List[Dict[str, Any]]:
19
- """Return list of MCP tool definitions."""
20
- pass
@@ -1,253 +0,0 @@
1
- from typing import List, Dict, Any
2
- from pathlib import Path
3
- import os
4
- from .base import Capability
5
- from ... import commitment_ledger
6
-
7
- class BrainOps(Capability):
8
- @property
9
- def name(self) -> str:
10
- return "brain_ops"
11
-
12
- @property
13
- def description(self) -> str:
14
- return "Tools for managing the centralized commitment ledger (The Brain)."
15
-
16
- def get_tools(self) -> List[Dict[str, Any]]:
17
- return [
18
- {
19
- "name": "brain_add_commitment",
20
- "description": "Add a new commitment (task, todo, loop) to the ledger.",
21
- "parameters": {
22
- "type": "object",
23
- "properties": {
24
- "description": {"type": "string"},
25
- "loop_type": {"type": "string", "enum": ["task", "todo", "draft", "decision"]},
26
- "priority": {"type": "integer", "minimum": 1, "maximum": 5},
27
- "source": {"type": "string"}
28
- },
29
- "required": ["description"]
30
- }
31
- },
32
- {
33
- "name": "brain_get_open_loops",
34
- "description": "Get all active open loops.",
35
- "parameters": {
36
- "type": "object",
37
- "properties": {
38
- "type_filter": {"type": "string"}
39
- }
40
- }
41
- },
42
- {
43
- "name": "brain_scan_commitments",
44
- "description": "Trigger the Librarian to scan artifacts for checklist items.",
45
- "parameters": {"type": "object", "properties": {}}
46
- },
47
- {
48
- "name": "brain_archive_stale",
49
- "description": "Trigger the Librarian to archive stale commitments.",
50
- "parameters": {"type": "object", "properties": {}}
51
- },
52
- {
53
- "name": "brain_orchestrate_swarm",
54
- "description": "Initialize a multi-agent swarm for a complex mission.",
55
- "parameters": {
56
- "type": "object",
57
- "properties": {
58
- "mission": {"type": "string"},
59
- "agents": {"type": "array", "items": {"type": "string"}},
60
- "swarm_type": {"type": "string", "enum": ["genesis", "execution"]}
61
- },
62
- "required": ["mission"]
63
- }
64
- },
65
- {
66
- "name": "brain_export",
67
- "description": "Export the brain content to a zip file (respecting .brainignore).",
68
- "parameters": {"type": "object", "properties": {}}
69
- },
70
- {
71
- "name": "brain_consolidate_logs",
72
- "description": "Consolidate raw JSON logs in .brain/raw/ into a daily archive.",
73
- "parameters": {"type": "object", "properties": {}}
74
- },
75
- {
76
- "name": "brain_delegate_task",
77
- "description": "Delegate a task to another specialized agent persona. The agent will execute autonomously.",
78
- "parameters": {
79
- "type": "object",
80
- "properties": {
81
- "persona": {"type": "string", "description": "Target persona: librarian, researcher, critic, developer, architect, strategist, devops, synthesizer"},
82
- "intent": {"type": "string", "description": "The task description for the delegated agent"}
83
- },
84
- "required": ["persona", "intent"]
85
- }
86
- }
87
- ]
88
-
89
- def execute_tool(self, tool_name: str, args: Dict) -> str:
90
- """Execute the tool locally using commitment_ledger."""
91
- # Use configured brain path
92
- brain_path = Path(os.environ.get("NUCLEUS_BRAIN_PATH", "/Users/lokeshgarg/ai-mvp-backend/.brain"))
93
-
94
- if tool_name == "brain_add_commitment":
95
- result = commitment_ledger.add_commitment(
96
- brain_path=brain_path,
97
- source_file="NAR_AGENT",
98
- source_line=0,
99
- description=args['description'],
100
- comm_type=args.get('loop_type', 'task'),
101
- priority=args.get('priority', 3),
102
- source=args.get('source', 'nar_agent')
103
- )
104
- return f"Commitment Added: {result['id']}"
105
-
106
- elif tool_name == "brain_get_open_loops":
107
- loops = commitment_ledger.load_ledger(brain_path)["commitments"]
108
- # Filter logic could go here
109
- return f"Found {len([c for c in loops if c['status']=='open'])} open loops."
110
-
111
- elif tool_name == "brain_scan_commitments":
112
- result = commitment_ledger.scan_for_commitments(brain_path)
113
- return f"Scan Complete: {result}"
114
-
115
- elif tool_name == "brain_archive_stale":
116
- count = commitment_ledger.auto_archive_stale(brain_path)
117
- return f"Archived {count} stale items."
118
-
119
- elif tool_name == "brain_export":
120
- # Call export logic (implemented in commitment_ledger for centralization)
121
- if hasattr(commitment_ledger, 'export_brain'):
122
- result = commitment_ledger.export_brain(brain_path)
123
- return f"Export Complete: {result}"
124
- return "Error: export_brain not implemented in ledger."
125
-
126
- elif tool_name == "brain_delegate_task":
127
- # Dynamic import to avoid circular dependency
128
- from uuid import uuid4
129
- from ..factory import ContextFactory
130
- from ..agent import EphemeralAgent
131
- from ..llm_client import DualEngineLLM
132
-
133
- persona = args.get('persona')
134
- intent = args.get('intent')
135
-
136
- # Use factory to create specialized agent
137
- session_id = f"delegated-{str(uuid4())[:8]}"
138
- factory = ContextFactory()
139
-
140
- # Create context for the specific persona
141
- context = factory.create_context_for_persona(session_id, persona, intent)
142
-
143
- # Execute
144
- llm = DualEngineLLM()
145
- agent = EphemeralAgent(context, model=llm)
146
-
147
- # In a real async environment, we'd await this.
148
- # But execute_tool is currently sync in this base class signature.
149
- # We need to run it synchronously or change the signature.
150
- # Using asyncio.run() for now as a bridge.
151
- # Using asyncio.run() or existing loop via nest_asyncio
152
- import asyncio
153
- try:
154
- import nest_asyncio
155
- nest_asyncio.apply()
156
- except ImportError:
157
- pass
158
-
159
- try:
160
- # With nest_asyncio, we can just use asyncio.run or loop.run_until_complete
161
- # even if a loop is running.
162
- loop = asyncio.get_event_loop()
163
- if loop.is_running():
164
- log = loop.run_until_complete(agent.run())
165
- else:
166
- log = asyncio.run(agent.run())
167
-
168
- return f"✅ Delegation Complete:\n{log}"
169
-
170
- except RuntimeError:
171
- # Fallback if nest_asyncio failed or not installed and loop is running
172
- return f"⚠️ Cannot block on delegation (Async Loop Running & nest_asyncio missing). Task queued for {persona}: {intent}"
173
-
174
- elif tool_name == "brain_orchestrate_swarm":
175
- from ..orchestrator import SwarmsOrchestrator
176
- import asyncio
177
-
178
- orchestrator = SwarmsOrchestrator(brain_path=brain_path)
179
-
180
- # Extract agents list from args
181
- agents = args.get('agents', None) # List of agent personas
182
-
183
- # start_mission is async, need to run it properly
184
- try:
185
- import nest_asyncio
186
- nest_asyncio.apply()
187
- except ImportError:
188
- pass
189
-
190
- try:
191
- loop = asyncio.get_event_loop()
192
- if loop.is_running():
193
- result = loop.run_until_complete(orchestrator.start_mission(
194
- mission_goal=args['mission'],
195
- swarm_type=args.get('swarm_type', 'genesis'),
196
- agents=agents # Pass agents to orchestrator
197
- ))
198
- else:
199
- result = asyncio.run(orchestrator.start_mission(
200
- mission_goal=args['mission'],
201
- swarm_type=args.get('swarm_type', 'genesis'),
202
- agents=agents
203
- ))
204
-
205
- return f"✅ Swarm Initiated:\\nMission ID: {result.get('mission_id', 'unknown')}\\nAgents: {agents or 'auto-detected'}\\nStatus: {result.get('status', 'started')}"
206
- except RuntimeError as e:
207
- return f"⚠️ Swarm start failed (async issue): {e}. Mission queued."
208
-
209
-
210
- elif tool_name == "brain_consolidate_logs":
211
- # Consolidate raw logs into daily archives
212
- import json
213
- from datetime import datetime
214
-
215
- brain_path = Path(os.environ.get("NUCLEUS_BRAIN_PATH", "/Users/lokeshgarg/ai-mvp-backend/.brain"))
216
- raw_path = brain_path / "raw"
217
- archive_dir = brain_path / "archive"
218
- archive_dir.mkdir(parents=True, exist_ok=True)
219
-
220
- if not raw_path.exists():
221
- return "No raw logs to consolidate."
222
-
223
- logs = list(raw_path.glob("*.json"))
224
- if not logs:
225
- return "0 logs found. Nothing to consolidate."
226
-
227
- # Group by date
228
- count = 0
229
- size_saved = 0
230
-
231
- # Use current date for the archive bucket
232
- # Ideally we group by the timestamp in the file, but for V1 just dumping current pile is fine.
233
- today = datetime.now().strftime("%Y-%m-%d")
234
- archive_file = archive_dir / f"raw_interactions_{today}.jsonl"
235
-
236
- with open(archive_file, "a") as out_f:
237
- for log_file in logs:
238
- try:
239
- content = log_file.read_text()
240
- # Minify JSON line
241
- data = json.loads(content)
242
- out_f.write(json.dumps(data) + "\n")
243
-
244
- size_saved += log_file.stat().st_size
245
- log_file.unlink() # Delete original
246
- count += 1
247
- except Exception as e:
248
- print(f"Error consolidating {log_file}: {e}")
249
-
250
- return f"✅ Consolidated {count} raw logs into {archive_file.name}. Saved {size_saved/1024:.2f} KB of inode space."
251
-
252
- return f"Tool {tool_name} not found in BrainOps."
253
-