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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.agentic.yml +2 -0
  3. data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
  4. data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
  5. data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
  6. data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
  7. data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
  8. data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
  9. data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
  10. data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
  11. data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
  12. data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
  13. data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
  14. data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
  15. data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
  16. data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
  17. data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
  18. data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
  19. data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
  20. data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
  21. data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
  22. data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
  23. data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
  24. data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
  25. data/.architecture/decisions/capability_tools_distinction.md +150 -0
  26. data/.architecture/decisions/cli_command_structure.md +61 -0
  27. data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
  28. data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
  29. data/.architecture/members.yml +187 -0
  30. data/.architecture/planning/self_implementation_exercise.md +295 -0
  31. data/.architecture/planning/session_compaction_rule.md +43 -0
  32. data/.architecture/planning/streaming_observability_feature.md +223 -0
  33. data/.architecture/principles.md +151 -0
  34. data/.architecture/recalibration/0-2-0.md +92 -0
  35. data/.architecture/recalibration/agent_self_assembly.md +238 -0
  36. data/.architecture/recalibration/cli_command_structure.md +91 -0
  37. data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
  38. data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
  39. data/.architecture/recalibration_process.md +127 -0
  40. data/.architecture/reviews/0-2-0.md +181 -0
  41. data/.architecture/reviews/cli_command_duplication.md +98 -0
  42. data/.architecture/templates/adr.md +105 -0
  43. data/.architecture/templates/implementation_roadmap.md +125 -0
  44. data/.architecture/templates/progress_tracking.md +89 -0
  45. data/.architecture/templates/recalibration_plan.md +70 -0
  46. data/.architecture/templates/version_comparison.md +124 -0
  47. data/.claude/settings.local.json +13 -0
  48. data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
  49. data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
  50. data/.claude-sessions/architecture-governance-implementation.md +37 -0
  51. data/.claude-sessions/architecture-review-session.md +27 -0
  52. data/ArchitecturalFeatureBuilder.md +136 -0
  53. data/ArchitectureConsiderations.md +229 -0
  54. data/CHANGELOG.md +57 -2
  55. data/CLAUDE.md +111 -0
  56. data/CONTRIBUTING.md +286 -0
  57. data/MAINTAINING.md +301 -0
  58. data/README.md +582 -28
  59. data/docs/agent_capabilities_api.md +259 -0
  60. data/docs/artifact_extension_points.md +757 -0
  61. data/docs/artifact_generation_architecture.md +323 -0
  62. data/docs/artifact_implementation_plan.md +596 -0
  63. data/docs/artifact_integration_points.md +345 -0
  64. data/docs/artifact_verification_strategies.md +581 -0
  65. data/docs/streaming_observability_architecture.md +510 -0
  66. data/exe/agentic +6 -1
  67. data/lefthook.yml +5 -0
  68. data/lib/agentic/adaptation_engine.rb +124 -0
  69. data/lib/agentic/agent.rb +181 -4
  70. data/lib/agentic/agent_assembly_engine.rb +442 -0
  71. data/lib/agentic/agent_capability_registry.rb +260 -0
  72. data/lib/agentic/agent_config.rb +63 -0
  73. data/lib/agentic/agent_specification.rb +46 -0
  74. data/lib/agentic/capabilities/examples.rb +530 -0
  75. data/lib/agentic/capabilities.rb +14 -0
  76. data/lib/agentic/capability_provider.rb +146 -0
  77. data/lib/agentic/capability_specification.rb +118 -0
  78. data/lib/agentic/cli/agent.rb +31 -0
  79. data/lib/agentic/cli/capabilities.rb +191 -0
  80. data/lib/agentic/cli/config.rb +134 -0
  81. data/lib/agentic/cli/execution_observer.rb +796 -0
  82. data/lib/agentic/cli.rb +1068 -0
  83. data/lib/agentic/default_agent_provider.rb +35 -0
  84. data/lib/agentic/errors/llm_error.rb +184 -0
  85. data/lib/agentic/execution_plan.rb +53 -0
  86. data/lib/agentic/execution_result.rb +91 -0
  87. data/lib/agentic/expected_answer_format.rb +46 -0
  88. data/lib/agentic/extension/domain_adapter.rb +109 -0
  89. data/lib/agentic/extension/plugin_manager.rb +163 -0
  90. data/lib/agentic/extension/protocol_handler.rb +116 -0
  91. data/lib/agentic/extension.rb +45 -0
  92. data/lib/agentic/factory_methods.rb +9 -1
  93. data/lib/agentic/generation_stats.rb +61 -0
  94. data/lib/agentic/learning/README.md +84 -0
  95. data/lib/agentic/learning/capability_optimizer.rb +613 -0
  96. data/lib/agentic/learning/execution_history_store.rb +251 -0
  97. data/lib/agentic/learning/pattern_recognizer.rb +500 -0
  98. data/lib/agentic/learning/strategy_optimizer.rb +706 -0
  99. data/lib/agentic/learning.rb +131 -0
  100. data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
  101. data/lib/agentic/llm_client.rb +215 -15
  102. data/lib/agentic/llm_config.rb +65 -1
  103. data/lib/agentic/llm_response.rb +163 -0
  104. data/lib/agentic/logger.rb +1 -1
  105. data/lib/agentic/observable.rb +51 -0
  106. data/lib/agentic/persistent_agent_store.rb +385 -0
  107. data/lib/agentic/plan_execution_result.rb +129 -0
  108. data/lib/agentic/plan_orchestrator.rb +464 -0
  109. data/lib/agentic/plan_orchestrator_config.rb +57 -0
  110. data/lib/agentic/retry_config.rb +63 -0
  111. data/lib/agentic/retry_handler.rb +125 -0
  112. data/lib/agentic/structured_outputs.rb +1 -1
  113. data/lib/agentic/task.rb +193 -0
  114. data/lib/agentic/task_definition.rb +39 -0
  115. data/lib/agentic/task_execution_result.rb +92 -0
  116. data/lib/agentic/task_failure.rb +66 -0
  117. data/lib/agentic/task_output_schemas.rb +112 -0
  118. data/lib/agentic/task_planner.rb +54 -19
  119. data/lib/agentic/task_result.rb +48 -0
  120. data/lib/agentic/ui.rb +244 -0
  121. data/lib/agentic/verification/critic_framework.rb +116 -0
  122. data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
  123. data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
  124. data/lib/agentic/verification/verification_hub.rb +62 -0
  125. data/lib/agentic/verification/verification_result.rb +50 -0
  126. data/lib/agentic/verification/verification_strategy.rb +26 -0
  127. data/lib/agentic/version.rb +1 -1
  128. data/lib/agentic.rb +74 -2
  129. data/plugins/README.md +41 -0
  130. 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