activeagent 1.5.0 → 1.6.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.
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAgent
4
+ module Generators
5
+ # Writes a starter ActiveAgent::SchemaTools class for one model:
6
+ #
7
+ # bin/rails generate active_agent:schema_tools Reservation
8
+ #
9
+ # The class it writes exposes nothing beyond +id+ until someone uncomments
10
+ # a column, because which columns an agent may filter on and read back is
11
+ # a judgement about exposure, not a fact about the table (#440). Every
12
+ # column the model has is listed, commented out, minus the ones that look
13
+ # like secrets, so the allowlist is a review step rather than a blank page.
14
+ class SchemaToolsGenerator < ::Rails::Generators::NamedBase
15
+ source_root File.expand_path("templates", __dir__)
16
+
17
+ # Columns never suggested, whatever the model: reading one back would
18
+ # hand a model a credential, and filtering on one leaks it a character
19
+ # at a time through the row counts.
20
+ SECRET_COLUMNS = /password|digest|token|secret|api_key|otp|encrypted|ssn/i
21
+
22
+ class_option :policy, type: :boolean, default: nil,
23
+ desc: "Scope every read through <Model>Policy::Scope (default: when that policy exists)"
24
+
25
+ check_class_collision suffix: "Tools"
26
+
27
+ def create_tools_file
28
+ template "schema_tools.rb.tt", File.join("app/agent_tools", class_path, "#{file_name}_tools.rb")
29
+ end
30
+
31
+ private
32
+
33
+ # "Reservation" and "ReservationTools" both name the Reservation model.
34
+ def file_name # :doc:
35
+ @_file_name ||= super.sub(/_tools\z/i, "")
36
+ end
37
+
38
+ def model_class
39
+ @model_class ||= class_name.safe_constantize
40
+ end
41
+
42
+ def policy_class_name
43
+ "#{class_name}Policy"
44
+ end
45
+
46
+ def policy?
47
+ return options[:policy] unless options[:policy].nil?
48
+
49
+ "#{policy_class_name}::Scope".safe_constantize.present?
50
+ end
51
+
52
+ # [name, type] for every column the model has, or [] when the model or
53
+ # its table cannot be read from here (a model that does not exist yet,
54
+ # or a database that is not set up) — the file is still written.
55
+ def columns
56
+ @columns ||= begin
57
+ if model_class.respond_to?(:columns) && model_class.respond_to?(:table_exists?) && model_class.table_exists?
58
+ model_class.columns.map { |column| [ column.name, column.type ] }
59
+ else
60
+ []
61
+ end
62
+ rescue StandardError
63
+ []
64
+ end
65
+ end
66
+
67
+ def suggested_columns
68
+ columns.reject { |name, _type| name == "id" || name.match?(SECRET_COLUMNS) }
69
+ end
70
+
71
+ def secret_columns
72
+ columns.select { |name, _type| name.match?(SECRET_COLUMNS) }.map(&:first)
73
+ end
74
+
75
+ def collection_name
76
+ class_name.demodulize.underscore.pluralize
77
+ end
78
+
79
+ def resource_name
80
+ class_name.demodulize.underscore
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,48 @@
1
+ <% module_namespacing do -%>
2
+ # Bounded, read-only agent tools over <%= class_name %>:
3
+ # find_<%= collection_name %>, count_<%= collection_name %>, get_<%= resource_name %>.
4
+ #
5
+ # Nothing is exposed until you say so. Move a column from the commented lists
6
+ # into `filterable` (an agent may filter on it) or `returns` (an agent may
7
+ # read it back). Each one is a judgement about exposure, not a fact about the
8
+ # table: leave out anything a model should never see.
9
+ class <%= class_name %>Tools < ActiveAgent::SchemaTools
10
+ model <%= class_name %>
11
+
12
+ filterable :id
13
+ <% if suggested_columns.any? -%>
14
+ # filterable <%= suggested_columns.map { |name, _type| ":#{name}" }.join(", ") %>
15
+ <% else -%>
16
+ # filterable :status, :owner_id # columns an agent may filter on
17
+ <% end -%>
18
+
19
+ returns :id
20
+ <% if suggested_columns.any? -%>
21
+ # returns <%= suggested_columns.map { |name, _type| ":#{name}" }.join(", ") %>
22
+ <% else -%>
23
+ # returns :id, :title, :status # columns an agent may read back
24
+ <% end -%>
25
+ <% if suggested_columns.any? -%>
26
+ #
27
+ # Columns and their types:
28
+ <% suggested_columns.each do |name, type| -%>
29
+ # <%= name.ljust(24) %> <%= type %>
30
+ <% end -%>
31
+ <% end -%>
32
+ <% if secret_columns.any? -%>
33
+ #
34
+ # Not suggested, and not to be added: <%= secret_columns.join(", ") %>.
35
+ <% end -%>
36
+
37
+ <% if policy? -%>
38
+ # Every read runs through <%= policy_class_name %>::Scope for the acting
39
+ # user, so an agent sees exactly what that user could see.
40
+ scope_by_policy
41
+ <% else -%>
42
+ # Every read should run through a relation scoped to the acting user. Without
43
+ # a scope the tools read the whole table. Return <%= class_name %>.none for an
44
+ # actor with no access; never widen for a nil actor.
45
+ # scope { |actor| actor ? <%= class_name %>.where(owner: actor) : <%= class_name %>.none }
46
+ <% end -%>
47
+ end
48
+ <% end -%>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeagent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
@@ -409,6 +409,7 @@ files:
409
409
  - lib/active_agent.rb
410
410
  - lib/active_agent/base.rb
411
411
  - lib/active_agent/collector.rb
412
+ - lib/active_agent/concerns/authorization.rb
412
413
  - lib/active_agent/concerns/callbacks.rb
413
414
  - lib/active_agent/concerns/delegation.rb
414
415
  - lib/active_agent/concerns/observers.rb
@@ -562,6 +563,7 @@ files:
562
563
  - lib/active_agent/railtie.rb
563
564
  - lib/active_agent/railtie/schema_generator_extension.rb
564
565
  - lib/active_agent/schema_generator.rb
566
+ - lib/active_agent/schema_tools.rb
565
567
  - lib/active_agent/service.rb
566
568
  - lib/active_agent/telemetry.rb
567
569
  - lib/active_agent/telemetry/configuration.rb
@@ -576,6 +578,9 @@ files:
576
578
  - lib/generators/active_agent/agent/agent_generator.rb
577
579
  - lib/generators/active_agent/install/USAGE
578
580
  - lib/generators/active_agent/install/install_generator.rb
581
+ - lib/generators/active_agent/schema_tools/USAGE
582
+ - lib/generators/active_agent/schema_tools/schema_tools_generator.rb
583
+ - lib/generators/active_agent/schema_tools/templates/schema_tools.rb.tt
579
584
  - lib/generators/active_agent/templates/active_agent.yml
580
585
  - lib/generators/active_agent/templates/agent.rb.tt
581
586
  - lib/generators/active_agent/templates/application_agent.rb.tt
@@ -614,7 +619,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
614
619
  - !ruby/object:Gem::Version
615
620
  version: '0'
616
621
  requirements: []
617
- rubygems_version: 4.0.16
622
+ rubygems_version: 3.6.9
618
623
  specification_version: 4
619
624
  summary: Rails AI Agents Framework
620
625
  test_files: []