arkaos 2.0.0 → 2.0.2
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/README.md +100 -74
- package/VERSION +1 -1
- package/bin/arkaos +1 -1
- package/config/constitution.yaml +4 -0
- package/config/hooks/user-prompt-submit-v2.sh +20 -38
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/loader.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/validator.cpython-313.pyc +0 -0
- package/core/budget/__init__.py +6 -0
- package/core/budget/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/budget/__pycache__/manager.cpython-313.pyc +0 -0
- package/core/budget/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/budget/manager.py +193 -0
- package/core/budget/schema.py +82 -0
- package/core/conclave/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/advisor_db.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/display.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/matcher.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/persistence.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/profiler.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/prompts.cpython-313.pyc +0 -0
- package/core/conclave/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/constitution.cpython-313.pyc +0 -0
- package/core/obsidian/__init__.py +6 -0
- package/core/obsidian/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/obsidian/__pycache__/templates.cpython-313.pyc +0 -0
- package/core/obsidian/__pycache__/writer.cpython-313.pyc +0 -0
- package/core/obsidian/templates.py +76 -0
- package/core/obsidian/writer.py +148 -0
- package/core/orchestration/__init__.py +6 -0
- package/core/orchestration/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/orchestration/__pycache__/patterns.cpython-313.pyc +0 -0
- package/core/orchestration/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/orchestration/patterns.py +136 -0
- package/core/orchestration/protocol.py +96 -0
- package/core/registry/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/subagent.cpython-313.pyc +0 -0
- package/core/specs/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/specs/__pycache__/manager.cpython-313.pyc +0 -0
- package/core/specs/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/squads/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/squads/__pycache__/loader.cpython-313.pyc +0 -0
- package/core/squads/__pycache__/registry.cpython-313.pyc +0 -0
- package/core/squads/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/tasks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/tasks/__pycache__/manager.cpython-313.pyc +0 -0
- package/core/tasks/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/tasks/schema.py +6 -0
- package/core/workflow/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/loader.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/workflow/engine.py +44 -0
- package/core/workflow/schema.py +1 -0
- package/departments/dev/skills/agent-design/SKILL.md +4 -0
- package/departments/dev/skills/agent-design/references/architecture-patterns.md +223 -0
- package/departments/dev/skills/ai-security/SKILL.md +4 -0
- package/departments/dev/skills/ai-security/references/prompt-injection-catalog.md +230 -0
- package/departments/dev/skills/ci-cd-pipeline/SKILL.md +4 -0
- package/departments/dev/skills/ci-cd-pipeline/references/github-actions-patterns.md +202 -0
- package/departments/dev/skills/db-schema/SKILL.md +4 -0
- package/departments/dev/skills/db-schema/references/indexing-strategy.md +197 -0
- package/departments/dev/skills/dependency-audit/SKILL.md +4 -0
- package/departments/dev/skills/dependency-audit/references/license-matrix.md +191 -0
- package/departments/dev/skills/incident/SKILL.md +4 -0
- package/departments/dev/skills/incident/references/severity-playbook.md +221 -0
- package/departments/dev/skills/observability/SKILL.md +4 -0
- package/departments/dev/skills/observability/references/slo-design.md +200 -0
- package/departments/dev/skills/rag-architect/SKILL.md +5 -0
- package/departments/dev/skills/rag-architect/references/chunking-strategies.md +129 -0
- package/departments/dev/skills/rag-architect/references/evaluation-guide.md +158 -0
- package/departments/dev/skills/red-team/SKILL.md +4 -0
- package/departments/dev/skills/red-team/references/mitre-attack-web.md +165 -0
- package/departments/dev/skills/security-audit/SKILL.md +4 -0
- package/departments/dev/skills/security-audit/references/owasp-2025-deep.md +409 -0
- package/departments/dev/skills/security-compliance/SKILL.md +117 -0
- package/departments/finance/skills/ciso-advisor/SKILL.md +4 -0
- package/departments/finance/skills/ciso-advisor/references/compliance-roadmap.md +172 -0
- package/departments/marketing/skills/programmatic-seo/SKILL.md +4 -0
- package/departments/marketing/skills/programmatic-seo/references/template-playbooks.md +289 -0
- package/departments/ops/skills/gdpr-compliance/SKILL.md +104 -0
- package/departments/ops/skills/iso27001/SKILL.md +113 -0
- package/departments/ops/skills/quality-management/SKILL.md +118 -0
- package/departments/ops/skills/risk-management/SKILL.md +120 -0
- package/departments/ops/skills/soc2-compliance/SKILL.md +120 -0
- package/departments/strategy/skills/cto-advisor/SKILL.md +4 -0
- package/departments/strategy/skills/cto-advisor/references/build-vs-buy-framework.md +190 -0
- package/installer/cli.js +13 -2
- package/installer/index.js +1 -2
- package/installer/migrate.js +123 -0
- package/installer/update.js +28 -15
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/core/agents/__pycache__/registry_gen.cpython-313.pyc +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Obsidian frontmatter templates and conventions."""
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def build_frontmatter(
|
|
8
|
+
department: str = "",
|
|
9
|
+
agent: str = "",
|
|
10
|
+
workflow: str = "",
|
|
11
|
+
tags: list[str] | None = None,
|
|
12
|
+
extra: dict[str, Any] | None = None,
|
|
13
|
+
) -> str:
|
|
14
|
+
"""Build YAML frontmatter for an Obsidian note.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
String with opening/closing --- delimiters.
|
|
18
|
+
"""
|
|
19
|
+
fields: dict[str, Any] = {}
|
|
20
|
+
fields["created"] = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
21
|
+
fields["source"] = "arkaos"
|
|
22
|
+
|
|
23
|
+
if department:
|
|
24
|
+
fields["department"] = department
|
|
25
|
+
if agent:
|
|
26
|
+
fields["agent"] = agent
|
|
27
|
+
if workflow:
|
|
28
|
+
fields["workflow"] = workflow
|
|
29
|
+
|
|
30
|
+
all_tags = ["arkaos"]
|
|
31
|
+
if department:
|
|
32
|
+
all_tags.append(f"dept/{department}")
|
|
33
|
+
if tags:
|
|
34
|
+
all_tags.extend(tags)
|
|
35
|
+
fields["tags"] = all_tags
|
|
36
|
+
|
|
37
|
+
if extra:
|
|
38
|
+
fields.update(extra)
|
|
39
|
+
|
|
40
|
+
lines = ["---"]
|
|
41
|
+
for key, value in fields.items():
|
|
42
|
+
if isinstance(value, list):
|
|
43
|
+
lines.append(f"{key}:")
|
|
44
|
+
for item in value:
|
|
45
|
+
lines.append(f" - {item}")
|
|
46
|
+
elif isinstance(value, bool):
|
|
47
|
+
lines.append(f"{key}: {'true' if value else 'false'}")
|
|
48
|
+
else:
|
|
49
|
+
lines.append(f"{key}: {value}")
|
|
50
|
+
lines.append("---")
|
|
51
|
+
|
|
52
|
+
return "\n".join(lines)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def resolve_template_vars(path: str, vars: dict[str, str] | None = None) -> str:
|
|
56
|
+
"""Resolve template variables in an Obsidian path.
|
|
57
|
+
|
|
58
|
+
Supported variables:
|
|
59
|
+
{project}, {department}, {agent}, {date}, {number}, {name}
|
|
60
|
+
"""
|
|
61
|
+
defaults = {
|
|
62
|
+
"date": date.today().isoformat(),
|
|
63
|
+
"project": "Default",
|
|
64
|
+
"department": "general",
|
|
65
|
+
"agent": "unknown",
|
|
66
|
+
"number": "001",
|
|
67
|
+
"name": "untitled",
|
|
68
|
+
}
|
|
69
|
+
if vars:
|
|
70
|
+
defaults.update(vars)
|
|
71
|
+
|
|
72
|
+
result = path
|
|
73
|
+
for key, value in defaults.items():
|
|
74
|
+
result = result.replace(f"{{{key}}}", value)
|
|
75
|
+
|
|
76
|
+
return result
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Obsidian vault writer — save workflow outputs to knowledge base.
|
|
2
|
+
|
|
3
|
+
Resolves vault path from:
|
|
4
|
+
1. Constructor argument
|
|
5
|
+
2. knowledge/obsidian-config.json → vault_path
|
|
6
|
+
3. ARKAOS_VAULT environment variable
|
|
7
|
+
4. Fallback: ~/.arkaos/vault/
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Optional
|
|
15
|
+
|
|
16
|
+
from core.obsidian.templates import build_frontmatter, resolve_template_vars
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ObsidianWriter:
|
|
20
|
+
"""Writes markdown files to an Obsidian vault with frontmatter."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, vault_path: str | Path | None = None, arkaos_root: str | Path | None = None) -> None:
|
|
23
|
+
self._vault_path = self._resolve_vault_path(vault_path, arkaos_root)
|
|
24
|
+
|
|
25
|
+
def save(
|
|
26
|
+
self,
|
|
27
|
+
obsidian_path: str,
|
|
28
|
+
content: str,
|
|
29
|
+
department: str = "",
|
|
30
|
+
agent: str = "",
|
|
31
|
+
workflow: str = "",
|
|
32
|
+
tags: list[str] | None = None,
|
|
33
|
+
template_vars: dict[str, str] | None = None,
|
|
34
|
+
extra_frontmatter: dict[str, Any] | None = None,
|
|
35
|
+
) -> Path:
|
|
36
|
+
"""Save content to the Obsidian vault.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
obsidian_path: Relative path within vault (may contain template vars).
|
|
40
|
+
content: Markdown content to save.
|
|
41
|
+
department: Source department.
|
|
42
|
+
agent: Agent that produced the output.
|
|
43
|
+
workflow: Workflow that generated this output.
|
|
44
|
+
tags: Additional tags for frontmatter.
|
|
45
|
+
template_vars: Variables to resolve in path ({project}, {date}, etc.).
|
|
46
|
+
extra_frontmatter: Additional frontmatter fields.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Absolute path to the saved file.
|
|
50
|
+
"""
|
|
51
|
+
# Resolve template variables in path
|
|
52
|
+
resolved_path = resolve_template_vars(obsidian_path, template_vars)
|
|
53
|
+
|
|
54
|
+
# Build full path
|
|
55
|
+
full_path = self._vault_path / resolved_path
|
|
56
|
+
|
|
57
|
+
# If path doesn't end with .md, treat as directory and add filename
|
|
58
|
+
if not full_path.suffix:
|
|
59
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M")
|
|
60
|
+
filename = f"{department or 'output'}-{timestamp}.md"
|
|
61
|
+
full_path = full_path / filename
|
|
62
|
+
elif full_path.suffix != ".md":
|
|
63
|
+
full_path = full_path.with_suffix(".md")
|
|
64
|
+
|
|
65
|
+
# Handle duplicate filenames
|
|
66
|
+
if full_path.exists():
|
|
67
|
+
stem = full_path.stem
|
|
68
|
+
timestamp = datetime.now().strftime("%H%M%S")
|
|
69
|
+
full_path = full_path.with_stem(f"{stem}-{timestamp}")
|
|
70
|
+
|
|
71
|
+
# Create directories
|
|
72
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
|
|
74
|
+
# Build frontmatter
|
|
75
|
+
frontmatter = build_frontmatter(
|
|
76
|
+
department=department,
|
|
77
|
+
agent=agent,
|
|
78
|
+
workflow=workflow,
|
|
79
|
+
tags=tags,
|
|
80
|
+
extra=extra_frontmatter,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Write file
|
|
84
|
+
file_content = f"{frontmatter}\n\n{content}"
|
|
85
|
+
full_path.write_text(file_content, encoding="utf-8")
|
|
86
|
+
|
|
87
|
+
return full_path
|
|
88
|
+
|
|
89
|
+
def ensure_vault(self) -> bool:
|
|
90
|
+
"""Verify the vault directory exists."""
|
|
91
|
+
return self._vault_path.exists()
|
|
92
|
+
|
|
93
|
+
def list_outputs(self, department: str = "", limit: int = 50) -> list[Path]:
|
|
94
|
+
"""List recent ArkaOS outputs in the vault."""
|
|
95
|
+
if not self._vault_path.exists():
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
pattern = "**/*.md"
|
|
99
|
+
files = sorted(self._vault_path.glob(pattern), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
100
|
+
|
|
101
|
+
results = []
|
|
102
|
+
for f in files:
|
|
103
|
+
if len(results) >= limit:
|
|
104
|
+
break
|
|
105
|
+
try:
|
|
106
|
+
head = f.read_text(encoding="utf-8")[:200]
|
|
107
|
+
if "source: arkaos" in head:
|
|
108
|
+
if not department or f"department: {department}" in head:
|
|
109
|
+
results.append(f)
|
|
110
|
+
except (OSError, UnicodeDecodeError):
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
return results
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def vault_path(self) -> Path:
|
|
117
|
+
return self._vault_path
|
|
118
|
+
|
|
119
|
+
def _resolve_vault_path(self, explicit: str | Path | None, arkaos_root: str | Path | None) -> Path:
|
|
120
|
+
"""Resolve vault path from multiple sources."""
|
|
121
|
+
# 1. Explicit argument
|
|
122
|
+
if explicit:
|
|
123
|
+
return Path(explicit)
|
|
124
|
+
|
|
125
|
+
# 2. Config file
|
|
126
|
+
if arkaos_root:
|
|
127
|
+
config_path = Path(arkaos_root) / "knowledge" / "obsidian-config.json"
|
|
128
|
+
else:
|
|
129
|
+
config_path = Path(__file__).resolve().parent.parent.parent / "knowledge" / "obsidian-config.json"
|
|
130
|
+
|
|
131
|
+
if config_path.exists():
|
|
132
|
+
try:
|
|
133
|
+
config = json.loads(config_path.read_text())
|
|
134
|
+
vault = config.get("vault_path", "")
|
|
135
|
+
if vault and Path(vault).exists():
|
|
136
|
+
return Path(vault)
|
|
137
|
+
except (json.JSONDecodeError, OSError):
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
# 3. Environment variable
|
|
141
|
+
env_vault = os.environ.get("ARKAOS_VAULT", "")
|
|
142
|
+
if env_vault and Path(env_vault).exists():
|
|
143
|
+
return Path(env_vault)
|
|
144
|
+
|
|
145
|
+
# 4. Fallback
|
|
146
|
+
fallback = Path.home() / ".arkaos" / "vault"
|
|
147
|
+
fallback.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
return fallback
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Orchestration protocol — coordination patterns for multi-agent work."""
|
|
2
|
+
|
|
3
|
+
from core.orchestration.protocol import OrchestrationPattern, PhaseHandoff, OrchestrationPlan
|
|
4
|
+
from core.orchestration.patterns import PATTERNS
|
|
5
|
+
|
|
6
|
+
__all__ = ["OrchestrationPattern", "PhaseHandoff", "OrchestrationPlan", "PATTERNS"]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Built-in orchestration patterns.
|
|
2
|
+
|
|
3
|
+
Four patterns adapted from claude-skills orchestration protocol,
|
|
4
|
+
mapped to ArkaOS department/agent/skill system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from core.orchestration.protocol import OrchestrationPattern, PatternType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
SOLO_SPRINT = OrchestrationPattern(
|
|
11
|
+
type=PatternType.SOLO_SPRINT,
|
|
12
|
+
name="Solo Sprint",
|
|
13
|
+
description="One department lead drives a multi-phase sprint, pulling skills from their squad. Fast, focused execution.",
|
|
14
|
+
when_to_use=[
|
|
15
|
+
"Single-domain task with clear scope",
|
|
16
|
+
"Time-constrained delivery (< 1 week)",
|
|
17
|
+
"One department has all required expertise",
|
|
18
|
+
],
|
|
19
|
+
structure="Lead → Phase 1 (skills A,B) → Phase 2 (skills C,D) → Quality Gate → Deliver",
|
|
20
|
+
example=(
|
|
21
|
+
"Objective: Launch MVP landing page\n"
|
|
22
|
+
"Lead: Ines (Landing)\n"
|
|
23
|
+
"Phase 1: /landing copy-framework + /landing funnel-design\n"
|
|
24
|
+
"Phase 2: /landing page-build + /landing seo-optimize\n"
|
|
25
|
+
"Phase 3: Quality Gate → Ship"
|
|
26
|
+
),
|
|
27
|
+
anti_patterns=[
|
|
28
|
+
"Using for cross-department work (use Multi-Agent Handoff instead)",
|
|
29
|
+
"Skipping Quality Gate because 'it's just one department'",
|
|
30
|
+
],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
DOMAIN_DEEP_DIVE = OrchestrationPattern(
|
|
34
|
+
type=PatternType.DOMAIN_DEEP_DIVE,
|
|
35
|
+
name="Domain Deep-Dive",
|
|
36
|
+
description="One agent, multiple stacked skills for thorough analysis. Deep expertise over breadth.",
|
|
37
|
+
when_to_use=[
|
|
38
|
+
"Complex technical audit or review",
|
|
39
|
+
"Due diligence or compliance assessment",
|
|
40
|
+
"Research requiring depth in one area",
|
|
41
|
+
],
|
|
42
|
+
structure="Agent → Skill 1 (foundation) → Skill 2 (analysis) → Skill 3 (recommendations) → Report",
|
|
43
|
+
example=(
|
|
44
|
+
"Objective: Full security assessment\n"
|
|
45
|
+
"Agent: Bruno (Security Engineer)\n"
|
|
46
|
+
"Skills stacked:\n"
|
|
47
|
+
" 1. /dev security-audit (OWASP scan)\n"
|
|
48
|
+
" 2. /dev dependency-audit (CVE check)\n"
|
|
49
|
+
" 3. /dev red-team (attack simulation)\n"
|
|
50
|
+
" 4. /dev ai-security (LLM-specific risks)\n"
|
|
51
|
+
"Output: Consolidated security report with severity rankings"
|
|
52
|
+
),
|
|
53
|
+
anti_patterns=[
|
|
54
|
+
"Stacking unrelated skills (each skill should build on previous)",
|
|
55
|
+
"Skipping the foundational skill and going straight to advanced",
|
|
56
|
+
"No consolidated output (each skill reports independently)",
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
MULTI_AGENT_HANDOFF = OrchestrationPattern(
|
|
61
|
+
type=PatternType.MULTI_AGENT_HANDOFF,
|
|
62
|
+
name="Multi-Agent Handoff",
|
|
63
|
+
description="Work flows between departments with structured handoffs. Each department adds its expertise.",
|
|
64
|
+
when_to_use=[
|
|
65
|
+
"Cross-department projects (product launch, brand + marketing)",
|
|
66
|
+
"Sequential expertise needed (strategy → dev → marketing)",
|
|
67
|
+
"Each phase requires different domain knowledge",
|
|
68
|
+
],
|
|
69
|
+
structure="Dept A → Handoff → Dept B → Handoff → Dept C → Quality Gate → Deliver",
|
|
70
|
+
example=(
|
|
71
|
+
"Objective: Launch new SaaS product\n"
|
|
72
|
+
"Phase 1: Tomas (Strategy) → /strat bmc + /strat five-forces\n"
|
|
73
|
+
" Handoff: Market validation, pricing strategy, competitive position\n"
|
|
74
|
+
"Phase 2: Paulo (Dev) → /dev spec + /dev feature\n"
|
|
75
|
+
" Handoff: MVP built, deployed to staging\n"
|
|
76
|
+
"Phase 3: Luna (Marketing) → /mkt growth-plan + /mkt email-sequence\n"
|
|
77
|
+
" Handoff: Landing page, email funnel, launch plan\n"
|
|
78
|
+
"Phase 4: Quality Gate → Launch"
|
|
79
|
+
),
|
|
80
|
+
anti_patterns=[
|
|
81
|
+
"No handoff context (next department starts from zero)",
|
|
82
|
+
"Too many departments (>4 phases becomes unwieldy)",
|
|
83
|
+
"Skipping departments that should review (e.g., security for a public launch)",
|
|
84
|
+
],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
SKILL_CHAIN = OrchestrationPattern(
|
|
88
|
+
type=PatternType.SKILL_CHAIN,
|
|
89
|
+
name="Skill Chain",
|
|
90
|
+
description="Sequential procedural skills with no specific agent identity. Pure execution pipeline.",
|
|
91
|
+
when_to_use=[
|
|
92
|
+
"Procedural work with well-defined inputs/outputs",
|
|
93
|
+
"Automated pipelines (generate → validate → publish)",
|
|
94
|
+
"No judgment calls needed, just execution",
|
|
95
|
+
],
|
|
96
|
+
structure="Skill A (input) → Skill B (transform) → Skill C (output) → Done",
|
|
97
|
+
example=(
|
|
98
|
+
"Objective: Content production pipeline\n"
|
|
99
|
+
"Chain:\n"
|
|
100
|
+
" 1. /content hook-write → 10 headline options\n"
|
|
101
|
+
" 2. python scripts/tools/headline_scorer.py → scored + ranked\n"
|
|
102
|
+
" 3. /content viral-design → full post with winning hook\n"
|
|
103
|
+
" 4. /mkt social-strategy → distribution plan\n"
|
|
104
|
+
"No persona needed — pure skill execution"
|
|
105
|
+
),
|
|
106
|
+
anti_patterns=[
|
|
107
|
+
"Using for work that needs judgment (use Solo Sprint or Handoff)",
|
|
108
|
+
"Long chains with no validation checkpoints",
|
|
109
|
+
"Mixing judgment skills with procedural skills",
|
|
110
|
+
],
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
PATTERNS = {
|
|
115
|
+
PatternType.SOLO_SPRINT: SOLO_SPRINT,
|
|
116
|
+
PatternType.DOMAIN_DEEP_DIVE: DOMAIN_DEEP_DIVE,
|
|
117
|
+
PatternType.MULTI_AGENT_HANDOFF: MULTI_AGENT_HANDOFF,
|
|
118
|
+
PatternType.SKILL_CHAIN: SKILL_CHAIN,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def select_pattern(
|
|
123
|
+
departments_involved: int,
|
|
124
|
+
needs_judgment: bool,
|
|
125
|
+
is_sequential: bool,
|
|
126
|
+
) -> PatternType:
|
|
127
|
+
"""Recommend an orchestration pattern based on task characteristics."""
|
|
128
|
+
if departments_involved == 1 and needs_judgment:
|
|
129
|
+
return PatternType.SOLO_SPRINT
|
|
130
|
+
if departments_involved == 1 and not needs_judgment:
|
|
131
|
+
return PatternType.SKILL_CHAIN
|
|
132
|
+
if departments_involved > 1 and is_sequential:
|
|
133
|
+
return PatternType.MULTI_AGENT_HANDOFF
|
|
134
|
+
if departments_involved == 1:
|
|
135
|
+
return PatternType.DOMAIN_DEEP_DIVE
|
|
136
|
+
return PatternType.MULTI_AGENT_HANDOFF
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Orchestration protocol schemas.
|
|
2
|
+
|
|
3
|
+
Defines the structure for multi-agent coordination across departments.
|
|
4
|
+
Four patterns: Solo Sprint, Domain Deep-Dive, Multi-Agent Handoff, Skill Chain.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PatternType(str, Enum):
|
|
14
|
+
SOLO_SPRINT = "solo_sprint"
|
|
15
|
+
DOMAIN_DEEP_DIVE = "domain_deep_dive"
|
|
16
|
+
MULTI_AGENT_HANDOFF = "multi_agent_handoff"
|
|
17
|
+
SKILL_CHAIN = "skill_chain"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PhaseHandoff(BaseModel):
|
|
21
|
+
"""Context passed between orchestration phases."""
|
|
22
|
+
phase_number: int
|
|
23
|
+
phase_name: str
|
|
24
|
+
decisions: list[str] = Field(default_factory=list)
|
|
25
|
+
artifacts: list[str] = Field(default_factory=list)
|
|
26
|
+
open_questions: list[str] = Field(default_factory=list)
|
|
27
|
+
next_department: str = ""
|
|
28
|
+
next_agent: str = ""
|
|
29
|
+
next_skills: list[str] = Field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
def to_context(self) -> str:
|
|
32
|
+
"""Render handoff as context string for next phase."""
|
|
33
|
+
lines = [f"Phase {self.phase_number} ({self.phase_name}) complete."]
|
|
34
|
+
if self.decisions:
|
|
35
|
+
lines.append(f"Decisions: {', '.join(self.decisions)}")
|
|
36
|
+
if self.artifacts:
|
|
37
|
+
lines.append(f"Artifacts: {', '.join(self.artifacts)}")
|
|
38
|
+
if self.open_questions:
|
|
39
|
+
lines.append(f"Open questions: {', '.join(self.open_questions)}")
|
|
40
|
+
if self.next_department:
|
|
41
|
+
lines.append(f"Next: {self.next_department}/{self.next_agent}")
|
|
42
|
+
return "\n".join(lines)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class OrchestrationPhase(BaseModel):
|
|
46
|
+
"""A single phase in an orchestration plan."""
|
|
47
|
+
number: int
|
|
48
|
+
name: str
|
|
49
|
+
department: str
|
|
50
|
+
agent_id: str
|
|
51
|
+
skills: list[str] = Field(default_factory=list)
|
|
52
|
+
objective: str = ""
|
|
53
|
+
outputs: list[str] = Field(default_factory=list)
|
|
54
|
+
gate: str = "user_approval" # user_approval, auto, quality_gate
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class OrchestrationPlan(BaseModel):
|
|
58
|
+
"""A complete orchestration plan spanning multiple departments."""
|
|
59
|
+
objective: str
|
|
60
|
+
pattern: PatternType
|
|
61
|
+
constraints: list[str] = Field(default_factory=list)
|
|
62
|
+
success_criteria: list[str] = Field(default_factory=list)
|
|
63
|
+
phases: list[OrchestrationPhase] = Field(default_factory=list)
|
|
64
|
+
current_phase: int = 0
|
|
65
|
+
|
|
66
|
+
def next_phase(self) -> Optional[OrchestrationPhase]:
|
|
67
|
+
"""Get the next phase to execute."""
|
|
68
|
+
if self.current_phase < len(self.phases):
|
|
69
|
+
return self.phases[self.current_phase]
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
def advance(self, handoff: PhaseHandoff) -> Optional[OrchestrationPhase]:
|
|
73
|
+
"""Record handoff and advance to next phase."""
|
|
74
|
+
self.current_phase += 1
|
|
75
|
+
return self.next_phase()
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def is_complete(self) -> bool:
|
|
79
|
+
return self.current_phase >= len(self.phases)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def progress_percent(self) -> int:
|
|
83
|
+
if not self.phases:
|
|
84
|
+
return 0
|
|
85
|
+
return int((self.current_phase / len(self.phases)) * 100)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class OrchestrationPattern(BaseModel):
|
|
89
|
+
"""Definition of a coordination pattern."""
|
|
90
|
+
type: PatternType
|
|
91
|
+
name: str
|
|
92
|
+
description: str
|
|
93
|
+
when_to_use: list[str] = Field(default_factory=list)
|
|
94
|
+
structure: str = ""
|
|
95
|
+
example: str = ""
|
|
96
|
+
anti_patterns: list[str] = Field(default_factory=list)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/core/tasks/schema.py
CHANGED
|
@@ -47,6 +47,12 @@ class Task(BaseModel):
|
|
|
47
47
|
output_data: dict[str, Any] = Field(default_factory=dict)
|
|
48
48
|
output_path: str = "" # File path for output
|
|
49
49
|
|
|
50
|
+
# Budget
|
|
51
|
+
tokens_estimated: int = 0 # Pre-execution estimate
|
|
52
|
+
tokens_actual: int = 0 # Post-execution actual
|
|
53
|
+
budget_approved: bool = True # True if within budget or approved
|
|
54
|
+
approved_by: str = "" # Tier 0 agent who approved overrun
|
|
55
|
+
|
|
50
56
|
# Progress
|
|
51
57
|
progress_percent: int = 0 # 0-100
|
|
52
58
|
progress_message: str = ""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|