actionagent 1.5.2 → 1.6.2
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 +54 -54
- data/app/controllers/action_agent/api/agent_runs_controller.rb +2 -2
- data/app/controllers/action_agent/api/agents_controller.rb +47 -10
- data/app/controllers/action_agent/api/analytics_controller.rb +1 -1
- data/app/controllers/action_agent/api/base_controller.rb +37 -0
- data/app/controllers/action_agent/api/interactions_controller.rb +3 -3
- data/app/controllers/action_agent/api/mcp_controller.rb +115 -4
- data/app/controllers/action_agent/api/sandboxes_controller.rb +6 -1
- data/app/controllers/action_agent/api/session_recordings_controller.rb +24 -7
- data/app/jobs/action_agent/agent_execution_job.rb +4 -1
- data/app/models/action_agent/agent.rb +88 -7
- data/app/models/action_agent/agent_run.rb +63 -0
- data/app/models/action_agent/agent_version.rb +10 -0
- data/app/models/action_agent/evaluation.rb +11 -0
- data/app/models/action_agent/evaluation_run.rb +4 -0
- data/app/models/action_agent/telemetry_trace.rb +28 -1
- data/app/services/action_agent/agent_execution_service.rb +28 -2
- data/app/services/action_agent/agent_registrar.rb +23 -1
- data/app/services/action_agent/agent_release.rb +61 -0
- data/app/services/action_agent/agent_tool_roster.rb +295 -0
- data/app/services/action_agent/agent_toolbox.rb +12 -3
- data/app/services/action_agent/evaluation_runner_service.rb +11 -9
- data/app/services/action_agent/scenario_evaluation_runner.rb +17 -1
- data/config/routes.rb +6 -0
- data/lib/action_agent/version.rb +1 -1
- data/lib/action_agent.rb +66 -4
- data/lib/generators/action_agent/install_generator.rb +6 -0
- data/lib/generators/action_agent/templates/action_agent.rb.erb +8 -0
- data/lib/generators/action_agent/templates/add_agent_releases.rb.erb +50 -0
- data/lib/tasks/action_agent.rake +33 -0
- metadata +5 -2
|
@@ -468,15 +468,17 @@ module ActionAgent
|
|
|
468
468
|
end
|
|
469
469
|
|
|
470
470
|
def judge_class
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
471
|
+
@judge_class ||= begin
|
|
472
|
+
provider = judge_provider
|
|
473
|
+
model = @evaluation.judge_model.presence
|
|
474
|
+
options = {}
|
|
475
|
+
options[:model] = model if model
|
|
476
|
+
options.merge!(owner_provider_options(provider))
|
|
477
|
+
|
|
478
|
+
Class.new(ActiveAgent::Base) do
|
|
479
|
+
define_singleton_method(:name) { "EvaluationJudgeAgent" }
|
|
480
|
+
generate_with provider, **options
|
|
481
|
+
end
|
|
480
482
|
end
|
|
481
483
|
end
|
|
482
484
|
|
|
@@ -147,7 +147,8 @@ module ActionAgent
|
|
|
147
147
|
agent_run = @evaluation.agent.test_execute(
|
|
148
148
|
scenario.prompt,
|
|
149
149
|
model_override: spec.model,
|
|
150
|
-
provider_override: spec.provider
|
|
150
|
+
provider_override: spec.provider,
|
|
151
|
+
actor: replay_actor
|
|
151
152
|
)
|
|
152
153
|
|
|
153
154
|
Evals::Replay.new(
|
|
@@ -162,6 +163,21 @@ module ActionAgent
|
|
|
162
163
|
)
|
|
163
164
|
end
|
|
164
165
|
|
|
166
|
+
# The caller a replay runs on behalf of: the evaluation's owner, when the
|
|
167
|
+
# install owns agents per user. A run with no caller reads, through any
|
|
168
|
+
# host scope, as "no access" — every tool answers empty and the suite
|
|
169
|
+
# grades an agent that never saw a row — so the person the evaluation
|
|
170
|
+
# belongs to is the right default, as the key's owner is over MCP. An
|
|
171
|
+
# account is who is billed, not who is allowed (see Api::BaseController
|
|
172
|
+
# #agent_actor), so a multi-tenant install replays unattributed unless a
|
|
173
|
+
# host adapter (ActionAgent.scenario_evaluation_adapter_resolver) runs
|
|
174
|
+
# the suite itself.
|
|
175
|
+
def replay_actor
|
|
176
|
+
return nil if ActionAgent.multi_tenant? || ActionAgent.user_class.blank?
|
|
177
|
+
|
|
178
|
+
owner
|
|
179
|
+
end
|
|
180
|
+
|
|
165
181
|
# Each tool call the run made, rebuilt from the run's progress events
|
|
166
182
|
# (AgentRun#append_event pairs a "started" event with its "done"/"error"
|
|
167
183
|
# by eid). Falls back to the bare names in the run's metadata for a run
|
data/config/routes.rb
CHANGED
|
@@ -38,6 +38,9 @@ ActionAgent::Engine.routes.draw do
|
|
|
38
38
|
post :duplicate
|
|
39
39
|
get :export
|
|
40
40
|
get :analytics
|
|
41
|
+
# The Tools tab's roster: offerable tools and MCP services, each
|
|
42
|
+
# with what the window recorded for it.
|
|
43
|
+
get :tool_roster
|
|
41
44
|
# The runner's conversation picker: this agent's persisted contexts,
|
|
42
45
|
# and a fresh one to pin a first message to.
|
|
43
46
|
get :conversations
|
|
@@ -158,6 +161,9 @@ ActionAgent::Engine.routes.draw do
|
|
|
158
161
|
# dashboard API key rather than a session, so it sits outside the api
|
|
159
162
|
# namespace's session-authenticated controllers.
|
|
160
163
|
post "mcp", to: "api/mcp#create"
|
|
164
|
+
match "mcp", to: ->(_env) { [ 405, { "Allow" => "POST" }, [] ] },
|
|
165
|
+
via: [ :get, :delete ],
|
|
166
|
+
constraints: ->(request) { request.delete? || !request.format.html? }
|
|
161
167
|
|
|
162
168
|
# MCP Streamable HTTP (2025-03-26): a client MAY open the server-to-client
|
|
163
169
|
# SSE stream with GET, and ends a session with DELETE. This facade offers
|
data/lib/action_agent/version.rb
CHANGED
data/lib/action_agent.rb
CHANGED
|
@@ -145,6 +145,26 @@ module ActionAgent
|
|
|
145
145
|
# @return [Proc, nil]
|
|
146
146
|
attr_accessor :current_user_resolver
|
|
147
147
|
|
|
148
|
+
# Resolves the caller an agent run executes on behalf of — what reaches
|
|
149
|
+
# +ActiveAgent::Base#current_user+, a SchemaTools +scope+ block, and any
|
|
150
|
+
# authorization gem an agent calls from its callbacks.
|
|
151
|
+
#
|
|
152
|
+
# Called with the controller, so the same seam covers a browser session
|
|
153
|
+
# and an MCP request. Whatever it returns is passed through untouched:
|
|
154
|
+
# the engine never interprets an actor, and never widens one.
|
|
155
|
+
#
|
|
156
|
+
# config.agent_actor_resolver = ->(controller) { controller.current_user }
|
|
157
|
+
#
|
|
158
|
+
# Unset means the dashboard's signed-in user, and, for the MCP endpoint,
|
|
159
|
+
# the API key's owner — the identity that authenticated the call. A host
|
|
160
|
+
# whose keys are issued per end user overrides this to return that user.
|
|
161
|
+
#
|
|
162
|
+
# Returning nil runs the agent unattributed, which a correctly written
|
|
163
|
+
# host scope reads as "no access". That is the safe direction, and it is
|
|
164
|
+
# why this is never defaulted to something more privileged.
|
|
165
|
+
# @return [Proc, nil]
|
|
166
|
+
attr_accessor :agent_actor_resolver
|
|
167
|
+
|
|
148
168
|
# Resolves the current tenant from the controller. See
|
|
149
169
|
# current_user_resolver.
|
|
150
170
|
# @return [Proc, nil]
|
|
@@ -348,9 +368,21 @@ module ActionAgent
|
|
|
348
368
|
# @return [Array<Class, String>, nil]
|
|
349
369
|
attr_accessor :schema_tools
|
|
350
370
|
|
|
371
|
+
# Whether the MCP facade (POST <mount>/mcp) offers the host's schema tools
|
|
372
|
+
# directly — find_<records>, count_<records>, get_<record> — beside the
|
|
373
|
+
# run_<slug> agent tools. Each call runs as the key's caller, through the
|
|
374
|
+
# host's own scope, exactly as it would inside an agent run. On by
|
|
375
|
+
# default: the host declared the tools; set it to false to keep them
|
|
376
|
+
# reachable only through agents.
|
|
377
|
+
# @return [Boolean]
|
|
378
|
+
attr_accessor :mcp_schema_tools
|
|
379
|
+
|
|
351
380
|
# Directory scanned for SchemaTools subclasses when {#schema_tools} is
|
|
352
381
|
# unset. Relative to the host's root. Set to nil to disable discovery and
|
|
353
|
-
# require an explicit declaration.
|
|
382
|
+
# require an explicit declaration. Classes built at runtime with
|
|
383
|
+
# +ActiveAgent::SchemaTools.define+ are discovered alongside the files
|
|
384
|
+
# whatever this is set to; only an explicit {#schema_tools} list excludes
|
|
385
|
+
# them.
|
|
354
386
|
# @return [String, nil]
|
|
355
387
|
attr_accessor :schema_tools_path
|
|
356
388
|
|
|
@@ -368,6 +400,13 @@ module ActionAgent
|
|
|
368
400
|
@multi_tenant == true
|
|
369
401
|
end
|
|
370
402
|
|
|
403
|
+
# Whether the MCP facade serves the host's schema tools directly.
|
|
404
|
+
#
|
|
405
|
+
# @return [Boolean]
|
|
406
|
+
def mcp_schema_tools?
|
|
407
|
+
@mcp_schema_tools != false
|
|
408
|
+
end
|
|
409
|
+
|
|
371
410
|
# Returns whether agent execution is permitted.
|
|
372
411
|
#
|
|
373
412
|
# @return [Boolean]
|
|
@@ -519,8 +558,10 @@ module ActionAgent
|
|
|
519
558
|
@sign_out_path = nil
|
|
520
559
|
@sign_in_path = nil
|
|
521
560
|
@mcp_catalog = []
|
|
561
|
+
@agent_actor_resolver = nil
|
|
522
562
|
@schema_tools = nil
|
|
523
563
|
@schema_tools_path = "app/agent_tools"
|
|
564
|
+
@mcp_schema_tools = nil
|
|
524
565
|
end
|
|
525
566
|
|
|
526
567
|
# Host-declared schema tool classes, resolved from names and filtered to
|
|
@@ -550,14 +591,31 @@ module ActionAgent
|
|
|
550
591
|
schema_tool_classes.flat_map(&:tool_names).map(&:to_s)
|
|
551
592
|
end
|
|
552
593
|
|
|
553
|
-
# SchemaTools
|
|
594
|
+
# SchemaTools classes on offer when {#schema_tools} is unset: the files
|
|
595
|
+
# under {#schema_tools_path}, and every runtime definition in
|
|
596
|
+
# +ActiveAgent::SchemaTools.registry+.
|
|
554
597
|
#
|
|
555
598
|
# The files are loaded before reading +descendants+: in development nothing
|
|
556
599
|
# has referenced those constants yet, so the list would otherwise be empty
|
|
557
600
|
# at boot and fill in only once something happened to touch them.
|
|
601
|
+
# Runtime-built classes are read from the registry, never from
|
|
602
|
+
# +descendants+, where every class ever built stays until collected and a
|
|
603
|
+
# superseded definition would be offered beside its replacement. A runtime
|
|
604
|
+
# definition for a model also supersedes a file for that model: it is the
|
|
605
|
+
# more recent intent.
|
|
558
606
|
# @return [Array<Class>]
|
|
559
607
|
def discovered_schema_tools
|
|
560
|
-
return []
|
|
608
|
+
return [] unless defined?(ActiveAgent::SchemaTools)
|
|
609
|
+
|
|
610
|
+
from_registry = ActiveAgent::SchemaTools.respond_to?(:registry) ? ActiveAgent::SchemaTools.registry.values : []
|
|
611
|
+
(from_registry + file_defined_schema_tools).uniq { |klass| klass.model.name }
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# The named SchemaTools subclasses under {#schema_tools_path}; none when
|
|
615
|
+
# the path is unset or the directory does not exist.
|
|
616
|
+
# @return [Array<Class>]
|
|
617
|
+
def file_defined_schema_tools
|
|
618
|
+
return [] if @schema_tools_path.blank?
|
|
561
619
|
return [] unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
562
620
|
|
|
563
621
|
root = Rails.root.join(@schema_tools_path)
|
|
@@ -569,7 +627,11 @@ module ActionAgent
|
|
|
569
627
|
warn "[ActionAgent] could not load #{path}: #{e.class} - #{e.message}"
|
|
570
628
|
end
|
|
571
629
|
|
|
572
|
-
ActiveAgent::SchemaTools.descendants.select { |klass| klass.name.present? }
|
|
630
|
+
ActiveAgent::SchemaTools.descendants.select { |klass| klass.name.present? && !runtime_schema_tools?(klass) }
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def runtime_schema_tools?(klass)
|
|
634
|
+
klass.respond_to?(:runtime?) && klass.runtime?
|
|
573
635
|
end
|
|
574
636
|
|
|
575
637
|
# The schema tool class that generated +name+, or nil.
|
|
@@ -57,6 +57,12 @@ module ActionAgent
|
|
|
57
57
|
)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
# Agent releases arrived after both tables shipped; the migration guards
|
|
61
|
+
# every column, so it is emitted for any install that lacks it.
|
|
62
|
+
unless existing_migration?("add_agent_releases")
|
|
63
|
+
migration_template("add_agent_releases.rb.erb", "db/migrate/add_agent_releases.rb")
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
# The rest of the dashboard — agents, runs, versions, conversations,
|
|
61
67
|
# evaluations, sandboxes, recordings, keys. Skippable for an app that
|
|
62
68
|
# only wants to be a trace sink.
|
|
@@ -31,6 +31,14 @@ ActionAgent.configure do |config|
|
|
|
31
31
|
# config.authentication_method = nil
|
|
32
32
|
#
|
|
33
33
|
config.authentication_method = nil
|
|
34
|
+
#
|
|
35
|
+
# Where a browser is sent when it asks for a dashboard page without a valid
|
|
36
|
+
# session; unset, it gets a minimal session-expired page. API clients get a
|
|
37
|
+
# bare 401 either way. The sign-out path is what the header's "Sign out"
|
|
38
|
+
# item POSTs to (with _method=delete); unset, the item is not shown.
|
|
39
|
+
#
|
|
40
|
+
# config.sign_in_path = "/sign_in"
|
|
41
|
+
# config.sign_out_path = "/sign_out"
|
|
34
42
|
|
|
35
43
|
# ==========================================================================
|
|
36
44
|
# Ingest API authentication
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Agent releases: a version cut from the agent's code carries the digest of
|
|
4
|
+
# what the model was given and the deploy it shipped in, and every trace,
|
|
5
|
+
# run and evaluation run records the version it ran under. Emitted for an
|
|
6
|
+
# install whose dashboard tables predate releases; the create-table
|
|
7
|
+
# migration carries the same columns for a fresh install.
|
|
8
|
+
#
|
|
9
|
+
# Each step is guarded so the migration is safe to re-run against a database
|
|
10
|
+
# that already has some of these columns.
|
|
11
|
+
class AddAgentReleases < ActiveRecord::Migration<%= migration_version %>
|
|
12
|
+
def up
|
|
13
|
+
add_column_unless_exists :active_agent_agents, :release_digest, :string
|
|
14
|
+
|
|
15
|
+
add_column_unless_exists :active_agent_agent_versions, :release_digest, :string
|
|
16
|
+
add_column_unless_exists :active_agent_agent_versions, :revision, :string
|
|
17
|
+
add_index_unless_exists :active_agent_agent_versions, [ :agent_id, :release_digest ]
|
|
18
|
+
|
|
19
|
+
%i[active_agent_telemetry_traces active_agent_agent_runs active_agent_evaluation_runs].each do |table|
|
|
20
|
+
add_column_unless_exists table, :agent_version_id, :bigint
|
|
21
|
+
add_index_unless_exists table, :agent_version_id
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def down
|
|
26
|
+
remove_column :active_agent_agents, :release_digest if column_exists?(:active_agent_agents, :release_digest)
|
|
27
|
+
remove_column :active_agent_agent_versions, :release_digest if column_exists?(:active_agent_agent_versions, :release_digest)
|
|
28
|
+
remove_column :active_agent_agent_versions, :revision if column_exists?(:active_agent_agent_versions, :revision)
|
|
29
|
+
|
|
30
|
+
%i[active_agent_telemetry_traces active_agent_agent_runs active_agent_evaluation_runs].each do |table|
|
|
31
|
+
remove_column table, :agent_version_id if column_exists?(table, :agent_version_id)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def add_column_unless_exists(table, column, type)
|
|
38
|
+
return unless table_exists?(table)
|
|
39
|
+
return if column_exists?(table, column)
|
|
40
|
+
|
|
41
|
+
add_column table, column, type
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_index_unless_exists(table, columns)
|
|
45
|
+
return unless table_exists?(table)
|
|
46
|
+
return if index_exists?(table, columns)
|
|
47
|
+
|
|
48
|
+
add_index table, columns
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/tasks/action_agent.rake
CHANGED
|
@@ -7,3 +7,36 @@ namespace :action_agent do
|
|
|
7
7
|
puts "Agent template library: #{ActionAgent::AgentTemplate.count} templates"
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
|
+
|
|
11
|
+
namespace :action_agent do
|
|
12
|
+
namespace :agents do
|
|
13
|
+
desc "Cut a dashboard version for every agent whose code changed (run on deploy; REVISION=<git sha> to label it)"
|
|
14
|
+
task :release, [ :revision ] => :environment do |_task, args|
|
|
15
|
+
Rails.application.eager_load!
|
|
16
|
+
result = ActionAgent::AgentRelease.call(revision: args[:revision] || ENV["REVISION"], released_by: ENV["RELEASED_BY"])
|
|
17
|
+
|
|
18
|
+
puts "Release #{result.revision.presence || '(no revision)'}"
|
|
19
|
+
result.rows.each do |row|
|
|
20
|
+
if row.skipped
|
|
21
|
+
puts format(" %-28s skipped — %s", row.agent.name, row.skipped)
|
|
22
|
+
elsif row.cut
|
|
23
|
+
puts format(" %-28s v%-3d cut %s", row.agent.name, row.version.version_number, row.version.change_summary)
|
|
24
|
+
else
|
|
25
|
+
puts format(" %-28s v%-3d unchanged (%s)", row.agent.name, row.version.version_number, row.version.release_digest)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
puts "#{result.cut.size} cut, #{result.rows.size - result.cut.size - result.skipped.size} unchanged, #{result.skipped.size} skipped"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc "List each agent's current version and release"
|
|
32
|
+
task versions: :environment do
|
|
33
|
+
ActionAgent::Agent.order(:name).find_each do |agent|
|
|
34
|
+
version = agent.latest_version
|
|
35
|
+
release = agent.latest_release
|
|
36
|
+
puts format("%-28s v%-3s %s%s", agent.name, version&.version_number || "-",
|
|
37
|
+
release ? "release #{release.release_digest}" : "no release",
|
|
38
|
+
release&.revision.present? ? " · #{release.revision}" : "")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
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.
|
|
4
|
+
version: 1.6.2
|
|
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-09-
|
|
11
|
+
date: 2026-09-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activeagent
|
|
@@ -193,7 +193,9 @@ files:
|
|
|
193
193
|
- app/serializers/action_agent/trace_interaction_serializer.rb
|
|
194
194
|
- app/services/action_agent/agent_execution_service.rb
|
|
195
195
|
- app/services/action_agent/agent_registrar.rb
|
|
196
|
+
- app/services/action_agent/agent_release.rb
|
|
196
197
|
- app/services/action_agent/agent_scorecard.rb
|
|
198
|
+
- app/services/action_agent/agent_tool_roster.rb
|
|
197
199
|
- app/services/action_agent/agent_toolbox.rb
|
|
198
200
|
- app/services/action_agent/dashboard_assistant_service.rb
|
|
199
201
|
- app/services/action_agent/evaluation_evidence.rb
|
|
@@ -226,6 +228,7 @@ files:
|
|
|
226
228
|
- lib/generators/action_agent/install_generator.rb
|
|
227
229
|
- lib/generators/action_agent/templates/action_agent.rb.erb
|
|
228
230
|
- lib/generators/action_agent/templates/add_agent_id_to_active_agent_telemetry_traces.rb.erb
|
|
231
|
+
- lib/generators/action_agent/templates/add_agent_releases.rb.erb
|
|
229
232
|
- lib/generators/action_agent/templates/create_active_agent_dashboard_tables.rb.erb
|
|
230
233
|
- lib/generators/action_agent/templates/create_active_agent_evaluation_scenarios.rb.erb
|
|
231
234
|
- lib/generators/action_agent/templates/create_active_agent_telemetry_traces.rb.erb
|