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,488 +0,0 @@
1
- """
2
- TaskScheduler - Intelligent Task Assignment Engine
3
- Implements Priority Queue + Agent State Machine + Dependency Resolution
4
-
5
- Path A: Simple, deterministic, scales 1→1000 agents
6
- - Priority queue per tier (T1, T2, T3, T4)
7
- - Agent capacity tracking (busy vs available)
8
- - Dependency resolution (blocked tasks)
9
- - FIFO fairness within tier
10
- - Deadline-aware scheduling
11
- - Zero conflicts guarantee
12
-
13
- Author: NOP V3 - January 2026
14
- """
15
-
16
- import time
17
- import threading
18
- from typing import Dict, List, Optional, Set
19
- from collections import defaultdict
20
- from copy import deepcopy
21
- from enum import Enum
22
-
23
-
24
- class TaskStatus(str, Enum):
25
- """Task status in scheduler."""
26
- PENDING = "PENDING"
27
- SCHEDULED = "SCHEDULED"
28
- BLOCKED = "BLOCKED"
29
- ASSIGNED = "ASSIGNED"
30
- COMPLETED = "COMPLETED"
31
-
32
-
33
- class TaskPriority(str, Enum):
34
- """Task priority levels."""
35
- HIGH = "HIGH"
36
- MEDIUM = "MEDIUM"
37
- LOW = "LOW"
38
-
39
- def sort_value(self) -> int:
40
- """Return numeric value for sorting (lower = higher priority)."""
41
- return {"HIGH": 0, "MEDIUM": 1, "LOW": 2}[self.value]
42
-
43
-
44
- class TaskTier(str, Enum):
45
- """Task difficulty tiers."""
46
- T1_PLANNING = "T1_PLANNING"
47
- T2_CODE = "T2_CODE"
48
- T3_REVIEW = "T3_REVIEW"
49
- T4_DEPLOY = "T4_DEPLOY"
50
-
51
- def sort_value(self) -> int:
52
- """Return numeric value for tier ordering."""
53
- return {"T1_PLANNING": 0, "T2_CODE": 1, "T3_REVIEW": 2, "T4_DEPLOY": 3}[self.value]
54
-
55
-
56
- class AgentState:
57
- """Represents state of a single agent."""
58
-
59
- def __init__(self, agent_id: str, tier: str, capacity: int = 5):
60
- self.id = agent_id
61
- self.tier = tier
62
- self.capacity = capacity
63
- self.current_tasks: Set[str] = set()
64
- self.available = True
65
- self.last_heartbeat = int(time.time() * 1000)
66
- self.tasks_completed = 0
67
-
68
- def is_available(self) -> bool:
69
- """Check if agent has capacity for new task."""
70
- return len(self.current_tasks) < self.capacity
71
-
72
- def assign_task(self, task_id: str) -> bool:
73
- """Assign task to agent if capacity available."""
74
- if not self.is_available():
75
- return False
76
- self.current_tasks.add(task_id)
77
- return True
78
-
79
- def complete_task(self, task_id: str) -> bool:
80
- """Mark task as completed, free capacity."""
81
- if task_id in self.current_tasks:
82
- self.current_tasks.remove(task_id)
83
- self.tasks_completed += 1
84
- return True
85
- return False
86
-
87
- def to_dict(self) -> Dict:
88
- """Convert to dictionary."""
89
- return {
90
- "id": self.id,
91
- "tier": self.tier,
92
- "capacity": self.capacity,
93
- "current_tasks": list(self.current_tasks),
94
- "available": self.is_available(),
95
- "last_heartbeat": self.last_heartbeat,
96
- "tasks_completed": self.tasks_completed,
97
- }
98
-
99
-
100
- class ScheduleDecision:
101
- """Represents a scheduling decision for a task."""
102
-
103
- def __init__(
104
- self,
105
- task_id: str,
106
- agent_id: Optional[str] = None,
107
- reason: str = "queued",
108
- ):
109
- self.task_id = task_id
110
- self.agent_id = agent_id
111
- self.scheduled_at = int(time.time() * 1000)
112
- self.reason = reason # assigned, blocked, queued
113
-
114
- def to_dict(self) -> Dict:
115
- """Convert to dictionary."""
116
- return {
117
- "task_id": self.task_id,
118
- "agent_id": self.agent_id,
119
- "scheduled_at": self.scheduled_at,
120
- "reason": self.reason,
121
- }
122
-
123
-
124
- class TaskScheduler:
125
- """
126
- Intelligent task assignment engine.
127
-
128
- Key guarantees:
129
- - Zero scheduling conflicts: Each task assigned to at most one agent
130
- - Fairness: Tasks distributed evenly across agents in tier
131
- - Dependency resolution: Blocked tasks queued, not scheduled
132
- - Deadline-aware: Urgent tasks scheduled first
133
- - FIFO within tier: Stable ordering
134
- - Capacity respected: No over-booking agents
135
- - Tier matching: Tasks only assigned to agents of matching tier
136
- - Performance: <500ms for 100K tasks
137
-
138
- Single-process implementation (thread-safe).
139
- Multi-agent coordination ready for future distributed scheduler.
140
- """
141
-
142
- def __init__(self, max_agents: int = 1000):
143
- """
144
- Initialize task scheduler.
145
-
146
- Args:
147
- max_agents: Maximum number of agents (for allocation)
148
- """
149
- self.max_agents = max_agents
150
- self.agent_registry: Dict[str, AgentState] = {} # agent_id -> AgentState
151
- self.task_states: Dict[str, Dict] = {} # task_id -> TaskState
152
- self.dependency_graph: Dict[str, Set[str]] = defaultdict(set) # task_id -> blocked_by task_ids
153
- self.task_queues: Dict[str, List] = {
154
- "T1_PLANNING": [],
155
- "T2_CODE": [],
156
- "T3_REVIEW": [],
157
- "T4_DEPLOY": [],
158
- } # tier -> priority queue
159
- self.lock = threading.RLock()
160
- self.stats = {
161
- "scheduled": 0,
162
- "blocked": 0,
163
- "queued": 0,
164
- "completed": 0,
165
- }
166
-
167
- def register_agent(self, agent_id: str, tier: str, capacity: int = 5) -> Dict:
168
- """
169
- Register new agent into scheduler.
170
-
171
- Args:
172
- agent_id: Unique agent identifier
173
- tier: Task tier (T1_PLANNING, T2_CODE, T3_REVIEW, T4_DEPLOY)
174
- capacity: Max concurrent tasks this agent can handle
175
-
176
- Returns:
177
- AgentState as dictionary
178
- """
179
- with self.lock:
180
- if agent_id in self.agent_registry:
181
- raise ValueError(f"Agent {agent_id} already registered")
182
-
183
- agent = AgentState(agent_id, tier, capacity)
184
- self.agent_registry[agent_id] = agent
185
- return agent.to_dict()
186
-
187
- def unregister_agent(self, agent_id: str) -> bool:
188
- """
189
- Unregister agent from scheduler.
190
-
191
- Args:
192
- agent_id: Agent to unregister
193
-
194
- Returns:
195
- True if unregistered, False if not found
196
- """
197
- with self.lock:
198
- if agent_id not in self.agent_registry:
199
- return False
200
-
201
- agent = self.agent_registry[agent_id]
202
-
203
- # Free all assigned tasks
204
- for task_id in list(agent.current_tasks):
205
- if task_id in self.task_states:
206
- self.task_states[task_id]["assigned_to"] = None
207
- self.task_states[task_id]["status"] = TaskStatus.PENDING.value
208
-
209
- del self.agent_registry[agent_id]
210
- return True
211
-
212
- def mark_task_done(self, task_id: str, agent_id: str) -> bool:
213
- """
214
- Mark task as completed, free agent capacity.
215
-
216
- Args:
217
- task_id: Task that completed
218
- agent_id: Agent that completed it
219
-
220
- Returns:
221
- True if marked done, False if not found
222
- """
223
- with self.lock:
224
- if agent_id not in self.agent_registry:
225
- return False
226
-
227
- agent = self.agent_registry[agent_id]
228
- if not agent.complete_task(task_id):
229
- return False
230
-
231
- if task_id in self.task_states:
232
- self.task_states[task_id]["status"] = TaskStatus.COMPLETED.value
233
- self.stats["completed"] += 1
234
-
235
- return True
236
-
237
- def resolve_dependencies(self, task_id: str) -> bool:
238
- """
239
- Check if all dependencies of a task are satisfied.
240
-
241
- Args:
242
- task_id: Task to check
243
-
244
- Returns:
245
- True if all blocked_by tasks are completed, False otherwise
246
- """
247
- with self.lock:
248
- if task_id not in self.task_states:
249
- return False
250
-
251
- blocked_by = self.task_states[task_id].get("blocked_by", [])
252
-
253
- for blocker_id in blocked_by:
254
- if blocker_id not in self.task_states:
255
- continue # Blocker not in scheduler
256
-
257
- blocker_status = self.task_states[blocker_id].get("status")
258
- if blocker_status != TaskStatus.COMPLETED.value:
259
- return False # Blocker not completed
260
-
261
- return True # All blockers completed
262
-
263
- def schedule_batch(
264
- self,
265
- tasks: List[Dict],
266
- force_schedule: bool = False,
267
- ) -> List[Dict]:
268
- """
269
- Schedule a batch of tasks across agents.
270
-
271
- Args:
272
- tasks: List of task dicts with id, tier, priority, blocked_by, deadline
273
- force_schedule: If True, use best-effort (may over-book)
274
-
275
- Returns:
276
- List of ScheduleDecision dicts (assigned, blocked, queued)
277
-
278
- Algorithm:
279
- 1. Add tasks to internal state
280
- 2. Resolve dependencies (mark blocked)
281
- 3. Sort by: priority, deadline, FIFO (created_at)
282
- 4. For each non-blocked task:
283
- - Find agent with matching tier + available capacity
284
- - Assign task to agent
285
- - Mark as scheduled
286
- 5. Return decisions
287
- """
288
- with self.lock:
289
- decisions = []
290
- time.time()
291
-
292
- # Phase 1: Add tasks to internal state
293
- for task in tasks:
294
- task_id = task.get("id")
295
- if task_id in self.task_states:
296
- continue # Already tracked
297
-
298
- self.task_states[task_id] = {
299
- "id": task_id,
300
- "status": TaskStatus.PENDING.value,
301
- "tier": task.get("tier", "T1_PLANNING"),
302
- "priority": task.get("priority", "MEDIUM"),
303
- "deadline": task.get("deadline"),
304
- "created_at": task.get("created_at", int(time.time() * 1000)),
305
- "assigned_to": None,
306
- "blocked_by": task.get("blocked_by", []),
307
- }
308
-
309
- # Phase 2: Resolve dependencies, categorize tasks
310
- blocked_tasks = set()
311
- pending_tasks = []
312
-
313
- for task in tasks:
314
- task_id = task.get("id")
315
- task_state = self.task_states[task_id]
316
-
317
- # Check if task is blocked
318
- is_blocked = not self.resolve_dependencies(task_id)
319
-
320
- if is_blocked:
321
- blocked_tasks.add(task_id)
322
- task_state["status"] = TaskStatus.BLOCKED.value
323
- decisions.append(
324
- ScheduleDecision(task_id, reason="blocked").to_dict()
325
- )
326
- self.stats["blocked"] += 1
327
- else:
328
- pending_tasks.append(task_state)
329
-
330
- # Phase 3: Sort pending tasks by priority/deadline/FIFO
331
- def sort_key(task_state):
332
- priority = TaskPriority[task_state["priority"]].sort_value()
333
- deadline = task_state.get("deadline") or float("inf")
334
- created_at = task_state.get("created_at", float("inf"))
335
- # Sort by: priority (low first), deadline (early first), FIFO (early created first)
336
- return (priority, deadline, created_at)
337
-
338
- pending_tasks.sort(key=sort_key)
339
-
340
- # Phase 4: Assign tasks to agents
341
- for task_state in pending_tasks:
342
- task_id = task_state["id"]
343
- tier = task_state["tier"]
344
-
345
- # Find available agent with matching tier
346
- assigned_agent = None
347
- for agent in self.agent_registry.values():
348
- if agent.tier == tier and agent.is_available():
349
- assigned_agent = agent
350
- break
351
-
352
- if assigned_agent:
353
- # Assign task to agent
354
- if assigned_agent.assign_task(task_id):
355
- task_state["status"] = TaskStatus.SCHEDULED.value
356
- task_state["assigned_to"] = assigned_agent.id
357
- decisions.append(
358
- ScheduleDecision(
359
- task_id, agent_id=assigned_agent.id, reason="assigned"
360
- ).to_dict()
361
- )
362
- self.stats["scheduled"] += 1
363
- else:
364
- # Capacity exhausted
365
- task_state["status"] = TaskStatus.PENDING.value
366
- decisions.append(
367
- ScheduleDecision(task_id, reason="queued").to_dict()
368
- )
369
- self.stats["queued"] += 1
370
- else:
371
- # No available agent with matching tier
372
- task_state["status"] = TaskStatus.PENDING.value
373
- decisions.append(
374
- ScheduleDecision(task_id, reason="queued").to_dict()
375
- )
376
- self.stats["queued"] += 1
377
-
378
- return decisions
379
-
380
- def get_agent_state(self, agent_id: str) -> Optional[Dict]:
381
- """
382
- Get current state of an agent.
383
-
384
- Args:
385
- agent_id: Agent to query
386
-
387
- Returns:
388
- AgentState dict or None if not found
389
- """
390
- with self.lock:
391
- agent = self.agent_registry.get(agent_id)
392
- return agent.to_dict() if agent else None
393
-
394
- def get_all_agents(self) -> List[Dict]:
395
- """
396
- Get all registered agents.
397
-
398
- Returns:
399
- List of AgentState dicts
400
- """
401
- with self.lock:
402
- return [agent.to_dict() for agent in self.agent_registry.values()]
403
-
404
- def get_pending_tasks(self, agent_id: str) -> List[Dict]:
405
- """
406
- Get tasks assigned to an agent (pending execution).
407
-
408
- Args:
409
- agent_id: Agent to query
410
-
411
- Returns:
412
- List of task dicts
413
- """
414
- with self.lock:
415
- if agent_id not in self.agent_registry:
416
- return []
417
-
418
- agent = self.agent_registry[agent_id]
419
- pending = []
420
-
421
- for task_id in agent.current_tasks:
422
- if task_id in self.task_states:
423
- pending.append(deepcopy(self.task_states[task_id]))
424
-
425
- return pending
426
-
427
- def get_scheduling_stats(self) -> Dict:
428
- """
429
- Get scheduler statistics.
430
-
431
- Returns:
432
- Dict with scheduled, blocked, queued, completed counts
433
- """
434
- with self.lock:
435
- return {
436
- "scheduled": self.stats["scheduled"],
437
- "blocked": self.stats["blocked"],
438
- "queued": self.stats["queued"],
439
- "completed": self.stats["completed"],
440
- "total_agents": len(self.agent_registry),
441
- "total_tasks": len(self.task_states),
442
- }
443
-
444
- def get_task_state(self, task_id: str) -> Optional[Dict]:
445
- """
446
- Get state of a specific task.
447
-
448
- Args:
449
- task_id: Task to query
450
-
451
- Returns:
452
- TaskState dict or None if not found
453
- """
454
- with self.lock:
455
- return deepcopy(self.task_states.get(task_id))
456
-
457
- def get_fairness_metrics(self) -> Dict:
458
- """
459
- Get fairness metrics for each tier.
460
-
461
- Returns:
462
- Dict with per-tier fairness stats (task distribution across agents)
463
- """
464
- with self.lock:
465
- fairness = {}
466
-
467
- for tier in ["T1_PLANNING", "T2_CODE", "T3_REVIEW", "T4_DEPLOY"]:
468
- tier_agents = [
469
- a for a in self.agent_registry.values() if a.tier == tier
470
- ]
471
-
472
- if not tier_agents:
473
- continue
474
-
475
- task_counts = [len(a.current_tasks) for a in tier_agents]
476
- avg_tasks = sum(task_counts) / len(tier_agents) if tier_agents else 0
477
- variance = sum((x - avg_tasks) ** 2 for x in task_counts) / len(
478
- task_counts
479
- ) if tier_agents else 0
480
-
481
- fairness[tier] = {
482
- "agents": len(tier_agents),
483
- "task_counts": task_counts,
484
- "avg_tasks_per_agent": avg_tasks,
485
- "variance": variance,
486
- }
487
-
488
- return fairness
@@ -1,59 +0,0 @@
1
- """
2
- TeamManager: The Hive Mind.
3
- Manages Shared Configuration and Trust Roots for the Team.
4
-
5
- Strategic Role:
6
- - SYNC: Ensures all agents on the team use the same Registry.
7
- - TRUST: Distributes the Team Lead's public keys.
8
- - POLICY: Enforces team-wide rules (e.g. "Signed Only").
9
- """
10
-
11
- import json
12
- import logging
13
- from typing import List, Optional
14
- from pathlib import Path
15
- from pydantic import BaseModel, Field
16
-
17
- logger = logging.getLogger("TEAM")
18
-
19
- class TeamPolicy(BaseModel):
20
- require_signed: bool = True
21
- min_trust_level: str = "verified"
22
-
23
- class TeamConfig(BaseModel):
24
- team_id: str
25
- name: str
26
- registry_url: Optional[str] = None
27
- trusted_keys: List[str] = []
28
- policy: TeamPolicy = Field(default_factory=TeamPolicy)
29
-
30
- class TeamManager:
31
- def __init__(self, brain_path: Path):
32
- self.brain_path = brain_path
33
- self.config_path = brain_path / "config" / "team.json"
34
-
35
- def get_config(self) -> Optional[TeamConfig]:
36
- """Load and return the current team configuration."""
37
- if not self.config_path.exists():
38
- return None
39
-
40
- try:
41
- data = json.loads(self.config_path.read_text())
42
- return TeamConfig(**data)
43
- except Exception as e:
44
- logger.error(f"Failed to load team config: {e}")
45
- return None
46
-
47
- def get_trusted_roots(self) -> List[str]:
48
- """Return the list of trusted key fingerprints defined by the team."""
49
- config = self.get_config()
50
- if config:
51
- return config.trusted_keys
52
- return []
53
-
54
- def get_registry_url(self) -> Optional[str]:
55
- """Return the team's private registry URL if configured."""
56
- config = self.get_config()
57
- if config:
58
- return config.registry_url
59
- return None