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,204 @@
|
|
|
1
|
+
"""Context Rehydrator — rebuild context from persisted session.
|
|
2
|
+
|
|
3
|
+
Restores:
|
|
4
|
+
- Workflow position (current phase, completed phases)
|
|
5
|
+
- Active violations
|
|
6
|
+
- Pending deliverables
|
|
7
|
+
- Agent handoff state
|
|
8
|
+
- Previous outputs for context
|
|
9
|
+
|
|
10
|
+
Called automatically on session start to restore state.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from core.memory.session_store import SessionStore, load_session, SessionMeta, WorkflowSnapshot
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class RehydratedContext:
|
|
23
|
+
"""Context rebuilt from a persisted session."""
|
|
24
|
+
|
|
25
|
+
session_id: str
|
|
26
|
+
project: str
|
|
27
|
+
workflow_snapshot: WorkflowSnapshot | None = None
|
|
28
|
+
recent_outputs: list[dict] = field(default_factory=list)
|
|
29
|
+
pending_items: list[str] = field(default_factory=list)
|
|
30
|
+
violations: list[dict] = field(default_factory=list)
|
|
31
|
+
metadata: dict = field(default_factory=dict)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class HandoffState:
|
|
36
|
+
"""Agent handoff state for rehydration."""
|
|
37
|
+
|
|
38
|
+
from_agent: str
|
|
39
|
+
to_agent: str
|
|
40
|
+
task: str
|
|
41
|
+
context_summary: str
|
|
42
|
+
relevant_files: list[str] = field(default_factory=list)
|
|
43
|
+
at: str = ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ContextRehydrator:
|
|
47
|
+
"""Reconstruct context from persisted session data."""
|
|
48
|
+
|
|
49
|
+
def __init__(self, session_store: SessionStore):
|
|
50
|
+
self.store = session_store
|
|
51
|
+
|
|
52
|
+
def rehydrate(self) -> RehydratedContext:
|
|
53
|
+
"""Rebuild complete context from session.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
RehydratedContext with all restored state
|
|
57
|
+
"""
|
|
58
|
+
meta = self.store.load_meta()
|
|
59
|
+
workflow = self.store.load_workflow_snapshot()
|
|
60
|
+
|
|
61
|
+
if meta is None:
|
|
62
|
+
return RehydratedContext(
|
|
63
|
+
session_id=self.store.session_id,
|
|
64
|
+
project="unknown",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
outputs = self._load_recent_outputs(limit=10)
|
|
68
|
+
|
|
69
|
+
return RehydratedContext(
|
|
70
|
+
session_id=meta.session_id,
|
|
71
|
+
project=meta.project,
|
|
72
|
+
workflow_snapshot=workflow,
|
|
73
|
+
recent_outputs=outputs,
|
|
74
|
+
pending_items=self._extract_pending_items(workflow),
|
|
75
|
+
violations=workflow.violations if workflow else [],
|
|
76
|
+
metadata=meta.metadata,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def _load_recent_outputs(self, limit: int = 10) -> list[dict]:
|
|
80
|
+
"""Load recent agent outputs."""
|
|
81
|
+
outputs_dir = self.store._outputs_dir
|
|
82
|
+
if not outputs_dir.exists():
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
all_outputs = []
|
|
86
|
+
for output_file in outputs_dir.glob("*.json"):
|
|
87
|
+
try:
|
|
88
|
+
data = json.loads(output_file.read_text(encoding="utf-8"))
|
|
89
|
+
if isinstance(data, list):
|
|
90
|
+
all_outputs.extend(data[-limit:])
|
|
91
|
+
elif isinstance(data, dict):
|
|
92
|
+
all_outputs.append(data)
|
|
93
|
+
except (json.JSONDecodeError, OSError):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
all_outputs.sort(key=lambda x: x.get("at", ""), reverse=True)
|
|
97
|
+
return all_outputs[-limit:]
|
|
98
|
+
|
|
99
|
+
def _extract_pending_items(self, workflow: WorkflowSnapshot | None) -> list[str]:
|
|
100
|
+
"""Extract pending items from workflow snapshot."""
|
|
101
|
+
if workflow is None:
|
|
102
|
+
return []
|
|
103
|
+
|
|
104
|
+
pending = []
|
|
105
|
+
for phase_id, status in workflow.phases.items():
|
|
106
|
+
if status in ("pending", "in_progress"):
|
|
107
|
+
pending.append(phase_id)
|
|
108
|
+
return pending
|
|
109
|
+
|
|
110
|
+
def build_context_string(self) -> str:
|
|
111
|
+
"""Build a context string for injection.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Human-readable context summary
|
|
115
|
+
"""
|
|
116
|
+
ctx = self.rehydrate()
|
|
117
|
+
|
|
118
|
+
lines = [
|
|
119
|
+
f"[SESSION] Resuming session: {ctx.session_id[:8]}",
|
|
120
|
+
f"[SESSION] Project: {ctx.project}",
|
|
121
|
+
"",
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
if ctx.workflow_snapshot:
|
|
125
|
+
wf = ctx.workflow_snapshot
|
|
126
|
+
lines.append(f"[WORKFLOW] {wf.workflow_name}")
|
|
127
|
+
lines.append(f"[WORKFLOW] Current phase: {wf.current_phase}")
|
|
128
|
+
if ctx.pending_items:
|
|
129
|
+
lines.append(f"[WORKFLOW] Pending: {', '.join(ctx.pending_items)}")
|
|
130
|
+
|
|
131
|
+
if ctx.violations:
|
|
132
|
+
lines.append(f"[WORKFLOW] Violations: {len(ctx.violations)}")
|
|
133
|
+
for v in ctx.violations[-3:]:
|
|
134
|
+
lines.append(f" - [{v.get('rule', '?')}] {v.get('detail', '')[:50]}")
|
|
135
|
+
|
|
136
|
+
if ctx.recent_outputs:
|
|
137
|
+
lines.append(f"[CONTEXT] {len(ctx.recent_outputs)} recent outputs available")
|
|
138
|
+
|
|
139
|
+
lines.append("")
|
|
140
|
+
lines.append("[SESSION] Type /session resume to continue from last state.")
|
|
141
|
+
|
|
142
|
+
return "\n".join(lines)
|
|
143
|
+
|
|
144
|
+
def get_handoff_state(self, from_agent: str, to_agent: str) -> HandoffState | None:
|
|
145
|
+
"""Get handoff state between two agents.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
from_agent: Source agent ID
|
|
149
|
+
to_agent: Target agent ID
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
HandoffState if found, None otherwise
|
|
153
|
+
"""
|
|
154
|
+
outputs = self.store.load_agent_outputs(from_agent)
|
|
155
|
+
if not outputs:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
last_output = outputs[-1]
|
|
159
|
+
context_summary = last_output.output[:500] if last_output.output else ""
|
|
160
|
+
|
|
161
|
+
return HandoffState(
|
|
162
|
+
from_agent=from_agent,
|
|
163
|
+
to_agent=to_agent,
|
|
164
|
+
task="continued from previous session",
|
|
165
|
+
context_summary=context_summary,
|
|
166
|
+
relevant_files=[],
|
|
167
|
+
at=last_output.at,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def rehydrate_session(session_id: str) -> RehydratedContext | None:
|
|
172
|
+
"""Convenience function to rehydrate a session.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
session_id: Session UUID to rehydrate
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
RehydratedContext or None if session not found
|
|
179
|
+
"""
|
|
180
|
+
store = load_session(session_id)
|
|
181
|
+
if store is None:
|
|
182
|
+
return None
|
|
183
|
+
return ContextRehydrator(store).rehydrate()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def build_resume_context() -> str:
|
|
187
|
+
"""Build context string for session resume.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Context string for injection
|
|
191
|
+
"""
|
|
192
|
+
from core.memory.session_store import SessionStore
|
|
193
|
+
|
|
194
|
+
store = SessionStore()
|
|
195
|
+
active_id = store.get_active_session()
|
|
196
|
+
if not active_id:
|
|
197
|
+
return ""
|
|
198
|
+
|
|
199
|
+
session_store = load_session(active_id)
|
|
200
|
+
if session_store is None:
|
|
201
|
+
return ""
|
|
202
|
+
|
|
203
|
+
rehydrator = ContextRehydrator(session_store)
|
|
204
|
+
return rehydrator.build_context_string()
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Session Memory Store — persist session state across restarts.
|
|
2
|
+
|
|
3
|
+
Stores session data to ~/.arkaos/sessions/:
|
|
4
|
+
- workflow-state.json: Current workflow phase, violations
|
|
5
|
+
- agent-outputs/: Per-agent outputs from this session
|
|
6
|
+
- session-meta.json: Session metadata (start time, project, etc.)
|
|
7
|
+
|
|
8
|
+
Auto-saved on every state change. Loads automatically on session start.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import dataclass, field, asdict
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _sessions_dir() -> Path:
|
|
20
|
+
return Path.home() / ".arkaos" / "sessions"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _ensure_sessions_dir() -> Path:
|
|
24
|
+
sessions = _sessions_dir()
|
|
25
|
+
sessions.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
return sessions
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SessionMeta:
|
|
31
|
+
"""Metadata about a session."""
|
|
32
|
+
|
|
33
|
+
session_id: str
|
|
34
|
+
project: str
|
|
35
|
+
started_at: str
|
|
36
|
+
ended_at: str = ""
|
|
37
|
+
workflow_id: str = ""
|
|
38
|
+
agent_id: str = ""
|
|
39
|
+
metadata: dict = field(default_factory=dict)
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> dict[str, Any]:
|
|
42
|
+
return asdict(self)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class WorkflowSnapshot:
|
|
47
|
+
"""Snapshot of workflow state at a point in time."""
|
|
48
|
+
|
|
49
|
+
workflow_id: str
|
|
50
|
+
workflow_name: str
|
|
51
|
+
current_phase: str
|
|
52
|
+
phases: dict[str, str]
|
|
53
|
+
violations: list[dict] = field(default_factory=list)
|
|
54
|
+
artifacts: list[str] = field(default_factory=list)
|
|
55
|
+
at: str = ""
|
|
56
|
+
|
|
57
|
+
def to_dict(self) -> dict[str, Any]:
|
|
58
|
+
return asdict(self)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class AgentOutput:
|
|
63
|
+
"""Output from an agent during the session."""
|
|
64
|
+
|
|
65
|
+
agent_id: str
|
|
66
|
+
phase_id: str
|
|
67
|
+
output: str
|
|
68
|
+
at: str = ""
|
|
69
|
+
|
|
70
|
+
def to_dict(self) -> dict[str, Any]:
|
|
71
|
+
return asdict(self)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class SessionStore:
|
|
75
|
+
"""Persistent session memory store."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, session_id: str | None = None):
|
|
78
|
+
self.session_id = session_id or str(uuid.uuid4())
|
|
79
|
+
self._sessions_dir = _ensure_sessions_dir()
|
|
80
|
+
self._session_dir = self._sessions_dir / self.session_id
|
|
81
|
+
self._session_dir.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
self._meta_file = self._session_dir / "session-meta.json"
|
|
84
|
+
self._workflow_file = self._session_dir / "workflow-state.json"
|
|
85
|
+
self._outputs_dir = self._session_dir / "agent-outputs"
|
|
86
|
+
self._outputs_dir.mkdir(exist_ok=True)
|
|
87
|
+
|
|
88
|
+
def save_meta(self, meta: SessionMeta) -> None:
|
|
89
|
+
"""Save session metadata."""
|
|
90
|
+
self._meta_file.write_text(json.dumps(meta.to_dict(), indent=2), encoding="utf-8")
|
|
91
|
+
|
|
92
|
+
def load_meta(self) -> SessionMeta | None:
|
|
93
|
+
"""Load session metadata."""
|
|
94
|
+
if not self._meta_file.exists():
|
|
95
|
+
return None
|
|
96
|
+
try:
|
|
97
|
+
data = json.loads(self._meta_file.read_text(encoding="utf-8"))
|
|
98
|
+
return SessionMeta(**data)
|
|
99
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def save_workflow_snapshot(self, snapshot: WorkflowSnapshot) -> None:
|
|
103
|
+
"""Save workflow state snapshot."""
|
|
104
|
+
if not snapshot.at:
|
|
105
|
+
snapshot.at = datetime.now(timezone.utc).isoformat()
|
|
106
|
+
self._workflow_file.write_text(json.dumps(snapshot.to_dict(), indent=2), encoding="utf-8")
|
|
107
|
+
|
|
108
|
+
def load_workflow_snapshot(self) -> WorkflowSnapshot | None:
|
|
109
|
+
"""Load workflow state snapshot."""
|
|
110
|
+
if not self._workflow_file.exists():
|
|
111
|
+
return None
|
|
112
|
+
try:
|
|
113
|
+
data = json.loads(self._workflow_file.read_text(encoding="utf-8"))
|
|
114
|
+
return WorkflowSnapshot(**data)
|
|
115
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def save_agent_output(self, output: AgentOutput) -> None:
|
|
119
|
+
"""Save an agent's output."""
|
|
120
|
+
if not output.at:
|
|
121
|
+
output.at = datetime.now(timezone.utc).isoformat()
|
|
122
|
+
|
|
123
|
+
file_path = self._outputs_dir / f"{output.agent_id}.json"
|
|
124
|
+
outputs = []
|
|
125
|
+
if file_path.exists():
|
|
126
|
+
try:
|
|
127
|
+
outputs = json.loads(file_path.read_text(encoding="utf-8"))
|
|
128
|
+
except json.JSONDecodeError:
|
|
129
|
+
outputs = []
|
|
130
|
+
|
|
131
|
+
outputs.append(output.to_dict())
|
|
132
|
+
file_path.write_text(json.dumps(outputs, indent=2), encoding="utf-8")
|
|
133
|
+
|
|
134
|
+
def load_agent_outputs(self, agent_id: str) -> list[AgentOutput]:
|
|
135
|
+
"""Load all outputs for a specific agent."""
|
|
136
|
+
file_path = self._outputs_dir / f"{agent_id}.json"
|
|
137
|
+
if not file_path.exists():
|
|
138
|
+
return []
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
data = json.loads(file_path.read_text(encoding="utf-8"))
|
|
142
|
+
return [AgentOutput(**o) for o in data]
|
|
143
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
def list_sessions(self, limit: int = 10) -> list[SessionMeta]:
|
|
147
|
+
"""List recent sessions."""
|
|
148
|
+
sessions = []
|
|
149
|
+
for session_dir in sorted(
|
|
150
|
+
self._sessions_dir.iterdir(),
|
|
151
|
+
key=lambda p: p.stat().st_mtime,
|
|
152
|
+
reverse=True,
|
|
153
|
+
)[:limit]:
|
|
154
|
+
if session_dir.is_dir():
|
|
155
|
+
meta_file = session_dir / "session-meta.json"
|
|
156
|
+
if meta_file.exists():
|
|
157
|
+
try:
|
|
158
|
+
data = json.loads(meta_file.read_text(encoding="utf-8"))
|
|
159
|
+
sessions.append(SessionMeta(**data))
|
|
160
|
+
except (json.JSONDecodeError, KeyError):
|
|
161
|
+
pass
|
|
162
|
+
return sessions
|
|
163
|
+
|
|
164
|
+
def get_active_session(self) -> str | None:
|
|
165
|
+
"""Get the most recent active session ID."""
|
|
166
|
+
sessions = self.list_sessions(limit=1)
|
|
167
|
+
if sessions:
|
|
168
|
+
return sessions[0].session_id
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
def end_session(self) -> None:
|
|
172
|
+
"""Mark session as ended."""
|
|
173
|
+
meta = self.load_meta()
|
|
174
|
+
if meta:
|
|
175
|
+
meta.ended_at = datetime.now(timezone.utc).isoformat()
|
|
176
|
+
self.save_meta(meta)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def create_session(project: str, agent_id: str = "", metadata: dict | None = None) -> SessionStore:
|
|
180
|
+
"""Create a new session store.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
project: Project name
|
|
184
|
+
agent_id: Agent ID creating the session
|
|
185
|
+
metadata: Additional metadata
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
New SessionStore instance
|
|
189
|
+
"""
|
|
190
|
+
store = SessionStore()
|
|
191
|
+
meta = SessionMeta(
|
|
192
|
+
session_id=store.session_id,
|
|
193
|
+
project=project,
|
|
194
|
+
started_at=datetime.now(timezone.utc).isoformat(),
|
|
195
|
+
agent_id=agent_id,
|
|
196
|
+
metadata=metadata or {},
|
|
197
|
+
)
|
|
198
|
+
store.save_meta(meta)
|
|
199
|
+
return store
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def load_session(session_id: str) -> SessionStore | None:
|
|
203
|
+
"""Load an existing session by ID.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
session_id: The session UUID
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
SessionStore instance or None if not found
|
|
210
|
+
"""
|
|
211
|
+
session_dir = _sessions_dir() / session_id
|
|
212
|
+
if not session_dir.exists():
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
store = SessionStore(session_id=session_id)
|
|
216
|
+
if store.load_meta() is None:
|
|
217
|
+
return None
|
|
218
|
+
return store
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def load_or_create_session(project: str, agent_id: str = "") -> SessionStore:
|
|
222
|
+
"""Load the most recent session or create a new one.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
project: Project name
|
|
226
|
+
agent_id: Agent ID
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
SessionStore for the session
|
|
230
|
+
"""
|
|
231
|
+
sessions_dir = _sessions_dir()
|
|
232
|
+
if not sessions_dir.exists():
|
|
233
|
+
return create_session(project, agent_id)
|
|
234
|
+
|
|
235
|
+
sessions = []
|
|
236
|
+
for session_dir in sorted(
|
|
237
|
+
sessions_dir.iterdir(),
|
|
238
|
+
key=lambda p: p.stat().st_mtime,
|
|
239
|
+
reverse=True,
|
|
240
|
+
):
|
|
241
|
+
if session_dir.is_dir():
|
|
242
|
+
meta_file = session_dir / "session-meta.json"
|
|
243
|
+
if meta_file.exists():
|
|
244
|
+
try:
|
|
245
|
+
data = json.loads(meta_file.read_text(encoding="utf-8"))
|
|
246
|
+
sessions.append(SessionMeta(**data))
|
|
247
|
+
except (json.JSONDecodeError, KeyError):
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
for session in sessions:
|
|
251
|
+
if not session.ended_at:
|
|
252
|
+
existing = load_session(session.session_id)
|
|
253
|
+
if existing:
|
|
254
|
+
return existing
|
|
255
|
+
|
|
256
|
+
return create_session(project, agent_id)
|
|
Binary file
|
|
Binary file
|
package/core/synapse/__init__.py
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
"""Synapse v2 —
|
|
1
|
+
"""Synapse v2 — 10-layer context injection engine.
|
|
2
2
|
|
|
3
3
|
Injects relevant context into every prompt with <100ms target latency
|
|
4
4
|
and 65% context reduction through intelligent filtering.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from core.synapse.engine import SynapseEngine
|
|
8
|
-
from core.synapse.layers import Layer, LayerResult, ForgeContextLayer
|
|
8
|
+
from core.synapse.layers import Layer, LayerResult, ForgeContextLayer, SessionContextLayer
|
|
9
9
|
from core.synapse.cache import LayerCache
|
|
10
10
|
|
|
11
|
-
__all__ = [
|
|
11
|
+
__all__ = [
|
|
12
|
+
"SynapseEngine",
|
|
13
|
+
"Layer",
|
|
14
|
+
"LayerResult",
|
|
15
|
+
"LayerCache",
|
|
16
|
+
"ForgeContextLayer",
|
|
17
|
+
"SessionContextLayer",
|
|
18
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/core/synapse/engine.py
CHANGED
|
@@ -19,12 +19,13 @@ from core.synapse.cache import LayerCache
|
|
|
19
19
|
@dataclass
|
|
20
20
|
class SynapseResult:
|
|
21
21
|
"""Complete result of Synapse context injection."""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
|
|
23
|
+
context_string: str # The combined context to inject
|
|
24
|
+
layers: list[LayerResult] # Individual layer results
|
|
25
|
+
total_ms: int # Total computation time
|
|
26
|
+
total_tokens_est: int # Estimated total tokens injected
|
|
27
|
+
cache_stats: dict # Cache hit/miss statistics
|
|
28
|
+
layers_skipped: int # Layers that returned empty results
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class SynapseEngine:
|
|
@@ -83,13 +84,15 @@ class SynapseEngine:
|
|
|
83
84
|
total_ms = int((time.time() - start) * 1000)
|
|
84
85
|
|
|
85
86
|
# Record metrics
|
|
86
|
-
self._metrics.append(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
self._metrics.append(
|
|
88
|
+
{
|
|
89
|
+
"timestamp": time.time(),
|
|
90
|
+
"total_ms": total_ms,
|
|
91
|
+
"layers_computed": len(results),
|
|
92
|
+
"layers_skipped": skipped,
|
|
93
|
+
"tokens_injected": total_tokens,
|
|
94
|
+
}
|
|
95
|
+
)
|
|
93
96
|
# Keep only last 500 metrics
|
|
94
97
|
if len(self._metrics) > 500:
|
|
95
98
|
self._metrics = self._metrics[-500:]
|
|
@@ -166,10 +169,17 @@ def create_default_engine(
|
|
|
166
169
|
Configured SynapseEngine ready to use.
|
|
167
170
|
"""
|
|
168
171
|
from core.synapse.layers import (
|
|
169
|
-
ConstitutionLayer,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
ConstitutionLayer,
|
|
173
|
+
DepartmentLayer,
|
|
174
|
+
AgentLayer,
|
|
175
|
+
ProjectLayer,
|
|
176
|
+
BranchLayer,
|
|
177
|
+
CommandHintsLayer,
|
|
178
|
+
QualityGateLayer,
|
|
179
|
+
TimeLayer,
|
|
180
|
+
KnowledgeRetrievalLayer,
|
|
172
181
|
ForgeContextLayer,
|
|
182
|
+
SessionContextLayer,
|
|
173
183
|
)
|
|
174
184
|
|
|
175
185
|
engine = SynapseEngine()
|
|
@@ -186,5 +196,6 @@ def create_default_engine(
|
|
|
186
196
|
engine.register_layer(QualityGateLayer())
|
|
187
197
|
engine.register_layer(TimeLayer())
|
|
188
198
|
engine.register_layer(ForgeContextLayer())
|
|
199
|
+
engine.register_layer(SessionContextLayer())
|
|
189
200
|
|
|
190
201
|
return engine
|