Skip to content

Commit 2ac2775

Browse files
Complete agent attribution coverage (#181)
Amp-Thread-ID: https://ampcode.com/threads/T-01a01147-8d44-7468-ac98-bf40b04e3cee Co-authored-by: Amp <amp@ampcode.com>
1 parent 50c2337 commit 2ac2775

18 files changed

Lines changed: 89 additions & 12 deletions
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class AddAgentProvenanceToLibraryEvents < ActiveRecord::Migration[8.1]
2+
def change
3+
add_column :coplan_library_events, :agent_name, :string
4+
add_column :coplan_library_events, :api_token_id, :string, limit: 36
5+
add_index :coplan_library_events, :api_token_id
6+
add_foreign_key :coplan_library_events, :coplan_api_tokens, column: :api_token_id
7+
end
8+
end

db/schema.rb

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

engine/app/controllers/coplan/api/v1/comments_controller.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,14 @@ def destroy
104104
return
105105
end
106106

107-
Comments::SoftDelete.call(comment: comment, actor: current_user)
107+
Comments::SoftDelete.call(
108+
comment: comment,
109+
actor: current_user,
110+
actor_type: api_author_type,
111+
actor_id: api_user_id,
112+
agent_name: api_agent_name,
113+
api_token_id: api_token_id
114+
)
108115

109116
thread = comment.comment_thread
110117
if thread.reload.empty?

engine/app/controllers/coplan/api/v1/folders_controller.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def create
4848
created_by_user: current_user
4949
)
5050
Libraries::LogEvent.call(
51-
library: library, actor: current_user, actor_type: api_author_type,
51+
library: library, actor: current_user, **library_event_identity,
5252
event_type: "folder_created", folder: folder, after: folder.path
5353
)
5454
render json: folder_json(folder), status: :created
@@ -93,7 +93,7 @@ def destroy
9393
path = @folder.path
9494
if @folder.destroy
9595
Libraries::LogEvent.call(
96-
library: @folder.library, actor: current_user, actor_type: api_author_type,
96+
library: @folder.library, actor: current_user, **library_event_identity,
9797
event_type: "folder_deleted", before: path,
9898
metadata: { folder_name: @folder.name }
9999
)
@@ -117,26 +117,34 @@ def log_folder_update(old_path, old_description)
117117
new_path = @folder.path
118118
if @folder.saved_change_to_name?
119119
Libraries::LogEvent.call(
120-
library: @folder.library, actor: current_user, actor_type: api_author_type,
120+
library: @folder.library, actor: current_user, **library_event_identity,
121121
event_type: "folder_renamed", folder: @folder, before: old_path, after: new_path
122122
)
123123
end
124124
if @folder.saved_change_to_parent_id?
125125
Libraries::LogEvent.call(
126-
library: @folder.library, actor: current_user, actor_type: api_author_type,
126+
library: @folder.library, actor: current_user, **library_event_identity,
127127
event_type: "folder_moved", folder: @folder, before: old_path, after: new_path
128128
)
129129
end
130130
if @folder.saved_change_to_description?
131131
Libraries::LogEvent.call(
132-
library: @folder.library, actor: current_user, actor_type: api_author_type,
132+
library: @folder.library, actor: current_user, **library_event_identity,
133133
event_type: "folder_described", folder: @folder,
134134
before: old_description, after: @folder.description,
135135
metadata: { path: new_path }
136136
)
137137
end
138138
end
139139

140+
def library_event_identity
141+
{
142+
actor_type: api_author_type,
143+
agent_name: api_agent_name,
144+
api_token_id: api_token_id
145+
}
146+
end
147+
140148
# `paths` and `counts` let index serialize the whole tree without
141149
# per-folder queries. `plans_count` is the folder's own visible
142150
# placements (not including subfolders).

engine/app/controllers/coplan/api/v1/libraries_controller.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ def event_json(event)
243243
actor_type: event.actor_type,
244244
agent: event.actor_type != "human",
245245
actor: event.actor_user && { id: event.actor_user.id, name: event.actor_user.name },
246+
agent_name: event.agent_name,
247+
api_token_id: event.api_token_id,
246248
actor_label: event.metadata["actor_label"],
247249
run_id: event.run_id,
248250
plan_id: event.plan_id,

engine/app/controllers/coplan/api/v1/plans_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def resolve_folder_params
327327
created.each do |f|
328328
Libraries::LogEvent.call(
329329
library: current_user.library, actor: current_user, actor_type: api_author_type,
330+
agent_name: api_agent_name, api_token_id: api_token_id,
330331
event_type: "folder_created", folder: f, after: f.path
331332
)
332333
end

engine/app/models/coplan/comment.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class Comment < ApplicationRecord
33
AUTHOR_TYPES = %w[human local_agent cloud_persona system].freeze
44

55
belongs_to :comment_thread
6+
belongs_to :api_token, class_name: "CoPlan::ApiToken", optional: true
67

78
validates :body_markdown, presence: true
89
validates :author_type, presence: true, inclusion: { in: AUTHOR_TYPES }

engine/app/models/coplan/library_event.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,22 @@ class LibraryEvent < ApplicationRecord
2828

2929
belongs_to :library, class_name: "CoPlan::Library", inverse_of: :library_events
3030
belongs_to :actor_user, class_name: "CoPlan::User", foreign_key: "actor_id", optional: true
31+
belongs_to :api_token, class_name: "CoPlan::ApiToken", optional: true
3132

3233
after_initialize { self.metadata ||= {} }
3334

3435
validates :actor_type, presence: true, inclusion: { in: ACTOR_TYPES }
3536
validates :event_type, presence: true, inclusion: { in: EVENT_TYPES }
37+
validates :agent_name, length: { maximum: ApiToken::AGENT_NAME_LIMIT }, allow_nil: true
3638

3739
scope :recent_first, -> { order(created_at: :desc, id: :desc) }
3840

3941
def self.ransackable_attributes(_auth_object = nil)
40-
%w[id library_id actor_id actor_type event_type plan_id folder_id run_id before_value after_value created_at]
42+
%w[id library_id actor_id actor_type agent_name api_token_id event_type plan_id folder_id run_id before_value after_value created_at]
4143
end
4244

4345
def self.ransackable_associations(_auth_object = nil)
44-
%w[library actor_user]
46+
%w[library actor_user api_token]
4547
end
4648
end
4749
end

engine/app/models/coplan/plan_event.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class PlanEvent < ApplicationRecord
3737

3838
belongs_to :plan
3939
belongs_to :actor_user, class_name: "CoPlan::User", foreign_key: "actor_id", optional: true
40+
belongs_to :api_token, class_name: "CoPlan::ApiToken", optional: true
4041

4142
after_initialize { self.metadata ||= {} }
4243

@@ -49,11 +50,11 @@ class PlanEvent < ApplicationRecord
4950
scope :for_history, -> { order(created_at: :desc) }
5051

5152
def self.ransackable_attributes(_auth_object = nil)
52-
%w[id plan_id actor_id actor_type event_type field before_value after_value created_at]
53+
%w[id plan_id actor_id actor_type agent_name api_token_id event_type field before_value after_value created_at]
5354
end
5455

5556
def self.ransackable_associations(_auth_object = nil)
56-
%w[plan actor_user]
57+
%w[plan actor_user api_token]
5758
end
5859

5960
# Marker so history rendering can branch on the kind of item without

engine/app/models/coplan/plan_version.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class PlanVersion < ApplicationRecord
44

55
belongs_to :plan
66
belongs_to :actor_user, class_name: "CoPlan::User", foreign_key: "actor_id", optional: true
7+
belongs_to :api_token, class_name: "CoPlan::ApiToken", optional: true
78
has_many :comment_threads, dependent: :nullify
89

910
# has_attribute? guard: list pages load lean stubs (id + sha only) via

0 commit comments

Comments
 (0)