arkaos 2.1.0 → 2.1.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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.0
1
+ 2.1.2
@@ -119,6 +119,22 @@ class IngestEngine:
119
119
 
120
120
  progress(100, f"Done — {count} chunks indexed")
121
121
 
122
+ # Record token usage in budget
123
+ try:
124
+ from core.budget.manager import BudgetManager
125
+ from pathlib import Path as BudgetPath
126
+ budget_mgr = BudgetManager(storage_path=BudgetPath.home() / ".arkaos" / "budget-usage.json")
127
+ tokens_est = len(text) // 4 # ~1 token per 4 chars
128
+ budget_mgr.record_usage(
129
+ agent_id="kb-indexer",
130
+ tokens=tokens_est,
131
+ tier=2,
132
+ department="kb",
133
+ description=f"ingest-{source_type}: {source[:60]}",
134
+ )
135
+ except Exception:
136
+ pass
137
+
122
138
  return IngestResult(
123
139
  source=source,
124
140
  source_type=source_type,
@@ -0,0 +1,6 @@
1
+ """Persona system — create, store, and clone personas as agents."""
2
+
3
+ from core.personas.schema import Persona
4
+ from core.personas.manager import PersonaManager
5
+
6
+ __all__ = ["Persona", "PersonaManager"]
@@ -0,0 +1,102 @@
1
+ """Persona manager — CRUD operations and cloning to agents."""
2
+
3
+ import json
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import yaml
9
+
10
+ from core.personas.schema import Persona
11
+
12
+
13
+ class PersonaManager:
14
+ """Manages persona lifecycle: create, store, list, clone to agent."""
15
+
16
+ def __init__(self, storage_path: str | Path = "") -> None:
17
+ self._personas: dict[str, Persona] = {}
18
+ self._storage_path = Path(storage_path) if storage_path else None
19
+ if self._storage_path and self._storage_path.exists():
20
+ self._load()
21
+
22
+ def create(self, persona: Persona) -> Persona:
23
+ """Create a new persona."""
24
+ persona.created_at = datetime.now().isoformat()
25
+ persona.updated_at = persona.created_at
26
+ self._personas[persona.id] = persona
27
+ self._save()
28
+ return persona
29
+
30
+ def get(self, persona_id: str) -> Optional[Persona]:
31
+ return self._personas.get(persona_id)
32
+
33
+ def list_all(self) -> list[Persona]:
34
+ return list(self._personas.values())
35
+
36
+ def update(self, persona_id: str, updates: dict) -> Optional[Persona]:
37
+ persona = self._personas.get(persona_id)
38
+ if not persona:
39
+ return None
40
+ for key, value in updates.items():
41
+ if hasattr(persona, key):
42
+ setattr(persona, key, value)
43
+ persona.updated_at = datetime.now().isoformat()
44
+ self._save()
45
+ return persona
46
+
47
+ def delete(self, persona_id: str) -> bool:
48
+ if persona_id in self._personas:
49
+ del self._personas[persona_id]
50
+ self._save()
51
+ return True
52
+ return False
53
+
54
+ def clone_to_agent(
55
+ self,
56
+ persona_id: str,
57
+ department: str = "strategy",
58
+ tier: int = 2,
59
+ agents_dir: str | Path = "",
60
+ ) -> Optional[str]:
61
+ """Clone a persona to an ArkaOS agent YAML file.
62
+
63
+ Returns the agent ID if successful, None if persona not found.
64
+ """
65
+ persona = self._personas.get(persona_id)
66
+ if not persona:
67
+ return None
68
+
69
+ agent_data = persona.to_agent_yaml(department=department, tier=tier)
70
+ agent_id = agent_data["id"]
71
+
72
+ if agents_dir:
73
+ output_dir = Path(agents_dir)
74
+ output_dir.mkdir(parents=True, exist_ok=True)
75
+ output_path = output_dir / f"{agent_id}.yaml"
76
+ with open(output_path, "w") as f:
77
+ yaml.dump(agent_data, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
78
+
79
+ # Track the clone
80
+ persona.cloned_to_agents.append(agent_id)
81
+ persona.updated_at = datetime.now().isoformat()
82
+ self._save()
83
+
84
+ return agent_id
85
+
86
+ def _save(self) -> None:
87
+ if self._storage_path is None:
88
+ return
89
+ self._storage_path.parent.mkdir(parents=True, exist_ok=True)
90
+ data = {pid: p.model_dump(mode="json") for pid, p in self._personas.items()}
91
+ with open(self._storage_path, "w") as f:
92
+ json.dump(data, f, indent=2)
93
+
94
+ def _load(self) -> None:
95
+ if self._storage_path is None or not self._storage_path.exists():
96
+ return
97
+ content = self._storage_path.read_text().strip()
98
+ if not content:
99
+ return
100
+ data = json.loads(content)
101
+ for pid, pdata in data.items():
102
+ self._personas[pid] = Persona.model_validate(pdata)
@@ -0,0 +1,127 @@
1
+ """Persona schema — models for persona creation and cloning."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional, Any
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class PersonaDISC(BaseModel):
10
+ primary: str = "C"
11
+ secondary: str = "S"
12
+ communication_style: str = ""
13
+ under_pressure: str = ""
14
+ motivator: str = ""
15
+
16
+
17
+ class PersonaEnneagram(BaseModel):
18
+ type: int = 5
19
+ wing: int = 6
20
+ core_motivation: str = ""
21
+ core_fear: str = ""
22
+ subtype: str = "self-preservation"
23
+
24
+
25
+ class PersonaBigFive(BaseModel):
26
+ openness: int = 50
27
+ conscientiousness: int = 50
28
+ extraversion: int = 50
29
+ agreeableness: int = 50
30
+ neuroticism: int = 50
31
+
32
+
33
+ class PersonaCommunication(BaseModel):
34
+ tone: str = ""
35
+ vocabulary_level: str = "specialist"
36
+ preferred_format: str = ""
37
+ avoid: list[str] = Field(default_factory=list)
38
+
39
+
40
+ class Persona(BaseModel):
41
+ """A persona based on a real person or archetype."""
42
+ id: str
43
+ name: str
44
+ title: str = "" # e.g., "Business Strategy", "Growth Marketing"
45
+ tagline: str = "" # e.g., "The Natural Commander with emotional depth"
46
+ source: str = "" # e.g., "Alex Hormozi", "Naval Ravikant"
47
+ avatar_url: str = ""
48
+
49
+ # Behavioral DNA
50
+ disc: PersonaDISC = Field(default_factory=PersonaDISC)
51
+ enneagram: PersonaEnneagram = Field(default_factory=PersonaEnneagram)
52
+ big_five: PersonaBigFive = Field(default_factory=PersonaBigFive)
53
+ mbti: str = "INTJ"
54
+
55
+ # Knowledge
56
+ mental_models: list[str] = Field(default_factory=list)
57
+ expertise_domains: list[str] = Field(default_factory=list)
58
+ frameworks: list[str] = Field(default_factory=list)
59
+ key_quotes: list[str] = Field(default_factory=list)
60
+
61
+ # Communication
62
+ communication: PersonaCommunication = Field(default_factory=PersonaCommunication)
63
+
64
+ # Metadata
65
+ created_at: str = ""
66
+ updated_at: str = ""
67
+ cloned_to_agents: list[str] = Field(default_factory=list)
68
+
69
+ def to_agent_yaml(self, department: str = "strategy", tier: int = 2) -> dict:
70
+ """Convert persona to an ArkaOS agent YAML structure."""
71
+ agent_id = f"persona-{self.id}"
72
+ return {
73
+ "id": agent_id,
74
+ "name": self.name,
75
+ "role": self.title or f"{self.source} Persona",
76
+ "department": department,
77
+ "tier": tier,
78
+ "behavioral_dna": {
79
+ "disc": {
80
+ "primary": self.disc.primary,
81
+ "secondary": self.disc.secondary,
82
+ "communication_style": self.disc.communication_style,
83
+ "under_pressure": self.disc.under_pressure,
84
+ "motivator": self.disc.motivator,
85
+ },
86
+ "enneagram": {
87
+ "type": self.enneagram.type,
88
+ "wing": self.enneagram.wing,
89
+ "core_motivation": self.enneagram.core_motivation,
90
+ "core_fear": self.enneagram.core_fear,
91
+ "subtype": self.enneagram.subtype,
92
+ },
93
+ "big_five": {
94
+ "openness": self.big_five.openness,
95
+ "conscientiousness": self.big_five.conscientiousness,
96
+ "extraversion": self.big_five.extraversion,
97
+ "agreeableness": self.big_five.agreeableness,
98
+ "neuroticism": self.big_five.neuroticism,
99
+ },
100
+ "mbti": {"type": self.mbti},
101
+ },
102
+ "mental_models": {
103
+ "primary": self.mental_models[:3],
104
+ "secondary": self.mental_models[3:6],
105
+ },
106
+ "authority": {
107
+ "veto": False,
108
+ "approve_budget": False,
109
+ "approve_architecture": False,
110
+ "orchestrate": False,
111
+ "delegates_to": [],
112
+ "escalates_to": None,
113
+ },
114
+ "expertise": {
115
+ "domains": self.expertise_domains[:5],
116
+ "frameworks": self.frameworks[:5],
117
+ "depth": "advanced",
118
+ "years_equivalent": 10,
119
+ },
120
+ "communication": {
121
+ "language": "en",
122
+ "tone": self.communication.tone,
123
+ "vocabulary_level": self.communication.vocabulary_level,
124
+ "preferred_format": self.communication.preferred_format,
125
+ "avoid": self.communication.avoid,
126
+ },
127
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "2.1.0"
3
+ version = "2.1.2"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}