arkaos 2.17.1 → 2.17.4
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/VERSION +1 -1
- package/arka/SKILL.md +9 -67
- package/arka/skills/comfyui/SKILL.md +50 -12
- package/arka/skills/conclave/SKILL.md +43 -141
- package/arka/skills/conclave/references/advisors.md +36 -0
- package/arka/skills/human-writing/SKILL.md +15 -100
- package/arka/skills/human-writing/references/forbidden-patterns.md +32 -0
- package/config/hooks/post-tool-use.sh +72 -0
- package/config/hooks/session-start.sh +16 -0
- package/config/hooks/user-prompt-submit.ps1 +2 -2
- package/config/hooks/user-prompt-submit.sh +102 -26
- package/core/agents/__pycache__/behavior_enforcer.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/dna_registry.cpython-313.pyc +0 -0
- package/core/agents/adapters/__pycache__/disc_adapter.cpython-313.pyc +0 -0
- package/core/agents/adapters/disc_adapter.py +149 -0
- package/core/agents/behavior_enforcer.py +255 -0
- package/core/agents/dna_registry.py +235 -0
- package/core/forge/__init__.py +36 -0
- package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
- package/core/forge/orchestrator.py +770 -0
- package/core/forge/runtime_dispatcher.py +465 -0
- package/core/governance/__pycache__/quality_api.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/quality_router.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
- package/core/governance/quality_api.py +280 -0
- package/core/governance/quality_router.py +304 -0
- package/core/governance/review_workflow.py +386 -0
- package/core/memory/__pycache__/compressor.cpython-313.pyc +0 -0
- package/core/memory/__pycache__/rehydrator.cpython-313.pyc +0 -0
- package/core/memory/__pycache__/session_store.cpython-313.pyc +0 -0
- package/core/memory/compressor.py +269 -0
- package/core/memory/rehydrator.py +204 -0
- package/core/memory/session_store.py +256 -0
- package/core/runtime/__pycache__/context_compactor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/subagent.cpython-313.pyc +0 -0
- package/core/synapse/__init__.py +10 -3
- package/core/synapse/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/engine.py +27 -16
- package/core/synapse/kb_cache.py +382 -0
- package/core/synapse/layers.py +253 -50
- package/core/sync/__pycache__/self_healing.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/announcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/dashboard.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/state.cpython-313.pyc +0 -0
- package/core/workflow/announcer.py +246 -0
- package/core/workflow/dashboard.py +194 -0
- package/core/workflow/enforcer.py +234 -0
- package/core/workflow/recovery.py +196 -0
- package/core/workflow/rules_registry.py +484 -0
- package/core/workflow/session_summary.py +204 -0
- package/core/workflow/state.py +12 -2
- package/departments/dev/SKILL.md +10 -42
- package/departments/dev/skills/agent-design/SKILL.md +6 -26
- package/departments/dev/skills/ci-cd-pipeline/SKILL.md +6 -29
- package/departments/dev/skills/db-schema/SKILL.md +6 -24
- package/departments/dev/skills/dependency-audit/SKILL.md +1 -3
- package/departments/dev/skills/incident/SKILL.md +6 -24
- package/departments/dev/skills/mcp-builder/SKILL.md +4 -17
- package/departments/dev/skills/observability/SKILL.md +6 -29
- package/departments/dev/skills/performance-profiler/SKILL.md +5 -26
- package/departments/dev/skills/rag-architect/SKILL.md +5 -23
- package/departments/dev/skills/release/SKILL.md +4 -17
- package/departments/dev/skills/spec/SKILL.md +47 -148
- package/departments/landing/skills/landing-gen/SKILL.md +3 -15
- package/departments/marketing/skills/cold-email/SKILL.md +5 -17
- package/departments/marketing/skills/programmatic-seo/SKILL.md +6 -21
- package/departments/strategy/skills/board-advisor/SKILL.md +7 -21
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/synapse-bridge.py +27 -3
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Behavior enforcement middleware.
|
|
2
|
+
|
|
3
|
+
Validates agent outputs against their stated behavioral DNA profile.
|
|
4
|
+
Detects "behavior drift" — when an agent acts outside their profile.
|
|
5
|
+
|
|
6
|
+
This is advisory only (WARN severity) — it doesn't block operations
|
|
7
|
+
but surfaces discrepancies for human review.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from core.agents.dna_registry import DNARegistry, get_registry
|
|
15
|
+
from core.agents.schema import Agent, DISCType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class BehaviorDrift:
|
|
20
|
+
"""A detected deviation from an agent's DNA profile."""
|
|
21
|
+
|
|
22
|
+
agent_id: str
|
|
23
|
+
drift_type: str # "tone", "vocabulary", "approach", "communication_style"
|
|
24
|
+
expected: str
|
|
25
|
+
detected: str
|
|
26
|
+
severity: str = "WARN" # WARN or INFO
|
|
27
|
+
suggestion: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BehaviorEnforcer:
|
|
31
|
+
"""Middleware to enforce behavioral DNA profiles on agent outputs."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, registry: DNARegistry | None = None):
|
|
34
|
+
self.registry = registry or get_registry()
|
|
35
|
+
|
|
36
|
+
def check_output(
|
|
37
|
+
self,
|
|
38
|
+
agent_id: str,
|
|
39
|
+
output: str,
|
|
40
|
+
context: str = "",
|
|
41
|
+
) -> list[BehaviorDrift]:
|
|
42
|
+
"""Check if an output deviates from the agent's DNA profile.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
agent_id: ID of the agent that produced the output
|
|
46
|
+
output: The text output to check
|
|
47
|
+
context: Optional context about the interaction type
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of detected behavior drifts
|
|
51
|
+
"""
|
|
52
|
+
agent = self.registry.get(agent_id)
|
|
53
|
+
if not agent:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
drifts: list[BehaviorDrift] = []
|
|
57
|
+
|
|
58
|
+
drifts.extend(self._check_tone(agent, output))
|
|
59
|
+
drifts.extend(self._check_vocabulary(agent, output))
|
|
60
|
+
drifts.extend(self._check_communication_style(agent, output))
|
|
61
|
+
drifts.extend(self._check_avoid_list(agent, output))
|
|
62
|
+
|
|
63
|
+
return drifts
|
|
64
|
+
|
|
65
|
+
def _check_tone(self, agent: Agent, output: str) -> list[BehaviorDrift]:
|
|
66
|
+
"""Check if output tone matches agent's stated DISC style."""
|
|
67
|
+
drifts = []
|
|
68
|
+
disc = agent.behavioral_dna.disc
|
|
69
|
+
output_lower = output.lower()
|
|
70
|
+
|
|
71
|
+
if disc.primary == DISCType.D:
|
|
72
|
+
if not any(
|
|
73
|
+
word in output_lower for word in ["need", "must", "require", "demand", "critical"]
|
|
74
|
+
):
|
|
75
|
+
if len(output) > 100 and "?" not in output:
|
|
76
|
+
drifts.append(
|
|
77
|
+
BehaviorDrift(
|
|
78
|
+
agent_id=agent.id,
|
|
79
|
+
drift_type="tone",
|
|
80
|
+
expected="Direct, decisive, bottom-line first",
|
|
81
|
+
detected="Lacks direct command language",
|
|
82
|
+
suggestion="Lead with conclusions. Use 'must', 'need', 'critical'.",
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
elif disc.primary == DISCType.I:
|
|
87
|
+
if not any(
|
|
88
|
+
word in output_lower
|
|
89
|
+
for word in [
|
|
90
|
+
"great",
|
|
91
|
+
"amazing",
|
|
92
|
+
"exciting",
|
|
93
|
+
"fantastic",
|
|
94
|
+
"!" * 3 if "!!" in output else "",
|
|
95
|
+
]
|
|
96
|
+
):
|
|
97
|
+
if len(output) > 200 and "!" not in output and "great" not in output_lower:
|
|
98
|
+
drifts.append(
|
|
99
|
+
BehaviorDrift(
|
|
100
|
+
agent_id=agent.id,
|
|
101
|
+
drift_type="tone",
|
|
102
|
+
expected="Enthusiastic, engaging, positive",
|
|
103
|
+
detected="Lacks enthusiasm markers",
|
|
104
|
+
suggestion="Show energy. Use 'great', 'amazing', 'exciting'.",
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
elif disc.primary == DISCType.S:
|
|
109
|
+
if any(word in output_lower for word in ["must", "immediately", "urgent", "asap"]):
|
|
110
|
+
if output.count("must") > 2 or "immediately" in output:
|
|
111
|
+
drifts.append(
|
|
112
|
+
BehaviorDrift(
|
|
113
|
+
agent_id=agent.id,
|
|
114
|
+
drift_type="tone",
|
|
115
|
+
expected="Supportive, steady, patient",
|
|
116
|
+
detected="Overly urgent or demanding",
|
|
117
|
+
suggestion="Soften language. Avoid 'must', 'immediately', 'asap'.",
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
elif disc.primary == DISCType.C:
|
|
122
|
+
if not any(
|
|
123
|
+
pattern in output
|
|
124
|
+
for pattern in ["1.", "2.", "3.", "• ", "- ", "data:", "analysis:"]
|
|
125
|
+
):
|
|
126
|
+
if len(output) > 150 and not re.search(r"\d+", output):
|
|
127
|
+
drifts.append(
|
|
128
|
+
BehaviorDrift(
|
|
129
|
+
agent_id=agent.id,
|
|
130
|
+
drift_type="tone",
|
|
131
|
+
expected="Precise, analytical, structured",
|
|
132
|
+
detected="Lacks structured format",
|
|
133
|
+
suggestion="Use numbered lists, bullet points, and data references.",
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return drifts
|
|
138
|
+
|
|
139
|
+
def _check_vocabulary(self, agent: Agent, output: str) -> list[BehaviorDrift]:
|
|
140
|
+
"""Check if output uses appropriate vocabulary level."""
|
|
141
|
+
drifts = []
|
|
142
|
+
vocab_level = agent.communication.vocabulary_level
|
|
143
|
+
output_lower = output.lower()
|
|
144
|
+
|
|
145
|
+
if vocab_level == "specialist":
|
|
146
|
+
tech_terms = ["architecture", "infrastructure", "optimization", "latency", "throughput"]
|
|
147
|
+
if not any(term in output_lower for term in tech_terms):
|
|
148
|
+
if len(output) > 150:
|
|
149
|
+
drifts.append(
|
|
150
|
+
BehaviorDrift(
|
|
151
|
+
agent_id=agent.id,
|
|
152
|
+
drift_type="vocabulary",
|
|
153
|
+
expected="Specialist-level technical vocabulary",
|
|
154
|
+
detected="Generic language detected",
|
|
155
|
+
suggestion="Use precise technical terms relevant to the domain.",
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return drifts
|
|
160
|
+
|
|
161
|
+
def _check_communication_style(self, agent: Agent, output: str) -> list[BehaviorDrift]:
|
|
162
|
+
"""Check if output matches preferred communication format."""
|
|
163
|
+
drifts = []
|
|
164
|
+
preferred = agent.communication.preferred_format
|
|
165
|
+
output_lower = output.lower()
|
|
166
|
+
|
|
167
|
+
if "structured" in preferred or "diagrams" in preferred:
|
|
168
|
+
if len(output) > 300 and not any(
|
|
169
|
+
marker in output for marker in ["1.", "2.", "3.", "##", "###", "|"]
|
|
170
|
+
):
|
|
171
|
+
drifts.append(
|
|
172
|
+
BehaviorDrift(
|
|
173
|
+
agent_id=agent.id,
|
|
174
|
+
drift_type="communication_style",
|
|
175
|
+
expected=f"Structured with {preferred}",
|
|
176
|
+
detected="Free-form text without structure",
|
|
177
|
+
suggestion="Add headers, numbered steps, or tabular format.",
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if "code examples" in preferred:
|
|
182
|
+
if "```" not in output and len(output) > 200:
|
|
183
|
+
drifts.append(
|
|
184
|
+
BehaviorDrift(
|
|
185
|
+
agent_id=agent.id,
|
|
186
|
+
drift_type="communication_style",
|
|
187
|
+
expected="Includes code examples",
|
|
188
|
+
detected="No code blocks found",
|
|
189
|
+
suggestion="Add code examples to illustrate points.",
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return drifts
|
|
194
|
+
|
|
195
|
+
def _check_avoid_list(self, agent: Agent, output: str) -> list[BehaviorDrift]:
|
|
196
|
+
"""Check if output contains items from agent's avoid list."""
|
|
197
|
+
drifts = []
|
|
198
|
+
avoid_list = agent.communication.avoid
|
|
199
|
+
output_lower = output.lower()
|
|
200
|
+
|
|
201
|
+
for item in avoid_list:
|
|
202
|
+
if item.lower() in output_lower:
|
|
203
|
+
drifts.append(
|
|
204
|
+
BehaviorDrift(
|
|
205
|
+
agent_id=agent.id,
|
|
206
|
+
drift_type="tone",
|
|
207
|
+
expected=f"Avoid: {item}",
|
|
208
|
+
detected=f"Contains '{item}'",
|
|
209
|
+
severity="INFO",
|
|
210
|
+
suggestion=f"Avoid using '{item}' when communicating.",
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return drifts
|
|
215
|
+
|
|
216
|
+
def enforce_output(
|
|
217
|
+
self,
|
|
218
|
+
agent_id: str,
|
|
219
|
+
output: str,
|
|
220
|
+
context: str = "",
|
|
221
|
+
) -> tuple[str, list[BehaviorDrift]]:
|
|
222
|
+
"""Check output and return (possibly modified) output with drifts.
|
|
223
|
+
|
|
224
|
+
This is advisory — it doesn't modify output but returns drifts
|
|
225
|
+
for logging or review purposes.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
agent_id: ID of the agent
|
|
229
|
+
output: The output to check
|
|
230
|
+
context: Optional interaction context
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Tuple of (output unchanged, list of drifts)
|
|
234
|
+
"""
|
|
235
|
+
drifts = self.check_output(agent_id, output, context)
|
|
236
|
+
return output, drifts
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def check_agent_behavior(
|
|
240
|
+
agent_id: str,
|
|
241
|
+
output: str,
|
|
242
|
+
context: str = "",
|
|
243
|
+
) -> list[BehaviorDrift]:
|
|
244
|
+
"""Convenience function to check an agent's output.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
agent_id: ID of the agent
|
|
248
|
+
output: Output text to check
|
|
249
|
+
context: Optional context
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
List of BehaviorDrift objects
|
|
253
|
+
"""
|
|
254
|
+
enforcer = BehaviorEnforcer()
|
|
255
|
+
return enforcer.check_output(agent_id, output, context)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Agent DNA Registry — query and filter agents by behavioral DNA traits.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- Load all agents from departments/*/agents/*.yaml
|
|
5
|
+
- Query agents by DISC type, Enneagram, Big Five traits, MBTI
|
|
6
|
+
- Find compatible agents for collaboration
|
|
7
|
+
- Validate DNA profile completeness
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Iterator
|
|
12
|
+
|
|
13
|
+
from core.agents.loader import load_all_agents
|
|
14
|
+
from core.agents.schema import (
|
|
15
|
+
Agent,
|
|
16
|
+
BehavioralDNA,
|
|
17
|
+
DISCType,
|
|
18
|
+
EnneagramType,
|
|
19
|
+
BigFiveProfile,
|
|
20
|
+
MBTIType,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DNARegistry:
|
|
25
|
+
"""Registry for querying agents by behavioral DNA traits."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, base_dir: str | Path | None = None):
|
|
28
|
+
"""Initialize registry.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
base_dir: Root of departments tree. Defaults to repo root.
|
|
32
|
+
"""
|
|
33
|
+
if base_dir is None:
|
|
34
|
+
base_dir = Path(__file__).parent.parent.parent / "departments"
|
|
35
|
+
self.base_dir = Path(base_dir)
|
|
36
|
+
self._agents: list[Agent] = []
|
|
37
|
+
self._by_id: dict[str, Agent] = {}
|
|
38
|
+
self._load_agents()
|
|
39
|
+
|
|
40
|
+
def _load_agents(self) -> None:
|
|
41
|
+
"""Load all agents from YAML files."""
|
|
42
|
+
self._agents = load_all_agents(self.base_dir)
|
|
43
|
+
self._by_id = {a.id: a for a in self._agents}
|
|
44
|
+
|
|
45
|
+
def reload(self) -> None:
|
|
46
|
+
"""Reload all agents from disk."""
|
|
47
|
+
self._load_agents()
|
|
48
|
+
|
|
49
|
+
def get(self, agent_id: str) -> Agent | None:
|
|
50
|
+
"""Get agent by ID."""
|
|
51
|
+
return self._by_id.get(agent_id)
|
|
52
|
+
|
|
53
|
+
def all(self) -> list[Agent]:
|
|
54
|
+
"""Get all loaded agents."""
|
|
55
|
+
return self._agents.copy()
|
|
56
|
+
|
|
57
|
+
def by_disc(self, primary: DISCType) -> list[Agent]:
|
|
58
|
+
"""Find agents by DISC primary type."""
|
|
59
|
+
return [a for a in self._agents if a.behavioral_dna.disc.primary == primary]
|
|
60
|
+
|
|
61
|
+
def by_enneagram(self, type_: EnneagramType) -> list[Agent]:
|
|
62
|
+
"""Find agents by Enneagram type."""
|
|
63
|
+
return [a for a in self._agents if a.behavioral_dna.enneagram.type == type_]
|
|
64
|
+
|
|
65
|
+
def by_mbti(self, type_: MBTIType) -> list[Agent]:
|
|
66
|
+
"""Find agents by MBTI type."""
|
|
67
|
+
return [a for a in self._agents if a.behavioral_dna.mbti.type == type_]
|
|
68
|
+
|
|
69
|
+
def by_department(self, department: str) -> list[Agent]:
|
|
70
|
+
"""Find agents by department."""
|
|
71
|
+
return [a for a in self._agents if a.department == department]
|
|
72
|
+
|
|
73
|
+
def by_tier(self, tier: int) -> list[Agent]:
|
|
74
|
+
"""Find agents by tier (0-3)."""
|
|
75
|
+
return [a for a in self._agents if a.tier == tier]
|
|
76
|
+
|
|
77
|
+
def by_big_five(
|
|
78
|
+
self,
|
|
79
|
+
openness_min: int = 0,
|
|
80
|
+
conscientiousness_min: int = 0,
|
|
81
|
+
extraversion_min: int = 0,
|
|
82
|
+
agreeableness_min: int = 0,
|
|
83
|
+
neuroticism_max: int = 100,
|
|
84
|
+
) -> list[Agent]:
|
|
85
|
+
"""Find agents matching Big Five thresholds."""
|
|
86
|
+
results = []
|
|
87
|
+
for a in self._agents:
|
|
88
|
+
bf = a.behavioral_dna.big_five
|
|
89
|
+
if (
|
|
90
|
+
bf.openness >= openness_min
|
|
91
|
+
and bf.conscientiousness >= conscientiousness_min
|
|
92
|
+
and bf.extraversion >= extraversion_min
|
|
93
|
+
and bf.agreeableness >= agreeableness_min
|
|
94
|
+
and bf.neuroticism <= neuroticism_max
|
|
95
|
+
):
|
|
96
|
+
results.append(a)
|
|
97
|
+
return results
|
|
98
|
+
|
|
99
|
+
def compatible_with(self, agent_id: str) -> list[Agent]:
|
|
100
|
+
"""Find agents compatible for collaboration.
|
|
101
|
+
|
|
102
|
+
Compatibility is based on complementary DISC profiles
|
|
103
|
+
and similar communication styles.
|
|
104
|
+
"""
|
|
105
|
+
agent = self.get(agent_id)
|
|
106
|
+
if not agent:
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
disc = agent.behavioral_dna.disc
|
|
110
|
+
disc_map = {
|
|
111
|
+
DISCType.D: {DISCType.I, DISCType.S}, # D works with I and S
|
|
112
|
+
DISCType.I: {DISCType.D, DISCType.S}, # I works with D and S
|
|
113
|
+
DISCType.S: {DISCType.D, DISCType.I, DISCType.C}, # S works with most
|
|
114
|
+
DISCType.C: {DISCType.S, DISCType.C}, # C works with S and C
|
|
115
|
+
}
|
|
116
|
+
compatible_types = disc_map.get(disc.primary, set())
|
|
117
|
+
|
|
118
|
+
return [
|
|
119
|
+
a
|
|
120
|
+
for a in self._agents
|
|
121
|
+
if a.id != agent_id and a.behavioral_dna.disc.primary in compatible_types
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
def contrasting_disc(self, agent_id: str) -> list[Agent]:
|
|
125
|
+
"""Find agents with contrasting DISC profiles.
|
|
126
|
+
|
|
127
|
+
Useful for getting different perspectives.
|
|
128
|
+
"""
|
|
129
|
+
agent = self.get(agent_id)
|
|
130
|
+
if not agent:
|
|
131
|
+
return []
|
|
132
|
+
|
|
133
|
+
primary = agent.behavioral_dna.disc.primary
|
|
134
|
+
opposites = {
|
|
135
|
+
DISCType.D: {DISCType.S, DISCType.C},
|
|
136
|
+
DISCType.I: {DISCType.C},
|
|
137
|
+
DISCType.S: {DISCType.D},
|
|
138
|
+
DISCType.C: {DISCType.D, DISCType.I},
|
|
139
|
+
}
|
|
140
|
+
opposite_types = opposites.get(primary, set())
|
|
141
|
+
|
|
142
|
+
return [
|
|
143
|
+
a
|
|
144
|
+
for a in self._agents
|
|
145
|
+
if a.id != agent_id and a.behavioral_dna.disc.primary in opposite_types
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
def validate_dna_completeness(self) -> dict[str, list[str]]:
|
|
149
|
+
"""Validate that all agents have complete DNA profiles.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Dict mapping agent_id to list of missing fields
|
|
153
|
+
"""
|
|
154
|
+
missing: dict[str, list[str]] = {}
|
|
155
|
+
for agent in self._agents:
|
|
156
|
+
issues = []
|
|
157
|
+
dna = agent.behavioral_dna
|
|
158
|
+
|
|
159
|
+
if not dna.disc.primary:
|
|
160
|
+
issues.append("disc.primary")
|
|
161
|
+
if not dna.enneagram.type:
|
|
162
|
+
issues.append("enneagram.type")
|
|
163
|
+
if not dna.big_five.openness:
|
|
164
|
+
issues.append("big_five.openness")
|
|
165
|
+
if not dna.mbti.type:
|
|
166
|
+
issues.append("mbti.type")
|
|
167
|
+
|
|
168
|
+
if issues:
|
|
169
|
+
missing[agent.id] = issues
|
|
170
|
+
|
|
171
|
+
return missing
|
|
172
|
+
|
|
173
|
+
def get_communication_style(self, agent_id: str) -> str | None:
|
|
174
|
+
"""Get the communication style hint for an agent."""
|
|
175
|
+
agent = self.get(agent_id)
|
|
176
|
+
if not agent:
|
|
177
|
+
return None
|
|
178
|
+
return agent.communication.tone
|
|
179
|
+
|
|
180
|
+
def get_tone_for_recipient(self, sender_id: str, recipient_id: str) -> str:
|
|
181
|
+
"""Get adapted tone when sender communicates with recipient.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
sender_id: The sender's agent ID
|
|
185
|
+
recipient_id: The recipient's agent ID
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Communication tone adapted for the recipient
|
|
189
|
+
"""
|
|
190
|
+
sender = self.get(sender_id)
|
|
191
|
+
recipient = self.get(recipient_id)
|
|
192
|
+
if not sender or not recipient:
|
|
193
|
+
return "professional"
|
|
194
|
+
|
|
195
|
+
sender_disc = sender.behavioral_dna.disc.primary
|
|
196
|
+
recipient_disc = recipient.behavioral_dna.disc.primary
|
|
197
|
+
|
|
198
|
+
tone_map = {
|
|
199
|
+
(DISCType.D, DISCType.D): "direct and decisive",
|
|
200
|
+
(DISCType.D, DISCType.I): "direct but enthusiastic",
|
|
201
|
+
(DISCType.D, DISCType.S): "direct and supportive",
|
|
202
|
+
(DISCType.D, DISCType.C): "direct and precise",
|
|
203
|
+
(DISCType.I, DISCType.D): "enthusiastic but direct",
|
|
204
|
+
(DISCType.I, DISCType.I): "enthusiastic and collaborative",
|
|
205
|
+
(DISCType.I, DISCType.S): "enthusiastic and warm",
|
|
206
|
+
(DISCType.I, DISCType.C): "enthusiastic but detailed",
|
|
207
|
+
(DISCType.S, DISCType.D): "supportive but direct",
|
|
208
|
+
(DISCType.S, DISCType.I): "warm and collaborative",
|
|
209
|
+
(DISCType.S, DISCType.S): "warm and steady",
|
|
210
|
+
(DISCType.S, DISCType.C): "supportive and precise",
|
|
211
|
+
(DISCType.C, DISCType.D): "precise and direct",
|
|
212
|
+
(DISCType.C, DISCType.I): "precise but friendly",
|
|
213
|
+
(DISCType.C, DISCType.S): "precise and supportive",
|
|
214
|
+
(DISCType.C, DISCType.C): "precise and thorough",
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return tone_map.get((sender_disc, recipient_disc), "professional")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
_REGISTRY: DNARegistry | None = None
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_registry() -> DNARegistry:
|
|
224
|
+
"""Get the global DNA registry (singleton)."""
|
|
225
|
+
global _REGISTRY
|
|
226
|
+
if _REGISTRY is None:
|
|
227
|
+
_REGISTRY = DNARegistry()
|
|
228
|
+
return _REGISTRY
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def reload_registry() -> DNARegistry:
|
|
232
|
+
"""Reload and return the global registry."""
|
|
233
|
+
global _REGISTRY
|
|
234
|
+
_REGISTRY = DNARegistry()
|
|
235
|
+
return _REGISTRY
|
package/core/forge/__init__.py
CHANGED
|
@@ -55,6 +55,26 @@ from core.forge.handoff import (
|
|
|
55
55
|
check_repo_drift,
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
+
from core.forge.orchestrator import (
|
|
59
|
+
ForgeOrchestrator,
|
|
60
|
+
ForgeDecision,
|
|
61
|
+
ForgeStep,
|
|
62
|
+
ForgeStatusOutput,
|
|
63
|
+
ForgeHistoryEntry,
|
|
64
|
+
ForgeCompareOutput,
|
|
65
|
+
CONSTITUTION_PHASES,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
from core.forge.runtime_dispatcher import (
|
|
69
|
+
ForgeTaskDispatcher,
|
|
70
|
+
ClaudeCodeForgeDispatcher,
|
|
71
|
+
ExplorerDispatchRequest,
|
|
72
|
+
CriticDispatchRequest,
|
|
73
|
+
DispatchResult,
|
|
74
|
+
_tier_to_model,
|
|
75
|
+
create_dispatcher,
|
|
76
|
+
)
|
|
77
|
+
|
|
58
78
|
__all__ = [
|
|
59
79
|
# schema
|
|
60
80
|
"ForgeTier",
|
|
@@ -101,4 +121,20 @@ __all__ = [
|
|
|
101
121
|
"select_execution_path",
|
|
102
122
|
"generate_workflow_yaml",
|
|
103
123
|
"check_repo_drift",
|
|
124
|
+
# orchestrator
|
|
125
|
+
"ForgeOrchestrator",
|
|
126
|
+
"ForgeDecision",
|
|
127
|
+
"ForgeStep",
|
|
128
|
+
"ForgeStatusOutput",
|
|
129
|
+
"ForgeHistoryEntry",
|
|
130
|
+
"ForgeCompareOutput",
|
|
131
|
+
"CONSTITUTION_PHASES",
|
|
132
|
+
# runtime dispatcher
|
|
133
|
+
"ForgeTaskDispatcher",
|
|
134
|
+
"ClaudeCodeForgeDispatcher",
|
|
135
|
+
"ExplorerDispatchRequest",
|
|
136
|
+
"CriticDispatchRequest",
|
|
137
|
+
"DispatchResult",
|
|
138
|
+
"_tier_to_model",
|
|
139
|
+
"create_dispatcher",
|
|
104
140
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|