aidp 0.34.1 → 0.39.3

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -14
  3. data/lib/aidp/agent_harness_adapter.rb +309 -0
  4. data/lib/aidp/cli/issue_importer.rb +20 -6
  5. data/lib/aidp/cli/jobs_command.rb +1 -1
  6. data/lib/aidp/cli/models_command.rb +9 -16
  7. data/lib/aidp/cli/prompts_command.rb +253 -0
  8. data/lib/aidp/cli/security_command.rb +4 -10
  9. data/lib/aidp/cli/storage_command.rb +277 -0
  10. data/lib/aidp/cli/temporal_command.rb +460 -0
  11. data/lib/aidp/cli.rb +24 -2
  12. data/lib/aidp/config.rb +15 -0
  13. data/lib/aidp/config_paths.rb +12 -2
  14. data/lib/aidp/database/migrations.rb +114 -0
  15. data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
  16. data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
  17. data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
  18. data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
  19. data/lib/aidp/database/repositories/job_repository.rb +278 -0
  20. data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
  21. data/lib/aidp/database/repositories/progress_repository.rb +210 -0
  22. data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
  23. data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
  24. data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
  25. data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
  26. data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
  27. data/lib/aidp/database/repositories/task_repository.rb +231 -0
  28. data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
  29. data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
  30. data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
  31. data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
  32. data/lib/aidp/database/repository.rb +166 -0
  33. data/lib/aidp/database/schema.rb +347 -0
  34. data/lib/aidp/database/storage_migrator.rb +693 -0
  35. data/lib/aidp/database.rb +139 -0
  36. data/lib/aidp/debug_mixin.rb +2 -2
  37. data/lib/aidp/execute/checkpoint.rb +26 -0
  38. data/lib/aidp/execute/checkpoint_display.rb +16 -0
  39. data/lib/aidp/execute/prompt_evaluator.rb +30 -57
  40. data/lib/aidp/execute/work_loop_runner.rb +294 -46
  41. data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
  42. data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
  43. data/lib/aidp/harness/ai_decision_engine.rb +78 -296
  44. data/lib/aidp/harness/config_schema.rb +59 -0
  45. data/lib/aidp/harness/configuration.rb +229 -10
  46. data/lib/aidp/harness/deprecation_cache.rb +53 -11
  47. data/lib/aidp/harness/enhanced_runner.rb +1 -1
  48. data/lib/aidp/harness/error_handler.rb +11 -5
  49. data/lib/aidp/harness/provider_config.rb +36 -0
  50. data/lib/aidp/harness/provider_factory.rb +26 -24
  51. data/lib/aidp/harness/provider_info.rb +9 -5
  52. data/lib/aidp/harness/provider_manager.rb +2 -2
  53. data/lib/aidp/harness/provider_metrics.rb +103 -1
  54. data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
  55. data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
  56. data/lib/aidp/harness/runner.rb +7 -2
  57. data/lib/aidp/harness/state_manager.rb +1 -1
  58. data/lib/aidp/harness/test_runner.rb +167 -11
  59. data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
  60. data/lib/aidp/harness/usage_limit.rb +179 -0
  61. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  62. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  63. data/lib/aidp/harness/usage_period.rb +234 -0
  64. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  65. data/lib/aidp/init/runner.rb +25 -4
  66. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  67. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  68. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  69. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  70. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  71. data/lib/aidp/logger.rb +25 -7
  72. data/lib/aidp/orchestration_adapter.rb +279 -0
  73. data/lib/aidp/pr_worktree_manager.rb +4 -15
  74. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  75. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  76. data/lib/aidp/prompts/template_evolver.rb +305 -0
  77. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  78. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  79. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  80. data/lib/aidp/setup/wizard.rb +616 -176
  81. data/lib/aidp/shell_executor.rb +58 -0
  82. data/lib/aidp/style_guide/selector.rb +56 -1
  83. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  84. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  85. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  86. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  87. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  88. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  89. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  90. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  91. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  92. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  93. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  94. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  95. data/lib/aidp/temporal/configuration.rb +169 -0
  96. data/lib/aidp/temporal/connection.rb +93 -0
  97. data/lib/aidp/temporal/worker.rb +119 -0
  98. data/lib/aidp/temporal/workflow_client.rb +171 -0
  99. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  100. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  101. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  102. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  103. data/lib/aidp/temporal.rb +120 -0
  104. data/lib/aidp/version.rb +1 -1
  105. data/lib/aidp/watch/base_processor.rb +35 -0
  106. data/lib/aidp/watch/build_processor.rb +132 -0
  107. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  108. data/lib/aidp/watch/rebase_processor.rb +245 -0
  109. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  110. data/lib/aidp/watch/runner.rb +366 -285
  111. data/lib/aidp/watch/state_store.rb +80 -0
  112. data/lib/aidp/watch/work_item.rb +96 -0
  113. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  114. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  115. data/lib/aidp/workflows/guided_agent.rb +1 -1
  116. data/lib/aidp/worktree.rb +62 -0
  117. data/templates/aidp.yml.example +70 -0
  118. metadata +117 -16
  119. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  120. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  121. data/lib/aidp/providers/adapter.rb +0 -239
  122. data/lib/aidp/providers/aider.rb +0 -264
  123. data/lib/aidp/providers/anthropic.rb +0 -614
  124. data/lib/aidp/providers/base.rb +0 -617
  125. data/lib/aidp/providers/capability_registry.rb +0 -204
  126. data/lib/aidp/providers/codex.rb +0 -267
  127. data/lib/aidp/providers/cursor.rb +0 -304
  128. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  129. data/lib/aidp/providers/gemini.rb +0 -110
  130. data/lib/aidp/providers/github_copilot.rb +0 -227
  131. data/lib/aidp/providers/kilocode.rb +0 -157
  132. data/lib/aidp/providers/opencode.rb +0 -139
@@ -1,204 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aidp
4
- module Providers
5
- # CapabilityRegistry maintains a queryable registry of provider capabilities
6
- # and features. This enables runtime feature detection and provider selection
7
- # based on required capabilities.
8
- #
9
- # @see https://github.com/viamin/aidp/issues/243
10
- class CapabilityRegistry
11
- # Standard capability keys
12
- CAPABILITY_KEYS = [
13
- :reasoning_tiers, # Array of supported reasoning tiers (mini, standard, thinking, etc.)
14
- :context_window, # Maximum context window size in tokens
15
- :supports_json_mode, # Boolean: supports JSON mode output
16
- :supports_tool_use, # Boolean: supports tool/function calling
17
- :supports_vision, # Boolean: supports image/vision inputs
18
- :supports_file_upload, # Boolean: supports file uploads
19
- :supports_mcp, # Boolean: supports Model Context Protocol
20
- :max_tokens, # Maximum tokens per response
21
- :supports_dangerous_mode # Boolean: supports elevated permissions mode
22
- ].freeze
23
-
24
- def initialize
25
- @capabilities = {}
26
- @providers = {}
27
- end
28
-
29
- # Register a provider and its capabilities
30
- # @param provider [Aidp::Providers::Base] provider instance
31
- # @return [void]
32
- def register(provider)
33
- provider_name = provider.name
34
- @providers[provider_name] = provider
35
-
36
- # Collect capabilities from provider
37
- caps = provider.capabilities.dup
38
- caps[:supports_mcp] = provider.supports_mcp?
39
- caps[:supports_dangerous_mode] = provider.supports_dangerous_mode?
40
-
41
- @capabilities[provider_name] = caps
42
-
43
- Aidp.log_debug("CapabilityRegistry", "registered provider",
44
- provider: provider_name,
45
- capabilities: caps.keys)
46
- end
47
-
48
- # Unregister a provider
49
- # @param provider_name [String] provider identifier
50
- # @return [void]
51
- def unregister(provider_name)
52
- @capabilities.delete(provider_name)
53
- @providers.delete(provider_name)
54
- end
55
-
56
- # Get capabilities for a specific provider
57
- # @param provider_name [String] provider identifier
58
- # @return [Hash, nil] capabilities hash or nil if not found
59
- def capabilities_for(provider_name)
60
- @capabilities[provider_name]
61
- end
62
-
63
- # Check if a provider has a specific capability
64
- # @param provider_name [String] provider identifier
65
- # @param capability [Symbol] capability key
66
- # @param value [Object, nil] optional value to match
67
- # @return [Boolean] true if provider has the capability
68
- def has_capability?(provider_name, capability, value = nil)
69
- caps = @capabilities[provider_name]
70
- return false unless caps
71
-
72
- if value.nil?
73
- # Just check if capability exists and is truthy
74
- caps.key?(capability) && caps[capability]
75
- else
76
- # Check if capability matches specific value
77
- caps[capability] == value
78
- end
79
- end
80
-
81
- # Find providers that match capability requirements
82
- # @param requirements [Hash] capability requirements
83
- # @return [Array<String>] array of matching provider names
84
- # @example
85
- # registry.find_providers(supports_vision: true, min_context_window: 100_000)
86
- def find_providers(**requirements)
87
- matching = []
88
-
89
- @capabilities.each do |provider_name, caps|
90
- matches = requirements.all? do |key, required_value|
91
- case key
92
- when :min_context_window
93
- caps[:context_window] && caps[:context_window] >= required_value
94
- when :max_context_window
95
- caps[:context_window] && caps[:context_window] <= required_value
96
- when :reasoning_tier
97
- caps[:reasoning_tiers]&.include?(required_value)
98
- else
99
- # Exact match for boolean and other values
100
- caps[key] == required_value
101
- end
102
- end
103
-
104
- matching << provider_name if matches
105
- end
106
-
107
- matching
108
- end
109
-
110
- # Get all registered providers
111
- # @return [Array<String>] array of provider names
112
- def registered_providers
113
- @providers.keys
114
- end
115
-
116
- # Get detailed information about all registered providers
117
- # @return [Hash] provider information indexed by provider name
118
- def provider_info
119
- info = {}
120
-
121
- @providers.each do |provider_name, provider|
122
- caps = @capabilities[provider_name] || {}
123
-
124
- info[provider_name] = {
125
- display_name: provider.display_name,
126
- available: provider.available?,
127
- capabilities: caps,
128
- dangerous_mode_enabled: provider.dangerous_mode_enabled?,
129
- health_status: provider.health_status
130
- }
131
- end
132
-
133
- info
134
- end
135
-
136
- # Check capability compatibility between providers
137
- # @param provider_name1 [String] first provider
138
- # @param provider_name2 [String] second provider
139
- # @return [Hash] compatibility report
140
- def compatibility_report(provider_name1, provider_name2)
141
- caps1 = @capabilities[provider_name1]
142
- caps2 = @capabilities[provider_name2]
143
-
144
- return {error: "Provider not found"} unless caps1 && caps2
145
-
146
- common = {}
147
- differences = {}
148
-
149
- all_keys = (caps1.keys + caps2.keys).uniq
150
-
151
- all_keys.each do |key|
152
- val1 = caps1[key]
153
- val2 = caps2[key]
154
-
155
- if val1 == val2
156
- common[key] = val1
157
- else
158
- differences[key] = {provider_name1 => val1, provider_name2 => val2}
159
- end
160
- end
161
-
162
- {
163
- common_capabilities: common,
164
- differences: differences,
165
- compatibility_score: common.size.to_f / all_keys.size
166
- }
167
- end
168
-
169
- # Get capability statistics across all providers
170
- # @return [Hash] statistics about capability support
171
- def capability_statistics
172
- stats = {}
173
-
174
- CAPABILITY_KEYS.each do |key|
175
- stats[key] = {
176
- total_providers: @providers.size,
177
- supporting_providers: 0,
178
- providers: []
179
- }
180
- end
181
-
182
- @capabilities.each do |provider_name, caps|
183
- caps.each do |key, value|
184
- next unless stats.key?(key)
185
-
186
- if value.is_a?(TrueClass) || (value.is_a?(Array) && !value.empty?) || (value.is_a?(Integer) && value > 0)
187
- stats[key][:supporting_providers] += 1
188
- stats[key][:providers] << provider_name
189
- end
190
- end
191
- end
192
-
193
- stats
194
- end
195
-
196
- # Clear all registered providers
197
- # @return [void]
198
- def clear
199
- @capabilities.clear
200
- @providers.clear
201
- end
202
- end
203
- end
204
- end
@@ -1,267 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "timeout"
4
- require_relative "base"
5
- require_relative "../util"
6
- require_relative "../debug_mixin"
7
-
8
- module Aidp
9
- module Providers
10
- class Codex < Base
11
- include Aidp::DebugMixin
12
-
13
- # Model name pattern for OpenAI models (since Codex uses OpenAI)
14
- MODEL_PATTERN = /^gpt-[\d.o-]+(?:-turbo)?(?:-mini)?$/i
15
- LONG_PROMPT_THRESHOLD = 8000
16
- LONG_PROMPT_TIMEOUT = 900 # 15 minutes for large prompts
17
-
18
- def self.available?
19
- !!Aidp::Util.which("codex")
20
- end
21
-
22
- # Check if this provider supports a given model family
23
- #
24
- # @param family_name [String] The model family name
25
- # @return [Boolean] True if it matches OpenAI model pattern
26
- def self.supports_model_family?(family_name)
27
- MODEL_PATTERN.match?(family_name)
28
- end
29
-
30
- # Discover available models from registry
31
- #
32
- # Note: Codex CLI doesn't have a standard model listing command
33
- # Returns registry-based models that match OpenAI patterns
34
- #
35
- # @return [Array<Hash>] Array of discovered models
36
- def self.discover_models
37
- return [] unless available?
38
-
39
- discover_models_from_registry(MODEL_PATTERN, "codex")
40
- end
41
-
42
- # Get firewall requirements for Codex provider
43
- # Codex uses OpenAI APIs
44
- def self.firewall_requirements
45
- {
46
- domains: [
47
- "api.openai.com",
48
- "auth.openai.com",
49
- "openai.com",
50
- "chat.openai.com",
51
- "chatgpt.com",
52
- "cdn.openai.com",
53
- "oaiusercontent.com"
54
- ],
55
- ip_ranges: []
56
- }
57
- end
58
-
59
- def name
60
- "codex"
61
- end
62
-
63
- def display_name
64
- "Codex CLI"
65
- end
66
-
67
- def available?
68
- return false unless self.class.available?
69
-
70
- # Additional check to ensure the CLI is properly configured
71
- begin
72
- result = Aidp::Util.execute_command("codex", ["--version"], timeout: 10)
73
- result.exit_status == 0
74
- rescue
75
- false
76
- end
77
- end
78
-
79
- def send_message(prompt:, session: nil, options: {})
80
- raise "codex CLI not available" unless self.class.available?
81
-
82
- # Smart timeout calculation (store prompt length for adaptive logic)
83
- @current_codex_prompt_length = prompt.length
84
- timeout_seconds = calculate_timeout
85
-
86
- debug_provider("codex", "Starting execution", {timeout: timeout_seconds})
87
- debug_log("šŸ“ Sending prompt to codex (length: #{prompt.length})", level: :info)
88
-
89
- # Set up activity monitoring
90
- setup_activity_monitoring("codex", method(:activity_callback))
91
- record_activity("Starting codex execution")
92
-
93
- # Create a spinner for activity display
94
- spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
95
- spinner.auto_spin
96
-
97
- activity_display_thread = Thread.new do
98
- start_time = Time.now
99
- loop do
100
- sleep 0.5 # Update every 500ms to reduce spam
101
- elapsed = Time.now - start_time
102
-
103
- # Break if we've been running too long or state changed
104
- break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
105
-
106
- update_spinner_status(spinner, elapsed, "šŸ¤– Codex CLI")
107
- end
108
- end
109
-
110
- begin
111
- # Use non-interactive mode (exec) for automation
112
- args = ["exec", prompt]
113
-
114
- # Add model if configured
115
- if @model && !@model.empty?
116
- args += ["--model", @model]
117
- end
118
-
119
- # Add session support if provided (codex may support session/thread continuation)
120
- if session && !session.empty?
121
- args += ["--session", session]
122
- end
123
-
124
- # In devcontainer, ensure sandbox mode and approval policy are set
125
- # These are already set via environment variables in devcontainer.json
126
- # but we verify and log them here for visibility
127
- if in_devcontainer_or_codespace?
128
- unless ENV["CODEX_SANDBOX_MODE"] == "danger-full-access"
129
- ENV["CODEX_SANDBOX_MODE"] = "danger-full-access"
130
- debug_log("šŸ”“ Set CODEX_SANDBOX_MODE=danger-full-access for devcontainer", level: :info)
131
- end
132
- unless ENV["CODEX_APPROVAL_POLICY"] == "never"
133
- ENV["CODEX_APPROVAL_POLICY"] = "never"
134
- debug_log("šŸ”“ Set CODEX_APPROVAL_POLICY=never for devcontainer", level: :info)
135
- end
136
- end
137
-
138
- # Use debug_execute_command for better debugging
139
- result = debug_execute_command("codex", args: args, timeout: timeout_seconds)
140
-
141
- # Log the results
142
- debug_command("codex", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
143
-
144
- if result.exit_status == 0
145
- spinner.success("āœ“")
146
- mark_completed
147
- result.out
148
- else
149
- spinner.error("āœ—")
150
- mark_failed("codex failed with exit code #{result.exit_status}")
151
- debug_error(StandardError.new("codex failed"), {exit_code: result.exit_status, stderr: result.err})
152
- raise "codex failed with exit code #{result.exit_status}: #{result.err}"
153
- end
154
- rescue => e
155
- spinner&.error("āœ—")
156
- mark_failed("codex execution failed: #{e.message}")
157
- debug_error(e, {provider: "codex", prompt_length: prompt.length})
158
- raise
159
- ensure
160
- cleanup_activity_display(activity_display_thread, spinner)
161
- @current_codex_prompt_length = nil
162
- end
163
- end
164
-
165
- # Enhanced send method with additional options
166
- def send_with_options(prompt:, session: nil, model: nil, ask_for_approval: false)
167
- args = ["exec", prompt]
168
-
169
- # Add session support
170
- if session && !session.empty?
171
- args += ["--session", session]
172
- end
173
-
174
- # Add model selection (from parameter or configured model)
175
- model_to_use = model || @model
176
- if model_to_use
177
- args += ["--model", model_to_use]
178
- end
179
-
180
- # Add approval flag - but warn about interactive behavior
181
- if ask_for_approval
182
- debug_log("āš ļø WARNING: --ask-for-approval flag may cause interactive prompts that could hang AIDP", level: :warn)
183
- args += ["--ask-for-approval"]
184
- end
185
-
186
- # Use the enhanced version of send
187
- send_with_custom_args(prompt: prompt, args: args)
188
- end
189
-
190
- # Override health check for Codex specific considerations
191
- def harness_healthy?
192
- return false unless super
193
-
194
- # Additional health checks specific to Codex CLI
195
- # Check if we can access the CLI (basic connectivity test)
196
- begin
197
- result = Aidp::Util.execute_command("codex", ["--help"], timeout: 5)
198
- result.exit_status == 0
199
- rescue
200
- false
201
- end
202
- end
203
-
204
- private
205
-
206
- # Internal helper for send_with_options - executes with custom arguments
207
- def send_with_custom_args(prompt:, args:)
208
- @current_codex_prompt_length = prompt.length
209
- timeout_seconds = calculate_timeout
210
-
211
- debug_provider("codex", "Starting execution", {timeout: timeout_seconds, args: args})
212
- debug_log("šŸ“ Sending prompt to codex with custom args", level: :info)
213
-
214
- setup_activity_monitoring("codex", method(:activity_callback))
215
- record_activity("Starting codex execution with custom args")
216
-
217
- begin
218
- result = debug_execute_command("codex", args: args, timeout: timeout_seconds)
219
- debug_command("codex", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
220
-
221
- if result.exit_status == 0
222
- mark_completed
223
- result.out
224
- else
225
- mark_failed("codex failed with exit code #{result.exit_status}")
226
- debug_error(StandardError.new("codex failed"), {exit_code: result.exit_status, stderr: result.err})
227
- raise "codex failed with exit code #{result.exit_status}: #{result.err}"
228
- end
229
- rescue => e
230
- mark_failed("codex execution failed: #{e.message}")
231
- debug_error(e, {provider: "codex", prompt_length: prompt.length})
232
- raise
233
- ensure
234
- @current_codex_prompt_length = nil
235
- end
236
- end
237
-
238
- def activity_callback(state, message, provider)
239
- # Handle activity state changes
240
- case state
241
- when :stuck
242
- display_message("\nāš ļø Codex CLI appears stuck: #{message}", type: :warning)
243
- when :completed
244
- display_message("\nāœ… Codex CLI completed: #{message}", type: :success)
245
- when :failed
246
- display_message("\nāŒ Codex CLI failed: #{message}", type: :error)
247
- end
248
- end
249
-
250
- def calculate_timeout
251
- env_override = ENV["AIDP_CODEX_TIMEOUT"]
252
- return env_override.to_i if env_override&.match?(/^\d+$/)
253
-
254
- base_timeout = super
255
-
256
- prompt_length = @current_codex_prompt_length
257
- return base_timeout unless prompt_length && prompt_length >= LONG_PROMPT_THRESHOLD
258
-
259
- extended_timeout = [base_timeout, LONG_PROMPT_TIMEOUT].max
260
- if extended_timeout > base_timeout
261
- display_message("ā±ļø Codex prompt length #{prompt_length} detected - extending timeout to #{extended_timeout} seconds", type: :info)
262
- end
263
- extended_timeout
264
- end
265
- end
266
- end
267
- end