alma-memory 0.3.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- alma/__init__.py +99 -29
- alma/confidence/__init__.py +47 -0
- alma/confidence/engine.py +540 -0
- alma/confidence/types.py +351 -0
- alma/config/loader.py +3 -2
- alma/consolidation/__init__.py +23 -0
- alma/consolidation/engine.py +678 -0
- alma/consolidation/prompts.py +84 -0
- alma/core.py +15 -15
- alma/domains/__init__.py +6 -6
- alma/domains/factory.py +12 -9
- alma/domains/schemas.py +17 -3
- alma/domains/types.py +8 -4
- alma/events/__init__.py +75 -0
- alma/events/emitter.py +284 -0
- alma/events/storage_mixin.py +246 -0
- alma/events/types.py +126 -0
- alma/events/webhook.py +425 -0
- alma/exceptions.py +49 -0
- alma/extraction/__init__.py +31 -0
- alma/extraction/auto_learner.py +264 -0
- alma/extraction/extractor.py +420 -0
- alma/graph/__init__.py +81 -0
- alma/graph/backends/__init__.py +18 -0
- alma/graph/backends/memory.py +236 -0
- alma/graph/backends/neo4j.py +417 -0
- alma/graph/base.py +159 -0
- alma/graph/extraction.py +198 -0
- alma/graph/store.py +860 -0
- alma/harness/__init__.py +4 -4
- alma/harness/base.py +18 -9
- alma/harness/domains.py +27 -11
- alma/initializer/__init__.py +37 -0
- alma/initializer/initializer.py +418 -0
- alma/initializer/types.py +250 -0
- alma/integration/__init__.py +9 -9
- alma/integration/claude_agents.py +10 -10
- alma/integration/helena.py +32 -22
- alma/integration/victor.py +57 -33
- alma/learning/__init__.py +27 -27
- alma/learning/forgetting.py +198 -148
- alma/learning/heuristic_extractor.py +40 -24
- alma/learning/protocols.py +62 -14
- alma/learning/validation.py +7 -2
- alma/mcp/__init__.py +4 -4
- alma/mcp/__main__.py +2 -1
- alma/mcp/resources.py +17 -16
- alma/mcp/server.py +102 -44
- alma/mcp/tools.py +174 -37
- alma/progress/__init__.py +3 -3
- alma/progress/tracker.py +26 -20
- alma/progress/types.py +8 -12
- alma/py.typed +0 -0
- alma/retrieval/__init__.py +11 -11
- alma/retrieval/cache.py +20 -21
- alma/retrieval/embeddings.py +4 -4
- alma/retrieval/engine.py +114 -35
- alma/retrieval/scoring.py +73 -63
- alma/session/__init__.py +2 -2
- alma/session/manager.py +5 -5
- alma/session/types.py +5 -4
- alma/storage/__init__.py +41 -0
- alma/storage/azure_cosmos.py +107 -31
- alma/storage/base.py +157 -4
- alma/storage/chroma.py +1443 -0
- alma/storage/file_based.py +56 -20
- alma/storage/pinecone.py +1080 -0
- alma/storage/postgresql.py +1452 -0
- alma/storage/qdrant.py +1306 -0
- alma/storage/sqlite_local.py +376 -31
- alma/types.py +62 -14
- alma_memory-0.5.0.dist-info/METADATA +905 -0
- alma_memory-0.5.0.dist-info/RECORD +76 -0
- {alma_memory-0.3.0.dist-info → alma_memory-0.5.0.dist-info}/WHEEL +1 -1
- alma_memory-0.3.0.dist-info/METADATA +0 -438
- alma_memory-0.3.0.dist-info/RECORD +0 -46
- {alma_memory-0.3.0.dist-info → alma_memory-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Initializer Types.
|
|
3
|
+
|
|
4
|
+
Data structures for the Session Initializer pattern.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class CodebaseOrientation:
|
|
15
|
+
"""Codebase orientation information."""
|
|
16
|
+
|
|
17
|
+
# Git state
|
|
18
|
+
current_branch: str
|
|
19
|
+
has_uncommitted_changes: bool
|
|
20
|
+
recent_commits: List[str] # Last N commit messages
|
|
21
|
+
|
|
22
|
+
# File structure
|
|
23
|
+
root_path: str
|
|
24
|
+
key_directories: List[str] # src/, tests/, etc.
|
|
25
|
+
config_files: List[str] # package.json, pyproject.toml, etc.
|
|
26
|
+
|
|
27
|
+
# Summary
|
|
28
|
+
summary: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
def to_prompt(self) -> str:
|
|
31
|
+
"""Format orientation for prompt injection."""
|
|
32
|
+
lines = [
|
|
33
|
+
f"Branch: {self.current_branch}",
|
|
34
|
+
f"Uncommitted changes: {'Yes' if self.has_uncommitted_changes else 'No'}",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
if self.recent_commits:
|
|
38
|
+
lines.append("Recent commits:")
|
|
39
|
+
for commit in self.recent_commits[:5]:
|
|
40
|
+
lines.append(f" - {commit}")
|
|
41
|
+
|
|
42
|
+
if self.key_directories:
|
|
43
|
+
lines.append(f"Key directories: {', '.join(self.key_directories)}")
|
|
44
|
+
|
|
45
|
+
if self.summary:
|
|
46
|
+
lines.append(f"Summary: {self.summary}")
|
|
47
|
+
|
|
48
|
+
return "\n".join(lines)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class RulesOfEngagement:
|
|
53
|
+
"""Rules governing agent behavior during session."""
|
|
54
|
+
|
|
55
|
+
# What agent CAN do
|
|
56
|
+
scope_rules: List[str] = field(default_factory=list)
|
|
57
|
+
|
|
58
|
+
# What agent CANNOT do
|
|
59
|
+
constraints: List[str] = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
# Must pass before marking "done"
|
|
62
|
+
quality_gates: List[str] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
def to_prompt(self) -> str:
|
|
65
|
+
"""Format rules for prompt injection."""
|
|
66
|
+
lines = []
|
|
67
|
+
|
|
68
|
+
if self.scope_rules:
|
|
69
|
+
lines.append("You CAN:")
|
|
70
|
+
for rule in self.scope_rules:
|
|
71
|
+
lines.append(f" - {rule}")
|
|
72
|
+
|
|
73
|
+
if self.constraints:
|
|
74
|
+
lines.append("You CANNOT:")
|
|
75
|
+
for constraint in self.constraints:
|
|
76
|
+
lines.append(f" - {constraint}")
|
|
77
|
+
|
|
78
|
+
if self.quality_gates:
|
|
79
|
+
lines.append("Before marking DONE, verify:")
|
|
80
|
+
for gate in self.quality_gates:
|
|
81
|
+
lines.append(f" - {gate}")
|
|
82
|
+
|
|
83
|
+
return "\n".join(lines)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class InitializationResult:
|
|
88
|
+
"""
|
|
89
|
+
Result of session initialization.
|
|
90
|
+
|
|
91
|
+
Contains everything an agent needs to start work:
|
|
92
|
+
- Expanded goal and work items
|
|
93
|
+
- Codebase orientation
|
|
94
|
+
- Relevant memories
|
|
95
|
+
- Rules of engagement
|
|
96
|
+
- Recommended starting point
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
id: str
|
|
100
|
+
session_id: str
|
|
101
|
+
project_id: str
|
|
102
|
+
agent: str
|
|
103
|
+
|
|
104
|
+
# Original and expanded goal
|
|
105
|
+
original_prompt: str
|
|
106
|
+
goal: str
|
|
107
|
+
|
|
108
|
+
# Work items extracted from goal
|
|
109
|
+
work_items: List[Any] = field(default_factory=list) # WorkItem objects
|
|
110
|
+
|
|
111
|
+
# Codebase orientation
|
|
112
|
+
orientation: Optional[CodebaseOrientation] = None
|
|
113
|
+
|
|
114
|
+
# Recent activity
|
|
115
|
+
recent_activity: List[str] = field(default_factory=list)
|
|
116
|
+
|
|
117
|
+
# Relevant memories (MemorySlice)
|
|
118
|
+
relevant_memories: Optional[Any] = None
|
|
119
|
+
|
|
120
|
+
# Rules of engagement
|
|
121
|
+
rules: RulesOfEngagement = field(default_factory=RulesOfEngagement)
|
|
122
|
+
|
|
123
|
+
# Suggested first action
|
|
124
|
+
recommended_start: Optional[Any] = None # WorkItem
|
|
125
|
+
|
|
126
|
+
# Metadata
|
|
127
|
+
initialized_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
128
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def create(
|
|
132
|
+
cls,
|
|
133
|
+
project_id: str,
|
|
134
|
+
agent: str,
|
|
135
|
+
original_prompt: str,
|
|
136
|
+
goal: Optional[str] = None,
|
|
137
|
+
session_id: Optional[str] = None,
|
|
138
|
+
) -> "InitializationResult":
|
|
139
|
+
"""Create a new initialization result."""
|
|
140
|
+
return cls(
|
|
141
|
+
id=str(uuid.uuid4()),
|
|
142
|
+
session_id=session_id or str(uuid.uuid4()),
|
|
143
|
+
project_id=project_id,
|
|
144
|
+
agent=agent,
|
|
145
|
+
original_prompt=original_prompt,
|
|
146
|
+
goal=goal or original_prompt,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def to_prompt(self) -> str:
|
|
150
|
+
"""
|
|
151
|
+
Format initialization result for prompt injection.
|
|
152
|
+
|
|
153
|
+
This is the "briefing" that prepares the agent.
|
|
154
|
+
"""
|
|
155
|
+
sections = []
|
|
156
|
+
|
|
157
|
+
# Header
|
|
158
|
+
sections.append(f"## Session Initialization for {self.agent}")
|
|
159
|
+
sections.append(f"Project: {self.project_id}")
|
|
160
|
+
sections.append(f"Session: {self.session_id}")
|
|
161
|
+
sections.append("")
|
|
162
|
+
|
|
163
|
+
# Goal
|
|
164
|
+
sections.append("### Goal")
|
|
165
|
+
sections.append(self.goal)
|
|
166
|
+
sections.append("")
|
|
167
|
+
|
|
168
|
+
# Work items
|
|
169
|
+
if self.work_items:
|
|
170
|
+
sections.append("### Work Items")
|
|
171
|
+
for i, item in enumerate(self.work_items, 1):
|
|
172
|
+
title = getattr(item, "title", str(item))
|
|
173
|
+
status = getattr(item, "status", "pending")
|
|
174
|
+
sections.append(f"{i}. [{status}] {title}")
|
|
175
|
+
sections.append("")
|
|
176
|
+
|
|
177
|
+
# Orientation
|
|
178
|
+
if self.orientation:
|
|
179
|
+
sections.append("### Codebase Orientation")
|
|
180
|
+
sections.append(self.orientation.to_prompt())
|
|
181
|
+
sections.append("")
|
|
182
|
+
|
|
183
|
+
# Relevant memories
|
|
184
|
+
if self.relevant_memories:
|
|
185
|
+
sections.append("### Relevant Knowledge from Past Runs")
|
|
186
|
+
if hasattr(self.relevant_memories, "to_prompt"):
|
|
187
|
+
sections.append(self.relevant_memories.to_prompt())
|
|
188
|
+
else:
|
|
189
|
+
sections.append(str(self.relevant_memories))
|
|
190
|
+
sections.append("")
|
|
191
|
+
|
|
192
|
+
# Rules of engagement
|
|
193
|
+
if self.rules.scope_rules or self.rules.constraints or self.rules.quality_gates:
|
|
194
|
+
sections.append("### Rules of Engagement")
|
|
195
|
+
sections.append(self.rules.to_prompt())
|
|
196
|
+
sections.append("")
|
|
197
|
+
|
|
198
|
+
# Recommended start
|
|
199
|
+
if self.recommended_start:
|
|
200
|
+
sections.append("### Recommended First Action")
|
|
201
|
+
title = getattr(
|
|
202
|
+
self.recommended_start, "title", str(self.recommended_start)
|
|
203
|
+
)
|
|
204
|
+
sections.append(f"Start with: {title}")
|
|
205
|
+
sections.append("")
|
|
206
|
+
|
|
207
|
+
return "\n".join(sections)
|
|
208
|
+
|
|
209
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
210
|
+
"""Serialize to dictionary."""
|
|
211
|
+
return {
|
|
212
|
+
"id": self.id,
|
|
213
|
+
"session_id": self.session_id,
|
|
214
|
+
"project_id": self.project_id,
|
|
215
|
+
"agent": self.agent,
|
|
216
|
+
"original_prompt": self.original_prompt,
|
|
217
|
+
"goal": self.goal,
|
|
218
|
+
"work_items": [
|
|
219
|
+
item.to_dict() if hasattr(item, "to_dict") else str(item)
|
|
220
|
+
for item in self.work_items
|
|
221
|
+
],
|
|
222
|
+
"orientation": (
|
|
223
|
+
{
|
|
224
|
+
"current_branch": self.orientation.current_branch,
|
|
225
|
+
"has_uncommitted_changes": self.orientation.has_uncommitted_changes,
|
|
226
|
+
"recent_commits": self.orientation.recent_commits,
|
|
227
|
+
"root_path": self.orientation.root_path,
|
|
228
|
+
"key_directories": self.orientation.key_directories,
|
|
229
|
+
"config_files": self.orientation.config_files,
|
|
230
|
+
"summary": self.orientation.summary,
|
|
231
|
+
}
|
|
232
|
+
if self.orientation
|
|
233
|
+
else None
|
|
234
|
+
),
|
|
235
|
+
"recent_activity": self.recent_activity,
|
|
236
|
+
"rules": {
|
|
237
|
+
"scope_rules": self.rules.scope_rules,
|
|
238
|
+
"constraints": self.rules.constraints,
|
|
239
|
+
"quality_gates": self.rules.quality_gates,
|
|
240
|
+
},
|
|
241
|
+
"recommended_start": (
|
|
242
|
+
self.recommended_start.to_dict()
|
|
243
|
+
if self.recommended_start and hasattr(self.recommended_start, "to_dict")
|
|
244
|
+
else str(self.recommended_start)
|
|
245
|
+
if self.recommended_start
|
|
246
|
+
else None
|
|
247
|
+
),
|
|
248
|
+
"initialized_at": self.initialized_at.isoformat(),
|
|
249
|
+
"metadata": self.metadata,
|
|
250
|
+
}
|
alma/integration/__init__.py
CHANGED
|
@@ -5,32 +5,32 @@ Provides integration hooks for Claude Code agents (Helena, Victor, etc).
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from alma.integration.claude_agents import (
|
|
8
|
+
AgentIntegration,
|
|
8
9
|
AgentType,
|
|
10
|
+
ClaudeAgentHooks,
|
|
9
11
|
TaskContext,
|
|
10
12
|
TaskOutcome,
|
|
11
|
-
ClaudeAgentHooks,
|
|
12
|
-
AgentIntegration,
|
|
13
13
|
create_integration,
|
|
14
14
|
)
|
|
15
15
|
from alma.integration.helena import (
|
|
16
|
+
HELENA_CATEGORIES,
|
|
17
|
+
HELENA_FORBIDDEN,
|
|
18
|
+
HelenaHooks,
|
|
16
19
|
UITestContext,
|
|
17
20
|
UITestOutcome,
|
|
18
|
-
HelenaHooks,
|
|
19
21
|
create_helena_hooks,
|
|
20
|
-
helena_pre_task,
|
|
21
22
|
helena_post_task,
|
|
22
|
-
|
|
23
|
-
HELENA_FORBIDDEN,
|
|
23
|
+
helena_pre_task,
|
|
24
24
|
)
|
|
25
25
|
from alma.integration.victor import (
|
|
26
|
+
VICTOR_CATEGORIES,
|
|
27
|
+
VICTOR_FORBIDDEN,
|
|
26
28
|
APITestContext,
|
|
27
29
|
APITestOutcome,
|
|
28
30
|
VictorHooks,
|
|
29
31
|
create_victor_hooks,
|
|
30
|
-
victor_pre_task,
|
|
31
32
|
victor_post_task,
|
|
32
|
-
|
|
33
|
-
VICTOR_FORBIDDEN,
|
|
33
|
+
victor_pre_task,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
__all__ = [
|
|
@@ -9,20 +9,21 @@ These hooks enable agents to:
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
|
-
from typing import Optional, Dict, Any, List
|
|
13
12
|
from dataclasses import dataclass, field
|
|
14
13
|
from datetime import datetime, timezone
|
|
15
14
|
from enum import Enum
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
16
|
|
|
17
17
|
from alma.core import ALMA
|
|
18
|
+
from alma.harness.base import Context, Harness, RunResult
|
|
18
19
|
from alma.types import MemorySlice
|
|
19
|
-
from alma.harness.base import Harness, Context, RunResult
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class AgentType(Enum):
|
|
25
25
|
"""Supported Claude Code agent types."""
|
|
26
|
+
|
|
26
27
|
HELENA = "helena"
|
|
27
28
|
VICTOR = "victor"
|
|
28
29
|
CLARA = "clara"
|
|
@@ -37,6 +38,7 @@ class TaskContext:
|
|
|
37
38
|
|
|
38
39
|
Captures all relevant information for memory retrieval and learning.
|
|
39
40
|
"""
|
|
41
|
+
|
|
40
42
|
task_description: str
|
|
41
43
|
task_type: str
|
|
42
44
|
agent_name: str
|
|
@@ -67,6 +69,7 @@ class TaskOutcome:
|
|
|
67
69
|
|
|
68
70
|
Used for learning from task results.
|
|
69
71
|
"""
|
|
72
|
+
|
|
70
73
|
success: bool
|
|
71
74
|
strategy_used: str
|
|
72
75
|
output: Any = None
|
|
@@ -272,17 +275,17 @@ class ClaudeAgentHooks:
|
|
|
272
275
|
sections.append(f"- [{dk.domain}] {dk.fact}")
|
|
273
276
|
|
|
274
277
|
# User preferences
|
|
275
|
-
if memories.
|
|
278
|
+
if memories.preferences:
|
|
276
279
|
if include_section_headers:
|
|
277
280
|
sections.append("\n### User Preferences:")
|
|
278
|
-
for up in memories.
|
|
281
|
+
for up in memories.preferences:
|
|
279
282
|
sections.append(f"- [{up.category}] {up.preference}")
|
|
280
283
|
|
|
281
284
|
# Recent outcomes
|
|
282
|
-
if memories.
|
|
285
|
+
if memories.outcomes:
|
|
283
286
|
if include_section_headers:
|
|
284
287
|
sections.append("\n### Recent Outcomes:")
|
|
285
|
-
for o in memories.
|
|
288
|
+
for o in memories.outcomes[:3]: # Limit to 3 most recent
|
|
286
289
|
status = "✓" if o.success else "✗"
|
|
287
290
|
sections.append(
|
|
288
291
|
f"- {status} {o.task_type}: {o.task_description[:50]}..."
|
|
@@ -391,10 +394,7 @@ class AgentIntegration:
|
|
|
391
394
|
|
|
392
395
|
def get_all_stats(self) -> Dict[str, Dict[str, Any]]:
|
|
393
396
|
"""Get memory statistics for all registered agents."""
|
|
394
|
-
return {
|
|
395
|
-
name: hooks.get_agent_stats()
|
|
396
|
-
for name, hooks in self._agents.items()
|
|
397
|
-
}
|
|
397
|
+
return {name: hooks.get_agent_stats() for name, hooks in self._agents.items()}
|
|
398
398
|
|
|
399
399
|
|
|
400
400
|
def create_integration(
|
alma/integration/helena.py
CHANGED
|
@@ -14,18 +14,18 @@ This module provides Helena-specific memory categories, prompts, and utilities.
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
-
from typing import Optional, Dict, Any, List
|
|
18
17
|
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
19
|
|
|
20
20
|
from alma.core import ALMA
|
|
21
|
-
from alma.types import MemorySlice
|
|
22
21
|
from alma.harness.domains import CodingDomain
|
|
23
22
|
from alma.integration.claude_agents import (
|
|
23
|
+
AgentType,
|
|
24
24
|
ClaudeAgentHooks,
|
|
25
25
|
TaskContext,
|
|
26
26
|
TaskOutcome,
|
|
27
|
-
AgentType,
|
|
28
27
|
)
|
|
28
|
+
from alma.types import MemorySlice
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
@@ -55,6 +55,7 @@ class UITestContext(TaskContext):
|
|
|
55
55
|
|
|
56
56
|
Extends TaskContext with UI testing-specific fields.
|
|
57
57
|
"""
|
|
58
|
+
|
|
58
59
|
component_type: Optional[str] = None
|
|
59
60
|
page_url: Optional[str] = None
|
|
60
61
|
viewport: Optional[Dict[str, int]] = None
|
|
@@ -89,6 +90,7 @@ class UITestOutcome(TaskOutcome):
|
|
|
89
90
|
|
|
90
91
|
Extends TaskOutcome with UI testing-specific results.
|
|
91
92
|
"""
|
|
93
|
+
|
|
92
94
|
selectors_used: List[str] = field(default_factory=list)
|
|
93
95
|
accessibility_issues: List[Dict[str, Any]] = field(default_factory=list)
|
|
94
96
|
visual_diffs: List[str] = field(default_factory=list)
|
|
@@ -148,11 +150,13 @@ class HelenaHooks(ClaudeAgentHooks):
|
|
|
148
150
|
patterns = []
|
|
149
151
|
for h in memories.heuristics:
|
|
150
152
|
if "selector" in h.condition.lower():
|
|
151
|
-
patterns.append(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
patterns.append(
|
|
154
|
+
{
|
|
155
|
+
"pattern": h.strategy,
|
|
156
|
+
"confidence": h.confidence,
|
|
157
|
+
"occurrences": h.occurrence_count,
|
|
158
|
+
}
|
|
159
|
+
)
|
|
156
160
|
|
|
157
161
|
return patterns
|
|
158
162
|
|
|
@@ -171,11 +175,13 @@ class HelenaHooks(ClaudeAgentHooks):
|
|
|
171
175
|
strategies = []
|
|
172
176
|
for h in memories.heuristics:
|
|
173
177
|
if any(kw in h.condition.lower() for kw in ["form", "validation", "input"]):
|
|
174
|
-
strategies.append(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
strategies.append(
|
|
179
|
+
{
|
|
180
|
+
"condition": h.condition,
|
|
181
|
+
"strategy": h.strategy,
|
|
182
|
+
"confidence": h.confidence,
|
|
183
|
+
}
|
|
184
|
+
)
|
|
179
185
|
|
|
180
186
|
return strategies
|
|
181
187
|
|
|
@@ -194,18 +200,22 @@ class HelenaHooks(ClaudeAgentHooks):
|
|
|
194
200
|
patterns = []
|
|
195
201
|
for h in memories.heuristics:
|
|
196
202
|
if any(kw in h.condition.lower() for kw in ["access", "aria", "keyboard"]):
|
|
197
|
-
patterns.append(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
203
|
+
patterns.append(
|
|
204
|
+
{
|
|
205
|
+
"condition": h.condition,
|
|
206
|
+
"strategy": h.strategy,
|
|
207
|
+
"confidence": h.confidence,
|
|
208
|
+
}
|
|
209
|
+
)
|
|
202
210
|
|
|
203
211
|
for dk in memories.domain_knowledge:
|
|
204
212
|
if dk.domain == "accessibility_testing":
|
|
205
|
-
patterns.append(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
patterns.append(
|
|
214
|
+
{
|
|
215
|
+
"fact": dk.fact,
|
|
216
|
+
"source": dk.source,
|
|
217
|
+
}
|
|
218
|
+
)
|
|
209
219
|
|
|
210
220
|
return patterns
|
|
211
221
|
|
alma/integration/victor.py
CHANGED
|
@@ -14,18 +14,18 @@ This module provides Victor-specific memory categories, prompts, and utilities.
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
-
from typing import Optional, Dict, Any, List
|
|
18
17
|
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
19
|
|
|
20
20
|
from alma.core import ALMA
|
|
21
|
-
from alma.types import MemorySlice
|
|
22
21
|
from alma.harness.domains import CodingDomain
|
|
23
22
|
from alma.integration.claude_agents import (
|
|
23
|
+
AgentType,
|
|
24
24
|
ClaudeAgentHooks,
|
|
25
25
|
TaskContext,
|
|
26
26
|
TaskOutcome,
|
|
27
|
-
AgentType,
|
|
28
27
|
)
|
|
28
|
+
from alma.types import MemorySlice
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
@@ -55,6 +55,7 @@ class APITestContext(TaskContext):
|
|
|
55
55
|
|
|
56
56
|
Extends TaskContext with API/backend testing-specific fields.
|
|
57
57
|
"""
|
|
58
|
+
|
|
58
59
|
endpoint: Optional[str] = None
|
|
59
60
|
method: str = "GET"
|
|
60
61
|
expected_status: Optional[int] = None
|
|
@@ -91,6 +92,7 @@ class APITestOutcome(TaskOutcome):
|
|
|
91
92
|
|
|
92
93
|
Extends TaskOutcome with API/backend testing-specific results.
|
|
93
94
|
"""
|
|
95
|
+
|
|
94
96
|
response_status: Optional[int] = None
|
|
95
97
|
response_time_ms: Optional[int] = None
|
|
96
98
|
response_body: Optional[Dict[str, Any]] = None
|
|
@@ -153,12 +155,14 @@ class VictorHooks(ClaudeAgentHooks):
|
|
|
153
155
|
patterns = []
|
|
154
156
|
for h in memories.heuristics:
|
|
155
157
|
if "api" in h.condition.lower() or "endpoint" in h.condition.lower():
|
|
156
|
-
patterns.append(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
patterns.append(
|
|
159
|
+
{
|
|
160
|
+
"pattern": h.strategy,
|
|
161
|
+
"condition": h.condition,
|
|
162
|
+
"confidence": h.confidence,
|
|
163
|
+
"occurrences": h.occurrence_count,
|
|
164
|
+
}
|
|
165
|
+
)
|
|
162
166
|
|
|
163
167
|
return patterns
|
|
164
168
|
|
|
@@ -176,12 +180,16 @@ class VictorHooks(ClaudeAgentHooks):
|
|
|
176
180
|
|
|
177
181
|
patterns = []
|
|
178
182
|
for h in memories.heuristics:
|
|
179
|
-
if any(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
if any(
|
|
184
|
+
kw in h.condition.lower() for kw in ["error", "exception", "validation"]
|
|
185
|
+
):
|
|
186
|
+
patterns.append(
|
|
187
|
+
{
|
|
188
|
+
"condition": h.condition,
|
|
189
|
+
"strategy": h.strategy,
|
|
190
|
+
"confidence": h.confidence,
|
|
191
|
+
}
|
|
192
|
+
)
|
|
185
193
|
|
|
186
194
|
return patterns
|
|
187
195
|
|
|
@@ -199,19 +207,26 @@ class VictorHooks(ClaudeAgentHooks):
|
|
|
199
207
|
|
|
200
208
|
strategies = []
|
|
201
209
|
for h in memories.heuristics:
|
|
202
|
-
if any(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
if any(
|
|
211
|
+
kw in h.condition.lower()
|
|
212
|
+
for kw in ["performance", "cache", "query", "slow"]
|
|
213
|
+
):
|
|
214
|
+
strategies.append(
|
|
215
|
+
{
|
|
216
|
+
"condition": h.condition,
|
|
217
|
+
"strategy": h.strategy,
|
|
218
|
+
"confidence": h.confidence,
|
|
219
|
+
}
|
|
220
|
+
)
|
|
208
221
|
|
|
209
222
|
for dk in memories.domain_knowledge:
|
|
210
223
|
if dk.domain in ["performance_optimization", "caching_strategies"]:
|
|
211
|
-
strategies.append(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
224
|
+
strategies.append(
|
|
225
|
+
{
|
|
226
|
+
"fact": dk.fact,
|
|
227
|
+
"source": dk.source,
|
|
228
|
+
}
|
|
229
|
+
)
|
|
215
230
|
|
|
216
231
|
return strategies
|
|
217
232
|
|
|
@@ -229,12 +244,17 @@ class VictorHooks(ClaudeAgentHooks):
|
|
|
229
244
|
|
|
230
245
|
patterns = []
|
|
231
246
|
for h in memories.heuristics:
|
|
232
|
-
if any(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
247
|
+
if any(
|
|
248
|
+
kw in h.condition.lower()
|
|
249
|
+
for kw in ["auth", "token", "permission", "jwt"]
|
|
250
|
+
):
|
|
251
|
+
patterns.append(
|
|
252
|
+
{
|
|
253
|
+
"condition": h.condition,
|
|
254
|
+
"strategy": h.strategy,
|
|
255
|
+
"confidence": h.confidence,
|
|
256
|
+
}
|
|
257
|
+
)
|
|
238
258
|
|
|
239
259
|
return patterns
|
|
240
260
|
|
|
@@ -318,11 +338,15 @@ class VictorHooks(ClaudeAgentHooks):
|
|
|
318
338
|
sections.append(f"- **Task Type**: {test_context.task_type}")
|
|
319
339
|
|
|
320
340
|
if test_context.endpoint:
|
|
321
|
-
sections.append(
|
|
341
|
+
sections.append(
|
|
342
|
+
f"- **Endpoint**: {test_context.method} {test_context.endpoint}"
|
|
343
|
+
)
|
|
322
344
|
if test_context.expected_status:
|
|
323
345
|
sections.append(f"- **Expected Status**: {test_context.expected_status}")
|
|
324
346
|
if test_context.request_body:
|
|
325
|
-
sections.append(
|
|
347
|
+
sections.append(
|
|
348
|
+
f"- **Request Body**: {len(test_context.request_body)} fields"
|
|
349
|
+
)
|
|
326
350
|
|
|
327
351
|
if test_context.is_auth_test:
|
|
328
352
|
sections.append("- **Focus**: Authentication/Authorization")
|