aidp 0.30.0 → 0.32.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/lib/aidp/config.rb +2 -8
- data/lib/aidp/execute/work_loop_runner.rb +70 -0
- data/lib/aidp/harness/configuration.rb +1 -1
- data/lib/aidp/harness/provider_manager.rb +74 -5
- data/lib/aidp/harness/provider_metrics.rb +5 -3
- data/lib/aidp/pr_worktree_manager.rb +582 -0
- data/lib/aidp/providers/anthropic.rb +17 -14
- data/lib/aidp/providers/cursor.rb +7 -1
- data/lib/aidp/setup/wizard.rb +65 -0
- data/lib/aidp/util.rb +11 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/auto_pr_processor.rb +86 -0
- data/lib/aidp/watch/auto_processor.rb +78 -0
- data/lib/aidp/watch/change_request_processor.rb +105 -27
- data/lib/aidp/watch/runner.rb +104 -0
- data/lib/aidp/watch/state_store.rb +37 -0
- data/lib/aidp/worktree_branch_manager.rb +216 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7a02d5fb9cd901a6a74a2b402c5b9d70ea07f47727ddbd388993e9ede64c96b
|
|
4
|
+
data.tar.gz: fc92260f2b244295b98b5e2e2cdedf4872a18f94f5c3b8bf0031b6a4fc1f6554
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 673505d4036ad47cff7ff1b534e1798e893dcf7fc77f086ee1b6f46ba90b48de4b72333cf63b15db1fd0b16b2b0060c324fe6a1bdf371ae7ec796ce606b454e7
|
|
7
|
+
data.tar.gz: f99df6bea4120ec723a907e4f4a1cc15dc1c5c52abf3c4753bc5cdd24e6288a5207dca6ae6dc85a6afab442dc2b845de7d7794731f1bd27caeaf22c9e46f656f
|
data/lib/aidp/config.rb
CHANGED
|
@@ -111,11 +111,10 @@ module Aidp
|
|
|
111
111
|
model_family: "claude",
|
|
112
112
|
max_tokens: 100_000,
|
|
113
113
|
default_flags: ["--dangerously-skip-permissions"],
|
|
114
|
-
models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"
|
|
114
|
+
models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"],
|
|
115
115
|
model_weights: {
|
|
116
116
|
"claude-3-5-sonnet-20241022" => 3,
|
|
117
|
-
"claude-3-5-haiku-20241022" => 2
|
|
118
|
-
"claude-3-opus-20240229" => 1
|
|
117
|
+
"claude-3-5-haiku-20241022" => 2
|
|
119
118
|
},
|
|
120
119
|
models_config: {
|
|
121
120
|
"claude-3-5-sonnet-20241022" => {
|
|
@@ -127,11 +126,6 @@ module Aidp
|
|
|
127
126
|
flags: ["--dangerously-skip-permissions"],
|
|
128
127
|
max_tokens: 200_000,
|
|
129
128
|
timeout: 180
|
|
130
|
-
},
|
|
131
|
-
"claude-3-opus-20240229" => {
|
|
132
|
-
flags: ["--dangerously-skip-permissions"],
|
|
133
|
-
max_tokens: 200_000,
|
|
134
|
-
timeout: 600
|
|
135
129
|
}
|
|
136
130
|
},
|
|
137
131
|
auth: {
|
|
@@ -7,6 +7,7 @@ require_relative "guard_policy"
|
|
|
7
7
|
require_relative "work_loop_unit_scheduler"
|
|
8
8
|
require_relative "deterministic_unit"
|
|
9
9
|
require_relative "agent_signal_parser"
|
|
10
|
+
require_relative "steps"
|
|
10
11
|
require_relative "../harness/test_runner"
|
|
11
12
|
require_relative "../errors"
|
|
12
13
|
|
|
@@ -81,6 +82,7 @@ module Aidp
|
|
|
81
82
|
|
|
82
83
|
display_message("🔄 Starting hybrid work loop for step: #{step_name}", type: :info)
|
|
83
84
|
display_message(" Flow: Deterministic ↔ Agentic with fix-forward core", type: :info)
|
|
85
|
+
display_work_context(step_name, context)
|
|
84
86
|
|
|
85
87
|
display_guard_policy_status
|
|
86
88
|
display_pending_tasks
|
|
@@ -1286,6 +1288,74 @@ module Aidp
|
|
|
1286
1288
|
display_message("")
|
|
1287
1289
|
end
|
|
1288
1290
|
|
|
1291
|
+
# Show watch-mode context (issue/PR, step position) to improve situational awareness
|
|
1292
|
+
def display_work_context(step_name, context)
|
|
1293
|
+
parts = work_context_parts(step_name, context)
|
|
1294
|
+
return if parts.empty?
|
|
1295
|
+
|
|
1296
|
+
Aidp.log_debug("work_loop", "work_context", step: step_name, parts: parts)
|
|
1297
|
+
display_message(" 📡 Context: #{parts.join(" | ")}", type: :info)
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
def work_context_parts(step_name, context)
|
|
1301
|
+
ctx = (@options || {}).merge(context || {})
|
|
1302
|
+
parts = []
|
|
1303
|
+
|
|
1304
|
+
if (step_label = step_position_label(step_name, ctx))
|
|
1305
|
+
parts << step_label
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
if (issue_label = issue_context_label(ctx))
|
|
1309
|
+
parts << issue_label
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
if (pr_label = pr_context_label(ctx))
|
|
1313
|
+
parts << pr_label
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
parts << "Watch mode" if ctx[:workflow_type].to_s == "watch_mode"
|
|
1317
|
+
|
|
1318
|
+
parts.compact
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
def step_position_label(step_name, context)
|
|
1322
|
+
steps = Array(context[:selected_steps]).map(&:to_s)
|
|
1323
|
+
steps = Aidp::Execute::Steps::SPEC.keys if steps.empty?
|
|
1324
|
+
steps = [step_name] if steps.empty?
|
|
1325
|
+
steps << step_name unless steps.include?(step_name)
|
|
1326
|
+
|
|
1327
|
+
index = steps.index(step_name)
|
|
1328
|
+
return nil unless index
|
|
1329
|
+
|
|
1330
|
+
"Step #{index + 1}/#{steps.size} (#{step_name})"
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1333
|
+
def issue_context_label(context)
|
|
1334
|
+
issue_number = context[:issue_number] ||
|
|
1335
|
+
context.dig(:issue, :number) ||
|
|
1336
|
+
extract_number_from_url(context[:issue_url] || context.dig(:issue, :url) || context.dig(:user_input, "Issue URL"), /issues\/(\d+)/)
|
|
1337
|
+
|
|
1338
|
+
return nil unless issue_number
|
|
1339
|
+
|
|
1340
|
+
"Issue ##{issue_number}"
|
|
1341
|
+
end
|
|
1342
|
+
|
|
1343
|
+
def pr_context_label(context)
|
|
1344
|
+
pr_number = context[:pr_number] ||
|
|
1345
|
+
context.dig(:pull_request, :number) ||
|
|
1346
|
+
extract_number_from_url(context[:pr_url] || context.dig(:pull_request, :url) || context.dig(:user_input, "PR URL") || context.dig(:user_input, "Pull Request URL"), /pull\/(\d+)/)
|
|
1347
|
+
|
|
1348
|
+
return nil unless pr_number
|
|
1349
|
+
|
|
1350
|
+
"PR ##{pr_number}"
|
|
1351
|
+
end
|
|
1352
|
+
|
|
1353
|
+
def extract_number_from_url(url, pattern)
|
|
1354
|
+
return nil unless url
|
|
1355
|
+
match = url.to_s.match(pattern)
|
|
1356
|
+
match && match[1]
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1289
1359
|
# Append task completion requirement to PROMPT.md
|
|
1290
1360
|
def append_task_requirement_to_prompt(message)
|
|
1291
1361
|
task_requirement = []
|
|
@@ -1057,7 +1057,7 @@ module Aidp
|
|
|
1057
1057
|
def default_thinking_config
|
|
1058
1058
|
{
|
|
1059
1059
|
default_tier: "mini", # Use mini tier by default for cost optimization
|
|
1060
|
-
max_tier: "
|
|
1060
|
+
max_tier: "pro", # Max tier rarely needed; pro is sufficient for most tasks
|
|
1061
1061
|
allow_provider_switch: true,
|
|
1062
1062
|
auto_escalate: true,
|
|
1063
1063
|
escalation_threshold: 2,
|
|
@@ -38,6 +38,7 @@ module Aidp
|
|
|
38
38
|
@model_fallback_chains = {}
|
|
39
39
|
@model_switching_enabled = true
|
|
40
40
|
@model_weights = {}
|
|
41
|
+
@model_denylist = Hash.new { |h, k| h[k] = [] }
|
|
41
42
|
@unavailable_cache = {}
|
|
42
43
|
@binary_check_cache = {}
|
|
43
44
|
@binary_check_ttl = 300 # seconds
|
|
@@ -387,10 +388,30 @@ module Aidp
|
|
|
387
388
|
# Check if model is configured for provider
|
|
388
389
|
return false unless model_configured?(provider_name, model_name)
|
|
389
390
|
|
|
391
|
+
# Skip models that were explicitly denied (e.g., unsupported by provider)
|
|
392
|
+
return false if model_denied?(provider_name, model_name)
|
|
393
|
+
|
|
390
394
|
# Check if model is not rate limited
|
|
391
395
|
!is_model_rate_limited?(provider_name, model_name)
|
|
392
396
|
end
|
|
393
397
|
|
|
398
|
+
# Check if a model has been denylisted for a provider
|
|
399
|
+
def model_denied?(provider_name, model_name)
|
|
400
|
+
@model_denylist[provider_name]&.include?(model_name)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Add a model to the denylist for a provider (e.g., unsupported model errors)
|
|
404
|
+
def deny_model(provider_name, model_name, error: nil)
|
|
405
|
+
return if provider_name.nil? || model_name.nil?
|
|
406
|
+
return if model_denied?(provider_name, model_name)
|
|
407
|
+
|
|
408
|
+
@model_denylist[provider_name] << model_name
|
|
409
|
+
Aidp.log_debug("provider_manager", "model_denylisted",
|
|
410
|
+
provider: provider_name,
|
|
411
|
+
model: model_name,
|
|
412
|
+
error: error&.message)
|
|
413
|
+
end
|
|
414
|
+
|
|
394
415
|
# Check if model is configured for provider
|
|
395
416
|
def model_configured?(provider_name, model_name)
|
|
396
417
|
models = provider_models(provider_name)
|
|
@@ -1279,6 +1300,7 @@ module Aidp
|
|
|
1279
1300
|
@model_health.clear
|
|
1280
1301
|
@model_metrics.clear
|
|
1281
1302
|
@model_fallback_chains.clear
|
|
1303
|
+
@model_denylist.clear
|
|
1282
1304
|
@model_rate_limit_info&.clear
|
|
1283
1305
|
@model_history&.clear
|
|
1284
1306
|
initialize_fallback_chains
|
|
@@ -1394,9 +1416,9 @@ module Aidp
|
|
|
1394
1416
|
|
|
1395
1417
|
# Execute a prompt with a specific provider
|
|
1396
1418
|
def execute_with_provider(provider_type, prompt, options = {})
|
|
1397
|
-
# Extract model
|
|
1419
|
+
# Extract model from options if provided
|
|
1398
1420
|
model_name = options.delete(:model)
|
|
1399
|
-
|
|
1421
|
+
retry_on_rate_limit = options.delete(:retry_on_rate_limit) != false # Default true
|
|
1400
1422
|
|
|
1401
1423
|
# Create provider factory instance
|
|
1402
1424
|
provider_factory = ProviderFactory.new
|
|
@@ -1415,11 +1437,10 @@ module Aidp
|
|
|
1415
1437
|
Aidp.logger.debug("provider_manager", "Executing with provider",
|
|
1416
1438
|
provider: provider_type,
|
|
1417
1439
|
model: model_name,
|
|
1418
|
-
tier: tier,
|
|
1419
1440
|
prompt_length: prompt.length)
|
|
1420
1441
|
|
|
1421
|
-
# Execute the prompt with the provider
|
|
1422
|
-
result = provider.send_message(prompt: prompt, session: nil
|
|
1442
|
+
# Execute the prompt with the provider
|
|
1443
|
+
result = provider.send_message(prompt: prompt, session: nil)
|
|
1423
1444
|
|
|
1424
1445
|
# Return structured result
|
|
1425
1446
|
{
|
|
@@ -1436,6 +1457,42 @@ module Aidp
|
|
|
1436
1457
|
}
|
|
1437
1458
|
rescue => e
|
|
1438
1459
|
log_rescue(e, component: "provider_manager", action: "execute_with_provider", fallback: "error_result", provider: provider_type, model: model_name, prompt_length: prompt.length)
|
|
1460
|
+
|
|
1461
|
+
if unsupported_model_error?(e, model_name)
|
|
1462
|
+
deny_model(provider_type, model_name, error: e)
|
|
1463
|
+
end
|
|
1464
|
+
|
|
1465
|
+
# Detect rate limit / quota errors and attempt fallback
|
|
1466
|
+
error_message = e.message.to_s.downcase
|
|
1467
|
+
is_rate_limit = error_message.include?("rate limit") ||
|
|
1468
|
+
error_message.include?("quota") ||
|
|
1469
|
+
error_message.include?("limit reached") ||
|
|
1470
|
+
error_message.include?("resource exhausted") ||
|
|
1471
|
+
error_message.include?("too many requests")
|
|
1472
|
+
|
|
1473
|
+
if is_rate_limit && retry_on_rate_limit
|
|
1474
|
+
Aidp.logger.warn("provider_manager", "Rate limit detected, attempting fallback",
|
|
1475
|
+
provider: provider_type,
|
|
1476
|
+
model: model_name,
|
|
1477
|
+
error: e.message)
|
|
1478
|
+
|
|
1479
|
+
# Attempt to switch to fallback provider
|
|
1480
|
+
fallback_provider = switch_provider_for_error("rate_limit", {
|
|
1481
|
+
original_provider: provider_type,
|
|
1482
|
+
model: model_name,
|
|
1483
|
+
error_message: e.message
|
|
1484
|
+
})
|
|
1485
|
+
|
|
1486
|
+
if fallback_provider && fallback_provider != provider_type
|
|
1487
|
+
Aidp.logger.info("provider_manager", "Retrying with fallback provider",
|
|
1488
|
+
original: provider_type,
|
|
1489
|
+
fallback: fallback_provider)
|
|
1490
|
+
|
|
1491
|
+
# Retry with fallback provider (disable retry to prevent infinite loop)
|
|
1492
|
+
return execute_with_provider(fallback_provider, prompt, options.merge(retry_on_rate_limit: false))
|
|
1493
|
+
end
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1439
1496
|
# Return error result
|
|
1440
1497
|
{
|
|
1441
1498
|
status: "error",
|
|
@@ -1552,6 +1609,18 @@ module Aidp
|
|
|
1552
1609
|
((order[a] || 0) >= (order[b] || 0)) ? a : b
|
|
1553
1610
|
end
|
|
1554
1611
|
|
|
1612
|
+
# Detect unsupported/invalid model errors to avoid reusing the model
|
|
1613
|
+
def unsupported_model_error?(error, model_name)
|
|
1614
|
+
return false if model_name.nil?
|
|
1615
|
+
|
|
1616
|
+
message = error&.message.to_s.downcase
|
|
1617
|
+
return false if message.empty?
|
|
1618
|
+
|
|
1619
|
+
(message.include?("unsupported") && message.include?("model")) ||
|
|
1620
|
+
(message.include?("model") && message.include?("not supported")) ||
|
|
1621
|
+
message.include?("invalid model")
|
|
1622
|
+
end
|
|
1623
|
+
|
|
1555
1624
|
public
|
|
1556
1625
|
|
|
1557
1626
|
# Log provider switch
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require_relative "../rescue_logging"
|
|
6
|
+
require_relative "../util"
|
|
6
7
|
|
|
7
8
|
module Aidp
|
|
8
9
|
module Harness
|
|
@@ -14,9 +15,10 @@ module Aidp
|
|
|
14
15
|
attr_reader :project_dir, :metrics_file, :rate_limit_file
|
|
15
16
|
|
|
16
17
|
def initialize(project_dir)
|
|
17
|
-
|
|
18
|
-
@
|
|
19
|
-
@
|
|
18
|
+
# Store metrics at the repository root so different worktrees/modes share state
|
|
19
|
+
@project_dir = Aidp::Util.find_project_root(project_dir)
|
|
20
|
+
@metrics_file = File.join(@project_dir, ".aidp", "provider_metrics.yml")
|
|
21
|
+
@rate_limit_file = File.join(@project_dir, ".aidp", "provider_rate_limits.yml")
|
|
20
22
|
ensure_directory
|
|
21
23
|
end
|
|
22
24
|
|