agentic 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.agentic.yml +2 -0
- data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
- data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
- data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
- data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
- data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
- data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
- data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
- data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
- data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
- data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
- data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
- data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
- data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
- data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
- data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
- data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
- data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
- data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
- data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
- data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
- data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
- data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
- data/.architecture/decisions/capability_tools_distinction.md +150 -0
- data/.architecture/decisions/cli_command_structure.md +61 -0
- data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
- data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
- data/.architecture/members.yml +187 -0
- data/.architecture/planning/self_implementation_exercise.md +295 -0
- data/.architecture/planning/session_compaction_rule.md +43 -0
- data/.architecture/planning/streaming_observability_feature.md +223 -0
- data/.architecture/principles.md +151 -0
- data/.architecture/recalibration/0-2-0.md +92 -0
- data/.architecture/recalibration/agent_self_assembly.md +238 -0
- data/.architecture/recalibration/cli_command_structure.md +91 -0
- data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
- data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
- data/.architecture/recalibration_process.md +127 -0
- data/.architecture/reviews/0-2-0.md +181 -0
- data/.architecture/reviews/cli_command_duplication.md +98 -0
- data/.architecture/templates/adr.md +105 -0
- data/.architecture/templates/implementation_roadmap.md +125 -0
- data/.architecture/templates/progress_tracking.md +89 -0
- data/.architecture/templates/recalibration_plan.md +70 -0
- data/.architecture/templates/version_comparison.md +124 -0
- data/.claude/settings.local.json +13 -0
- data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
- data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
- data/.claude-sessions/architecture-governance-implementation.md +37 -0
- data/.claude-sessions/architecture-review-session.md +27 -0
- data/ArchitecturalFeatureBuilder.md +136 -0
- data/ArchitectureConsiderations.md +229 -0
- data/CHANGELOG.md +57 -2
- data/CLAUDE.md +111 -0
- data/CONTRIBUTING.md +286 -0
- data/MAINTAINING.md +301 -0
- data/README.md +582 -28
- data/docs/agent_capabilities_api.md +259 -0
- data/docs/artifact_extension_points.md +757 -0
- data/docs/artifact_generation_architecture.md +323 -0
- data/docs/artifact_implementation_plan.md +596 -0
- data/docs/artifact_integration_points.md +345 -0
- data/docs/artifact_verification_strategies.md +581 -0
- data/docs/streaming_observability_architecture.md +510 -0
- data/exe/agentic +6 -1
- data/lefthook.yml +5 -0
- data/lib/agentic/adaptation_engine.rb +124 -0
- data/lib/agentic/agent.rb +181 -4
- data/lib/agentic/agent_assembly_engine.rb +442 -0
- data/lib/agentic/agent_capability_registry.rb +260 -0
- data/lib/agentic/agent_config.rb +63 -0
- data/lib/agentic/agent_specification.rb +46 -0
- data/lib/agentic/capabilities/examples.rb +530 -0
- data/lib/agentic/capabilities.rb +14 -0
- data/lib/agentic/capability_provider.rb +146 -0
- data/lib/agentic/capability_specification.rb +118 -0
- data/lib/agentic/cli/agent.rb +31 -0
- data/lib/agentic/cli/capabilities.rb +191 -0
- data/lib/agentic/cli/config.rb +134 -0
- data/lib/agentic/cli/execution_observer.rb +796 -0
- data/lib/agentic/cli.rb +1068 -0
- data/lib/agentic/default_agent_provider.rb +35 -0
- data/lib/agentic/errors/llm_error.rb +184 -0
- data/lib/agentic/execution_plan.rb +53 -0
- data/lib/agentic/execution_result.rb +91 -0
- data/lib/agentic/expected_answer_format.rb +46 -0
- data/lib/agentic/extension/domain_adapter.rb +109 -0
- data/lib/agentic/extension/plugin_manager.rb +163 -0
- data/lib/agentic/extension/protocol_handler.rb +116 -0
- data/lib/agentic/extension.rb +45 -0
- data/lib/agentic/factory_methods.rb +9 -1
- data/lib/agentic/generation_stats.rb +61 -0
- data/lib/agentic/learning/README.md +84 -0
- data/lib/agentic/learning/capability_optimizer.rb +613 -0
- data/lib/agentic/learning/execution_history_store.rb +251 -0
- data/lib/agentic/learning/pattern_recognizer.rb +500 -0
- data/lib/agentic/learning/strategy_optimizer.rb +706 -0
- data/lib/agentic/learning.rb +131 -0
- data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
- data/lib/agentic/llm_client.rb +215 -15
- data/lib/agentic/llm_config.rb +65 -1
- data/lib/agentic/llm_response.rb +163 -0
- data/lib/agentic/logger.rb +1 -1
- data/lib/agentic/observable.rb +51 -0
- data/lib/agentic/persistent_agent_store.rb +385 -0
- data/lib/agentic/plan_execution_result.rb +129 -0
- data/lib/agentic/plan_orchestrator.rb +464 -0
- data/lib/agentic/plan_orchestrator_config.rb +57 -0
- data/lib/agentic/retry_config.rb +63 -0
- data/lib/agentic/retry_handler.rb +125 -0
- data/lib/agentic/structured_outputs.rb +1 -1
- data/lib/agentic/task.rb +193 -0
- data/lib/agentic/task_definition.rb +39 -0
- data/lib/agentic/task_execution_result.rb +92 -0
- data/lib/agentic/task_failure.rb +66 -0
- data/lib/agentic/task_output_schemas.rb +112 -0
- data/lib/agentic/task_planner.rb +54 -19
- data/lib/agentic/task_result.rb +48 -0
- data/lib/agentic/ui.rb +244 -0
- data/lib/agentic/verification/critic_framework.rb +116 -0
- data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
- data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
- data/lib/agentic/verification/verification_hub.rb +62 -0
- data/lib/agentic/verification/verification_result.rb +50 -0
- data/lib/agentic/verification/verification_strategy.rb +26 -0
- data/lib/agentic/version.rb +1 -1
- data/lib/agentic.rb +74 -2
- data/plugins/README.md +41 -0
- metadata +245 -6
@@ -0,0 +1,251 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
require "json"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
module Agentic
|
8
|
+
module Learning
|
9
|
+
# ExecutionHistoryStore is responsible for capturing, storing, and retrieving
|
10
|
+
# execution metrics and performance data for agent tasks and plans.
|
11
|
+
#
|
12
|
+
# @example Recording a task execution
|
13
|
+
# history_store = Agentic::Learning::ExecutionHistoryStore.new
|
14
|
+
# history_store.record_execution(
|
15
|
+
# task_id: task.id,
|
16
|
+
# agent_type: "research_agent",
|
17
|
+
# duration_ms: 1200,
|
18
|
+
# success: true,
|
19
|
+
# metrics: { tokens_used: 1500, prompt_tokens: 500 }
|
20
|
+
# )
|
21
|
+
#
|
22
|
+
# @example Retrieving execution history for a specific agent type
|
23
|
+
# records = history_store.get_history(agent_type: "research_agent")
|
24
|
+
#
|
25
|
+
class ExecutionHistoryStore
|
26
|
+
# Initialize a new ExecutionHistoryStore with the given options
|
27
|
+
#
|
28
|
+
# @param options [Hash] Configuration options
|
29
|
+
# @option options [Logger] :logger Custom logger (defaults to Agentic.logger)
|
30
|
+
# @option options [String] :storage_path Directory to store execution history (defaults to ~/.agentic/history)
|
31
|
+
# @option options [Integer] :retention_days Number of days to retain history (defaults to 30)
|
32
|
+
# @option options [Boolean] :anonymize Whether to anonymize data (defaults to true)
|
33
|
+
def initialize(options = {})
|
34
|
+
@logger = options[:logger] || Agentic.logger
|
35
|
+
@storage_path = options[:storage_path] || default_storage_path
|
36
|
+
@retention_days = options[:retention_days] || 30
|
37
|
+
@anonymize = options.fetch(:anonymize, true)
|
38
|
+
@memory_cache = []
|
39
|
+
@cache_size_limit = options[:cache_size_limit] || 1000
|
40
|
+
|
41
|
+
ensure_storage_path_exists
|
42
|
+
end
|
43
|
+
|
44
|
+
# Record a new execution in the history store
|
45
|
+
#
|
46
|
+
# @param execution_data [Hash] Execution data to record
|
47
|
+
# @option execution_data [String] :task_id ID of the executed task
|
48
|
+
# @option execution_data [String] :plan_id ID of the plan (optional)
|
49
|
+
# @option execution_data [String] :agent_type Type of agent used
|
50
|
+
# @option execution_data [Integer] :duration_ms Execution time in milliseconds
|
51
|
+
# @option execution_data [Boolean] :success Whether execution succeeded
|
52
|
+
# @option execution_data [Hash] :metrics Additional metrics
|
53
|
+
# @option execution_data [Hash] :context Additional context information
|
54
|
+
# @return [Boolean] true if successfully recorded
|
55
|
+
def record_execution(execution_data)
|
56
|
+
record = build_record(execution_data)
|
57
|
+
|
58
|
+
# Add to memory cache
|
59
|
+
@memory_cache << record
|
60
|
+
@memory_cache.shift if @memory_cache.size > @cache_size_limit
|
61
|
+
|
62
|
+
# Persist to storage
|
63
|
+
save_record(record)
|
64
|
+
|
65
|
+
# Clean up old records periodically
|
66
|
+
cleanup_old_records if rand < 0.05 # 5% chance to trigger cleanup
|
67
|
+
|
68
|
+
true
|
69
|
+
rescue => e
|
70
|
+
@logger.error("Failed to record execution history: #{e.message}")
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Retrieve execution history based on filter criteria
|
75
|
+
#
|
76
|
+
# @param filters [Hash] Filter criteria
|
77
|
+
# @option filters [String] :task_id Filter by task ID
|
78
|
+
# @option filters [String] :plan_id Filter by plan ID
|
79
|
+
# @option filters [String] :agent_type Filter by agent type
|
80
|
+
# @option filters [Boolean] :success Filter by success status
|
81
|
+
# @option filters [Time] :start_time Filter by start time
|
82
|
+
# @option filters [Time] :end_time Filter by end time
|
83
|
+
# @option filters [Integer] :limit Maximum number of records to return
|
84
|
+
# @return [Array<Hash>] Array of matching history records
|
85
|
+
def get_history(filters = {})
|
86
|
+
records = load_records(filters)
|
87
|
+
limit = filters[:limit] || records.size
|
88
|
+
records.first(limit)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Retrieve aggregated metrics from execution history
|
92
|
+
#
|
93
|
+
# @param metric_name [Symbol] Name of the metric to aggregate
|
94
|
+
# @param filters [Hash] Filter criteria
|
95
|
+
# @option filters [String] :agent_type Filter by agent type
|
96
|
+
# @option filters [Boolean] :success Filter by success status
|
97
|
+
# @option filters [Time] :start_time Filter by start time
|
98
|
+
# @option filters [Time] :end_time Filter by end time
|
99
|
+
# @param aggregation [Symbol] Aggregation method (:avg, :sum, :min, :max)
|
100
|
+
# @return [Float, Integer] Aggregated metric value
|
101
|
+
def get_metric(metric_name, filters = {}, aggregation = :avg)
|
102
|
+
records = get_history(filters)
|
103
|
+
values = records.map { |r| r.dig(:metrics, metric_name.to_s) }.compact
|
104
|
+
|
105
|
+
return nil if values.empty?
|
106
|
+
|
107
|
+
case aggregation
|
108
|
+
when :avg then values.sum / values.size.to_f
|
109
|
+
when :sum then values.sum
|
110
|
+
when :min then values.min
|
111
|
+
when :max then values.max
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Delete all history older than retention_days
|
116
|
+
#
|
117
|
+
# @return [Integer] Number of records deleted
|
118
|
+
def cleanup_old_records
|
119
|
+
cutoff_date = Date.today - @retention_days
|
120
|
+
count = 0
|
121
|
+
|
122
|
+
Dir.glob(File.join(@storage_path, "**/*.json")).each do |file|
|
123
|
+
date_str = File.basename(file, ".json")
|
124
|
+
date = begin
|
125
|
+
Date.parse(date_str)
|
126
|
+
rescue
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
if date && date < cutoff_date
|
130
|
+
File.delete(file)
|
131
|
+
count += 1
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
@logger.info("Cleaned up #{count} old history records")
|
136
|
+
count
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def default_storage_path
|
142
|
+
path = File.join(Dir.home, ".agentic", "history")
|
143
|
+
FileUtils.mkdir_p(path) unless Dir.exist?(path)
|
144
|
+
path
|
145
|
+
end
|
146
|
+
|
147
|
+
def ensure_storage_path_exists
|
148
|
+
FileUtils.mkdir_p(@storage_path) unless Dir.exist?(@storage_path)
|
149
|
+
end
|
150
|
+
|
151
|
+
def build_record(execution_data)
|
152
|
+
timestamp = Time.now
|
153
|
+
{
|
154
|
+
id: SecureRandom.uuid,
|
155
|
+
timestamp: timestamp.iso8601,
|
156
|
+
task_id: execution_data[:task_id],
|
157
|
+
plan_id: execution_data[:plan_id],
|
158
|
+
agent_type: execution_data[:agent_type],
|
159
|
+
duration_ms: execution_data[:duration_ms],
|
160
|
+
success: execution_data[:success],
|
161
|
+
metrics: execution_data[:metrics] || {},
|
162
|
+
context: @anonymize ? anonymize_context(execution_data[:context]) : execution_data[:context]
|
163
|
+
}.compact
|
164
|
+
end
|
165
|
+
|
166
|
+
def anonymize_context(context)
|
167
|
+
return nil unless context
|
168
|
+
|
169
|
+
# Implement context anonymization logic
|
170
|
+
# For example, replace actual content with length information
|
171
|
+
anonymized = {}
|
172
|
+
context.each do |key, value|
|
173
|
+
anonymized[key] = if value.is_a?(String)
|
174
|
+
"#{value.length} chars"
|
175
|
+
elsif value.is_a?(Hash)
|
176
|
+
anonymize_context(value)
|
177
|
+
elsif value.is_a?(Array)
|
178
|
+
"#{value.length} items"
|
179
|
+
else
|
180
|
+
value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
anonymized
|
185
|
+
end
|
186
|
+
|
187
|
+
def save_record(record)
|
188
|
+
date_str = Date.parse(record[:timestamp]).iso8601
|
189
|
+
dir_path = File.join(@storage_path, date_str)
|
190
|
+
FileUtils.mkdir_p(dir_path) unless Dir.exist?(dir_path)
|
191
|
+
|
192
|
+
file_path = File.join(dir_path, "#{record[:id]}.json")
|
193
|
+
File.write(file_path, JSON.pretty_generate(record))
|
194
|
+
end
|
195
|
+
|
196
|
+
def load_records(filters)
|
197
|
+
# Apply memory cache first for recent records
|
198
|
+
records = @memory_cache.dup
|
199
|
+
|
200
|
+
# Determine date range for file loading
|
201
|
+
end_date = filters[:end_time] ? Date.parse(filters[:end_time].iso8601) : Date.today
|
202
|
+
start_date = if filters[:start_time]
|
203
|
+
Date.parse(filters[:start_time].iso8601)
|
204
|
+
else
|
205
|
+
end_date - (@retention_days / 2)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Load files within date range
|
209
|
+
(start_date..end_date).each do |date|
|
210
|
+
date_str = date.iso8601
|
211
|
+
dir_path = File.join(@storage_path, date_str)
|
212
|
+
next unless Dir.exist?(dir_path)
|
213
|
+
|
214
|
+
Dir.glob(File.join(dir_path, "*.json")).each do |file|
|
215
|
+
record = JSON.parse(File.read(file), symbolize_names: true)
|
216
|
+
records << record
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Apply filters
|
221
|
+
records = filter_records(records, filters)
|
222
|
+
|
223
|
+
# Sort by timestamp descending
|
224
|
+
records.sort_by { |r| r[:timestamp] }.reverse
|
225
|
+
end
|
226
|
+
|
227
|
+
def filter_records(records, filters)
|
228
|
+
records.select do |record|
|
229
|
+
matches = true
|
230
|
+
|
231
|
+
matches = false if filters[:task_id] && record[:task_id] != filters[:task_id]
|
232
|
+
matches = false if filters[:plan_id] && record[:plan_id] != filters[:plan_id]
|
233
|
+
matches = false if filters[:agent_type] && record[:agent_type] != filters[:agent_type]
|
234
|
+
matches = false if !filters[:success].nil? && record[:success] != filters[:success]
|
235
|
+
|
236
|
+
if filters[:start_time]
|
237
|
+
record_time = Time.parse(record[:timestamp])
|
238
|
+
matches = false if record_time < filters[:start_time]
|
239
|
+
end
|
240
|
+
|
241
|
+
if filters[:end_time]
|
242
|
+
record_time = Time.parse(record[:timestamp])
|
243
|
+
matches = false if record_time > filters[:end_time]
|
244
|
+
end
|
245
|
+
|
246
|
+
matches
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|