actionagent 1.8.0 → 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 +71 -12
- 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 +58 -7
- 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 +148 -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 +627 -20
- 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 +39 -10
- 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 +72 -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 +21 -5
- data/app/models/action_agent/draft_pull_request.rb +131 -0
- data/app/models/action_agent/evaluation.rb +148 -0
- data/app/models/action_agent/evaluation_run.rb +143 -53
- data/app/models/action_agent/evaluation_scenario_result.rb +15 -3
- 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/model_pricing.rb +214 -34
- 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 +207 -4
- 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 +300 -16
- 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/evaluation_serializer.rb +41 -10
- 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_scorecard.rb +50 -21
- 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/codex_session_events.rb +37 -0
- 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_report_import.rb +84 -5
- data/app/services/action_agent/evaluation_run_cost.rb +609 -0
- data/app/services/action_agent/evaluation_runner_service.rb +29 -10
- data/app/services/action_agent/evaluation_standing.rb +152 -0
- 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 +1263 -102
- 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 +290 -23
- 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 +121 -17
- 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 +153 -2
- 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 +513 -22
- data/lib/generators/action_agent/install_generator.rb +45 -5
- data/lib/generators/action_agent/templates/action_agent.rb.erb +161 -0
- data/lib/generators/action_agent/templates/add_code_session_runner.rb.erb +9 -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 +100 -2
data/lib/generators/action_agent/templates/migrations/021_create_active_agent_explorations.rb.erb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Explorations: a walk through a project's running app, by the engine's
|
|
4
|
+
# explorer agent or by an outside agent, and the candidate scenarios it
|
|
5
|
+
# proposed for review. Accepted candidates are merged into the project's
|
|
6
|
+
# evaluation; the rest stay here with their verdicts and provenance.
|
|
7
|
+
#
|
|
8
|
+
# Table names follow ActionAgent.table_name_prefix, and JSON columns are
|
|
9
|
+
# jsonb on PostgreSQL and json elsewhere, the same way as
|
|
10
|
+
# create_active_agent_dashboard_tables.
|
|
11
|
+
class CreateActiveAgentExplorations < ActiveRecord::Migration<%= migration_version %>
|
|
12
|
+
def change
|
|
13
|
+
prefix = ActionAgent.table_name_prefix
|
|
14
|
+
|
|
15
|
+
create_table "#{prefix}explorations" do |t|
|
|
16
|
+
# At least one is set: a project's explorations merge into the
|
|
17
|
+
# project's evaluation, and one without a project into its own.
|
|
18
|
+
t.bigint :project_id
|
|
19
|
+
t.bigint :evaluation_id
|
|
20
|
+
t.bigint :agent_run_id
|
|
21
|
+
t.bigint :sandbox_session_id
|
|
22
|
+
t.bigint :session_recording_id
|
|
23
|
+
# explorer: the engine's explorer agent; external: candidates an
|
|
24
|
+
# outside agent submitted.
|
|
25
|
+
t.string :source, null: false, default: "external"
|
|
26
|
+
t.string :status, null: false, default: "pending"
|
|
27
|
+
t.string :start_url
|
|
28
|
+
t.string :stop_reason
|
|
29
|
+
t.text :error_message
|
|
30
|
+
t.datetime :started_at
|
|
31
|
+
t.datetime :finished_at
|
|
32
|
+
# { minutes, steps, cost }, the limits and what was used of them.
|
|
33
|
+
t.column :budget, json_type, **json_default({})
|
|
34
|
+
t.column :usage, json_type, **json_default({})
|
|
35
|
+
t.column :candidates, json_type, **json_default([])
|
|
36
|
+
t.bigint :account_id
|
|
37
|
+
t.bigint :user_id
|
|
38
|
+
t.timestamps
|
|
39
|
+
t.index :project_id
|
|
40
|
+
t.index :evaluation_id
|
|
41
|
+
t.index :account_id
|
|
42
|
+
t.index :user_id
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def json_type
|
|
49
|
+
@json_type ||= postgres? ? :jsonb : :json
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# MySQL rejects a default on a JSON column outright, so the column is
|
|
53
|
+
# created without one there.
|
|
54
|
+
def json_default(value, null: nil)
|
|
55
|
+
return {} unless postgres?
|
|
56
|
+
|
|
57
|
+
null.nil? ? { default: value } : { default: value, null: null }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def postgres?
|
|
61
|
+
connection.adapter_name.to_s.downcase.include?("postgres")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Draft pull requests opened from a checkout sandbox's changes (Settings ->
|
|
4
|
+
# Integrations -> Open draft PR), and the state GitHub last reported for each.
|
|
5
|
+
#
|
|
6
|
+
# Table names follow ActionAgent.table_name_prefix, and JSON columns are
|
|
7
|
+
# jsonb on PostgreSQL and json elsewhere, the same way as
|
|
8
|
+
# create_active_agent_dashboard_tables.
|
|
9
|
+
class CreateActiveAgentDraftPullRequests < ActiveRecord::Migration<%= migration_version %>
|
|
10
|
+
def change
|
|
11
|
+
prefix = ActionAgent.table_name_prefix
|
|
12
|
+
|
|
13
|
+
create_table "#{prefix}draft_pull_requests" do |t|
|
|
14
|
+
t.bigint :sandbox_session_id, null: false
|
|
15
|
+
# "owner/name" on GitHub.
|
|
16
|
+
t.string :repository, null: false
|
|
17
|
+
# The branch the pull request merges into, and the branch published.
|
|
18
|
+
t.string :base_branch
|
|
19
|
+
t.string :branch, null: false
|
|
20
|
+
# The commit the sandbox checked out, and the last commit published.
|
|
21
|
+
t.string :base_commit, null: false
|
|
22
|
+
t.string :head_commit
|
|
23
|
+
t.string :title, null: false
|
|
24
|
+
t.text :body
|
|
25
|
+
# The message of the last update's commit.
|
|
26
|
+
t.text :commit_message
|
|
27
|
+
# The files of the last publish, each { path, status, mode, digest }.
|
|
28
|
+
t.column :files, json_type, **json_default([])
|
|
29
|
+
# What the last publish did ("create", "update", "open_draft",
|
|
30
|
+
# "open_regular") and how it went ("queued", "publishing", "published",
|
|
31
|
+
# "draft_refused", "failed").
|
|
32
|
+
t.string :operation, null: false, default: "create"
|
|
33
|
+
t.string :status, null: false, default: "queued"
|
|
34
|
+
# Why the last publish failed: a word the dashboard acts on (see
|
|
35
|
+
# DraftPullRequestPublisher::Refused), and a message for the user.
|
|
36
|
+
t.string :error_code
|
|
37
|
+
t.text :error_message
|
|
38
|
+
# "app" (a GitHub App installation) or "oauth" (the OAuth connection).
|
|
39
|
+
t.string :credential_kind
|
|
40
|
+
t.integer :number
|
|
41
|
+
t.string :url
|
|
42
|
+
t.string :compare_url
|
|
43
|
+
# "open", "closed" or "merged", and whether it is a draft, as GitHub
|
|
44
|
+
# last reported them.
|
|
45
|
+
t.string :state
|
|
46
|
+
t.boolean :draft
|
|
47
|
+
t.datetime :last_checked_at
|
|
48
|
+
# The user who published, and the owner.
|
|
49
|
+
t.bigint :user_id
|
|
50
|
+
t.bigint :account_id
|
|
51
|
+
t.timestamps
|
|
52
|
+
t.index :sandbox_session_id, name: "index_#{prefix}draft_pull_requests_on_sandbox_session"
|
|
53
|
+
t.index :account_id, name: "index_#{prefix}draft_pull_requests_on_account"
|
|
54
|
+
t.index :user_id, name: "index_#{prefix}draft_pull_requests_on_user"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def json_type
|
|
61
|
+
@json_type ||= postgres? ? :jsonb : :json
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# MySQL rejects a default on a JSON column outright, so the column is
|
|
65
|
+
# created without one there.
|
|
66
|
+
def json_default(value, null: nil)
|
|
67
|
+
return {} unless postgres?
|
|
68
|
+
|
|
69
|
+
null.nil? ? { default: value } : { default: value, null: null }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def postgres?
|
|
73
|
+
connection.adapter_name.to_s.downcase.include?("postgres")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Project secrets of three kinds: an environment variable a boot hands to
|
|
4
|
+
# the app ("env", every secret before this migration), the credentials the
|
|
5
|
+
# explorer signs in with ("sign_in"), and a browser's saved sign-in
|
|
6
|
+
# ("storage_state"). Only env secrets reach the sandbox's environment.
|
|
7
|
+
#
|
|
8
|
+
# Table names follow ActionAgent.table_name_prefix, the same way as
|
|
9
|
+
# create_active_agent_dashboard_tables.
|
|
10
|
+
class AddKindToActiveAgentProjectSecrets < ActiveRecord::Migration<%= migration_version %>
|
|
11
|
+
def change
|
|
12
|
+
add_column "#{ActionAgent.table_name_prefix}project_secrets", :kind, :string, null: false, default: "env"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Scenario catalogs: the YAML catalogs of evaluation scenarios an owner keeps
|
|
4
|
+
# in the dashboard, each a set of products (an agent or a project under test)
|
|
5
|
+
# with named sets of scenarios. A catalog remembers the document it was
|
|
6
|
+
# imported from and its digest, so a re-import of the same file changes
|
|
7
|
+
# nothing, and a set remembers the evaluation it was last materialized as.
|
|
8
|
+
#
|
|
9
|
+
# Table names follow ActionAgent.table_name_prefix, and JSON columns are
|
|
10
|
+
# jsonb on PostgreSQL and json elsewhere, the same way as
|
|
11
|
+
# create_active_agent_dashboard_tables.
|
|
12
|
+
class CreateActiveAgentScenarioCatalogs < ActiveRecord::Migration<%= migration_version %>
|
|
13
|
+
def change
|
|
14
|
+
prefix = ActionAgent.table_name_prefix
|
|
15
|
+
|
|
16
|
+
create_table "#{prefix}scenario_catalogs" do |t|
|
|
17
|
+
t.string :key, null: false
|
|
18
|
+
t.string :name, null: false
|
|
19
|
+
t.text :description
|
|
20
|
+
# upload: pasted or uploaded YAML; repository: read from a connected
|
|
21
|
+
# repository at a ref; api: built through the API or MCP tools.
|
|
22
|
+
t.string :source_kind, null: false, default: "upload"
|
|
23
|
+
# For a repository: "owner/name@ref:path".
|
|
24
|
+
t.string :source_path
|
|
25
|
+
# SHA-256 of the canonical document, and the document itself.
|
|
26
|
+
t.string :digest
|
|
27
|
+
t.text :document
|
|
28
|
+
# When the document was last written to Active Storage, and the digest
|
|
29
|
+
# it carried then.
|
|
30
|
+
t.datetime :synced_at
|
|
31
|
+
t.string :synced_digest
|
|
32
|
+
t.column :metadata, json_type, **json_default({})
|
|
33
|
+
t.bigint :account_id
|
|
34
|
+
t.bigint :user_id
|
|
35
|
+
t.timestamps
|
|
36
|
+
t.index :key
|
|
37
|
+
t.index :account_id
|
|
38
|
+
t.index :user_id
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
create_table "#{prefix}scenario_products" do |t|
|
|
42
|
+
t.bigint :scenario_catalog_id, null: false
|
|
43
|
+
t.string :key, null: false
|
|
44
|
+
t.string :name, null: false
|
|
45
|
+
t.text :description
|
|
46
|
+
# What the product's sets run against: a dashboard agent, a project
|
|
47
|
+
# (whose sandbox the replays reach), or neither yet. agent_name keeps
|
|
48
|
+
# the document's `agent:` reference for an agent that is not here.
|
|
49
|
+
t.bigint :agent_id
|
|
50
|
+
t.bigint :project_id
|
|
51
|
+
t.string :agent_name
|
|
52
|
+
t.integer :position
|
|
53
|
+
t.column :metadata, json_type, **json_default({})
|
|
54
|
+
t.timestamps
|
|
55
|
+
t.index [ :scenario_catalog_id, :key ], unique: true
|
|
56
|
+
t.index :agent_id
|
|
57
|
+
t.index :project_id
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
create_table "#{prefix}scenario_sets" do |t|
|
|
61
|
+
t.bigint :scenario_product_id, null: false
|
|
62
|
+
t.string :key, null: false
|
|
63
|
+
t.string :name, null: false
|
|
64
|
+
t.text :description
|
|
65
|
+
# { kind, model }: the judge of the evaluation the set becomes.
|
|
66
|
+
t.column :judge, json_type, **json_default({})
|
|
67
|
+
t.column :criteria, json_type, **json_default([])
|
|
68
|
+
t.column :metadata, json_type, **json_default({})
|
|
69
|
+
# The evaluation this set was last materialized as, if any.
|
|
70
|
+
t.bigint :evaluation_id
|
|
71
|
+
t.integer :position
|
|
72
|
+
t.timestamps
|
|
73
|
+
t.index [ :scenario_product_id, :key ], unique: true
|
|
74
|
+
t.index :evaluation_id
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
create_table "#{prefix}catalog_scenarios" do |t|
|
|
78
|
+
t.bigint :scenario_set_id, null: false
|
|
79
|
+
t.string :key, null: false
|
|
80
|
+
t.text :prompt, null: false
|
|
81
|
+
t.text :notes
|
|
82
|
+
t.column :expectations, json_type, **json_default({})
|
|
83
|
+
t.column :tags, json_type, **json_default([])
|
|
84
|
+
t.column :params, json_type, **json_default({})
|
|
85
|
+
t.boolean :production_only, null: false, default: false
|
|
86
|
+
t.boolean :enabled, null: false, default: true
|
|
87
|
+
t.integer :position
|
|
88
|
+
t.timestamps
|
|
89
|
+
t.index [ :scenario_set_id, :key ], unique: true
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def json_type
|
|
96
|
+
@json_type ||= postgres? ? :jsonb : :json
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# MySQL rejects a default on a JSON column outright, so the column is
|
|
100
|
+
# created without one there.
|
|
101
|
+
def json_default(value, null: nil)
|
|
102
|
+
return {} unless postgres?
|
|
103
|
+
|
|
104
|
+
null.nil? ? { default: value } : { default: value, null: null }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def postgres?
|
|
108
|
+
connection.adapter_name.to_s.downcase.include?("postgres")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Links a Claude Code session to the evaluation fix it implements (the run,
|
|
4
|
+
# the fix card, the follow-up it retries) and to the run that verified it,
|
|
5
|
+
# and records which user signed in to Claude Code inside a sandbox.
|
|
6
|
+
class AddEvaluationFixesToCodeSessions < ActiveRecord::Migration<%= migration_version %>
|
|
7
|
+
def change
|
|
8
|
+
sessions = "#{ActionAgent.table_name_prefix}code_sessions"
|
|
9
|
+
sandboxes = "#{ActionAgent.table_name_prefix}sandbox_sessions"
|
|
10
|
+
add_column sessions, :evaluation_run_id, :bigint
|
|
11
|
+
add_column sessions, :verification_run_id, :bigint
|
|
12
|
+
add_column sessions, :previous_code_session_id, :bigint
|
|
13
|
+
# jsonb on PostgreSQL and json elsewhere, like the dashboard tables.
|
|
14
|
+
add_column sessions, :fix_item, connection.adapter_name.to_s.downcase.include?("postgres") ? :jsonb : :json
|
|
15
|
+
add_column sessions, :credential_mode, :string
|
|
16
|
+
add_column sessions, :verification_error, :text
|
|
17
|
+
add_index sessions, :evaluation_run_id
|
|
18
|
+
add_index sessions, :verification_run_id
|
|
19
|
+
add_column sandboxes, :claude_login_user_id, :bigint
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/tasks/action_agent.rake
CHANGED
|
@@ -6,6 +6,39 @@ namespace :action_agent do
|
|
|
6
6
|
ActionAgent::AgentTemplate.seed_defaults!
|
|
7
7
|
puts "Agent template library: #{ActionAgent::AgentTemplate.count} templates"
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
# Sample data a workspace can show before it has run anything of its own.
|
|
11
|
+
# On a multi-tenant install name the workspace: ACCOUNT_ID=<id> (or
|
|
12
|
+
# USER_ID=<id> where agents belong to users).
|
|
13
|
+
namespace :sample do
|
|
14
|
+
desc "Seed the conference-ticket sample: an agent, a week of runs and traces, a replayable handoff, and an evaluation history"
|
|
15
|
+
task conference_ticket: :environment do
|
|
16
|
+
ActionAgent::AgentTemplate.seed_defaults!
|
|
17
|
+
agent = ActionAgent::SampleData::ConferenceTicket.seed!(owner: action_agent_sample_owner)
|
|
18
|
+
traces = ActionAgent.trace_model.where(agent_id: agent.id).count
|
|
19
|
+
evaluation = agent.evaluations.find_by(name: ActionAgent::SampleData::ConferenceTicket::EVALUATION_NAME)
|
|
20
|
+
puts "Conference ticket sample: agent ##{agent.id} (#{agent.slug})"
|
|
21
|
+
puts " #{agent.agent_runs.count} runs, #{traces} traces, " \
|
|
22
|
+
"#{ActionAgent::SessionRecording.where(agent_run_id: agent.agent_runs.select(:id)).count} session recording"
|
|
23
|
+
puts " evaluation \"#{evaluation&.name}\": #{evaluation&.evaluation_runs&.count} runs over #{evaluation&.scenarios&.count} scenarios"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "Remove the conference-ticket sample and everything it created"
|
|
27
|
+
task clear: :environment do
|
|
28
|
+
removed = ActionAgent::SampleData::ConferenceTicket.clear!(owner: action_agent_sample_owner)
|
|
29
|
+
puts removed.zero? ? "No conference ticket sample to remove" : "Conference ticket sample removed"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The workspace the sample belongs to, from ACCOUNT_ID or USER_ID; nil on a
|
|
35
|
+
# single-user install.
|
|
36
|
+
def action_agent_sample_owner
|
|
37
|
+
if ENV["ACCOUNT_ID"].present?
|
|
38
|
+
ActionAgent.account_class.to_s.constantize.find(ENV["ACCOUNT_ID"])
|
|
39
|
+
elsif ENV["USER_ID"].present?
|
|
40
|
+
ActionAgent.user_class.to_s.constantize.find(ENV["USER_ID"])
|
|
41
|
+
end
|
|
9
42
|
end
|
|
10
43
|
|
|
11
44
|
namespace :action_agent do
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :action_agent do
|
|
4
|
+
namespace :browser do
|
|
5
|
+
desc "Install the browser sidecar and its Chromium for the :local sandbox backend, under the local sandbox root"
|
|
6
|
+
task install: :environment do
|
|
7
|
+
ActionAgent::BrowserSidecar.install!
|
|
8
|
+
puts "Installed the browser sidecar under #{ActionAgent::BrowserSidecar.root}"
|
|
9
|
+
rescue ActionAgent::BrowserSidecar::Error => e
|
|
10
|
+
abort "action_agent:browser:install: #{e.message}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc "Check that a sandbox's browser can start on this machine: Node.js, the browser sidecar and Chromium"
|
|
14
|
+
task doctor: :environment do
|
|
15
|
+
checks = ActionAgent::BrowserSidecar.checks
|
|
16
|
+
checks.each { |check| puts "#{check.ok ? '[ok] ' : '[missing]'} #{check.name}: #{check.message}" }
|
|
17
|
+
abort "Browser sessions cannot start on this machine yet." unless checks.all?(&:ok)
|
|
18
|
+
|
|
19
|
+
puts "Browser sessions can start on this machine."
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/tasks/sandbox.rake
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace :action_agent do
|
|
4
4
|
namespace :sandbox do
|
|
5
|
-
desc "Write this app's checkout sandbox runtime manifest ({mcp_path, mcp_token}) to " \
|
|
5
|
+
desc "Write this app's checkout sandbox runtime manifest ({mcp_path, mcp_token, models}) to " \
|
|
6
6
|
"$ACTION_AGENT_SANDBOX_MANIFEST, or print it"
|
|
7
7
|
task manifest: :environment do
|
|
8
8
|
manifest = JSON.generate(ActionAgent::SandboxManifest.generate)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Scenario catalogs from the command line, for a repository that keeps its
|
|
4
|
+
# scenarios in YAML. On a multi-tenant install name the workspace with
|
|
5
|
+
# ACCOUNT_ID=<id> (or USER_ID=<id> where catalogs belong to users).
|
|
6
|
+
#
|
|
7
|
+
# bin/rails action_agent:catalogs:import FILE=config/evals/support.yml
|
|
8
|
+
# bin/rails action_agent:catalogs:export KEY=support FILE=tmp/support.yml
|
|
9
|
+
# bin/rails action_agent:catalogs:run KEY=support PRODUCT=triage SET=smoke [AGENT_ID=1 | PROJECT_ID=2]
|
|
10
|
+
namespace :action_agent do
|
|
11
|
+
namespace :catalogs do
|
|
12
|
+
desc "Import a scenario catalog from a YAML file (FILE=path); re-importing the same file changes nothing"
|
|
13
|
+
task import: :environment do
|
|
14
|
+
path = ENV.fetch("FILE") { abort "Name the file: FILE=config/evals/support.yml" }
|
|
15
|
+
abort "No file at #{path}" unless File.file?(path)
|
|
16
|
+
|
|
17
|
+
owner = action_agent_catalog_owner
|
|
18
|
+
catalog = ActionAgent::ScenarioCatalogImport.new(
|
|
19
|
+
owner: owner, document: File.read(path), name: File.basename(path, File.extname(path)),
|
|
20
|
+
source_kind: "upload", source_path: path,
|
|
21
|
+
agents: ActionAgent.agents_for(owner), projects: ActionAgent::Project.for_owner(owner)
|
|
22
|
+
).call
|
|
23
|
+
puts "Catalog #{catalog.key}: #{catalog.products.count} products, #{catalog.scenario_count} scenarios" \
|
|
24
|
+
"#{catalog.synced? ? ', synced to Active Storage' : ''}"
|
|
25
|
+
rescue ActionAgent::ScenarioCatalogImport::Invalid => e
|
|
26
|
+
abort "Import refused: #{e.message}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "Write a catalog's canonical YAML (KEY=catalog key, FILE=path; standard output without FILE)"
|
|
30
|
+
task export: :environment do
|
|
31
|
+
catalog = action_agent_catalog!
|
|
32
|
+
yaml = catalog.export_yaml
|
|
33
|
+
if ENV["FILE"].present?
|
|
34
|
+
File.write(ENV["FILE"], yaml)
|
|
35
|
+
puts "Wrote #{catalog.key} (#{catalog.scenario_count} scenarios) to #{ENV['FILE']}"
|
|
36
|
+
else
|
|
37
|
+
puts yaml
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "Run one set of a catalog (KEY, PRODUCT, SET) against AGENT_ID or PROJECT_ID, or the product's own target"
|
|
42
|
+
task run: :environment do
|
|
43
|
+
catalog = action_agent_catalog!
|
|
44
|
+
product = catalog.products.find_by!(key: ENV.fetch("PRODUCT") { abort "Name the product: PRODUCT=<key>" })
|
|
45
|
+
set = product.sets.find_by!(key: ENV.fetch("SET") { abort "Name the set: SET=<key>" })
|
|
46
|
+
agent = ENV["AGENT_ID"].present? ? ActionAgent::Agent.find(ENV["AGENT_ID"]) : nil
|
|
47
|
+
project = ENV["PROJECT_ID"].present? ? ActionAgent::Project.find(ENV["PROJECT_ID"]) : nil
|
|
48
|
+
|
|
49
|
+
run = set.run!(agent: agent, project: project)
|
|
50
|
+
puts "Run ##{run.id} of #{set.evaluation_name} is #{run.status} (evaluation ##{run.evaluation_id})"
|
|
51
|
+
rescue ActionAgent::ScenarioSet::NoAgent => e
|
|
52
|
+
abort e.message
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The workspace a catalog belongs to, from ACCOUNT_ID or USER_ID; nil on a
|
|
58
|
+
# single-user install.
|
|
59
|
+
def action_agent_catalog_owner
|
|
60
|
+
if ENV["ACCOUNT_ID"].present?
|
|
61
|
+
ActionAgent.account_class.to_s.constantize.find(ENV["ACCOUNT_ID"])
|
|
62
|
+
elsif ENV["USER_ID"].present?
|
|
63
|
+
ActionAgent.user_class.to_s.constantize.find(ENV["USER_ID"])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def action_agent_catalog!
|
|
68
|
+
key = ENV.fetch("KEY") { abort "Name the catalog: KEY=<catalog key>" }
|
|
69
|
+
ActionAgent::ScenarioCatalog.for_owner(action_agent_catalog_owner).find_by(key: key) ||
|
|
70
|
+
abort("No catalog #{key.inspect}#{ENV['ACCOUNT_ID'].present? || ENV['USER_ID'].present? ? ' in that workspace' : ''}")
|
|
71
|
+
end
|