actionagent 1.9.2 → 1.9.4
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/README.md +23 -0
- data/app/assets/builds/action_agent.css +1 -1
- data/app/assets/builds/action_agent.js +61 -61
- data/app/controllers/action_agent/api/agents_controller.rb +23 -0
- data/app/controllers/action_agent/api/code_sessions_controller.rb +49 -10
- data/app/controllers/action_agent/api/evaluation_reports_controller.rb +66 -2
- data/app/controllers/action_agent/api/evaluations_controller.rb +41 -3
- data/app/controllers/action_agent/api/interactions_controller.rb +55 -1
- data/app/controllers/action_agent/api/sandboxes_controller.rb +13 -2
- data/app/controllers/action_agent/api/trace_reports_controller.rb +11 -6
- data/app/controllers/action_agent/dashboard_controller.rb +1 -0
- data/app/controllers/action_agent/traces_controller.rb +6 -0
- data/app/jobs/action_agent/code_session_job.rb +19 -2
- data/app/models/action_agent/agent.rb +54 -1
- data/app/models/action_agent/code_session.rb +80 -0
- data/app/models/action_agent/evaluation_run.rb +17 -2
- data/app/models/action_agent/provider_key.rb +5 -1
- data/app/queries/action_agent/trace_window_report.rb +60 -0
- data/app/serializers/action_agent/code_conversation_serializer.rb +86 -0
- data/app/serializers/action_agent/evaluation_serializer.rb +13 -2
- data/app/services/action_agent/agent_execution_service.rb +37 -0
- data/app/services/action_agent/agent_tool_roster.rb +53 -18
- data/app/services/action_agent/agent_toolbox.rb +156 -3
- data/app/services/action_agent/browser_client_token.rb +86 -0
- data/app/services/action_agent/checkout_tools.rb +154 -0
- data/app/services/action_agent/code_conversation.rb +133 -0
- data/app/services/action_agent/codex_auth.rb +81 -0
- data/app/services/action_agent/dashboard_assistant_service.rb +19 -1
- data/app/services/action_agent/diff_stat.rb +27 -0
- data/app/services/action_agent/evaluation_report_export.rb +175 -0
- data/app/services/action_agent/evaluation_report_import.rb +40 -17
- data/app/services/action_agent/evaluation_runner_service.rb +48 -12
- data/app/services/action_agent/local_sandbox_backend.rb +295 -30
- data/app/services/action_agent/mcp_catalog.rb +25 -0
- data/app/services/action_agent/mcp_client.rb +1 -0
- data/app/services/action_agent/mcp_tool_dispatcher.rb +36 -1
- data/app/services/action_agent/project_boot_fix.rb +10 -3
- data/app/services/action_agent/sandbox_orchestrator.rb +41 -1
- data/app/services/action_agent/scenario_evaluation_runner.rb +1 -0
- data/app/services/action_agent/slack_tools.rb +101 -0
- data/app/views/action_agent/traces/_styles.html.erb +45 -0
- data/app/views/action_agent/traces/_summary.html.erb +8 -0
- data/app/views/action_agent/traces/_trace_detail.html.erb +33 -96
- data/app/views/action_agent/traces/index.html.erb +25 -134
- data/app/views/action_agent/traces/metrics.html.erb +22 -140
- data/app/views/action_agent/traces/show.html.erb +7 -36
- data/app/views/layouts/action_agent/application.html.erb +26 -86
- data/config/routes.rb +10 -1
- data/lib/action_agent/engine.rb +6 -0
- data/lib/action_agent/version.rb +1 -1
- data/lib/action_agent.rb +67 -2
- data/lib/generators/action_agent/templates/action_agent.rb.erb +4 -3
- data/lib/generators/action_agent/templates/migrations/028_add_conversations_to_code_sessions.rb.erb +18 -0
- data/lib/tasks/evaluations.rake +78 -0
- metadata +15 -2
|
@@ -1,36 +1,7 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<div>
|
|
9
|
-
<h1 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
10
|
-
<%= @trace.display_name %>
|
|
11
|
-
</h1>
|
|
12
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 font-mono">
|
|
13
|
-
<%= @trace.trace_id %>
|
|
14
|
-
</p>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="flex items-center gap-4">
|
|
17
|
-
<% if @trace.error? %>
|
|
18
|
-
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200">
|
|
19
|
-
Error
|
|
20
|
-
</span>
|
|
21
|
-
<% else %>
|
|
22
|
-
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
|
23
|
-
Success
|
|
24
|
-
</span>
|
|
25
|
-
<% end %>
|
|
26
|
-
<span class="text-sm text-gray-500">
|
|
27
|
-
<%= @trace.timestamp&.strftime("%Y-%m-%d %H:%M:%S") %>
|
|
28
|
-
</span>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
<div class="p-4">
|
|
34
|
-
<%= render partial: "trace_detail", locals: { trace: @trace } %>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
1
|
+
<p><%= link_to "Back to traces", traces_path %></p>
|
|
2
|
+
<article class="panel">
|
|
3
|
+
<h1><%= @trace.display_name %></h1>
|
|
4
|
+
<p class="badge <%= @trace.error? ? 'error' : 'success' %>"><%= @trace.error? ? "Error" : "Success" %></p>
|
|
5
|
+
<p><time><%= @trace.timestamp&.strftime("%Y-%m-%d %H:%M:%S") %></time></p>
|
|
6
|
+
<%= render "trace_detail", trace: @trace %>
|
|
7
|
+
</article>
|
|
@@ -1,94 +1,34 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="en"
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>ActiveAgent
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
[data-turbo-preview] { opacity: 0.5; }
|
|
12
|
-
.span-bar { transition: all 0.2s ease; }
|
|
13
|
-
.span-bar:hover { transform: scaleY(1.1); }
|
|
14
|
-
</style>
|
|
15
|
-
<script>
|
|
16
|
-
// Initialize Stimulus
|
|
17
|
-
const application = Stimulus.Application.start();
|
|
18
|
-
|
|
19
|
-
// Traces controller for expandable rows
|
|
20
|
-
application.register("traces", class extends Stimulus.Controller {
|
|
21
|
-
static targets = ["row", "detail"]
|
|
22
|
-
|
|
23
|
-
toggle(event) {
|
|
24
|
-
const traceId = event.currentTarget.dataset.traceId;
|
|
25
|
-
const detail = document.getElementById(`detail-${traceId}`);
|
|
26
|
-
if (detail) {
|
|
27
|
-
detail.classList.toggle("hidden");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Auto-refresh controller
|
|
33
|
-
application.register("refresh", class extends Stimulus.Controller {
|
|
34
|
-
static values = { interval: Number }
|
|
35
|
-
|
|
36
|
-
connect() {
|
|
37
|
-
if (this.intervalValue > 0) {
|
|
38
|
-
this.startRefresh();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
disconnect() {
|
|
43
|
-
this.stopRefresh();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
startRefresh() {
|
|
47
|
-
this.timer = setInterval(() => {
|
|
48
|
-
Turbo.visit(window.location.href, { action: "replace" });
|
|
49
|
-
}, this.intervalValue);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
stopRefresh() {
|
|
53
|
-
if (this.timer) clearInterval(this.timer);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
</script>
|
|
6
|
+
<title>ActiveAgent Trace Console</title>
|
|
7
|
+
<% if @refresh_interval %>
|
|
8
|
+
<meta http-equiv="refresh" content="<%= @refresh_interval %>">
|
|
9
|
+
<% end %>
|
|
10
|
+
<%= render "action_agent/traces/styles" %>
|
|
57
11
|
</head>
|
|
58
|
-
<body class="
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<div class="flex-shrink-0">
|
|
66
|
-
<span class="text-xl font-bold text-indigo-600 dark:text-indigo-400">
|
|
67
|
-
ActiveAgent
|
|
68
|
-
</span>
|
|
69
|
-
</div>
|
|
70
|
-
<div class="ml-10 flex items-baseline space-x-4">
|
|
71
|
-
<%= link_to "Traces",
|
|
72
|
-
traces_path,
|
|
73
|
-
class: "px-3 py-2 rounded-md text-sm font-medium #{request.path == traces_path ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-200' : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'}" %>
|
|
74
|
-
<%= link_to "Metrics",
|
|
75
|
-
metrics_traces_path,
|
|
76
|
-
class: "px-3 py-2 rounded-md text-sm font-medium #{request.path == metrics_traces_path ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-200' : 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'}" %>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="text-sm text-gray-500 dark:text-gray-400">
|
|
80
|
-
Local Telemetry
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
12
|
+
<body class="trace-console">
|
|
13
|
+
<header>
|
|
14
|
+
<nav aria-label="Trace console">
|
|
15
|
+
<strong>ActiveAgent</strong>
|
|
16
|
+
<%= link_to "Traces", traces_path %>
|
|
17
|
+
<%= link_to "Metrics", metrics_traces_path %>
|
|
18
|
+
<span class="muted">Local Telemetry</span>
|
|
84
19
|
</nav>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
<%=
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
20
|
+
</header>
|
|
21
|
+
<main>
|
|
22
|
+
<div class="refresh-controls">
|
|
23
|
+
<% if @refresh_interval %>
|
|
24
|
+
<span>Refreshes every <%= @refresh_interval %> seconds.</span>
|
|
25
|
+
<%= link_to "Stop refreshing", "#{request.path}?#{request.query_parameters.except('refresh').to_query}" %>
|
|
26
|
+
<% else %>
|
|
27
|
+
<%= link_to "Auto-refresh", "#{request.path}?#{request.query_parameters.merge('refresh' => '30').to_query}" %>
|
|
28
|
+
<% end %>
|
|
29
|
+
<%= link_to "Refresh now", request.path + (request.query_string.present? ? "?#{request.query_string}" : "") %>
|
|
30
|
+
</div>
|
|
31
|
+
<%= yield %>
|
|
32
|
+
</main>
|
|
93
33
|
</body>
|
|
94
34
|
</html>
|
data/config/routes.rb
CHANGED
|
@@ -29,8 +29,13 @@ ActionAgent::Engine.routes.draw do
|
|
|
29
29
|
|
|
30
30
|
# The collector for evaluation reports an application ran itself
|
|
31
31
|
# (ActiveAgent::Evals::Publisher), at <mount>/api/evaluation_reports.
|
|
32
|
-
# Authenticated like trace ingest, with a bearer token.
|
|
32
|
+
# Authenticated like trace ingest, with a bearer token. GET
|
|
33
|
+
# <mount>/api/evaluation_reports/:run_id exports a stored run in the
|
|
34
|
+
# envelope the collector takes. A run_id may hold dots, so the path has
|
|
35
|
+
# no format extension.
|
|
33
36
|
resources :evaluation_reports, only: [ :create ]
|
|
37
|
+
get "evaluation_reports/:run_id", to: "evaluation_reports#show", as: :evaluation_report,
|
|
38
|
+
constraints: { run_id: %r{[^/]+} }, format: false
|
|
34
39
|
|
|
35
40
|
post "feedback", to: "feedback_ingest#create"
|
|
36
41
|
get "feedback", to: "feedbacks#index"
|
|
@@ -59,6 +64,8 @@ ActionAgent::Engine.routes.draw do
|
|
|
59
64
|
end
|
|
60
65
|
collection do
|
|
61
66
|
get :presets
|
|
67
|
+
# The builder's roster for an agent not created yet.
|
|
68
|
+
get :tool_roster, action: :new_tool_roster
|
|
62
69
|
end
|
|
63
70
|
end
|
|
64
71
|
|
|
@@ -101,6 +108,8 @@ ActionAgent::Engine.routes.draw do
|
|
|
101
108
|
resources :code_sessions, only: [ :index, :create, :show ] do
|
|
102
109
|
member do
|
|
103
110
|
post :cancel
|
|
111
|
+
# Another turn of the session's conversation.
|
|
112
|
+
post :messages
|
|
104
113
|
end
|
|
105
114
|
end
|
|
106
115
|
resource :claude_login, only: [ :show, :create, :destroy ], controller: "claude_logins" do
|
data/lib/action_agent/engine.rb
CHANGED
|
@@ -80,6 +80,12 @@ module ActionAgent
|
|
|
80
80
|
app.config.filter_parameters += [ :credential, :api_key, :access_token, :password, :claude_login_code, /\Arecording_events\z/, /(?:\A|\.)(?:answer|value)\z/i ]
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Evaluation run reports are served as Markdown (`report.md`). Rails
|
|
84
|
+
# registers text/markdown as :md from 7.1; an older host gets it here.
|
|
85
|
+
initializer "action_agent.markdown_mime_type" do
|
|
86
|
+
Mime::Type.register "text/markdown", :md, [], %w[md markdown] unless Mime[:md]
|
|
87
|
+
end
|
|
88
|
+
|
|
83
89
|
# The project setup assistant's request_secret answers go to its
|
|
84
90
|
# handler. Registered on every reload, so the handler is always the
|
|
85
91
|
# current class's.
|
data/lib/action_agent/version.rb
CHANGED
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
|
|
593
|
-
#
|
|
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
|
|
@@ -719,6 +751,16 @@ module ActionAgent
|
|
|
719
751
|
# @return [String, nil]
|
|
720
752
|
attr_accessor :sign_out_path
|
|
721
753
|
|
|
754
|
+
# Items the host app adds to the header's user menu, above "Sign out":
|
|
755
|
+
# its workspace and member pages, its account settings, and for platform
|
|
756
|
+
# admins its admin pages. A Proc receives the signed-in user and the
|
|
757
|
+
# current owner and returns an Array of { label:, href: } Hashes (an
|
|
758
|
+
# Array is used as is), so the host decides per user what to show. The
|
|
759
|
+
# engine has no pages of its own to link here; unset, or when the Proc
|
|
760
|
+
# raises (logged), the menu holds "Sign out" alone.
|
|
761
|
+
# @return [Proc, Array<Hash>, nil]
|
|
762
|
+
attr_accessor :user_menu_links
|
|
763
|
+
|
|
722
764
|
# Where a browser is sent when it asks for a dashboard page without a
|
|
723
765
|
# valid session — the host app's sign-in page. Unset, an unauthenticated
|
|
724
766
|
# page request gets a minimal session-expired page instead of a bare
|
|
@@ -1151,6 +1193,27 @@ module ActionAgent
|
|
|
1151
1193
|
|
|
1152
1194
|
# The tenant +owner+ belongs to. Identity unless the host app says
|
|
1153
1195
|
# otherwise, which is right for every single-tenant install.
|
|
1196
|
+
# The host's user menu items for +user+ in +owner+'s dashboard, as
|
|
1197
|
+
# [{ label:, href: }] with blank or malformed entries dropped. Empty when
|
|
1198
|
+
# user_menu_links is unset or raises, which is logged, not raised: a
|
|
1199
|
+
# broken menu seam must not take the dashboard down with it.
|
|
1200
|
+
# @return [Array<Hash{Symbol => String}>]
|
|
1201
|
+
def user_menu_links_for(user, owner)
|
|
1202
|
+
links = user_menu_links
|
|
1203
|
+
links = links.call(user, owner) if links.respond_to?(:call)
|
|
1204
|
+
Array(links).filter_map do |link|
|
|
1205
|
+
label = link[:label] || link["label"]
|
|
1206
|
+
href = link[:href] || link["href"]
|
|
1207
|
+
next if label.blank? || href.blank?
|
|
1208
|
+
next unless href.to_s.match?(%r{\A(/|https?://)})
|
|
1209
|
+
|
|
1210
|
+
{ label: label.to_s, href: href.to_s }
|
|
1211
|
+
end
|
|
1212
|
+
rescue StandardError => error
|
|
1213
|
+
Rails.logger.warn("[ActionAgent] user_menu_links failed: #{error.class}: #{error.message}")
|
|
1214
|
+
[]
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1154
1217
|
def tenant_for(owner)
|
|
1155
1218
|
return owner if tenant_resolver.nil?
|
|
1156
1219
|
|
|
@@ -1248,6 +1311,7 @@ module ActionAgent
|
|
|
1248
1311
|
@claude_code_timeout = 1800
|
|
1249
1312
|
@codex_command = "codex"
|
|
1250
1313
|
@codex_timeout = 1800
|
|
1314
|
+
@codex_auth = :api_key
|
|
1251
1315
|
@claude_code_auth = :api_key
|
|
1252
1316
|
@claude_code_hosted_login_enabled = false
|
|
1253
1317
|
@claude_code_login_timeout = 300
|
|
@@ -1276,6 +1340,7 @@ module ActionAgent
|
|
|
1276
1340
|
@usage_resolver = nil
|
|
1277
1341
|
@upgrade_url = nil
|
|
1278
1342
|
@sign_out_path = nil
|
|
1343
|
+
@user_menu_links = nil
|
|
1279
1344
|
@sign_in_path = nil
|
|
1280
1345
|
@mcp_catalog = []
|
|
1281
1346
|
@agent_actor_resolver = nil
|
|
@@ -315,9 +315,10 @@ ActionAgent.configure do |config|
|
|
|
315
315
|
# UI
|
|
316
316
|
# ==========================================================================
|
|
317
317
|
#
|
|
318
|
-
# Custom layout for dashboard views
|
|
319
|
-
#
|
|
320
|
-
#
|
|
318
|
+
# Custom layout for dashboard views. The default layouts load nothing
|
|
319
|
+
# from external hosts, so this is only needed to wrap the dashboard in
|
|
320
|
+
# your application's own chrome. A custom layout replaces the trace
|
|
321
|
+
# console's built-in styles and refresh controls.
|
|
321
322
|
#
|
|
322
323
|
# config.layout = "application"
|
|
323
324
|
end
|
data/lib/generators/action_agent/templates/migrations/028_add_conversations_to_code_sessions.rb.erb
ADDED
|
@@ -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.
|
|
4
|
+
version: 1.9.4
|
|
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-
|
|
11
|
+
date: 2026-10-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activeagent
|
|
@@ -246,7 +246,9 @@ files:
|
|
|
246
246
|
- app/models/concerns/action_agent/session_recordable.rb
|
|
247
247
|
- app/queries/action_agent/agent_executions.rb
|
|
248
248
|
- app/queries/action_agent/metrics_report.rb
|
|
249
|
+
- app/queries/action_agent/trace_window_report.rb
|
|
249
250
|
- app/serializers/action_agent/agent_message_serializer.rb
|
|
251
|
+
- app/serializers/action_agent/code_conversation_serializer.rb
|
|
250
252
|
- app/serializers/action_agent/evaluation_serializer.rb
|
|
251
253
|
- app/serializers/action_agent/input_request_serializer.rb
|
|
252
254
|
- app/serializers/action_agent/interaction_preview.rb
|
|
@@ -261,17 +263,23 @@ files:
|
|
|
261
263
|
- app/services/action_agent/agent_sync.rb
|
|
262
264
|
- app/services/action_agent/agent_tool_roster.rb
|
|
263
265
|
- app/services/action_agent/agent_toolbox.rb
|
|
266
|
+
- app/services/action_agent/browser_client_token.rb
|
|
264
267
|
- app/services/action_agent/browser_live_ticket.rb
|
|
265
268
|
- app/services/action_agent/browser_sidecar.rb
|
|
266
269
|
- app/services/action_agent/browser_sign_in.rb
|
|
267
270
|
- app/services/action_agent/browser_tool_redaction.rb
|
|
271
|
+
- app/services/action_agent/checkout_tools.rb
|
|
268
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
|
|
269
275
|
- app/services/action_agent/codex_session_events.rb
|
|
270
276
|
- app/services/action_agent/dashboard_assistant_service.rb
|
|
277
|
+
- app/services/action_agent/diff_stat.rb
|
|
271
278
|
- app/services/action_agent/draft_pull_request_publisher.rb
|
|
272
279
|
- app/services/action_agent/evaluation_evidence.rb
|
|
273
280
|
- app/services/action_agent/evaluation_fix.rb
|
|
274
281
|
- app/services/action_agent/evaluation_fix_comparison.rb
|
|
282
|
+
- app/services/action_agent/evaluation_report_export.rb
|
|
275
283
|
- app/services/action_agent/evaluation_report_import.rb
|
|
276
284
|
- app/services/action_agent/evaluation_run_cost.rb
|
|
277
285
|
- app/services/action_agent/evaluation_runner_service.rb
|
|
@@ -316,10 +324,13 @@ files:
|
|
|
316
324
|
- app/services/action_agent/session_index.rb
|
|
317
325
|
- app/services/action_agent/session_recording_service.rb
|
|
318
326
|
- app/services/action_agent/session_timeline.rb
|
|
327
|
+
- app/services/action_agent/slack_tools.rb
|
|
319
328
|
- app/services/action_agent/tool_discovery.rb
|
|
320
329
|
- app/views/action_agent/dashboard/index.html.erb
|
|
321
330
|
- app/views/action_agent/github_app_manifests/created.html.erb
|
|
322
331
|
- app/views/action_agent/session_player/show.html.erb
|
|
332
|
+
- app/views/action_agent/traces/_styles.html.erb
|
|
333
|
+
- app/views/action_agent/traces/_summary.html.erb
|
|
323
334
|
- app/views/action_agent/traces/_trace_detail.html.erb
|
|
324
335
|
- app/views/action_agent/traces/index.html.erb
|
|
325
336
|
- app/views/action_agent/traces/metrics.html.erb
|
|
@@ -360,9 +371,11 @@ files:
|
|
|
360
371
|
- lib/generators/action_agent/templates/migrations/025_create_active_agent_scenario_catalogs.rb.erb
|
|
361
372
|
- lib/generators/action_agent/templates/migrations/026_add_evaluation_fixes_to_code_sessions.rb.erb
|
|
362
373
|
- lib/generators/action_agent/templates/migrations/027_create_active_agent_interaction_feedbacks.rb.erb
|
|
374
|
+
- lib/generators/action_agent/templates/migrations/028_add_conversations_to_code_sessions.rb.erb
|
|
363
375
|
- lib/tasks/action_agent.rake
|
|
364
376
|
- lib/tasks/browser.rake
|
|
365
377
|
- lib/tasks/claude_code.rake
|
|
378
|
+
- lib/tasks/evaluations.rake
|
|
366
379
|
- lib/tasks/sandbox.rake
|
|
367
380
|
- lib/tasks/scenario_catalogs.rake
|
|
368
381
|
homepage: https://activeagents.ai
|