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,256 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Represents an AI agent configuration.
6
- #
7
- # Agents are the core entity in the dashboard, storing all configuration
8
- # needed to execute AI interactions including provider settings, instructions,
9
- # tools, and appearance.
10
- #
11
- # Supports both local (single-user) and multi-tenant (account-scoped) modes.
12
- #
13
- # @example Creating an agent
14
- # agent = ActiveAgent::Dashboard::Agent.create!(
15
- # name: "Code Assistant",
16
- # provider: "openai",
17
- # model: "gpt-4o"
18
- # )
19
- #
20
- # @example Executing an agent
21
- # run = agent.execute("Explain this code", code: "def foo; end")
22
- #
23
- class Agent < ApplicationRecord
24
- # Associations - owner is optional to support both modes
25
- belongs_to :user, class_name: ActiveAgent::Dashboard.user_class, optional: true if ActiveAgent::Dashboard.user_class
26
- belongs_to :account, class_name: ActiveAgent::Dashboard.account_class, optional: true if ActiveAgent::Dashboard.multi_tenant?
27
-
28
- has_many :agent_versions, class_name: "ActiveAgent::Dashboard::AgentVersion", dependent: :destroy
29
- has_many :agent_runs, class_name: "ActiveAgent::Dashboard::AgentRun", dependent: :destroy
30
-
31
- # Validations
32
- validates :name, presence: true, length: { minimum: 2, maximum: 100 }
33
- validates :slug, presence: true, format: { with: /\A[a-z0-9\-_]+\z/ }
34
- validates :provider, presence: true
35
- validates :model, presence: true
36
-
37
- # Ensure slug uniqueness within scope
38
- if ActiveAgent::Dashboard.multi_tenant?
39
- validates :slug, uniqueness: { scope: :account_id }
40
- else
41
- validates :slug, uniqueness: { scope: :user_id }
42
- end
43
-
44
- # Status enum
45
- enum :status, { draft: 0, active: 1, archived: 2 }
46
-
47
- # Callbacks
48
- before_validation :generate_slug, on: :create
49
- after_create :create_initial_version
50
- after_update :create_version_on_config_change, if: :configuration_changed?
51
-
52
- # Scopes
53
- scope :active_agents, -> { where(status: :active) }
54
- scope :by_provider, ->(provider) { where(provider: provider) }
55
- scope :with_tool, ->(tool) { where("tools @> ?", [ tool ].to_json) }
56
-
57
- # Available presets matching AgentAvatar component
58
- PRESET_TYPES = %w[
59
- terminal webDeveloper documentAnalysis writing translation
60
- playwright research imageAnalysis computerUse productDesign
61
- ].freeze
62
-
63
- # Available instruction sets
64
- INSTRUCTION_SETS = %w[
65
- github ruby rails aws gcp python typescript docker kubernetes
66
- ].freeze
67
-
68
- # Available tools/MCPs
69
- AVAILABLE_TOOLS = %w[
70
- terminal playwright filesystem code database slack fetch search edit translate memory
71
- ].freeze
72
-
73
- # Available providers
74
- PROVIDERS = %w[openai anthropic ollama openrouter requesty].freeze
75
-
76
- # Returns the configuration as a hash for versioning
77
- def configuration_snapshot
78
- {
79
- name: name,
80
- description: description,
81
- provider: provider,
82
- model: model,
83
- instructions: instructions,
84
- preset_type: preset_type,
85
- appearance: appearance,
86
- instruction_sets: instruction_sets,
87
- tools: tools,
88
- mcp_servers: mcp_servers,
89
- model_config: model_config,
90
- response_format: response_format
91
- }
92
- end
93
-
94
- # Restore from a version
95
- def restore_from_version!(version)
96
- config = version.configuration_snapshot
97
- update!(
98
- instructions: config["instructions"],
99
- preset_type: config["preset_type"],
100
- appearance: config["appearance"],
101
- instruction_sets: config["instruction_sets"],
102
- tools: config["tools"],
103
- mcp_servers: config["mcp_servers"],
104
- model_config: config["model_config"],
105
- response_format: config["response_format"]
106
- )
107
- end
108
-
109
- # Get the latest version
110
- def latest_version
111
- agent_versions.order(version_number: :desc).first
112
- end
113
-
114
- # Get version count
115
- def version_count
116
- agent_versions.count
117
- end
118
-
119
- # Generate Ruby agent class code
120
- def to_agent_class_code
121
- <<~RUBY
122
- class #{agent_class_name || name.camelize}Agent < ApplicationAgent
123
- generate_with :#{provider}, model: "#{model}"#{model_config_code}
124
-
125
- def perform
126
- prompt#{instructions_code}
127
- end
128
- end
129
- RUBY
130
- end
131
-
132
- # Execute a run with this agent
133
- def execute(input_prompt, **params)
134
- run = agent_runs.create!(
135
- input_prompt: input_prompt,
136
- input_params: params,
137
- status: :pending,
138
- trace_id: SecureRandom.uuid
139
- )
140
-
141
- # Queue the execution job
142
- ActiveAgent::Dashboard::AgentExecutionJob.perform_later(run.id)
143
-
144
- run
145
- end
146
-
147
- # Quick test execution (synchronous)
148
- def test_execute(input_prompt, **params)
149
- run = agent_runs.create!(
150
- input_prompt: input_prompt,
151
- input_params: params,
152
- status: :running,
153
- trace_id: SecureRandom.uuid,
154
- started_at: Time.current
155
- )
156
-
157
- begin
158
- result = build_and_execute_agent(input_prompt, **params)
159
-
160
- run.update!(
161
- output: result[:output],
162
- output_metadata: result[:metadata],
163
- status: :complete,
164
- completed_at: Time.current,
165
- duration_ms: ((Time.current - run.started_at) * 1000).to_i,
166
- input_tokens: result.dig(:usage, :input_tokens),
167
- output_tokens: result.dig(:usage, :output_tokens),
168
- total_tokens: result.dig(:usage, :total_tokens)
169
- )
170
- rescue => e
171
- run.update!(
172
- status: :failed,
173
- completed_at: Time.current,
174
- error_message: e.message,
175
- error_backtrace: e.backtrace&.first(10)&.join("\n")
176
- )
177
- end
178
-
179
- run
180
- end
181
-
182
- private
183
-
184
- def generate_slug
185
- return if slug.present?
186
-
187
- base_slug = name.to_s.parameterize
188
- self.slug = base_slug
189
-
190
- # Ensure uniqueness within scope
191
- counter = 1
192
- scope = self.class.where(slug: slug)
193
- scope = scope.where(account_id: account_id) if respond_to?(:account_id) && account_id
194
- scope = scope.where(user_id: user_id) if respond_to?(:user_id) && user_id
195
-
196
- while scope.exists?
197
- self.slug = "#{base_slug}-#{counter}"
198
- scope = self.class.where(slug: slug)
199
- scope = scope.where(account_id: account_id) if respond_to?(:account_id) && account_id
200
- scope = scope.where(user_id: user_id) if respond_to?(:user_id) && user_id
201
- counter += 1
202
- end
203
- end
204
-
205
- def create_initial_version
206
- agent_versions.create!(
207
- version_number: 1,
208
- change_summary: "Initial creation",
209
- configuration_snapshot: configuration_snapshot
210
- )
211
- end
212
-
213
- def configuration_changed?
214
- saved_changes.keys.any? do |key|
215
- %w[instructions preset_type appearance instruction_sets tools mcp_servers model_config response_format].include?(key)
216
- end
217
- end
218
-
219
- def create_version_on_config_change
220
- next_version = (latest_version&.version_number || 0) + 1
221
- changed_fields = saved_changes.keys.select do |key|
222
- %w[instructions preset_type appearance instruction_sets tools mcp_servers model_config response_format].include?(key)
223
- end
224
-
225
- agent_versions.create!(
226
- version_number: next_version,
227
- change_summary: "Updated: #{changed_fields.join(', ')}",
228
- configuration_snapshot: configuration_snapshot
229
- )
230
- end
231
-
232
- def model_config_code
233
- return "" if model_config.blank?
234
-
235
- configs = model_config.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
236
- ", #{configs}"
237
- end
238
-
239
- def instructions_code
240
- return "" if instructions.blank?
241
-
242
- "\n prompt instructions: <<~INSTRUCTIONS\n #{instructions.gsub("\n", "\n ")}\n INSTRUCTIONS"
243
- end
244
-
245
- def build_and_execute_agent(input_prompt, **params)
246
- # TODO: Implement actual ActiveAgent execution
247
- # This will create a dynamic agent class and execute it
248
- {
249
- output: "Mock response for: #{input_prompt}",
250
- metadata: { provider: provider, model: model },
251
- usage: { input_tokens: 10, output_tokens: 20, total_tokens: 30 }
252
- }
253
- end
254
- end
255
- end
256
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Tracks individual agent execution runs.
6
- #
7
- # Each run captures input, output, timing, token usage, and any errors
8
- # that occurred during execution.
9
- #
10
- # @example Creating a run
11
- # run = agent.execute("Analyze this code", code: code)
12
- # run.status # => "pending"
13
- #
14
- # @example Monitoring a run
15
- # run.in_progress? # => true
16
- # run.finished? # => false
17
- #
18
- class AgentRun < ApplicationRecord
19
- belongs_to :agent, class_name: "ActiveAgent::Dashboard::Agent"
20
- has_one :session_recording, class_name: "ActiveAgent::Dashboard::SessionRecording", dependent: :nullify
21
-
22
- # Status enum
23
- enum :status, { pending: 0, running: 1, complete: 2, failed: 3, cancelled: 4 }
24
-
25
- # Validations
26
- validates :trace_id, presence: true
27
-
28
- # Scopes
29
- scope :recent, -> { order(created_at: :desc) }
30
- scope :successful, -> { where(status: :complete) }
31
- scope :failed_runs, -> { where(status: :failed) }
32
- scope :today, -> { where("created_at >= ?", Time.current.beginning_of_day) }
33
-
34
- # Callbacks
35
- before_validation :set_trace_id, on: :create
36
- after_update_commit :broadcast_update, if: :saved_change_to_status?
37
-
38
- # Add a log entry
39
- def add_log(message, level: :info)
40
- new_logs = logs || []
41
- new_logs << {
42
- timestamp: Time.current.iso8601,
43
- level: level.to_s,
44
- message: message
45
- }
46
- update!(logs: new_logs)
47
- end
48
-
49
- # Calculate duration if not set
50
- def calculated_duration_ms
51
- return duration_ms if duration_ms.present?
52
- return nil unless started_at && completed_at
53
-
54
- ((completed_at - started_at) * 1000).to_i
55
- end
56
-
57
- # Check if run is still in progress
58
- def in_progress?
59
- pending? || running?
60
- end
61
-
62
- # Check if run is finished
63
- def finished?
64
- complete? || failed? || cancelled?
65
- end
66
-
67
- # Get a summary for display
68
- def summary
69
- {
70
- id: id,
71
- status: status,
72
- input_preview: input_prompt&.truncate(100),
73
- output_preview: output&.truncate(200),
74
- duration_ms: calculated_duration_ms,
75
- tokens: total_tokens,
76
- created_at: created_at,
77
- error: error_message
78
- }
79
- end
80
-
81
- # Stream output updates via ActionCable
82
- def broadcast_update
83
- return unless defined?(ActionCable)
84
-
85
- ActionCable.server.broadcast(
86
- "agent_run_#{id}",
87
- {
88
- type: "update",
89
- run: summary
90
- }
91
- )
92
- end
93
-
94
- # Cancel a running execution
95
- def cancel!
96
- return unless in_progress?
97
-
98
- update!(
99
- status: :cancelled,
100
- completed_at: Time.current,
101
- error_message: "Cancelled by user"
102
- )
103
- broadcast_update
104
- end
105
-
106
- private
107
-
108
- def set_trace_id
109
- self.trace_id ||= SecureRandom.uuid
110
- end
111
- end
112
- end
113
- end
@@ -1,208 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Pre-built agent templates for quick agent creation.
6
- #
7
- # Templates provide starting configurations for common use cases like
8
- # code assistance, research, writing, and browser automation.
9
- #
10
- # @example Creating an agent from a template
11
- # template = ActiveAgent::Dashboard::AgentTemplate.find_by(slug: "code-assistant")
12
- # agent = template.create_agent_for(user)
13
- #
14
- class AgentTemplate < ApplicationRecord
15
- # Validations
16
- validates :name, presence: true
17
- validates :slug, presence: true, uniqueness: true
18
- validates :category, presence: true
19
-
20
- # Scopes
21
- scope :featured, -> { where(featured: true) }
22
- scope :by_category, ->(cat) { where(category: cat) }
23
- scope :popular, -> { order(usage_count: :desc) }
24
- scope :public_templates, -> { where(public: true) }
25
- scope :free_tier, -> { where(free_tier: true) }
26
-
27
- # Categories
28
- CATEGORIES = %w[
29
- productivity
30
- development
31
- research
32
- creative
33
- data
34
- automation
35
- ].freeze
36
-
37
- # Create an agent from this template for a user/account
38
- def create_agent_for(owner, name: nil)
39
- agent_class = ActiveAgent::Dashboard::Agent
40
-
41
- agent = agent_class.new(
42
- name: name || self.name,
43
- description: description,
44
- provider: provider,
45
- model: model,
46
- instructions: instructions,
47
- preset_type: preset_type,
48
- appearance: appearance,
49
- instruction_sets: instruction_sets,
50
- tools: tools,
51
- mcp_servers: mcp_servers,
52
- model_config: model_config,
53
- status: :draft
54
- )
55
-
56
- # Set owner based on mode
57
- if ActiveAgent::Dashboard.multi_tenant? && owner.respond_to?(:id)
58
- agent.account = owner
59
- elsif owner.respond_to?(:id)
60
- agent.user = owner if agent.respond_to?(:user=)
61
- end
62
-
63
- if agent.save
64
- increment!(:usage_count)
65
- end
66
-
67
- agent
68
- end
69
-
70
- # Seed default templates
71
- def self.seed_defaults!
72
- templates = [
73
- {
74
- name: "Code Assistant",
75
- slug: "code-assistant",
76
- description: "A helpful coding assistant that can explain code, suggest improvements, and help debug issues.",
77
- category: "development",
78
- provider: "openai",
79
- model: "gpt-4o",
80
- preset_type: "terminal",
81
- appearance: { hat: "fedora", heldItem: "terminal" },
82
- instruction_sets: %w[github ruby rails typescript],
83
- tools: %w[terminal code filesystem],
84
- model_config: { temperature: 0.3 },
85
- instructions: "You are a senior software engineer with expertise in multiple programming languages. Help users with:\n- Code explanations and reviews\n- Debugging issues\n- Suggesting best practices\n- Writing tests\n\nAlways explain your reasoning and provide examples when helpful.",
86
- icon: "💻",
87
- featured: true,
88
- free_tier: true
89
- },
90
- {
91
- name: "Research Assistant",
92
- slug: "research-assistant",
93
- description: "Helps research topics, summarize information, and organize findings.",
94
- category: "research",
95
- provider: "anthropic",
96
- model: "claude-sonnet-4-20250514",
97
- preset_type: "research",
98
- appearance: { hat: "safari", heldItem: "magnifyingGlass" },
99
- instruction_sets: %w[github python],
100
- tools: %w[fetch search memory],
101
- model_config: { temperature: 0.5 },
102
- instructions: "You are a thorough research assistant. Help users by:\n- Searching for relevant information\n- Summarizing complex topics\n- Organizing findings into clear reports\n- Identifying key insights and patterns\n\nAlways cite sources when available and distinguish between facts and opinions.",
103
- icon: "🔍",
104
- featured: true,
105
- free_tier: true
106
- },
107
- {
108
- name: "Writing Assistant",
109
- slug: "writing-assistant",
110
- description: "Helps with writing, editing, and improving text content.",
111
- category: "creative",
112
- provider: "openai",
113
- model: "gpt-4o",
114
- preset_type: "writing",
115
- appearance: { hat: "fedora", hatAccessory: "feather", heldItem: "scroll" },
116
- instruction_sets: [],
117
- tools: %w[edit translate],
118
- model_config: { temperature: 0.7 },
119
- instructions: "You are a skilled writer and editor. Help users with:\n- Writing and editing content\n- Improving clarity and flow\n- Adjusting tone for different audiences\n- Grammar and style corrections\n\nMaintain the author's voice while suggesting improvements.",
120
- icon: "✍️",
121
- featured: true,
122
- free_tier: true
123
- },
124
- {
125
- name: "Browser Automation",
126
- slug: "browser-automation",
127
- description: "Automates web browsing tasks like form filling, data extraction, and testing.",
128
- category: "automation",
129
- provider: "anthropic",
130
- model: "claude-sonnet-4-20250514",
131
- preset_type: "playwright",
132
- appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" },
133
- instruction_sets: %w[typescript],
134
- tools: %w[playwright filesystem],
135
- model_config: { temperature: 0.2 },
136
- instructions: "You are a browser automation specialist. Help users by:\n- Navigating web pages\n- Filling out forms\n- Extracting data from websites\n- Testing web applications\n\nAlways wait for page loads and handle errors gracefully.",
137
- icon: "🎭",
138
- featured: false,
139
- free_tier: true
140
- },
141
- {
142
- name: "Data Analyst",
143
- slug: "data-analyst",
144
- description: "Analyzes data, creates visualizations, and provides insights.",
145
- category: "data",
146
- provider: "openai",
147
- model: "gpt-4o",
148
- preset_type: "documentAnalysis",
149
- appearance: { hat: "fedora", heldItem: "document" },
150
- instruction_sets: %w[python],
151
- tools: %w[code database filesystem],
152
- model_config: { temperature: 0.3 },
153
- instructions: "You are a data analyst. Help users by:\n- Analyzing datasets\n- Creating visualizations\n- Finding patterns and insights\n- Generating reports\n\nExplain your methodology and provide clear interpretations of results.",
154
- icon: "📊",
155
- featured: true,
156
- free_tier: true
157
- },
158
- {
159
- name: "DevOps Assistant",
160
- slug: "devops-assistant",
161
- description: "Helps with infrastructure, deployments, and system administration.",
162
- category: "development",
163
- provider: "openai",
164
- model: "gpt-4o",
165
- preset_type: "terminal",
166
- appearance: { hat: "fedora", heldItem: "terminal" },
167
- instruction_sets: %w[docker kubernetes aws gcp],
168
- tools: %w[terminal filesystem code],
169
- model_config: { temperature: 0.2 },
170
- instructions: "You are a DevOps engineer. Help users with:\n- Infrastructure setup and management\n- CI/CD pipeline configuration\n- Container orchestration\n- Cloud resource management\n\nAlways prioritize security and follow best practices.",
171
- icon: "🚀",
172
- featured: false,
173
- free_tier: true
174
- },
175
- {
176
- name: "PlaywrightMCP Demo",
177
- slug: "playwright-mcp-demo",
178
- description: "Free browser automation demo using Playwright MCP. Navigate sites, take screenshots, and extract content.",
179
- category: "automation",
180
- provider: "anthropic",
181
- model: "claude-sonnet-4-20250514",
182
- preset_type: "playwright",
183
- appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" },
184
- instruction_sets: [],
185
- tools: %w[playwright],
186
- mcp_servers: {
187
- playwright: {
188
- command: "npx",
189
- args: [ "-y", "@anthropic/mcp-server-playwright" ]
190
- }
191
- },
192
- model_config: { temperature: 0.2, max_tokens: 4096 },
193
- instructions: "You are a browser automation assistant using Playwright MCP.\n\nAvailable actions:\n- browser_navigate: Go to a URL\n- browser_snapshot: Get the accessibility tree\n- browser_click: Click on an element\n- browser_type: Type text into an input\n- browser_take_screenshot: Capture the page\n- browser_wait_for: Wait for text or element\n\nGuidelines:\n1. Always take a snapshot first to understand the page\n2. Use element refs from snapshots for interactions\n3. Wait for page loads before taking actions\n4. Handle errors gracefully\n5. Limit yourself to 10 steps maximum\n\nAlways describe what you see and what actions you're taking.",
194
- icon: "🎭",
195
- featured: true,
196
- free_tier: true
197
- }
198
- ]
199
-
200
- templates.each do |template_attrs|
201
- find_or_create_by!(slug: template_attrs[:slug]) do |t|
202
- t.assign_attributes(template_attrs)
203
- end
204
- end
205
- end
206
- end
207
- end
208
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Tracks version history for agent configurations.
6
- #
7
- # Each time an agent's configuration changes, a new version is created
8
- # with a snapshot of the configuration at that point in time.
9
- #
10
- # @example Comparing versions
11
- # v1 = agent.agent_versions.find_by(version_number: 1)
12
- # v2 = agent.agent_versions.find_by(version_number: 2)
13
- # changes = v2.diff(v1)
14
- #
15
- class AgentVersion < ApplicationRecord
16
- belongs_to :agent, class_name: "ActiveAgent::Dashboard::Agent"
17
-
18
- validates :version_number, presence: true, uniqueness: { scope: :agent_id }
19
- validates :configuration_snapshot, presence: true
20
-
21
- # Scopes
22
- scope :recent, -> { order(version_number: :desc) }
23
- scope :by_version, ->(num) { where(version_number: num) }
24
-
25
- # Compare two versions
26
- def diff(other_version)
27
- return {} unless other_version
28
-
29
- changes = {}
30
- configuration_snapshot.each do |key, value|
31
- other_value = other_version.configuration_snapshot[key]
32
- if value != other_value
33
- changes[key] = { from: other_value, to: value }
34
- end
35
- end
36
- changes
37
- end
38
-
39
- # Get previous version
40
- def previous
41
- agent.agent_versions.where("version_number < ?", version_number).order(version_number: :desc).first
42
- end
43
-
44
- # Get next version
45
- def next_version
46
- agent.agent_versions.where("version_number > ?", version_number).order(version_number: :asc).first
47
- end
48
-
49
- # Check if this is the latest version
50
- def latest?
51
- agent.latest_version&.id == id
52
- end
53
-
54
- # Check if this is the initial version
55
- def initial?
56
- version_number == 1
57
- end
58
- end
59
- end
60
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Base class for all Dashboard engine models.
6
- #
7
- # Provides multi-tenant support when configured, allowing the same models
8
- # to work in both local (single-tenant) and platform (multi-tenant) modes.
9
- class ApplicationRecord < ::ActiveRecord::Base
10
- self.abstract_class = true
11
-
12
- # Override table name calculation to use active_agent_ prefix
13
- # without the "dashboard_" from the module namespace
14
- def self.table_name
15
- @table_name ||= "active_agent_#{name.demodulize.underscore.pluralize}"
16
- end
17
-
18
- class << self
19
- # Returns the owner association name based on configuration.
20
- # In multi-tenant mode, this returns :account.
21
- # In local mode, this returns :user (optional).
22
- def owner_association
23
- if ActiveAgent::Dashboard.multi_tenant?
24
- :account
25
- else
26
- :user
27
- end
28
- end
29
-
30
- # Scopes records to the current owner (account or user).
31
- # No-op in local mode without owner configuration.
32
- def for_owner(owner)
33
- return all if owner.nil?
34
-
35
- if ActiveAgent::Dashboard.multi_tenant?
36
- where(account: owner)
37
- elsif column_names.include?("user_id")
38
- where(user: owner)
39
- else
40
- all
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end