actionagent 1.9.1 → 1.9.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -0
  3. data/app/assets/builds/action_agent.css +1 -1
  4. data/app/assets/builds/action_agent.js +61 -61
  5. data/app/controllers/action_agent/api/agents_controller.rb +23 -0
  6. data/app/controllers/action_agent/api/code_sessions_controller.rb +49 -10
  7. data/app/controllers/action_agent/api/evaluation_reports_controller.rb +66 -2
  8. data/app/controllers/action_agent/api/evaluations_controller.rb +56 -6
  9. data/app/controllers/action_agent/api/interactions_controller.rb +55 -1
  10. data/app/controllers/action_agent/api/sandboxes_controller.rb +13 -2
  11. data/app/controllers/action_agent/api/trace_reports_controller.rb +11 -6
  12. data/app/controllers/concerns/action_agent/api/mcp_dashboard_tools.rb +2 -2
  13. data/app/jobs/action_agent/code_session_job.rb +19 -2
  14. data/app/jobs/action_agent/evaluation_judge_job.rb +39 -0
  15. data/app/jobs/action_agent/evaluation_replay_job.rb +28 -0
  16. data/app/models/action_agent/agent.rb +54 -1
  17. data/app/models/action_agent/code_session.rb +80 -0
  18. data/app/models/action_agent/evaluation_run.rb +37 -5
  19. data/app/models/action_agent/provider_key.rb +5 -1
  20. data/app/queries/action_agent/trace_window_report.rb +60 -0
  21. data/app/serializers/action_agent/code_conversation_serializer.rb +86 -0
  22. data/app/serializers/action_agent/evaluation_serializer.rb +13 -2
  23. data/app/services/action_agent/agent_execution_service.rb +37 -0
  24. data/app/services/action_agent/agent_tool_roster.rb +53 -18
  25. data/app/services/action_agent/agent_toolbox.rb +156 -3
  26. data/app/services/action_agent/browser_client_token.rb +86 -0
  27. data/app/services/action_agent/checkout_tools.rb +154 -0
  28. data/app/services/action_agent/code_conversation.rb +133 -0
  29. data/app/services/action_agent/codex_auth.rb +81 -0
  30. data/app/services/action_agent/dashboard_assistant_service.rb +19 -1
  31. data/app/services/action_agent/diff_stat.rb +27 -0
  32. data/app/services/action_agent/evaluation_fix_comparison.rb +4 -1
  33. data/app/services/action_agent/evaluation_report_export.rb +175 -0
  34. data/app/services/action_agent/evaluation_report_import.rb +47 -19
  35. data/app/services/action_agent/evaluation_runner_service.rb +106 -14
  36. data/app/services/action_agent/local_sandbox_backend.rb +295 -30
  37. data/app/services/action_agent/mcp_catalog.rb +25 -0
  38. data/app/services/action_agent/mcp_client.rb +1 -0
  39. data/app/services/action_agent/mcp_tool_dispatcher.rb +36 -1
  40. data/app/services/action_agent/project_boot_fix.rb +10 -3
  41. data/app/services/action_agent/sandbox_orchestrator.rb +41 -1
  42. data/app/services/action_agent/scenario_evaluation_runner.rb +381 -3
  43. data/app/services/action_agent/slack_tools.rb +101 -0
  44. data/config/routes.rb +10 -1
  45. data/lib/action_agent/engine.rb +6 -0
  46. data/lib/action_agent/version.rb +1 -1
  47. data/lib/action_agent.rb +50 -2
  48. data/lib/generators/action_agent/templates/migrations/028_add_conversations_to_code_sessions.rb.erb +18 -0
  49. data/lib/tasks/evaluations.rake +78 -0
  50. metadata +15 -2
data/lib/action_agent.rb CHANGED
@@ -111,6 +111,9 @@ module ActionAgent
111
111
  # What ActionAgent.claude_code_auth may be set to.
112
112
  CLAUDE_CODE_AUTH_MODES = %i[api_key local_login sandbox_login].freeze
113
113
 
114
+ # What ActionAgent.codex_auth may be set to.
115
+ CODEX_AUTH_MODES = %i[api_key local_login].freeze
116
+
114
117
  # What ActionAgent.provider_key_scope may be set to.
115
118
  PROVIDER_KEY_SCOPES = %i[organization personal_override].freeze
116
119
 
@@ -134,6 +137,9 @@ module ActionAgent
134
137
  # they are handed to, or delete a project that has
135
138
  # them
136
139
  # :take_over_browser drive a run's browser by hand
140
+ # :chat_code_session send a message to a Claude Code or Codex
141
+ # conversation someone may have started (the
142
+ # subject is its first session)
137
143
  # :manage_recordings delete a session recording
138
144
  # :replace_scenarios replace or merge an evaluation's scenarios
139
145
  PERMISSION_ACTIONS = %i[
@@ -144,6 +150,7 @@ module ActionAgent
144
150
  answer_input_request
145
151
  manage_project_secrets
146
152
  take_over_browser
153
+ chat_code_session
147
154
  manage_recordings
148
155
  replace_scenarios
149
156
  ].freeze
@@ -589,10 +596,35 @@ module ActionAgent
589
596
  # @return [Integer]
590
597
  attr_accessor :claude_code_timeout
591
598
 
592
- # Codex CLI sessions use the owner's OpenAI API key and workspace-write
593
- # sandboxing. They share checkout lifecycle and cancellation with Claude.
599
+ # Codex CLI sessions run with workspace-write sandboxing, and share the
600
+ # checkout lifecycle and cancellation with Claude Code.
594
601
  attr_accessor :codex_command, :codex_timeout
595
602
 
603
+ # How Codex sessions authenticate.
604
+ #
605
+ # :api_key (the default) runs them on the OpenAI API key the owner
606
+ # connected in Settings -> Integrations, handed to the session as
607
+ # CODEX_API_KEY with a Codex home of the sandbox's own. Usage is billed
608
+ # to that key's project.
609
+ #
610
+ # :local_login runs `codex` on whatever login this machine's user set up
611
+ # with `codex login` (a ChatGPT plan, or a key given to the CLI), which
612
+ # Codex keeps under ~/.codex. The dashboard never reads, copies or
613
+ # stores it; it only asks `codex login status` whether there is one.
614
+ # That login is the dashboard user's own, so this works with the :local
615
+ # sandbox backend only, and other backends refuse Codex sessions.
616
+ # @return [Symbol] :api_key or :local_login
617
+ attr_reader :codex_auth
618
+
619
+ def codex_auth=(value)
620
+ mode = value.to_s.to_sym
621
+ unless CODEX_AUTH_MODES.include?(mode)
622
+ raise ArgumentError, "ActionAgent.codex_auth must be :api_key or :local_login, not #{value.inspect}"
623
+ end
624
+
625
+ @codex_auth = mode
626
+ end
627
+
596
628
  # How Claude Code sessions authenticate.
597
629
  #
598
630
  # :api_key (the default) runs them on the Anthropic API key the owner
@@ -685,6 +717,20 @@ module ActionAgent
685
717
  # @return [Proc, nil]
686
718
  attr_accessor :scenario_evaluation_adapter_resolver
687
719
 
720
+ # Whether a scenario evaluation runs as background jobs, one per
721
+ # scenario x model (EvaluationReplayJob) followed by one to score it
722
+ # (EvaluationJudgeJob), instead of as one job that replays the suite in
723
+ # order. The jobs run on the :agents queue, so a run is as parallel as
724
+ # the host's workers for that queue: a single-worker box stays
725
+ # sequential. The last result to be scored completes the run.
726
+ #
727
+ # A run that drives a sandbox's browser, and a run a host adapter
728
+ # (scenario_evaluation_adapter_resolver) replays, stay sequential: the
729
+ # browser is one page, and the adapter owns its own scheduling.
730
+ # Off unless set.
731
+ # @return [Boolean]
732
+ attr_accessor :scenario_evaluation_fan_out
733
+
688
734
  # How many answerable candidates an exploration's review pre-selects for
689
735
  # acceptance: an Integer, or a Proc called with (owner) that returns one.
690
736
  # A platform can cap a free plan's suite this way. Unset, or nil from the
@@ -1234,6 +1280,7 @@ module ActionAgent
1234
1280
  @claude_code_timeout = 1800
1235
1281
  @codex_command = "codex"
1236
1282
  @codex_timeout = 1800
1283
+ @codex_auth = :api_key
1237
1284
  @claude_code_auth = :api_key
1238
1285
  @claude_code_hosted_login_enabled = false
1239
1286
  @claude_code_login_timeout = 300
@@ -1243,6 +1290,7 @@ module ActionAgent
1243
1290
  @capture_dashboard_sessions = true
1244
1291
 
1245
1292
  @scenario_evaluation_adapter_resolver = nil
1293
+ @scenario_evaluation_fan_out = false
1246
1294
  @table_name_prefix = "active_agent_"
1247
1295
  @agent_polymorphic_name = nil
1248
1296
  @encrypt_credentials = true
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Makes Claude Code and Codex sessions conversations: each session is one
4
+ # turn, and a turn after the first names the conversation's first session as
5
+ # its root. A conversation resumes the same CLI session (resume_session_id),
6
+ # and a turn may carry a note from the dashboard ahead of the person's
7
+ # message (context_note), such as who drove the browser since the last turn.
8
+ class AddConversationsToCodeSessions < ActiveRecord::Migration<%= migration_version %>
9
+ def change
10
+ sessions = "#{ActionAgent.table_name_prefix}code_sessions"
11
+ add_column sessions, :conversation_root_id, :bigint
12
+ add_column sessions, :turn_index, :integer, default: 0, null: false
13
+ add_column sessions, :origin, :string
14
+ add_column sessions, :context_note, :text
15
+ add_column sessions, :resume_session_id, :string
16
+ add_index sessions, :conversation_root_id
17
+ end
18
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Evaluation run reports from the command line: a run out as the version-1
4
+ # envelope the evaluation report collector takes (or as its HTML report page,
5
+ # or as GitHub-flavored Markdown for a pull request), and an envelope in
6
+ # through the collector's own import. On a multi-tenant install name the
7
+ # tenant with ACCOUNT_ID=<id>.
8
+ #
9
+ # bin/rails action_agent:evaluations:export RUN=dashboard-run-12-20261021093000 FILE=tmp/run.json
10
+ # bin/rails action_agent:evaluations:export EVALUATION=3 RUN=12 FORMAT=html FILE=tmp/run.html
11
+ # bin/rails action_agent:evaluations:export EVALUATION=3 RUN=12 FORMAT=md > tmp/run.md
12
+ # bin/rails action_agent:evaluations:import FILE=tmp/run.json
13
+ namespace :action_agent do
14
+ namespace :evaluations do
15
+ desc "Write a run's report (RUN=run_id, or EVALUATION=id RUN=id; FORMAT=json|html|md; FILE=path, standard output without FILE)"
16
+ task export: :environment do
17
+ run = action_agent_evaluation_run!
18
+ export = ActionAgent::EvaluationReportExport.new(run)
19
+ format = ENV.fetch("FORMAT", "json").downcase
20
+ body =
21
+ case format
22
+ when "json" then export.to_json
23
+ when "html" then run.to_report.to_html
24
+ when "md", "markdown" then export.markdown
25
+ else abort "FORMAT is json, html or md"
26
+ end
27
+
28
+ if ENV["FILE"].present?
29
+ File.write(ENV["FILE"], body)
30
+ puts "Wrote run #{export.run_id} of #{run.evaluation.name} (#{format}) to #{ENV['FILE']}"
31
+ else
32
+ puts body
33
+ end
34
+ rescue ActionAgent::EvaluationReportExport::NotExportable => e
35
+ abort "Export refused: #{e.message}"
36
+ end
37
+
38
+ desc "Store a version-1 evaluation report envelope (FILE=path [ACCOUNT_ID=id]); importing it again changes nothing"
39
+ task import: :environment do
40
+ path = ENV.fetch("FILE") { abort "Name the file: FILE=tmp/run.json" }
41
+ abort "No file at #{path}" unless File.file?(path)
42
+ abort "Unavailable: #{ActionAgent::EvaluationReportImport.unavailable_reason}" if ActionAgent::EvaluationReportImport.unavailable_reason
43
+
44
+ account = action_agent_evaluation_account
45
+ run, duplicate = ActionAgent::EvaluationReportImport.call(account: account, payload: JSON.parse(File.read(path)))
46
+ ActionAgent.record_usage(account, :evaluation_report) unless duplicate
47
+ puts "#{duplicate ? 'Already stored (identical retry)' : 'Stored'}: run #{ActionAgent::EvaluationReportExport.run_id_for(run)} " \
48
+ "as run ##{run.id} of evaluation ##{run.evaluation_id} (#{run.evaluation.name}), #{run.status}"
49
+ rescue JSON::ParserError
50
+ abort "Import refused: #{path} is not JSON"
51
+ rescue ActionAgent::EvaluationReportImport::Error, ActiveRecord::RecordInvalid => e
52
+ abort "Import refused: #{e.message}"
53
+ end
54
+ end
55
+ end
56
+
57
+ # The tenant, from ACCOUNT_ID (ACCOUNT also works); nil on a single-tenant
58
+ # install.
59
+ def action_agent_evaluation_account
60
+ id = ENV["ACCOUNT_ID"].presence || ENV["ACCOUNT"].presence
61
+ return nil unless id
62
+
63
+ abort "ACCOUNT_ID names a tenant, which only a multi-tenant install has" unless ActionAgent.multi_tenant?
64
+ ActionAgent.account_class.to_s.constantize.find_by(id: id) || abort("No account #{id}")
65
+ end
66
+
67
+ # The run RUN names: with EVALUATION, a run id of that evaluation; without,
68
+ # a run_id as the export endpoint takes it, within the tenant.
69
+ def action_agent_evaluation_run!
70
+ run_id = ENV.fetch("RUN") { abort "Name the run: RUN=<run_id>, or EVALUATION=<id> RUN=<id>" }
71
+ if ENV["EVALUATION"].present?
72
+ evaluation = ActionAgent::Evaluation.find_by(id: ENV["EVALUATION"]) || abort("No evaluation #{ENV['EVALUATION']}")
73
+ evaluation.evaluation_runs.find_by(id: run_id) || abort("No run #{run_id} in evaluation #{evaluation.id}")
74
+ else
75
+ ActionAgent::EvaluationReportExport.find(run_id, account: action_agent_evaluation_account) ||
76
+ abort("No run with run_id #{run_id.inspect}#{' for that account' if ENV['ACCOUNT_ID'].present? || ENV['ACCOUNT'].present?}")
77
+ end
78
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionagent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-17 00:00:00.000000000 Z
11
+ date: 2026-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeagent
@@ -190,6 +190,8 @@ files:
190
190
  - app/jobs/action_agent/application_job.rb
191
191
  - app/jobs/action_agent/code_session_job.rb
192
192
  - app/jobs/action_agent/draft_pull_request_job.rb
193
+ - app/jobs/action_agent/evaluation_judge_job.rb
194
+ - app/jobs/action_agent/evaluation_replay_job.rb
193
195
  - app/jobs/action_agent/evaluation_run_job.rb
194
196
  - app/jobs/action_agent/exploration_job.rb
195
197
  - app/jobs/action_agent/input_request_expiry_job.rb
@@ -244,7 +246,9 @@ files:
244
246
  - app/models/concerns/action_agent/session_recordable.rb
245
247
  - app/queries/action_agent/agent_executions.rb
246
248
  - app/queries/action_agent/metrics_report.rb
249
+ - app/queries/action_agent/trace_window_report.rb
247
250
  - app/serializers/action_agent/agent_message_serializer.rb
251
+ - app/serializers/action_agent/code_conversation_serializer.rb
248
252
  - app/serializers/action_agent/evaluation_serializer.rb
249
253
  - app/serializers/action_agent/input_request_serializer.rb
250
254
  - app/serializers/action_agent/interaction_preview.rb
@@ -259,17 +263,23 @@ files:
259
263
  - app/services/action_agent/agent_sync.rb
260
264
  - app/services/action_agent/agent_tool_roster.rb
261
265
  - app/services/action_agent/agent_toolbox.rb
266
+ - app/services/action_agent/browser_client_token.rb
262
267
  - app/services/action_agent/browser_live_ticket.rb
263
268
  - app/services/action_agent/browser_sidecar.rb
264
269
  - app/services/action_agent/browser_sign_in.rb
265
270
  - app/services/action_agent/browser_tool_redaction.rb
271
+ - app/services/action_agent/checkout_tools.rb
266
272
  - app/services/action_agent/claude_code_auth.rb
273
+ - app/services/action_agent/code_conversation.rb
274
+ - app/services/action_agent/codex_auth.rb
267
275
  - app/services/action_agent/codex_session_events.rb
268
276
  - app/services/action_agent/dashboard_assistant_service.rb
277
+ - app/services/action_agent/diff_stat.rb
269
278
  - app/services/action_agent/draft_pull_request_publisher.rb
270
279
  - app/services/action_agent/evaluation_evidence.rb
271
280
  - app/services/action_agent/evaluation_fix.rb
272
281
  - app/services/action_agent/evaluation_fix_comparison.rb
282
+ - app/services/action_agent/evaluation_report_export.rb
273
283
  - app/services/action_agent/evaluation_report_import.rb
274
284
  - app/services/action_agent/evaluation_run_cost.rb
275
285
  - app/services/action_agent/evaluation_runner_service.rb
@@ -314,6 +324,7 @@ files:
314
324
  - app/services/action_agent/session_index.rb
315
325
  - app/services/action_agent/session_recording_service.rb
316
326
  - app/services/action_agent/session_timeline.rb
327
+ - app/services/action_agent/slack_tools.rb
317
328
  - app/services/action_agent/tool_discovery.rb
318
329
  - app/views/action_agent/dashboard/index.html.erb
319
330
  - app/views/action_agent/github_app_manifests/created.html.erb
@@ -358,9 +369,11 @@ files:
358
369
  - lib/generators/action_agent/templates/migrations/025_create_active_agent_scenario_catalogs.rb.erb
359
370
  - lib/generators/action_agent/templates/migrations/026_add_evaluation_fixes_to_code_sessions.rb.erb
360
371
  - lib/generators/action_agent/templates/migrations/027_create_active_agent_interaction_feedbacks.rb.erb
372
+ - lib/generators/action_agent/templates/migrations/028_add_conversations_to_code_sessions.rb.erb
361
373
  - lib/tasks/action_agent.rake
362
374
  - lib/tasks/browser.rake
363
375
  - lib/tasks/claude_code.rake
376
+ - lib/tasks/evaluations.rake
364
377
  - lib/tasks/sandbox.rake
365
378
  - lib/tasks/scenario_catalogs.rake
366
379
  homepage: https://activeagents.ai