activeagent 1.1.0 → 1.2.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/README.md +24 -14
  4. data/lib/active_agent/telemetry/configuration.rb +13 -9
  5. data/lib/active_agent/telemetry/instrumentation.rb +4 -0
  6. data/lib/active_agent/telemetry/tool_origin.rb +90 -0
  7. data/lib/active_agent/telemetry.rb +1 -0
  8. data/lib/active_agent/version.rb +1 -1
  9. data/lib/active_agent.rb +0 -5
  10. metadata +22 -32
  11. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb +0 -138
  12. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb +0 -64
  13. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb +0 -129
  14. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb +0 -123
  15. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb +0 -56
  16. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb +0 -14
  17. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb +0 -49
  18. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb +0 -65
  19. data/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb +0 -86
  20. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +0 -256
  21. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb +0 -113
  22. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb +0 -208
  23. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb +0 -60
  24. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +0 -46
  25. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb +0 -125
  26. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb +0 -83
  27. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb +0 -52
  28. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +0 -169
  29. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +0 -193
  30. data/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +0 -214
  31. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb +0 -117
  32. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb +0 -135
  33. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb +0 -145
  34. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb +0 -36
  35. data/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb +0 -94
  36. data/lib/active_agent/dashboard/config/routes.rb +0 -19
  37. data/lib/active_agent/dashboard/engine.rb +0 -43
  38. data/lib/active_agent/dashboard.rb +0 -161
  39. data/lib/generators/active_agent/dashboard/install_generator.rb +0 -92
  40. data/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb +0 -67
  41. data/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb +0 -46
@@ -1,94 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" class="h-full">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>ActiveAgent Dashboard</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://unpkg.com/@hotwired/turbo@7.3.0/dist/turbo.es2017-esm.js" type="module"></script>
9
- <script src="https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js"></script>
10
- <style>
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>
57
- </head>
58
- <body class="h-full bg-gray-50 dark:bg-gray-900">
59
- <div class="min-h-full">
60
- <!-- Header -->
61
- <nav class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
62
- <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
63
- <div class="flex h-16 items-center justify-between">
64
- <div class="flex items-center">
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>
84
- </nav>
85
-
86
- <!-- Main content -->
87
- <main>
88
- <div class="mx-auto max-w-7xl py-6 px-4 sm:px-6 lg:px-8">
89
- <%= yield %>
90
- </div>
91
- </main>
92
- </div>
93
- </body>
94
- </html>
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ActiveAgent::Dashboard::Engine.routes.draw do
4
- # The functional dashboard surface: traces + metrics.
5
- root to: "traces#index"
6
- get "dashboard", to: "dashboard#index"
7
-
8
- resources :traces, only: [ :index, :show ] do
9
- collection do
10
- get :metrics
11
- end
12
- end
13
-
14
- # Telemetry ingestion, relative to wherever the engine is mounted:
15
- # <mount>/api/traces (e.g. /activeagents/api/traces at the default mount).
16
- namespace :api do
17
- resources :traces, only: [ :create ]
18
- end
19
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Rails Engine for the ActiveAgent Dashboard.
6
- #
7
- # Provides the free self-hosted dashboard: telemetry traces, metrics,
8
- # and the local trace ingestion API.
9
- #
10
- # Mount in your routes:
11
- # mount ActiveAgent::Dashboard::Engine => "/activeagents"
12
- #
13
- class Engine < ::Rails::Engine
14
- # The engine lives at lib/active_agent/dashboard rather than the gem
15
- # root. Rails derives an engine's load paths (app/models,
16
- # app/controllers, app/jobs, app/views, config/routes.rb) from
17
- # find_root when +config+ is first materialized — which
18
- # isolate_namespace below triggers via +routes+ — so this override
19
- # must be defined before anything touches config. Overriding only
20
- # +root+ leaves the engine's classes off host applications' autoload
21
- # paths entirely.
22
- def self.find_root(_from)
23
- root
24
- end
25
-
26
- def self.root
27
- @root ||= Pathname.new(File.expand_path("..", __FILE__))
28
- end
29
-
30
- isolate_namespace ActiveAgent::Dashboard
31
-
32
- # Use a unique engine name to avoid conflicts
33
- engine_name "active_agent"
34
-
35
- # Alias for consistency with existing code
36
- def self.dashboard_root
37
- root
38
- end
39
-
40
- config.active_agent_dashboard = ActiveSupport::OrderedOptions.new
41
- end
42
- end
43
- end
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_agent/dashboard/engine"
4
-
5
- module ActiveAgent
6
- # Dashboard engine for visualizing telemetry data and managing agents.
7
- #
8
- # Mount the engine in your routes to access the full dashboard:
9
- #
10
- # # config/routes.rb
11
- # mount ActiveAgent::Dashboard::Engine => "/activeagents"
12
- #
13
- # The dashboard provides:
14
- # - Agent management: Create, edit, version, and execute agents
15
- # - Traces view: See all agent invocations with spans, timing, and token usage
16
- # - Metrics view: Aggregate statistics and charts
17
- # - Sandbox execution: Run agents in isolated environments
18
- # - Session recordings: Capture and replay browser sessions
19
- #
20
- # = Configuration Modes
21
- #
22
- # == Local Mode (default)
23
- # For self-hosted, single-tenant deployments:
24
- #
25
- # ActiveAgent::Dashboard.configure do |config|
26
- # config.authentication_method = ->(controller) { controller.authenticate_admin! }
27
- # config.sandbox_service = :local # Docker/Incus
28
- # end
29
- #
30
- # == Multi-tenant Mode
31
- # For SaaS platforms with multiple accounts:
32
- #
33
- # ActiveAgent::Dashboard.configure do |config|
34
- # config.multi_tenant = true
35
- # config.account_class = "Account"
36
- # config.user_class = "User"
37
- # config.current_account_method = :current_account
38
- # config.current_user_method = :current_user
39
- # config.authentication_method = ->(controller) { controller.authenticate_user! }
40
- # config.sandbox_service = :cloud_run # Managed
41
- # config.use_inertia = true
42
- # end
43
- #
44
- module Dashboard
45
- class << self
46
- # Authentication method to call on controllers
47
- # @return [Proc, nil] A proc that receives the controller instance
48
- attr_accessor :authentication_method
49
-
50
- # Enable multi-tenant mode (requires account association)
51
- # @return [Boolean]
52
- attr_accessor :multi_tenant
53
-
54
- # Class name for the Account model (multi-tenant mode)
55
- # @return [String, nil]
56
- attr_accessor :account_class
57
-
58
- # Class name for the User model
59
- # @return [String, nil]
60
- attr_accessor :user_class
61
-
62
- # Method to call on controller to get current account (multi-tenant mode)
63
- # @return [Symbol, nil]
64
- attr_accessor :current_account_method
65
-
66
- # Method to call on controller to get current user
67
- # @return [Symbol, nil]
68
- attr_accessor :current_user_method
69
-
70
- # Custom trace model class (for host app overrides)
71
- # @return [String, nil]
72
- attr_accessor :trace_model_class
73
-
74
- # Enable React/Inertia frontend instead of ERB
75
- # @return [Boolean]
76
- attr_accessor :use_inertia
77
-
78
- # Custom layout for the dashboard
79
- # @return [String, nil]
80
- attr_accessor :layout
81
-
82
- # Sandbox service type (:local, :cloud_run, :kubernetes)
83
- # @return [Symbol]
84
- attr_accessor :sandbox_service
85
-
86
- # Custom sandbox limits (overrides defaults)
87
- # @return [Hash, nil]
88
- attr_accessor :sandbox_limits
89
-
90
- # Storage service for screenshots/snapshots
91
- # @return [Object, nil] Object responding to #signed_url_for and #fetch_snapshot
92
- attr_accessor :storage_service
93
-
94
- # Bearer token required by the ingest API in single-tenant mode. When
95
- # unset the local ingest endpoint accepts unauthenticated posts, so set
96
- # it whenever the mount is reachable beyond your own machine.
97
- # (Multi-tenant mode authenticates per-account keys instead.)
98
- # @return [String, nil]
99
- attr_accessor :ingest_api_key
100
-
101
- # @deprecated Never consumed — dashboard controllers inherit
102
- # ActionController::Base. Retained as a no-op so existing
103
- # initializers that set it keep booting; remove in the next major.
104
- # @return [String]
105
- attr_accessor :base_controller_class
106
-
107
- # Returns whether multi-tenant mode is enabled.
108
- #
109
- # @return [Boolean]
110
- def multi_tenant?
111
- @multi_tenant == true
112
- end
113
-
114
- # Returns the trace model class to use.
115
- #
116
- # @return [Class] The trace model class
117
- def trace_model
118
- if trace_model_class
119
- trace_model_class.constantize
120
- else
121
- ActiveAgent::TelemetryTrace
122
- end
123
- end
124
-
125
- # Returns the agent model class to use.
126
- #
127
- # @return [Class] The agent model class
128
- def agent_model
129
- ActiveAgent::Dashboard::Agent
130
- end
131
-
132
- # Configures the dashboard.
133
- #
134
- # @yield [config] Configuration block
135
- def configure
136
- yield self
137
- end
138
-
139
- # Reset configuration to defaults
140
- def reset!
141
- @authentication_method = nil
142
- @multi_tenant = false
143
- @account_class = nil
144
- @user_class = nil
145
- @current_account_method = nil
146
- @current_user_method = nil
147
- @trace_model_class = nil
148
- @use_inertia = false
149
- @layout = nil
150
- @sandbox_service = :local
151
- @sandbox_limits = nil
152
- @storage_service = nil
153
- @ingest_api_key = nil
154
- @base_controller_class = "ActionController::Base" # deprecated no-op
155
- end
156
- end
157
-
158
- # Set defaults
159
- reset!
160
- end
161
- end
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators"
4
- require "rails/generators/active_record"
5
-
6
- module ActiveAgent
7
- module Dashboard
8
- # Generator for installing the ActiveAgent Dashboard.
9
- #
10
- # @example Run the generator
11
- # rails generate active_agent:dashboard:install
12
- #
13
- # This will:
14
- # - Create the telemetry_traces table migration
15
- # - Add mount directive to routes
16
- # - Create initializer for dashboard configuration
17
- #
18
- class InstallGenerator < Rails::Generators::Base
19
- include ActiveRecord::Generators::Migration
20
-
21
- source_root File.expand_path("templates", __dir__)
22
-
23
- desc "Installs the ActiveAgent Dashboard with telemetry storage"
24
-
25
- class_option :multi_tenant, type: :boolean, default: false,
26
- desc: "Scope traces to an Account (adds account_id to the migration)"
27
-
28
- class_option :skip_migrations, type: :boolean, default: false,
29
- desc: "Skip copying the telemetry traces migration"
30
-
31
- class_option :skip_routes, type: :boolean, default: false,
32
- desc: "Skip adding the engine mount to routes.rb"
33
-
34
- def copy_migrations
35
- return if options[:skip_migrations]
36
-
37
- migration_template(
38
- "create_active_agent_telemetry_traces.rb.erb",
39
- "db/migrate/create_active_agent_telemetry_traces.rb"
40
- )
41
- end
42
-
43
- def add_route
44
- return if options[:skip_routes]
45
-
46
- # Rails' `route` action is only idempotent on an exact string match,
47
- # so an app installed before the default path changed would get a
48
- # second mount — and two unnamed mounts of the same engine raise
49
- # "Invalid route name, already in use: 'active_agent'" at boot.
50
- routes_file = File.join(destination_root, "config/routes.rb")
51
- if File.exist?(routes_file) && File.read(routes_file).include?("ActiveAgent::Dashboard::Engine")
52
- say_status :skip, "engine already mounted in config/routes.rb", :yellow
53
- return
54
- end
55
-
56
- route 'mount ActiveAgent::Dashboard::Engine => "/activeagents"'
57
- end
58
-
59
- def create_initializer
60
- template(
61
- "active_agent_dashboard.rb.erb",
62
- "config/initializers/active_agent_dashboard.rb"
63
- )
64
- end
65
-
66
- def show_readme
67
- say "\n"
68
- say "ActiveAgent Dashboard installed successfully!", :green
69
- say "\n"
70
- say "Next steps:"
71
- say " 1. Run migrations: rails db:migrate"
72
- say " 2. Configure telemetry in config/active_agent.yml:"
73
- say " telemetry:"
74
- say " enabled: true"
75
- say " local_storage: true"
76
- say " 3. Visit /activeagents to view the dashboard"
77
- say "\n"
78
- end
79
-
80
- private
81
-
82
- def migration_version
83
- "[#{ActiveRecord::Migration.current_version}]"
84
- end
85
-
86
- # Consumed by the migration template.
87
- def multi_tenant?
88
- options[:multi_tenant]
89
- end
90
- end
91
- end
92
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # ActiveAgent Dashboard Configuration
4
- #
5
- # This file configures the ActiveAgent telemetry dashboard.
6
- # The dashboard is mounted at /activeagents by default (see config/routes.rb).
7
-
8
- ActiveAgent::Dashboard.configure do |config|
9
- # ==========================================================================
10
- # Authentication (required in production)
11
- # ==========================================================================
12
- #
13
- # Provide a lambda that receives the controller and performs
14
- # authentication. Return false or raise to deny access. Without one, the
15
- # dashboard serves a 403 in production — traces contain prompts, outputs
16
- # and error messages.
17
- #
18
- # Basic auth:
19
- # config.authentication_method = ->(controller) {
20
- # controller.authenticate_or_request_with_http_basic do |username, password|
21
- # username == "admin" && password == Rails.application.credentials.dashboard_password
22
- # end
23
- # }
24
- #
25
- # Devise:
26
- # config.authentication_method = ->(controller) {
27
- # controller.authenticate_admin!
28
- # }
29
- #
30
- # No authentication (development only!):
31
- # config.authentication_method = nil
32
- #
33
- config.authentication_method = nil
34
-
35
- # ==========================================================================
36
- # Ingest API authentication
37
- # ==========================================================================
38
- #
39
- # Bearer token other apps must send when posting traces to the mounted
40
- # ingest endpoint (/activeagents/api/traces). Leave unset only when the
41
- # mount is not reachable beyond your own machine.
42
- #
43
- # config.ingest_api_key = Rails.application.credentials.dig(:active_agent, :ingest_api_key)
44
-
45
- # ==========================================================================
46
- # Multi-tenant mode (SaaS platforms only)
47
- # ==========================================================================
48
- #
49
- # Scopes traces per account and authenticates ingest with per-account
50
- # keys. Most self-hosted installs should leave this off.
51
- #
52
- # config.multi_tenant = true
53
- # config.account_class = "Account"
54
- # config.user_class = "User"
55
- # config.current_account_method = :current_account
56
- # config.current_user_method = :current_user
57
-
58
- # ==========================================================================
59
- # UI
60
- # ==========================================================================
61
- #
62
- # Custom layout for dashboard views (the default layout loads Tailwind,
63
- # Turbo and Stimulus from CDNs — override for CSP-strict or air-gapped
64
- # environments).
65
- #
66
- # config.layout = "application"
67
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class CreateActiveAgentTelemetryTraces < ActiveRecord::Migration<%= migration_version %>
4
- def change
5
- create_table :active_agent_telemetry_traces do |t|
6
- <% if multi_tenant? -%>
7
- # Multi-tenant mode: every query is scoped through `for_account`, and
8
- # ingest authenticates a per-account key. The foreign key requires an
9
- # existing `accounts` table — on PostgreSQL/MySQL this migration must
10
- # run after the one that creates it (drop `foreign_key: true` if your
11
- # tenant table is named differently).
12
- t.references :account, null: false, foreign_key: true
13
- <% end -%>
14
- t.string :trace_id, null: false
15
- t.string :service_name
16
- t.string :environment
17
- t.datetime :timestamp, index: true
18
- t.json :spans, default: []
19
- t.json :resource_attributes, default: {}
20
- t.json :sdk_info, default: {}
21
- t.decimal :total_duration_ms, precision: 12, scale: 3
22
- t.integer :total_input_tokens, default: 0
23
- t.integer :total_output_tokens, default: 0
24
- t.integer :total_thinking_tokens, default: 0
25
- t.string :status
26
- t.string :agent_class, index: true
27
- t.string :agent_action
28
- t.text :error_message
29
-
30
- t.timestamps
31
- end
32
-
33
- <% if multi_tenant? -%>
34
- # Ingest dedupes on trace_id (per account when multi-tenant); the unique
35
- # index makes that dedupe race-proof rather than check-then-insert.
36
- add_index :active_agent_telemetry_traces, [:account_id, :trace_id], unique: true
37
- add_index :active_agent_telemetry_traces, [:account_id, :timestamp]
38
- <% else -%>
39
- add_index :active_agent_telemetry_traces, :trace_id, unique: true
40
- <% end -%>
41
- add_index :active_agent_telemetry_traces, :status
42
- add_index :active_agent_telemetry_traces, [:agent_class, :agent_action]
43
- add_index :active_agent_telemetry_traces, [:service_name, :environment]
44
- add_index :active_agent_telemetry_traces, :created_at
45
- end
46
- end