actionagent 1.8.1 → 1.9.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/app/assets/builds/action_agent.css +1 -1
- data/app/assets/builds/action_agent.js +76 -73
- data/app/assets/builds/action_agent_recorder.js +78 -0
- data/app/assets/builds/action_agent_replay.js +226 -0
- data/app/controllers/action_agent/api/agent_runs_controller.rb +3 -1
- data/app/controllers/action_agent/api/agents_controller.rb +1 -0
- data/app/controllers/action_agent/api/api_keys_controller.rb +12 -2
- data/app/controllers/action_agent/api/base_controller.rb +63 -2
- data/app/controllers/action_agent/api/claude_logins_controller.rb +93 -0
- data/app/controllers/action_agent/api/code_sessions_controller.rb +53 -6
- data/app/controllers/action_agent/api/dashboard_assistant_controller.rb +2 -2
- data/app/controllers/action_agent/api/draft_pull_requests_controller.rb +248 -0
- data/app/controllers/action_agent/api/evaluation_fixes_controller.rb +27 -0
- data/app/controllers/action_agent/api/evaluations_controller.rb +9 -4
- data/app/controllers/action_agent/api/explorations_controller.rb +213 -0
- data/app/controllers/action_agent/api/github_app_manifests_controller.rb +125 -0
- data/app/controllers/action_agent/api/github_connections_controller.rb +40 -2
- data/app/controllers/action_agent/api/github_installations_controller.rb +280 -0
- data/app/controllers/action_agent/api/input_requests_controller.rb +77 -0
- data/app/controllers/action_agent/api/mcp_controller.rb +46 -12
- data/app/controllers/action_agent/api/members_controller.rb +36 -0
- data/app/controllers/action_agent/api/project_explorations_controller.rb +80 -0
- data/app/controllers/action_agent/api/project_install_pull_requests_controller.rb +305 -0
- data/app/controllers/action_agent/api/project_secrets_controller.rb +96 -0
- data/app/controllers/action_agent/api/project_setup_controller.rb +151 -0
- data/app/controllers/action_agent/api/project_sign_ins_controller.rb +112 -0
- data/app/controllers/action_agent/api/projects_controller.rb +512 -0
- data/app/controllers/action_agent/api/provider_keys_controller.rb +176 -24
- data/app/controllers/action_agent/api/provider_models_controller.rb +37 -15
- data/app/controllers/action_agent/api/recording_event_ingest_controller.rb +67 -0
- data/app/controllers/action_agent/api/sandbox_browser_tickets_controller.rb +50 -0
- data/app/controllers/action_agent/api/sandbox_browsers_controller.rb +65 -0
- data/app/controllers/action_agent/api/sandboxes_controller.rb +140 -8
- data/app/controllers/action_agent/api/scenario_catalog_entries_controller.rb +188 -0
- data/app/controllers/action_agent/api/scenario_catalogs_controller.rb +302 -0
- data/app/controllers/action_agent/api/session_recordings_controller.rb +165 -34
- data/app/controllers/action_agent/api/sessions_controller.rb +111 -0
- data/app/controllers/action_agent/api/telemetry_keys_controller.rb +21 -0
- data/app/controllers/action_agent/dashboard_controller.rb +17 -6
- data/app/controllers/action_agent/session_player_controller.rb +58 -0
- data/app/controllers/concerns/action_agent/api/agent_serialization.rb +1 -0
- data/app/controllers/concerns/action_agent/api/mcp_dashboard_tools.rb +623 -18
- data/app/controllers/concerns/action_agent/api/project_browser.rb +46 -0
- data/app/controllers/concerns/action_agent/api/project_secret_authorization.rb +57 -0
- data/app/jobs/action_agent/agent_execution_job.rb +9 -39
- data/app/jobs/action_agent/agent_resume_job.rb +84 -0
- data/app/jobs/action_agent/code_session_job.rb +26 -2
- data/app/jobs/action_agent/draft_pull_request_job.rb +43 -0
- data/app/jobs/action_agent/exploration_job.rb +80 -0
- data/app/jobs/action_agent/input_request_expiry_job.rb +22 -0
- data/app/jobs/action_agent/project_evaluation_job.rb +59 -0
- data/app/jobs/action_agent/project_setup_job.rb +21 -0
- data/app/jobs/action_agent/sandbox_cleanup_job.rb +20 -3
- data/app/jobs/action_agent/sandbox_provision_job.rb +77 -10
- data/app/jobs/action_agent/sandbox_run_job.rb +9 -37
- data/app/jobs/action_agent/verify_evaluation_fix_job.rb +37 -0
- data/app/models/action_agent/agent.rb +39 -26
- data/app/models/action_agent/agent_run.rb +134 -23
- data/app/models/action_agent/agent_template.rb +35 -1
- data/app/models/action_agent/api_key.rb +14 -0
- data/app/models/action_agent/catalog_scenario.rb +75 -0
- data/app/models/action_agent/code_session.rb +16 -4
- data/app/models/action_agent/draft_pull_request.rb +131 -0
- data/app/models/action_agent/evaluation.rb +84 -0
- data/app/models/action_agent/exploration.rb +847 -0
- data/app/models/action_agent/github_installation.rb +159 -0
- data/app/models/action_agent/input_request.rb +291 -0
- data/app/models/action_agent/project.rb +789 -0
- data/app/models/action_agent/project_secret.rb +339 -0
- data/app/models/action_agent/provider_key.rb +200 -3
- data/app/models/action_agent/recording_event.rb +145 -0
- data/app/models/action_agent/recording_snapshot.rb +7 -7
- data/app/models/action_agent/sandbox_session.rb +297 -14
- data/app/models/action_agent/scenario_catalog.rb +172 -0
- data/app/models/action_agent/scenario_product.rb +57 -0
- data/app/models/action_agent/scenario_set.rb +138 -0
- data/app/models/action_agent/session_recording.rb +121 -14
- data/app/models/action_agent/telemetry_trace.rb +74 -19
- data/app/models/concerns/action_agent/ownable.rb +84 -13
- data/app/serializers/action_agent/input_request_serializer.rb +47 -0
- data/app/serializers/action_agent/scenario_catalog_serializer.rb +23 -0
- data/app/serializers/action_agent/session_summary_serializer.rb +146 -0
- data/app/services/action_agent/agent_execution_service.rb +450 -72
- data/app/services/action_agent/agent_sync.rb +5 -2
- data/app/services/action_agent/agent_tool_roster.rb +19 -4
- data/app/services/action_agent/agent_toolbox.rb +215 -9
- data/app/services/action_agent/browser_live_ticket.rb +78 -0
- data/app/services/action_agent/browser_sidecar.rb +213 -0
- data/app/services/action_agent/browser_sign_in.rb +163 -0
- data/app/services/action_agent/browser_tool_redaction.rb +92 -0
- data/app/services/action_agent/claude_code_auth.rb +70 -4
- data/app/services/action_agent/dashboard_assistant_service.rb +24 -10
- data/app/services/action_agent/draft_pull_request_publisher.rb +719 -0
- data/app/services/action_agent/evaluation_fix.rb +101 -0
- data/app/services/action_agent/evaluation_fix_comparison.rb +66 -0
- data/app/services/action_agent/evaluation_runner_service.rb +4 -5
- data/app/services/action_agent/evaluation_standing.rb +15 -8
- data/app/services/action_agent/explorer_execution_service.rb +517 -0
- data/app/services/action_agent/github_client.rb +405 -64
- data/app/services/action_agent/live_updates.rb +31 -0
- data/app/services/action_agent/local_sandbox_backend.rb +1218 -83
- data/app/services/action_agent/local_sandbox_claude_login.rb +151 -0
- data/app/services/action_agent/mcp_client.rb +6 -2
- data/app/services/action_agent/mcp_recording_middleware.rb +104 -145
- data/app/services/action_agent/mcp_tool_dispatcher.rb +36 -13
- data/app/services/action_agent/mock_sandbox_backend.rb +3 -1
- data/app/services/action_agent/playwright_mcp_client.rb +7 -0
- data/app/services/action_agent/project_boot_fix.rb +309 -0
- data/app/services/action_agent/project_capabilities.rb +200 -0
- data/app/services/action_agent/project_install_pull_request.rb +225 -0
- data/app/services/action_agent/project_preflight.rb +178 -0
- data/app/services/action_agent/project_secret_discovery.rb +142 -0
- data/app/services/action_agent/project_setup.rb +429 -0
- data/app/services/action_agent/provider_credentials.rb +102 -0
- data/app/services/action_agent/recording_event_ingest.rb +181 -0
- data/app/services/action_agent/runtime_tool_roster.rb +52 -0
- data/app/services/action_agent/sample_data/conference_ticket.rb +516 -0
- data/app/services/action_agent/sandbox_boot_spec.rb +558 -0
- data/app/services/action_agent/sandbox_browser.rb +299 -0
- data/app/services/action_agent/sandbox_browser_driver.rb +85 -0
- data/app/services/action_agent/sandbox_credential_paths.rb +26 -0
- data/app/services/action_agent/sandbox_mail.rb +103 -0
- data/app/services/action_agent/sandbox_manifest.rb +166 -13
- data/app/services/action_agent/sandbox_orchestrator.rb +276 -22
- data/app/services/action_agent/sandbox_patch.rb +253 -0
- data/app/services/action_agent/scenario_catalog_import.rb +255 -0
- data/app/services/action_agent/scenario_catalog_repository_import.rb +81 -0
- data/app/services/action_agent/scenario_evaluation_runner.rb +119 -16
- data/app/services/action_agent/secret_scrubber.rb +33 -4
- data/app/services/action_agent/session_index.rb +270 -0
- data/app/services/action_agent/session_recording_service.rb +11 -0
- data/app/services/action_agent/session_timeline.rb +505 -0
- data/app/views/action_agent/github_app_manifests/created.html.erb +55 -0
- data/app/views/action_agent/session_player/show.html.erb +19 -0
- data/config/routes.rb +152 -1
- data/lib/action_agent/claude_login_process.rb +107 -0
- data/lib/action_agent/engine.rb +55 -5
- data/lib/action_agent/secret_requests.rb +80 -0
- data/lib/action_agent/version.rb +1 -1
- data/lib/action_agent.rb +507 -22
- data/lib/generators/action_agent/install_generator.rb +37 -0
- data/lib/generators/action_agent/templates/action_agent.rb.erb +158 -0
- data/lib/generators/action_agent/templates/migrations/015_add_provider_key_scope.rb.erb +72 -0
- data/lib/generators/action_agent/templates/migrations/016_create_active_agent_input_requests.rb.erb +85 -0
- data/lib/generators/action_agent/templates/migrations/017_create_active_agent_recording_events.rb.erb +57 -0
- data/lib/generators/action_agent/templates/migrations/018_add_browser_to_active_agent_sandbox_sessions.rb.erb +25 -0
- data/lib/generators/action_agent/templates/migrations/019_create_active_agent_projects.rb.erb +79 -0
- data/lib/generators/action_agent/templates/migrations/020_add_github_app_installations.rb.erb +62 -0
- data/lib/generators/action_agent/templates/migrations/021_create_active_agent_explorations.rb.erb +63 -0
- data/lib/generators/action_agent/templates/migrations/023_create_active_agent_draft_pull_requests.rb.erb +75 -0
- data/lib/generators/action_agent/templates/migrations/024_add_kind_to_active_agent_project_secrets.rb.erb +14 -0
- data/lib/generators/action_agent/templates/migrations/025_create_active_agent_scenario_catalogs.rb.erb +110 -0
- data/lib/generators/action_agent/templates/migrations/026_add_evaluation_fixes_to_code_sessions.rb.erb +21 -0
- data/lib/tasks/action_agent.rake +33 -0
- data/lib/tasks/browser.rake +22 -0
- data/lib/tasks/sandbox.rake +1 -1
- data/lib/tasks/scenario_catalogs.rake +71 -0
- metadata +96 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
# A bounded, server-built brief for one persisted fix card. Request bodies
|
|
5
|
+
# choose an existing card and a subset of its scope; they cannot inject a
|
|
6
|
+
# different prompt, agent, scenario or model into the automatic fix loop.
|
|
7
|
+
class EvaluationFix
|
|
8
|
+
class Invalid < StandardError; end
|
|
9
|
+
attr_reader :run, :item
|
|
10
|
+
|
|
11
|
+
def initialize(run, requested)
|
|
12
|
+
@run = run
|
|
13
|
+
raise Invalid, "Only completed scenario runs have implementable fixes" unless run.complete? && run.evaluation.scenario_suite?
|
|
14
|
+
requested = requested.to_h.deep_stringify_keys
|
|
15
|
+
original = EvaluationSerializer.fix_items(run).find do |candidate|
|
|
16
|
+
candidate = candidate.deep_stringify_keys
|
|
17
|
+
candidate["kind"] == requested["kind"] &&
|
|
18
|
+
(requested["kind"] == "instruction" ? candidate["quote"] == requested["quote"] : candidate["fault"] == requested["fault"])
|
|
19
|
+
end
|
|
20
|
+
raise Invalid, "This fix card no longer exists on the selected run" unless original && %w[fault instruction].include?(requested["kind"])
|
|
21
|
+
|
|
22
|
+
@item = original.deep_stringify_keys.slice("kind", "fault", "quote", "recommendation", "scenario_keys", "models")
|
|
23
|
+
keys = Array(requested["scenario_keys"])
|
|
24
|
+
allowed_models = Array(@item["models"]).presence || model_labels
|
|
25
|
+
models = Array(requested["models"]).presence || allowed_models
|
|
26
|
+
unless keys.any? && (keys - Array(@item["scenario_keys"])).empty? && models.any? && (models - allowed_models).empty?
|
|
27
|
+
raise Invalid, "Choose scenarios and models from this fix card"
|
|
28
|
+
end
|
|
29
|
+
@item.merge!("scenario_keys" => keys.uniq, "models" => models.uniq)
|
|
30
|
+
@item["scenarios"] = run.scenario_results.includes(:scenario).filter_map do |result|
|
|
31
|
+
snapshot = result.evaluated_scenario.slice("key", "prompt", "expectations", "notes", "enabled")
|
|
32
|
+
snapshot if keys.include?(snapshot["key"])
|
|
33
|
+
end.uniq
|
|
34
|
+
self.class.check_scenarios!(run.evaluation, @item)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.check_scenarios!(evaluation, item)
|
|
38
|
+
Array(item["scenarios"]).each do |snapshot|
|
|
39
|
+
current = evaluation.scenarios.find_by(key: snapshot.fetch("key"))
|
|
40
|
+
unless current&.enabled? && current.as_json_summary.stringify_keys.slice(*snapshot.keys) == snapshot
|
|
41
|
+
raise Invalid, "The selected scenarios changed since this run. Run the evaluation again before implementing a fix."
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def model_labels
|
|
47
|
+
run.scenario_results.map { |result| [ result.provider, result.model ].compact_blank.join("/") }.uniq
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# How much of each piece of evidence the brief quotes, at full size. A
|
|
51
|
+
# card with many scenarios and models, or a retry, shrinks them all
|
|
52
|
+
# (see #prompt) rather than refusing the card.
|
|
53
|
+
EXCERPTS = { prompt: 1000, expectations: 1000, diagnosis: 1200, answer: 1000,
|
|
54
|
+
previous_result: 1500, previous_diff: 5000, previous_results: 3000 }.freeze
|
|
55
|
+
SCALES = [ 1, 0.5, 0.25, 0.1 ].freeze
|
|
56
|
+
|
|
57
|
+
def prompt(previous: nil)
|
|
58
|
+
SCALES.each do |scale|
|
|
59
|
+
text = brief(previous, EXCERPTS.transform_values { |size| (size * scale).to_i })
|
|
60
|
+
return text if text.length <= CodeSession::MAX_PROMPT_CHARACTERS
|
|
61
|
+
end
|
|
62
|
+
raise Invalid, "This fix has too much context for one session; run the evaluation on fewer scenarios or models"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def brief(previous, limit)
|
|
68
|
+
agent = run.evaluation.agent
|
|
69
|
+
name = agent.agent_class_name.presence || agent.telemetry_agent_class
|
|
70
|
+
path = name.to_s.underscore
|
|
71
|
+
lines = [ "# Implement one evaluation fix", "", "Agent: #{name}", "Evaluation: #{run.evaluation.name} · run #{run.id}",
|
|
72
|
+
"", "## What to fix", item["fault"], item["recommendation"], item["quote"],
|
|
73
|
+
"", "## Where to look", "- app/agents/#{path}.rb and its tools.",
|
|
74
|
+
"- app/views/agents/#{path.delete_suffix('_agent')}/ and app/views/#{path}/ for instructions and prompts.",
|
|
75
|
+
"- Follow the checkout's own instructions and locate the source of a synced agent before editing.",
|
|
76
|
+
"", "## Failing scenarios" ]
|
|
77
|
+
run.scenario_results.includes(:scenario).each do |result|
|
|
78
|
+
scenario = result.evaluated_scenario
|
|
79
|
+
next unless item["scenario_keys"].include?(scenario["key"])
|
|
80
|
+
label = [ result.provider, result.model ].compact_blank.join("/")
|
|
81
|
+
next unless item["models"].include?(label) || item["models"].include?(result.model)
|
|
82
|
+
lines += [ "", "### #{scenario['key']} · #{label}",
|
|
83
|
+
"Prompt: #{scenario['prompt'].to_s.truncate(limit[:prompt])}", "Expectations: #{scenario['expectations'].to_json.truncate(limit[:expectations])}",
|
|
84
|
+
"Before: #{result.status} · score #{result.score} · fault #{result.fault}",
|
|
85
|
+
"Diagnosis: #{result.diagnosis.to_json.truncate(limit[:diagnosis])}", "Answer: #{result.output.to_s.truncate(limit[:answer])}" ]
|
|
86
|
+
end
|
|
87
|
+
if previous
|
|
88
|
+
lines += [ "", "## Previous attempt", previous.result.to_s.truncate(limit[:previous_result]),
|
|
89
|
+
"Previous diff:", previous.diff.to_s.truncate(limit[:previous_diff]),
|
|
90
|
+
"Verification results: #{EvaluationFixComparison.new(previous).rows.to_json.truncate(limit[:previous_results])}" ]
|
|
91
|
+
end
|
|
92
|
+
lines += [ "", "## Verify", "Change the agent, not the scenarios or their expectations.",
|
|
93
|
+
"Run the relevant checkout tests. Do not commit or push.",
|
|
94
|
+
"The dashboard will re-run these exact scenarios and models against this sandbox when you finish.",
|
|
95
|
+
"Do not call dashboard URLs or dashboard MCP tools from this sandbox.",
|
|
96
|
+
"Never read or copy the sandbox's Claude configuration or credentials.",
|
|
97
|
+
"Summarize the change and any remaining limitations." ]
|
|
98
|
+
lines.compact.join("\n")
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
class EvaluationFixComparison
|
|
5
|
+
def initialize(session)
|
|
6
|
+
@session = session
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.change(before, after)
|
|
10
|
+
return "pending" unless after
|
|
11
|
+
return after.passed? ? "added_pass" : "added_fail" unless before
|
|
12
|
+
return after.passed? ? "unchanged_pass" : "regressed" if before.passed?
|
|
13
|
+
after.passed? ? "fixed" : "still_failing"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def rows
|
|
17
|
+
before = indexed(@session.evaluation_run)
|
|
18
|
+
after = indexed(@session.verification_run)
|
|
19
|
+
(before.keys | after.keys).map do |key|
|
|
20
|
+
first, last = before[key], after[key]
|
|
21
|
+
{ scenario_key: key[0], model: key[1], change: self.class.change(first, last),
|
|
22
|
+
before: first&.status, after: last&.status, before_score: first&.score&.to_f, after_score: last&.score&.to_f,
|
|
23
|
+
score_change: first&.score && last&.score ? (last.score - first.score).to_f.round(3) : nil,
|
|
24
|
+
before_fault: first&.fault, fault: last&.fault }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def summary
|
|
29
|
+
{ run_id: @session.verification_run_id, status: @session.verification_run&.status,
|
|
30
|
+
error: @session.verification_error || @session.verification_run&.error_message,
|
|
31
|
+
counts: rows.group_by { |row| row[:change] }.transform_values(&:size), rows: rows }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The before/after section a fix's draft pull request carries, cut to
|
|
35
|
+
# +limit+ characters from the end (the session summary goes first), so
|
|
36
|
+
# appending it never pushes a body over DraftPullRequest's maximum.
|
|
37
|
+
def pull_request_body(mount:, limit: DraftPullRequest::MAX_BODY_CHARACTERS)
|
|
38
|
+
base = @session.evaluation_run
|
|
39
|
+
title = @session.fix_item["quote"].presence || @session.fix_item["fault"].to_s.humanize
|
|
40
|
+
lines = [ "## Evaluation fix", title, "",
|
|
41
|
+
"[Original run](#{mount}/evaluations/#{base.evaluation_id}/runs/#{base.id})",
|
|
42
|
+
"[Verification run](#{mount}/evaluations/#{base.evaluation_id}/runs/#{@session.verification_run_id})", "",
|
|
43
|
+
"| Scenario / model | Before | After | Score change | Fault |", "| --- | --- | --- | --- | --- |" ]
|
|
44
|
+
rows.each do |row|
|
|
45
|
+
cells = [ "#{row[:scenario_key]} / #{row[:model]}", row[:before], row[:after] || "pending", row[:score_change], row[:fault] || "—" ]
|
|
46
|
+
lines << "| #{cells.map { |cell| cell.to_s.gsub('|', '\\|').gsub(/[\r\n]/, ' ') }.join(' | ')} |"
|
|
47
|
+
end
|
|
48
|
+
lines += [ "", "## Session summary", @session.result.to_s, "", "Code session ##{@session.id}. Review the diff and full-suite regressions before merging." ]
|
|
49
|
+
SecretScrubber.scrub(lines.join("\n"), @session.secrets).truncate([ limit, 0 ].max, omission: "\n…")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def indexed(run)
|
|
55
|
+
return {} unless run
|
|
56
|
+
item = @session.fix_item || {}
|
|
57
|
+
run.scenario_results.includes(:scenario).each_with_object({}) do |result, index|
|
|
58
|
+
key = result.evaluated_scenario["key"]
|
|
59
|
+
model = [ result.provider, result.model ].compact_blank.join("/")
|
|
60
|
+
next unless Array(item["scenario_keys"]).include?(key) &&
|
|
61
|
+
(Array(item["models"]).include?(model) || Array(item["models"]).include?(result.model))
|
|
62
|
+
index[[ key, model ]] = result
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -594,13 +594,12 @@ module ActionAgent
|
|
|
594
594
|
|
|
595
595
|
# The evaluated agent owner's credential for +name+ (Settings ->
|
|
596
596
|
# Provider API Keys, or whatever the host app resolves); preferred over
|
|
597
|
-
# the host's config/active_agent.yml credentials for the judge.
|
|
597
|
+
# the host's config/active_agent.yml credentials for the judge. Resolved
|
|
598
|
+
# with no actor: the judge scores for the organization, so it never runs
|
|
599
|
+
# on a member's personal key.
|
|
598
600
|
def owner_provider_options(name)
|
|
599
601
|
@owner_provider_options ||= {}
|
|
600
|
-
@owner_provider_options[name.to_s] ||=
|
|
601
|
-
from_host = ActionAgent.provider_credentials(owner, name.to_s)
|
|
602
|
-
from_host.presence || ProviderKey.for_owner(owner).find_by(provider: name.to_s)&.generation_options || {}
|
|
603
|
-
end
|
|
602
|
+
@owner_provider_options[name.to_s] ||= ProviderCredentials.resolve(owner: owner, actor: nil, provider: name).options
|
|
604
603
|
end
|
|
605
604
|
|
|
606
605
|
def owner
|
|
@@ -17,16 +17,19 @@ module ActionAgent
|
|
|
17
17
|
# archived — the evaluation is archived (Evaluation#archive!)
|
|
18
18
|
# none — no complete run
|
|
19
19
|
#
|
|
20
|
-
# Only edits
|
|
21
|
-
#
|
|
22
|
-
# the
|
|
23
|
-
#
|
|
24
|
-
# agent
|
|
20
|
+
# Only edits that change how a run goes count as a new version: what the
|
|
21
|
+
# model is given (the instructions, the action prompts, the tools, the MCP
|
|
22
|
+
# servers, the model config and the response format), and the tools whose
|
|
23
|
+
# calls wait for a person's approval, which pause a replay. A dashboard
|
|
24
|
+
# edit to the agent's appearance cuts an AgentVersion like any other edit,
|
|
25
|
+
# but the run before it scored the same agent and stays current.
|
|
25
26
|
class EvaluationStanding
|
|
26
27
|
STANDINGS = %w[current stale unrecorded archived none].freeze
|
|
27
28
|
VERSION_STATES = %w[current earlier unrecorded].freeze
|
|
28
29
|
# The configuration snapshot keys the model is given.
|
|
29
30
|
MODEL_FACING = %w[instructions action_prompts tools mcp_servers model_config response_format].freeze
|
|
31
|
+
# The configuration snapshot keys that change how a run goes.
|
|
32
|
+
SCORED = (MODEL_FACING + %w[approval_required_tools]).freeze
|
|
30
33
|
|
|
31
34
|
# Gives every evaluation its standing with one query per table rather
|
|
32
35
|
# than one per evaluation: the agents' latest versions and whether each
|
|
@@ -95,7 +98,7 @@ module ActionAgent
|
|
|
95
98
|
|
|
96
99
|
latest = latest_version
|
|
97
100
|
return "current" if latest.nil? || latest.id == version.id
|
|
98
|
-
return "current" if
|
|
101
|
+
return "current" if scored_configuration(version) == scored_configuration(latest)
|
|
99
102
|
|
|
100
103
|
"earlier"
|
|
101
104
|
end
|
|
@@ -137,9 +140,13 @@ module ActionAgent
|
|
|
137
140
|
@has_releases = evaluation.agent&.agent_versions&.releases&.exists? || false
|
|
138
141
|
end
|
|
139
142
|
|
|
140
|
-
|
|
143
|
+
# A snapshot taken before an agent had an approval list reads as an empty
|
|
144
|
+
# one, so the list's arrival alone cuts no new version.
|
|
145
|
+
def scored_configuration(version)
|
|
141
146
|
snapshot = version.configuration_snapshot.to_h.stringify_keys
|
|
142
|
-
|
|
147
|
+
configuration = SCORED.to_h { |key| [ key, snapshot[key] ] }
|
|
148
|
+
configuration["approval_required_tools"] = Array(configuration["approval_required_tools"])
|
|
149
|
+
configuration
|
|
143
150
|
end
|
|
144
151
|
end
|
|
145
152
|
end
|