collavre 0.2.5 → 0.3.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/channels/collavre/comments_presence_channel.rb +6 -3
  3. data/app/controllers/collavre/admin/orchestration_controller.rb +177 -0
  4. data/app/controllers/collavre/comments_controller.rb +10 -1
  5. data/app/javascript/controllers/comments/presence_controller.js +5 -1
  6. data/app/jobs/collavre/ai_agent_job.rb +55 -4
  7. data/app/jobs/collavre/cron_action_job.rb +53 -0
  8. data/app/jobs/collavre/stuck_detector_job.rb +34 -0
  9. data/app/models/collavre/comment.rb +15 -15
  10. data/app/models/collavre/creative.rb +1 -1
  11. data/app/models/collavre/orchestrator_policy.rb +124 -0
  12. data/app/models/collavre/task.rb +3 -0
  13. data/app/models/collavre/user.rb +1 -1
  14. data/app/services/collavre/ai_agent_service.rb +32 -1
  15. data/app/services/collavre/ai_client.rb +4 -3
  16. data/app/services/collavre/comments/command_processor.rb +4 -1
  17. data/app/services/collavre/comments/topic_command.rb +101 -0
  18. data/app/services/collavre/orchestration/agent_context_builder.rb +201 -0
  19. data/app/services/collavre/orchestration/agent_orchestrator.rb +142 -0
  20. data/app/services/collavre/orchestration/arbiter.rb +257 -0
  21. data/app/services/collavre/orchestration/loop_breaker.rb +294 -0
  22. data/app/services/collavre/orchestration/matcher.rb +98 -0
  23. data/app/services/collavre/orchestration/policy_resolver.rb +170 -0
  24. data/app/services/collavre/orchestration/resource_tracker.rb +135 -0
  25. data/app/services/collavre/orchestration/scheduler.rb +145 -0
  26. data/app/services/collavre/orchestration/self_reflection_evaluator.rb +231 -0
  27. data/app/services/collavre/orchestration/stuck_detector.rb +303 -0
  28. data/app/services/collavre/system_events/context_builder.rb +34 -7
  29. data/app/services/collavre/system_events/dispatcher.rb +2 -7
  30. data/app/services/collavre/tools/cron_cancel_service.rb +49 -0
  31. data/app/services/collavre/tools/cron_create_service.rb +73 -0
  32. data/app/services/collavre/tools/cron_list_service.rb +65 -0
  33. data/app/services/collavre/tools/cron_update_service.rb +82 -0
  34. data/app/views/admin/shared/_tabs.html.erb +1 -0
  35. data/app/views/collavre/admin/orchestration/show.html.erb +66 -0
  36. data/app/views/collavre/creatives/index.html.erb +1 -1
  37. data/config/locales/ai_agent.en.yml +21 -0
  38. data/config/locales/ai_agent.ko.yml +21 -0
  39. data/config/locales/comments.en.yml +8 -0
  40. data/config/locales/comments.ko.yml +8 -0
  41. data/config/routes.rb +5 -1
  42. data/db/migrate/20260206005035_create_orchestrator_policies.rb +32 -0
  43. data/db/migrate/20260206094509_add_retry_count_to_tasks.rb +5 -0
  44. data/db/migrate/20260206100000_add_topic_id_to_tasks.rb +6 -0
  45. data/lib/collavre/version.rb +1 -1
  46. metadata +24 -13
  47. data/app/controllers/collavre/github_auth_controller.rb +0 -25
  48. data/app/models/collavre/github_account.rb +0 -10
  49. data/app/models/collavre/github_repository_link.rb +0 -19
  50. data/app/services/collavre/github/client.rb +0 -112
  51. data/app/services/collavre/github/pull_request_analyzer.rb +0 -280
  52. data/app/services/collavre/github/pull_request_processor.rb +0 -181
  53. data/app/services/collavre/github/webhook_provisioner.rb +0 -130
  54. data/app/services/collavre/system_events/router.rb +0 -96
  55. data/app/views/collavre/creatives/_github_integration_modal.html.erb +0 -77
  56. data/db/migrate/20250925000000_create_github_integrations.rb +0 -26
  57. data/db/migrate/20250927000000_add_webhook_secret_to_github_repository_links.rb +0 -29
  58. data/db/migrate/20250928105957_add_github_gemini_prompt_to_creatives.rb +0 -5
@@ -0,0 +1,66 @@
1
+ <h1 class="no-top-margin"><%= t('admin.title') %></h1>
2
+
3
+ <%= render "admin/shared/tabs" %>
4
+
5
+ <div class="tab-panels">
6
+ <section class="tab-panel active">
7
+ <%= tag.div(flash[:alert], class: "flash-alert") if flash[:alert] %>
8
+ <%= tag.div(flash[:notice], class: "flash-notice") if flash[:notice] %>
9
+
10
+ <div style="margin-bottom: 1.5em;">
11
+ <h2 style="font-size: 1.2em; margin-bottom: 0.5em;"><%= t('admin.orchestration.title') %></h2>
12
+ <p style="color: var(--color-text-muted); font-size: 0.9em;">
13
+ <%= t('admin.orchestration.description') %>
14
+ </p>
15
+ </div>
16
+
17
+ <%= form_with url: admin_orchestration_path, method: :patch, local: true, html: { class: 'profile-form' } do |f| %>
18
+ <div>
19
+ <label for="policies_yaml" style="font-weight: 600; display: block; margin-bottom: 0.5em;">
20
+ <%= t('admin.orchestration.policies_yaml') %>
21
+ </label>
22
+ <%= f.text_area :policies_yaml,
23
+ value: @policies_yaml,
24
+ rows: 30,
25
+ style: "width: 100%; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 0.9em; padding: 1em; border: 1px solid var(--color-border); border-radius: 4px; background: var(--color-surface); line-height: 1.5;" %>
26
+ <div class="help-text" style="font-size: 0.85em; color: var(--color-text-muted); margin-top: 0.5em;">
27
+ <%= t('admin.orchestration.yaml_hint') %>
28
+ </div>
29
+ </div>
30
+
31
+ <div style="margin-top: 1.5em; padding-top: 1.5em; border-top: 1px solid var(--color-border);">
32
+ <h3 style="font-size: 1em; margin-bottom: 0.75em;"><%= t('admin.orchestration.reference') %></h3>
33
+
34
+ <div style="background: var(--color-surface-alt); padding: 1em; border-radius: 4px; font-size: 0.85em;">
35
+ <p style="margin-bottom: 0.75em;"><strong><%= t('admin.orchestration.arbitration_strategies') %></strong></p>
36
+ <ul style="margin-left: 1.5em; margin-bottom: 1em;">
37
+ <li><code>all</code> — <%= t('admin.orchestration.strategy_all') %></li>
38
+ <li><code>primary_first</code> — <%= t('admin.orchestration.strategy_primary_first') %></li>
39
+ <li><code>round_robin</code> — <%= t('admin.orchestration.strategy_round_robin') %></li>
40
+ <li><code>bid</code> — <%= t('admin.orchestration.strategy_bid') %></li>
41
+ </ul>
42
+
43
+ <p style="margin-bottom: 0.75em;"><strong><%= t('admin.orchestration.scheduling_options') %></strong></p>
44
+ <ul style="margin-left: 1.5em; margin-bottom: 1em;">
45
+ <li><code>max_concurrent_jobs</code> — <%= t('admin.orchestration.opt_max_concurrent') %></li>
46
+ <li><code>daily_token_limit</code> — <%= t('admin.orchestration.opt_daily_token') %></li>
47
+ <li><code>rate_limit_per_minute</code> — <%= t('admin.orchestration.opt_rate_limit') %></li>
48
+ <li><code>backoff_strategy</code> — <%= t('admin.orchestration.opt_backoff') %></li>
49
+ <li><code>topic_max_concurrent_jobs</code> — <%= t('admin.orchestration.opt_topic_max_concurrent') %></li>
50
+ </ul>
51
+
52
+ <p style="margin-bottom: 0.75em;"><strong><%= t('admin.orchestration.scope_types') %></strong></p>
53
+ <ul style="margin-left: 1.5em;">
54
+ <li><code>Creative</code> — <%= t('admin.orchestration.scope_creative') %></li>
55
+ <li><code>Topic</code> — <%= t('admin.orchestration.scope_topic') %></li>
56
+ <li><code>User</code> — <%= t('admin.orchestration.scope_user') %></li>
57
+ </ul>
58
+ </div>
59
+ </div>
60
+
61
+ <div style="margin-top: 2em;">
62
+ <%= f.submit t('admin.orchestration.save'), class: 'btn-primary' %>
63
+ </div>
64
+ <% end %>
65
+ </section>
66
+ </div>
@@ -203,7 +203,7 @@
203
203
  <%= render 'set_plan_modal' %>
204
204
  <% if can_manage_integrations %>
205
205
  <%# Legacy hardcoded integration modals %>
206
- <%= render 'github_integration_modal', creative: current_creative %>
206
+ <%= render 'collavre_github/integrations/modal', creative: current_creative %>
207
207
  <%# Dynamically registered integration modals %>
208
208
  <%= render 'integration_modals', creative: current_creative %>
209
209
  <% end %>
@@ -1,5 +1,9 @@
1
1
  en:
2
2
  collavre:
3
+ stuck_detection:
4
+ task_stuck: "⚠️ Task '%{task_name}' assigned to %{agent_name} has been stuck for %{minutes} minutes."
5
+ creative_stalled: "⚠️ Creative '%{creative_title}' has stalled at %{progress}%% for %{hours} hours."
6
+ system_comment: "[System] Task '%{task_name}' by %{agent_name} appears stuck. Admin attention required."
3
7
  ai_agent:
4
8
  approval:
5
9
  message: |
@@ -13,3 +17,20 @@ en:
13
17
 
14
18
  Please approve or reject this action.
15
19
  no_arguments: "(no arguments)"
20
+ a2a:
21
+ request_header: "## ⚡ Agent Request"
22
+ request_description: "This message is a request from another AI Agent (@%{sender_name}, %{sender_type})."
23
+ focus_instruction: "- Focus on answering the request"
24
+ completion_instruction: "- Clearly communicate results when done"
25
+ followup_instruction: "- If you need more information, ask the requester via @mention"
26
+ collaboration:
27
+ header: "## Available Agents for Collaboration"
28
+ escalation_header: "### Escalation Targets (when stuck)"
29
+ collaborator_header: "### Collaborators (work together)"
30
+ reviewer_header: "### Reviewers (request review)"
31
+ reference_header: "### References (information only)"
32
+ rules_header: "## Collaboration Rules"
33
+ mention_rule: "- Call other agents: @name: request"
34
+ confidence_rule: "- Re-evaluate before responding if uncertain"
35
+ escalation_rule: "- Ask escalation targets for help when stuck"
36
+ review_rule: "- Request review from reviewers when code review is needed"
@@ -1,5 +1,9 @@
1
1
  ko:
2
2
  collavre:
3
+ stuck_detection:
4
+ task_stuck: "⚠️ '%{task_name}' 작업이 %{agent_name}에게 할당된 후 %{minutes}분 동안 진행되지 않고 있습니다."
5
+ creative_stalled: "⚠️ '%{creative_title}' Creative가 %{progress}%%에서 %{hours}시간 동안 정체되어 있습니다."
6
+ system_comment: "[시스템] %{agent_name}의 '%{task_name}' 작업이 막힌 것 같습니다. 관리자 확인이 필요합니다."
3
7
  ai_agent:
4
8
  approval:
5
9
  message: |
@@ -13,3 +17,20 @@ ko:
13
17
 
14
18
  승인 또는 거부해 주세요.
15
19
  no_arguments: "(인자 없음)"
20
+ a2a:
21
+ request_header: "## ⚡ Agent 요청"
22
+ request_description: "이 메시지는 다른 AI Agent(@%{sender_name}, %{sender_type})가 보낸 요청입니다."
23
+ focus_instruction: "- 요청에 집중해서 답변하세요"
24
+ completion_instruction: "- 완료되면 결과를 명확히 전달하세요"
25
+ followup_instruction: "- 추가 정보가 필요하면 요청자에게 @멘션으로 물어보세요"
26
+ collaboration:
27
+ header: "## 협업 가능한 Agent"
28
+ escalation_header: "### 에스컬레이션 대상 (문제 해결 불가 시)"
29
+ collaborator_header: "### 협업 가능 (함께 작업)"
30
+ reviewer_header: "### 리뷰어 (검토 요청)"
31
+ reference_header: "### 참조 (정보 요청만)"
32
+ rules_header: "## 협업 규칙"
33
+ mention_rule: "- 다른 Agent 호출: @이름: 요청내용"
34
+ confidence_rule: "- 확신이 낮으면 재검토 후 발화"
35
+ escalation_rule: "- 막히면 에스컬레이션 대상에게 도움 요청"
36
+ review_rule: "- 코드 리뷰가 필요하면 리뷰어에게 요청"
@@ -81,10 +81,18 @@ en:
81
81
  command_menu:
82
82
  calendar_description: "Create a Google Calendar event."
83
83
  calendar_args: "YYYY-MM-DD@HH:MM memo (today/tomorrow, +3days, +2weeks, +1months, +1years, +mon)"
84
+ topic_description: "Create a new topic with optional primary agent."
85
+ topic_args: '"topic name" @agent_name'
84
86
  calendar_command:
85
87
  event_created: 'event created: [Google Calendar event](%{url})'
86
88
  event_created_local: 'event created (local only - connect Google Calendar to sync)'
87
89
  event_created_sync_failed: 'event created (Google Calendar sync failed - please reconnect your Google account)'
90
+ topic_command:
91
+ missing_name: 'Please specify a topic name in quotes: /topic "topic name"'
92
+ created: 'Topic "%{name}" created.'
93
+ created_with_agent: 'Topic "%{name}" created with @%{agent} as primary agent.'
94
+ updated_agent: 'Topic "%{name}" primary agent updated to @%{agent}.'
95
+ already_exists: 'Topic "%{name}" already exists.'
88
96
  read_by: Read by %{name}
89
97
  activity_logs_summary: Activity Logs
90
98
  calendar_events:
@@ -78,10 +78,18 @@ ko:
78
78
  command_menu:
79
79
  calendar_description: 구글 캘린더 이벤트를 생성합니다.
80
80
  calendar_args: "YYYY-MM-DD@HH:MM 메모 (today/tomorrow, +3days, +2weeks, +1months, +1years, +mon/+월)"
81
+ topic_description: "새 토픽을 생성합니다. Primary Agent를 지정할 수 있습니다."
82
+ topic_args: '"토픽 이름" @에이전트이름'
81
83
  calendar_command:
82
84
  event_created: '이벤트가 생성되었습니다: [구글 캘린더 이벤트](%{url})'
83
85
  event_created_local: '이벤트가 생성되었습니다 (로컬 전용 - 구글 캘린더 연동 시 동기화됩니다)'
84
86
  event_created_sync_failed: '이벤트가 생성되었습니다 (구글 캘린더 동기화 실패 - 구글 계정을 다시 연동해주세요)'
87
+ topic_command:
88
+ missing_name: '토픽 이름을 따옴표로 지정하세요: /topic "토픽 이름"'
89
+ created: '토픽 "%{name}"이(가) 생성되었습니다.'
90
+ created_with_agent: '토픽 "%{name}"이(가) 생성되었습니다. @%{agent}이(가) Primary Agent로 설정되었습니다.'
91
+ updated_agent: '토픽 "%{name}"의 Primary Agent가 @%{agent}(으)로 변경되었습니다.'
92
+ already_exists: '토픽 "%{name}"이(가) 이미 존재합니다.'
85
93
  read_by: "%{name} 님이 읽음"
86
94
  activity_logs_summary: 활동 기록
87
95
  calendar_events:
data/config/routes.rb CHANGED
@@ -24,7 +24,6 @@ Collavre::Engine.routes.draw do
24
24
 
25
25
  # OAuth callback routes (paths match OmniAuth provider names)
26
26
  match "/auth/google_oauth2/callback", to: "google_auth#callback", via: [ :get, :post ]
27
- match "/auth/github/callback", to: "github_auth#callback", via: [ :get, :post ]
28
27
 
29
28
  delete "/attachments/:signed_id", to: "attachments#destroy", as: :attachment
30
29
 
@@ -94,4 +93,9 @@ Collavre::Engine.routes.draw do
94
93
 
95
94
  post "/creative_expanded_states/toggle", to: "creative_expanded_states#toggle"
96
95
  post "/comment_read_pointers/update", to: "comment_read_pointers#update"
96
+
97
+ # Admin orchestration
98
+ scope "/admin", as: :admin do
99
+ resource :orchestration, only: [ :show, :update ], controller: "admin/orchestration"
100
+ end
97
101
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateOrchestratorPolicies < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :orchestrator_policies do |t|
6
+ # Scope determines where this policy applies
7
+ # - global: applies everywhere (scope_type/scope_id null)
8
+ # - creative: applies to a specific creative
9
+ # - topic: applies to a specific topic
10
+ # - agent: applies to a specific agent
11
+ t.string :scope_type, null: true
12
+ t.bigint :scope_id, null: true
13
+
14
+ # Policy type: matching, arbitration, scheduling
15
+ t.string :policy_type, null: false
16
+
17
+ # Configuration stored as JSON
18
+ t.json :config, null: false, default: {}
19
+
20
+ # Priority: lower number = higher priority (evaluated first)
21
+ t.integer :priority, null: false, default: 100
22
+
23
+ t.boolean :enabled, null: false, default: true
24
+
25
+ t.timestamps
26
+ end
27
+
28
+ add_index :orchestrator_policies, [ :scope_type, :scope_id ]
29
+ add_index :orchestrator_policies, [ :policy_type, :enabled ]
30
+ add_index :orchestrator_policies, :priority
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ class AddRetryCountToTasks < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :tasks, :retry_count, :integer, default: 0, null: false
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddTopicIdToTasks < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :tasks, :topic_id, :integer, null: true
4
+ add_index :tasks, [ :topic_id, :status ]
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Collavre
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collavre
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collavre
@@ -186,6 +186,7 @@ files:
186
186
  - app/components/collavre/progress_filter_component.html.erb
187
187
  - app/components/collavre/progress_filter_component.rb
188
188
  - app/components/collavre/user_mention_menu_component.rb
189
+ - app/controllers/collavre/admin/orchestration_controller.rb
189
190
  - app/controllers/collavre/application_controller.rb
190
191
  - app/controllers/collavre/attachments_controller.rb
191
192
  - app/controllers/collavre/calendar_events_controller.rb
@@ -202,7 +203,6 @@ files:
202
203
  - app/controllers/collavre/devices_controller.rb
203
204
  - app/controllers/collavre/email_verifications_controller.rb
204
205
  - app/controllers/collavre/emails_controller.rb
205
- - app/controllers/collavre/github_auth_controller.rb
206
206
  - app/controllers/collavre/google_auth_controller.rb
207
207
  - app/controllers/collavre/inbox_items_controller.rb
208
208
  - app/controllers/collavre/invites_controller.rb
@@ -295,10 +295,12 @@ files:
295
295
  - app/javascript/utils/caret_position.js
296
296
  - app/javascript/utils/clipboard.js
297
297
  - app/jobs/collavre/ai_agent_job.rb
298
+ - app/jobs/collavre/cron_action_job.rb
298
299
  - app/jobs/collavre/inbox_summary_job.rb
299
300
  - app/jobs/collavre/permission_cache_cleanup_job.rb
300
301
  - app/jobs/collavre/permission_cache_job.rb
301
302
  - app/jobs/collavre/push_notification_job.rb
303
+ - app/jobs/collavre/stuck_detector_job.rb
302
304
  - app/mailers/collavre/application_mailer.rb
303
305
  - app/mailers/collavre/creative_mailer.rb
304
306
  - app/mailers/collavre/email_verification_mailer.rb
@@ -320,12 +322,11 @@ files:
320
322
  - app/models/collavre/current.rb
321
323
  - app/models/collavre/device.rb
322
324
  - app/models/collavre/email.rb
323
- - app/models/collavre/github_account.rb
324
- - app/models/collavre/github_repository_link.rb
325
325
  - app/models/collavre/inbox_item.rb
326
326
  - app/models/collavre/invitation.rb
327
327
  - app/models/collavre/label.rb
328
328
  - app/models/collavre/mcp_tool.rb
329
+ - app/models/collavre/orchestrator_policy.rb
329
330
  - app/models/collavre/plan.rb
330
331
  - app/models/collavre/session.rb
331
332
  - app/models/collavre/system_setting.rb
@@ -348,6 +349,7 @@ files:
348
349
  - app/services/collavre/comments/command_processor.rb
349
350
  - app/services/collavre/comments/mcp_command.rb
350
351
  - app/services/collavre/comments/mcp_command_builder.rb
352
+ - app/services/collavre/comments/topic_command.rb
351
353
  - app/services/collavre/creatives/filter_pipeline.rb
352
354
  - app/services/collavre/creatives/filters/assignee_filter.rb
353
355
  - app/services/collavre/creatives/filters/base_filter.rb
@@ -367,23 +369,33 @@ files:
367
369
  - app/services/collavre/creatives/tree_builder.rb
368
370
  - app/services/collavre/creatives/tree_formatter.rb
369
371
  - app/services/collavre/gemini_parent_recommender.rb
370
- - app/services/collavre/github/client.rb
371
- - app/services/collavre/github/pull_request_analyzer.rb
372
- - app/services/collavre/github/pull_request_processor.rb
373
- - app/services/collavre/github/webhook_provisioner.rb
374
372
  - app/services/collavre/google_calendar_service.rb
375
373
  - app/services/collavre/link_preview_fetcher.rb
376
374
  - app/services/collavre/markdown_importer.rb
377
375
  - app/services/collavre/mcp_service.rb
376
+ - app/services/collavre/orchestration/agent_context_builder.rb
377
+ - app/services/collavre/orchestration/agent_orchestrator.rb
378
+ - app/services/collavre/orchestration/arbiter.rb
379
+ - app/services/collavre/orchestration/loop_breaker.rb
380
+ - app/services/collavre/orchestration/matcher.rb
381
+ - app/services/collavre/orchestration/policy_resolver.rb
382
+ - app/services/collavre/orchestration/resource_tracker.rb
383
+ - app/services/collavre/orchestration/scheduler.rb
384
+ - app/services/collavre/orchestration/self_reflection_evaluator.rb
385
+ - app/services/collavre/orchestration/stuck_detector.rb
378
386
  - app/services/collavre/ppt_importer.rb
379
387
  - app/services/collavre/ruby_llm_interaction_logger.rb
380
388
  - app/services/collavre/system_events/context_builder.rb
381
389
  - app/services/collavre/system_events/dispatcher.rb
382
- - app/services/collavre/system_events/router.rb
383
390
  - app/services/collavre/tools/creative_create_service.rb
384
391
  - app/services/collavre/tools/creative_retrieval_service.rb
385
392
  - app/services/collavre/tools/creative_update_service.rb
393
+ - app/services/collavre/tools/cron_cancel_service.rb
394
+ - app/services/collavre/tools/cron_create_service.rb
395
+ - app/services/collavre/tools/cron_list_service.rb
396
+ - app/services/collavre/tools/cron_update_service.rb
386
397
  - app/views/admin/shared/_tabs.html.erb
398
+ - app/views/collavre/admin/orchestration/show.html.erb
387
399
  - app/views/collavre/comments/_activity_log_details.html.erb
388
400
  - app/views/collavre/comments/_comment.html.erb
389
401
  - app/views/collavre/comments/_comments_popup.html.erb
@@ -393,7 +405,6 @@ files:
393
405
  - app/views/collavre/comments/_read_receipts.html.erb
394
406
  - app/views/collavre/creatives/_add_button.html.erb
395
407
  - app/views/collavre/creatives/_delete_button.html.erb
396
- - app/views/collavre/creatives/_github_integration_modal.html.erb
397
408
  - app/views/collavre/creatives/_import_upload_zone.html.erb
398
409
  - app/views/collavre/creatives/_inline_edit_form.html.erb
399
410
  - app/views/collavre/creatives/_integration_modals.html.erb
@@ -516,11 +527,8 @@ files:
516
527
  - db/migrate/20250911084338_backfill_creative_in_comment_inbox_items.rb
517
528
  - db/migrate/20250923002959_deduplicate_device_fcm_tokens.rb
518
529
  - db/migrate/20250924000000_add_system_admin_to_users.rb
519
- - db/migrate/20250925000000_create_github_integrations.rb
520
- - db/migrate/20250927000000_add_webhook_secret_to_github_repository_links.rb
521
530
  - db/migrate/20250928000000_add_action_and_approver_to_comments.rb
522
531
  - db/migrate/20250928010000_add_action_execution_tracking_to_comments.rb
523
- - db/migrate/20250928105957_add_github_gemini_prompt_to_creatives.rb
524
532
  - db/migrate/20250929000000_add_comment_and_creative_refs_to_inbox_items.rb
525
533
  - db/migrate/20251001000001_create_contacts.rb
526
534
  - db/migrate/20251002000000_add_shared_by_to_creative_shares.rb
@@ -564,6 +572,9 @@ files:
564
572
  - db/migrate/20260203002637_add_requires_approval_to_mcp_tools.rb
565
573
  - db/migrate/20260203002643_add_pending_tool_call_to_tasks.rb
566
574
  - db/migrate/20260203083302_add_position_to_topics.rb
575
+ - db/migrate/20260206005035_create_orchestrator_policies.rb
576
+ - db/migrate/20260206094509_add_retry_count_to_tasks.rb
577
+ - db/migrate/20260206100000_add_topic_id_to_tasks.rb
567
578
  - lib/collavre.rb
568
579
  - lib/collavre/configuration.rb
569
580
  - lib/collavre/engine.rb
@@ -1,25 +0,0 @@
1
- module Collavre
2
- class GithubAuthController < ApplicationController
3
- allow_unauthenticated_access only: :callback
4
- before_action -> { enforce_auth_provider!(:github) }, only: :callback
5
-
6
- def callback
7
- auth = request.env["omniauth.auth"]
8
- gh = Collavre::GithubAccount.find_or_initialize_by(github_uid: auth.uid)
9
-
10
- if gh.new_record?
11
- unless Current.user
12
- redirect_to collavre.new_session_path, alert: I18n.t("collavre.github_auth.login_first")
13
- return
14
- end
15
- gh.user = Current.user
16
- end
17
-
18
- gh.token = auth.credentials.token
19
- gh.login = auth.info.nickname
20
- gh.save!
21
-
22
- redirect_to creatives_path, notice: I18n.t("collavre.github_auth.connected")
23
- end
24
- end
25
- end
@@ -1,10 +0,0 @@
1
- module Collavre
2
- class GithubAccount < ApplicationRecord
3
- self.table_name = "github_accounts"
4
-
5
- belongs_to :user, class_name: "Collavre::User"
6
- has_many :github_repository_links, class_name: "Collavre::GithubRepositoryLink", dependent: :destroy
7
-
8
- encrypts :token, deterministic: false
9
- end
10
- end
@@ -1,19 +0,0 @@
1
- module Collavre
2
- class GithubRepositoryLink < ApplicationRecord
3
- self.table_name = "github_repository_links"
4
-
5
- belongs_to :creative, class_name: "Collavre::Creative"
6
- belongs_to :github_account, class_name: "Collavre::GithubAccount"
7
-
8
- validates :repository_full_name, presence: true
9
- validates :webhook_secret, presence: true
10
-
11
- before_validation :ensure_webhook_secret
12
-
13
- private
14
-
15
- def ensure_webhook_secret
16
- self.webhook_secret ||= SecureRandom.hex(20)
17
- end
18
- end
19
- end
@@ -1,112 +0,0 @@
1
- module Collavre
2
- module Github
3
- class Client
4
- def initialize(account)
5
- @client = Octokit::Client.new(access_token: account.token)
6
- @client.auto_paginate = true
7
- end
8
-
9
- def organizations
10
- client.organizations
11
- rescue Octokit::Error, Faraday::Error => e
12
- Rails.logger.warn("GitHub organizations fetch failed: #{e.message}")
13
- []
14
- end
15
-
16
- def repositories_for_authenticated_user
17
- client.repos(nil, type: "all")
18
- rescue Octokit::Error, Faraday::Error => e
19
- Rails.logger.warn("GitHub user repos fetch failed: #{e.message}")
20
- []
21
- end
22
-
23
- def repositories_for_organization(org)
24
- client.org_repos(org, type: "all")
25
- rescue Octokit::Error, Faraday::Error => e
26
- Rails.logger.warn("GitHub org repos fetch failed: #{e.message}")
27
- []
28
- end
29
-
30
- def pull_request_details(repo_full_name, number)
31
- client.pull_request(repo_full_name, number)
32
- rescue Octokit::Error, Faraday::Error => e
33
- Rails.logger.warn("GitHub PR fetch failed: #{e.message}")
34
- nil
35
- end
36
-
37
- def pull_request_commit_messages(repo_full_name, number)
38
- client
39
- .pull_request_commits(repo_full_name, number)
40
- .map { |commit| commit.commit&.message }
41
- .compact
42
- rescue Octokit::Error, Faraday::Error => e
43
- Rails.logger.warn("GitHub PR commits fetch failed: #{e.message}")
44
- []
45
- end
46
-
47
- def pull_request_diff(repo_full_name, number)
48
- files = client.pull_request_files(repo_full_name, number)
49
- formatted = files.filter_map do |file|
50
- next unless file.patch.present?
51
-
52
- <<~DIFF.strip
53
- diff --git a/#{file.filename} b/#{file.filename}
54
- #{file.patch}
55
- DIFF
56
- end
57
- formatted.join("\n\n").presence
58
- rescue Octokit::Error, Faraday::Error => e
59
- Rails.logger.warn("GitHub PR files fetch failed: #{e.message}")
60
- nil
61
- end
62
-
63
- def repository_hooks(repo_full_name)
64
- client.hooks(repo_full_name)
65
- rescue Octokit::Error, Faraday::Error => e
66
- Rails.logger.warn("GitHub hooks fetch failed for #{repo_full_name}: #{e.message}")
67
- []
68
- end
69
-
70
- def create_repository_webhook(repo_full_name, url:, secret:, events:, content_type: "json")
71
- client.create_hook(
72
- repo_full_name,
73
- "web",
74
- {
75
- url: url,
76
- content_type: content_type,
77
- secret: secret
78
- },
79
- {
80
- events: events,
81
- active: true
82
- }
83
- )
84
- end
85
-
86
- def update_repository_webhook(repo_full_name, hook_id, url:, secret:, events:, content_type: "json")
87
- client.edit_hook(
88
- repo_full_name,
89
- hook_id,
90
- "web",
91
- {
92
- url: url,
93
- content_type: content_type,
94
- secret: secret
95
- },
96
- {
97
- events: events,
98
- active: true
99
- }
100
- )
101
- end
102
-
103
- def delete_repository_webhook(repo_full_name, hook_id)
104
- client.remove_hook(repo_full_name, hook_id)
105
- end
106
-
107
- private
108
-
109
- attr_reader :client
110
- end
111
- end
112
- end