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,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Configuration object for the RetryHandler
5
+ class RetryConfig
6
+ # @return [Integer] The maximum number of retry attempts
7
+ attr_accessor :max_retries
8
+
9
+ # @return [Array<Class, String>] List of retryable error types/names
10
+ attr_accessor :retryable_errors
11
+
12
+ # @return [Symbol] The backoff strategy to use
13
+ attr_accessor :backoff_strategy
14
+
15
+ # @return [Hash] Options for the backoff strategy
16
+ attr_accessor :backoff_options
17
+
18
+ # @return [Proc, nil] Optional block to run before each retry
19
+ attr_accessor :before_retry
20
+
21
+ # @return [Proc, nil] Optional block to run after each retry
22
+ attr_accessor :after_retry
23
+
24
+ # Initializes a new retry configuration
25
+ # @param max_retries [Integer] The maximum number of retry attempts
26
+ # @param retryable_errors [Array<Class, String>] List of retryable error types/names
27
+ # @param backoff_strategy [Symbol] The backoff strategy (:constant, :linear, :exponential)
28
+ # @param backoff_options [Hash] Options for the backoff strategy
29
+ # @param before_retry [Proc, nil] Optional block to run before each retry
30
+ # @param after_retry [Proc, nil] Optional block to run after each retry
31
+ def initialize(
32
+ max_retries: 3,
33
+ retryable_errors: [Errors::LlmTimeoutError, Errors::LlmRateLimitError, Errors::LlmServerError, Errors::LlmNetworkError],
34
+ backoff_strategy: :exponential,
35
+ backoff_options: {},
36
+ before_retry: nil,
37
+ after_retry: nil
38
+ )
39
+ @max_retries = max_retries
40
+ @retryable_errors = retryable_errors
41
+ @backoff_strategy = backoff_strategy
42
+ @backoff_options = {
43
+ base_delay: 1.0,
44
+ jitter_factor: 0.25
45
+ }.merge(backoff_options)
46
+ @before_retry = before_retry
47
+ @after_retry = after_retry
48
+ end
49
+
50
+ # Creates a RetryHandler from this configuration
51
+ # @return [RetryHandler] A new retry handler
52
+ def to_handler
53
+ RetryHandler.new(
54
+ max_retries: @max_retries,
55
+ retryable_errors: @retryable_errors,
56
+ backoff_strategy: @backoff_strategy,
57
+ backoff_options: @backoff_options,
58
+ before_retry: @before_retry,
59
+ after_retry: @after_retry
60
+ )
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Handles retrying operations with configurable backoff strategies
5
+ class RetryHandler
6
+ # @return [Integer] The maximum number of retry attempts
7
+ attr_reader :max_retries
8
+
9
+ # @return [Array<Class, String>] List of retryable error types/names
10
+ attr_reader :retryable_errors
11
+
12
+ # @return [Symbol] The backoff strategy to use
13
+ attr_reader :backoff_strategy
14
+
15
+ # @return [Proc, nil] Optional block to run before each retry
16
+ attr_reader :before_retry
17
+
18
+ # @return [Proc, nil] Optional block to run after each retry
19
+ attr_reader :after_retry
20
+
21
+ # Initializes a new RetryHandler
22
+ # @param max_retries [Integer] The maximum number of retry attempts
23
+ # @param retryable_errors [Array<Class, String>] List of retryable error types/names
24
+ # @param backoff_strategy [Symbol] The backoff strategy (:constant, :linear, :exponential)
25
+ # @param backoff_options [Hash] Options for the backoff strategy
26
+ # @param before_retry [Proc, nil] Optional block to run before each retry
27
+ # @param after_retry [Proc, nil] Optional block to run after each retry
28
+ # @option backoff_options [Float] :base_delay The base delay in seconds
29
+ # @option backoff_options [Float] :jitter_factor The jitter factor (0.0-1.0)
30
+ def initialize(
31
+ max_retries: 3,
32
+ retryable_errors: [Errors::LlmTimeoutError, Errors::LlmRateLimitError, Errors::LlmServerError, Errors::LlmNetworkError],
33
+ backoff_strategy: :exponential,
34
+ backoff_options: {},
35
+ before_retry: nil,
36
+ after_retry: nil
37
+ )
38
+ @max_retries = max_retries
39
+ @retryable_errors = retryable_errors
40
+ @backoff_strategy = backoff_strategy
41
+ @backoff_options = {
42
+ base_delay: 1.0,
43
+ jitter_factor: 0.25
44
+ }.merge(backoff_options)
45
+ @before_retry = before_retry
46
+ @after_retry = after_retry
47
+ end
48
+
49
+ # Executes the given block with retries
50
+ # @param block [Proc] The block to execute with retries
51
+ # @return [Object] The return value of the block
52
+ # @raise [StandardError] If the block failed after all retries
53
+ def with_retry(&block)
54
+ attempt = 0
55
+
56
+ begin
57
+ attempt += 1
58
+ block.call
59
+ rescue => e
60
+ error = e.is_a?(Errors::LlmError) ? e : Errors::LlmError.new(e.message, context: {original_error: e.class.name})
61
+
62
+ if retryable?(error) && attempt <= max_retries
63
+ delay = calculate_backoff_delay(attempt)
64
+ Agentic.logger.info("Retry #{attempt}/#{max_retries} for error: #{error.message}. Waiting #{delay.round(2)}s before retrying.")
65
+
66
+ @before_retry&.call(attempt: attempt, error: error, delay: delay)
67
+ sleep(delay)
68
+ @after_retry&.call(attempt: attempt, error: error, delay: delay)
69
+
70
+ retry
71
+ else
72
+ if attempt > max_retries
73
+ Agentic.logger.error("Max retries (#{max_retries}) exceeded for error: #{error.message}")
74
+ else
75
+ Agentic.logger.error("Non-retryable error: #{error.message}")
76
+ end
77
+
78
+ raise error
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # Determines if an error is retryable
86
+ # @param error [StandardError] The error to check
87
+ # @return [Boolean] True if the error is retryable
88
+ def retryable?(error)
89
+ return true if error.respond_to?(:retryable?) && error.retryable?
90
+
91
+ @retryable_errors.any? do |retryable|
92
+ if retryable.is_a?(Class)
93
+ error.is_a?(retryable)
94
+ else
95
+ error.instance_of?(retryable).to_s
96
+ end
97
+ end
98
+ end
99
+
100
+ # Calculates the backoff delay for a given attempt
101
+ # @param attempt [Integer] The current attempt number (1-based)
102
+ # @return [Float] The delay in seconds
103
+ def calculate_backoff_delay(attempt)
104
+ base_delay = @backoff_options[:base_delay]
105
+
106
+ delay = case @backoff_strategy
107
+ when :constant
108
+ base_delay
109
+ when :linear
110
+ base_delay * attempt
111
+ when :exponential
112
+ base_delay * (2**(attempt - 1))
113
+ else
114
+ base_delay
115
+ end
116
+
117
+ if @backoff_options[:jitter_factor] && @backoff_options[:jitter_factor] > 0
118
+ jitter = rand(-delay * @backoff_options[:jitter_factor]..delay * @backoff_options[:jitter_factor])
119
+ delay += jitter
120
+ end
121
+
122
+ [delay, 0].max # Ensure positive delay
123
+ end
124
+ end
125
+ end
@@ -117,7 +117,7 @@ module Agentic
117
117
  max_child_depth = schema[:properties].values.map do |prop|
118
118
  calculate_max_depth(prop, current_depth + 1)
119
119
  end.max
120
- [current_depth, max_child_depth].max
120
+ max_child_depth ? [current_depth, max_child_depth].max : current_depth
121
121
  end
122
122
  end
123
123
  end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "json"
5
+ require_relative "observable"
6
+ require_relative "task_definition"
7
+ require_relative "agent_specification"
8
+
9
+ module Agentic
10
+ # Represents an individual task to be executed by an agent
11
+ # @attr_reader [String] id Unique identifier for the task
12
+ # @attr_reader [String] description Human-readable description of the task
13
+ # @attr_reader [Hash] agent_spec Requirements for the agent that will execute this task
14
+ # @attr_reader [Hash] input Input data for the task
15
+ # @attr_reader [Hash, nil] output Output produced by the task, nil if not yet executed
16
+ # @attr_reader [Symbol] status Current status of the task (:pending, :in_progress, :completed, :failed)
17
+ # @attr_reader [TaskFailure, nil] failure Failure information if the task failed, nil otherwise
18
+ # @attr_reader [Boolean, nil] ready_to_execute Flag indicating if the task is ready to be executed
19
+ # @attr_accessor [Integer, nil] retry_count Number of times the task has been retried
20
+ # @attr_accessor [Symbol, nil] output_schema_name Name of the output schema to use
21
+ class Task
22
+ include Agentic::Observable
23
+
24
+ attr_reader :id, :description, :agent_spec, :input, :output, :status, :failure, :ready_to_execute
25
+ attr_accessor :retry_count, :output_schema_name
26
+
27
+ # Initializes a new task
28
+ # @param description [String] Human-readable description of the task
29
+ # @param agent_spec [Hash, AgentSpecification] Requirements for the agent that will execute this task
30
+ # @param input [Hash] Input data for the task
31
+ # @param output_schema_name [Symbol, nil] Name of the output schema to use for structured output
32
+ # @return [Task] A new task instance
33
+ def initialize(description:, agent_spec:, input: {}, output_schema_name: nil)
34
+ @id = SecureRandom.uuid
35
+ @description = description
36
+
37
+ # Convert agent_spec to AgentSpecification if it's a hash
38
+ @agent_spec = if agent_spec.is_a?(Hash)
39
+ AgentSpecification.new(
40
+ name: agent_spec["name"],
41
+ description: agent_spec["description"] || "",
42
+ instructions: agent_spec["instructions"]
43
+ )
44
+ else
45
+ agent_spec
46
+ end
47
+
48
+ @input = input
49
+ @output = nil
50
+ @failure = nil
51
+ @status = :pending
52
+ @ready_to_execute = nil
53
+ @output_schema_name = output_schema_name
54
+ end
55
+
56
+ # Creates a task from a TaskDefinition
57
+ # @param definition [TaskDefinition] The task definition
58
+ # @param input [Hash] Input data for the task
59
+ # @return [Task] A new task instance
60
+ def self.from_definition(definition, input = {})
61
+ new(
62
+ description: definition.description,
63
+ agent_spec: definition.agent,
64
+ input: input
65
+ )
66
+ end
67
+
68
+ # Executes the task using the given agent
69
+ # @param agent [Agent] The agent that will execute this task
70
+ # @return [TaskResult] The result of the task execution
71
+ def perform(agent)
72
+ old_status = @status
73
+ @status = :in_progress
74
+
75
+ notify_observers(:status_change, old_status, @status)
76
+
77
+ begin
78
+ @output = if has_output_schema?
79
+ agent.execute_with_schema(build_prompt, output_schema)
80
+ else
81
+ agent.execute(build_prompt)
82
+ end
83
+ old_status = @status
84
+ @status = :completed
85
+
86
+ notify_observers(:status_change, old_status, @status)
87
+
88
+ TaskResult.new(
89
+ task_id: @id,
90
+ success: true,
91
+ output: @output
92
+ )
93
+ rescue => e
94
+ @failure = TaskFailure.new(
95
+ message: e.message,
96
+ type: e.class.name,
97
+ context: {
98
+ backtrace: e.backtrace&.first(10),
99
+ agent_id: agent.respond_to?(:id) ? agent.id : nil
100
+ }
101
+ )
102
+
103
+ old_status = @status
104
+ @status = :failed
105
+
106
+ notify_observers(:status_change, old_status, @status)
107
+ notify_observers(:failure_occurred, @failure)
108
+
109
+ Agentic.logger.error("Task execution failed: #{e.message}")
110
+
111
+ TaskResult.new(
112
+ task_id: @id,
113
+ success: false,
114
+ failure: @failure
115
+ )
116
+ end
117
+ end
118
+
119
+ # Retries a failed task
120
+ # @param agent [Agent] The agent that will execute this task
121
+ # @return [TaskResult] The result of the task execution
122
+ # @raise [StandardError] If the task is not in a failed state
123
+ def retry(agent)
124
+ raise "Cannot retry a task that is not in a failed state" unless @status == :failed
125
+
126
+ old_status = @status
127
+ @status = :retrying
128
+
129
+ notify_observers(:status_change, old_status, @status)
130
+
131
+ perform(agent)
132
+ end
133
+
134
+ # Returns a serializable representation of the task
135
+ # @return [Hash] The task as a hash
136
+ def to_h
137
+ {
138
+ id: @id,
139
+ description: @description,
140
+ agent_spec: @agent_spec.is_a?(AgentSpecification) ? @agent_spec.to_h : @agent_spec,
141
+ input: @input,
142
+ output: @output,
143
+ status: @status,
144
+ failure: @failure&.to_h
145
+ }
146
+ end
147
+
148
+ # Returns the output schema for this task
149
+ # @return [Agentic::StructuredOutputs::Schema, nil] The schema or nil if none specified
150
+ def output_schema
151
+ return nil unless @output_schema_name
152
+ TaskOutputSchemas.get(@output_schema_name)
153
+ end
154
+
155
+ # Checks if this task has a structured output schema
156
+ # @return [Boolean] True if task has an output schema
157
+ def has_output_schema?
158
+ !@output_schema_name.nil? && TaskOutputSchemas.exists?(@output_schema_name)
159
+ end
160
+
161
+ # Sets the output schema for this task
162
+ # @param schema_name [Symbol] The name of the schema to use
163
+ def set_output_schema(schema_name)
164
+ @output_schema_name = schema_name
165
+ end
166
+
167
+ private
168
+
169
+ # Builds the prompt to be sent to the agent
170
+ # @return [String] The formatted prompt
171
+ def build_prompt
172
+ output_requirements = if has_output_schema?
173
+ "Provide your response as a structured JSON object that follows the specified schema. Do not include any markdown formatting, code blocks, or additional text - just the raw JSON."
174
+ else
175
+ "Provide your response as valid JSON only. Do not wrap the JSON in markdown code blocks or any other formatting. Return raw JSON that can be parsed directly."
176
+ end
177
+
178
+ <<~PROMPT
179
+ [System Instructions]
180
+ #{agent_spec.instructions}
181
+
182
+ [Task Description]
183
+ #{description}
184
+
185
+ [Input Parameters]
186
+ #{JSON.pretty_generate(input)}
187
+
188
+ [Output Requirements]
189
+ #{output_requirements}
190
+ PROMPT
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Value object representing a task definition
5
+ class TaskDefinition
6
+ # @return [String] A description of the task
7
+ attr_reader :description
8
+
9
+ # @return [AgentSpecification] The agent specification for this task
10
+ attr_reader :agent
11
+
12
+ # Initializes a new task definition
13
+ # @param description [String] A description of the task
14
+ # @param agent [AgentSpecification] The agent specification for this task
15
+ def initialize(description:, agent:)
16
+ @description = description
17
+ @agent = agent
18
+ end
19
+
20
+ # Returns a serializable representation of the task definition
21
+ # @return [Hash] The task definition as a hash
22
+ def to_h
23
+ {
24
+ "description" => @description,
25
+ "agent" => @agent.to_h
26
+ }
27
+ end
28
+
29
+ # Creates a TaskDefinition from a hash
30
+ # @param hash [Hash] The hash representation
31
+ # @return [TaskDefinition] A new task definition
32
+ def self.from_hash(hash)
33
+ new(
34
+ description: hash["description"],
35
+ agent: AgentSpecification.from_hash(hash["agent"])
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "task_failure"
4
+
5
+ module Agentic
6
+ # Value object representing the execution result of a task
7
+ class TaskExecutionResult
8
+ # @return [Symbol] The status of the task execution (:completed, :failed, :canceled)
9
+ attr_reader :status
10
+
11
+ # @return [Hash, nil] The output produced by the task (only if successful)
12
+ attr_reader :output
13
+
14
+ # @return [TaskFailure, nil] The failure details (only if failed)
15
+ attr_reader :failure
16
+
17
+ # @param status [Symbol] The status of the task execution
18
+ # @param output [Hash, nil] The output produced by the task
19
+ # @param failure [TaskFailure, nil] The failure details
20
+ def initialize(status:, output: nil, failure: nil)
21
+ @status = status
22
+ @output = output
23
+ @failure = failure
24
+ end
25
+
26
+ # Creates a successful execution result
27
+ # @param output [Hash] The task output
28
+ # @return [TaskExecutionResult] A successful execution result
29
+ def self.success(output)
30
+ new(status: :completed, output: output)
31
+ end
32
+
33
+ # Creates a failed execution result
34
+ # @param failure [TaskFailure] The failure details
35
+ # @return [TaskExecutionResult] A failed execution result
36
+ def self.failure(failure)
37
+ new(status: :failed, failure: failure)
38
+ end
39
+
40
+ # Creates a canceled execution result
41
+ # @return [TaskExecutionResult] A canceled execution result
42
+ def self.canceled
43
+ new(status: :canceled)
44
+ end
45
+
46
+ # Creates a task execution result from a hash
47
+ # @param hash [Hash] The hash representation of a task execution result
48
+ # @return [TaskExecutionResult] A task execution result
49
+ def self.from_hash(hash)
50
+ # Handle the case where hash is not actually a hash (could be nil, Integer, etc.)
51
+ return new(status: :completed) unless hash.is_a?(Hash)
52
+
53
+ # Convert string keys to symbols if necessary
54
+ hash = hash.transform_keys(&:to_sym) if hash.keys.first.is_a?(String)
55
+
56
+ failure = hash[:failure] ? TaskFailure.from_hash(hash[:failure]) : nil
57
+ new(
58
+ status: hash[:status] || :completed,
59
+ output: hash[:output],
60
+ failure: failure
61
+ )
62
+ end
63
+
64
+ # Checks if the task execution was successful
65
+ # @return [Boolean] True if successful, false otherwise
66
+ def successful?
67
+ @status == :completed
68
+ end
69
+
70
+ # Checks if the task execution failed
71
+ # @return [Boolean] True if failed, false otherwise
72
+ def failed?
73
+ @status == :failed
74
+ end
75
+
76
+ # Checks if the task execution was canceled
77
+ # @return [Boolean] True if canceled, false otherwise
78
+ def canceled?
79
+ @status == :canceled
80
+ end
81
+
82
+ # Returns a hash representation of the execution result
83
+ # @return [Hash] The execution result as a hash
84
+ def to_h
85
+ {
86
+ status: @status,
87
+ output: @output,
88
+ failure: @failure&.to_h
89
+ }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Represents a failure that occurred during task execution
5
+ # @attr_reader [String] message The failure message
6
+ # @attr_reader [String] type The type of failure
7
+ # @attr_reader [Time] timestamp When the failure occurred
8
+ # @attr_reader [Hash] context Additional context about the failure
9
+ class TaskFailure
10
+ attr_reader :message, :type, :timestamp, :context
11
+
12
+ # Initializes a new task failure
13
+ # @param message [String] The failure message
14
+ # @param type [String] The type of failure
15
+ # @param context [Hash] Additional context about the failure
16
+ # @return [TaskFailure] A new task failure instance
17
+ def initialize(message:, type:, context: {})
18
+ @message = message
19
+ @type = type
20
+ @timestamp = Time.now
21
+ @context = context
22
+ end
23
+
24
+ # Returns a serializable representation of the failure
25
+ # @return [Hash] The failure as a hash
26
+ def to_h
27
+ {
28
+ message: @message,
29
+ type: @type,
30
+ timestamp: @timestamp.iso8601,
31
+ context: @context
32
+ }
33
+ end
34
+
35
+ # Creates a task failure from an exception
36
+ # @param exception [Exception] The exception
37
+ # @param context [Hash] Additional context about the failure
38
+ # @return [TaskFailure] A new task failure instance
39
+ def self.from_exception(exception, context = {})
40
+ new(
41
+ message: exception.message,
42
+ type: exception.class.name,
43
+ context: context.merge(
44
+ backtrace: exception.backtrace&.first(10)
45
+ )
46
+ )
47
+ end
48
+
49
+ # Creates a task failure from a hash
50
+ # @param hash [Hash] The hash representation of a task failure
51
+ # @return [TaskFailure] A new task failure instance
52
+ def self.from_hash(hash)
53
+ # Handle the case where hash is not actually a hash
54
+ return new(message: "Unknown error", type: "UnknownError") unless hash.is_a?(Hash)
55
+
56
+ # Convert string keys to symbols if necessary
57
+ hash = hash.transform_keys(&:to_sym) if hash.keys.first.is_a?(String)
58
+
59
+ new(
60
+ message: hash[:message] || "Unknown error",
61
+ type: hash[:type] || "UnknownError",
62
+ context: hash[:context] || {}
63
+ )
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Registry for managing task output schemas
5
+ # Provides a centralized location for defining and accessing
6
+ # structured output schemas used by tasks
7
+ class TaskOutputSchemas
8
+ @schemas = {}
9
+
10
+ class << self
11
+ # Registers a new output schema
12
+ # @param name [Symbol] The schema name/identifier
13
+ # @param schema [Agentic::StructuredOutputs::Schema] The schema definition
14
+ def register(name, schema)
15
+ @schemas[name] = schema
16
+ end
17
+
18
+ # Retrieves a registered schema
19
+ # @param name [Symbol] The schema name/identifier
20
+ # @return [Agentic::StructuredOutputs::Schema, nil] The schema or nil if not found
21
+ def get(name)
22
+ @schemas[name] || ((name == :default) ? default_task_schema : nil)
23
+ end
24
+
25
+ # Lists all registered schema names
26
+ # @return [Array<Symbol>] Array of schema names
27
+ def list_schemas
28
+ (@schemas.keys + [:default]).uniq
29
+ end
30
+
31
+ # Checks if a schema is registered
32
+ # @param name [Symbol] The schema name/identifier
33
+ # @return [Boolean] True if schema exists
34
+ def exists?(name)
35
+ @schemas.key?(name) || name == :default
36
+ end
37
+
38
+ # Returns the default task output schema for general task responses
39
+ # @return [Agentic::StructuredOutputs::Schema] Default schema for task outputs
40
+ def default_task_schema
41
+ @default_schema ||= StructuredOutputs::Schema.new("task_output") do |schema|
42
+ # Simple, flexible schema for task results
43
+ schema.string(:status, enum: ["completed", "partial", "failed"])
44
+ schema.object(:result) do |result_schema|
45
+ result_schema.string(:summary)
46
+ # Additional properties will be allowed for flexible task outputs
47
+ end
48
+ schema.array(:steps, items: {type: "string"})
49
+ end
50
+ end
51
+
52
+ # Returns a simple object schema for maximum flexibility
53
+ # @return [Agentic::StructuredOutputs::Schema] Simple object schema
54
+ def simple_object_schema
55
+ @simple_object_schema ||= StructuredOutputs::Schema.new("simple_object") do |schema|
56
+ # Minimal schema that accepts any structured JSON object
57
+ # This is useful when we want structured JSON but maximum flexibility
58
+ schema.string(:type)
59
+ schema.object(:data) do
60
+ # Flexible data structure
61
+ end
62
+ end
63
+ end
64
+
65
+ # Returns a schema for code generation tasks
66
+ # @return [Agentic::StructuredOutputs::Schema] Code generation schema
67
+ def code_generation_schema
68
+ @code_generation_schema ||= StructuredOutputs::Schema.new("code_generation") do |schema|
69
+ schema.string(:language)
70
+ schema.string(:filename)
71
+ schema.string(:code)
72
+ schema.string(:description)
73
+ schema.array(:dependencies, items: {type: "string"})
74
+ end
75
+ end
76
+
77
+ # Returns a schema for analysis/research tasks
78
+ # @return [Agentic::StructuredOutputs::Schema] Analysis task schema
79
+ def analysis_schema
80
+ @analysis_schema ||= StructuredOutputs::Schema.new("analysis_result") do |schema|
81
+ schema.string(:summary)
82
+ schema.array(:key_findings, items: {type: "string"})
83
+ schema.object(:data) do
84
+ # Flexible analysis data
85
+ end
86
+ schema.array(:recommendations, items: {type: "string"})
87
+ schema.string(:confidence_level, enum: ["high", "medium", "low"])
88
+ end
89
+ end
90
+
91
+ # Resets all registered schemas (useful for testing)
92
+ def reset!
93
+ @schemas = {}
94
+ @default_schema = nil
95
+ @simple_object_schema = nil
96
+ @code_generation_schema = nil
97
+ @analysis_schema = nil
98
+ end
99
+
100
+ # Registers default schemas
101
+ def register_defaults!
102
+ register(:default, default_task_schema)
103
+ register(:simple_object, simple_object_schema)
104
+ register(:code_generation, code_generation_schema)
105
+ register(:analysis, analysis_schema)
106
+ end
107
+ end
108
+
109
+ # Register defaults when the class is loaded
110
+ register_defaults!
111
+ end
112
+ end