collavre 0.24.0 → 0.25.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 (440) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/app/assets/stylesheets/collavre/comments_popup.css +165 -99
  4. data/app/assets/stylesheets/collavre/creatives.css +346 -37
  5. data/app/assets/stylesheets/collavre/desktop_setup.css +246 -0
  6. data/app/assets/stylesheets/collavre/gnb.css +64 -3
  7. data/app/assets/stylesheets/collavre/landing.css +108 -3
  8. data/app/assets/stylesheets/collavre/mention_menu.css +32 -0
  9. data/app/assets/stylesheets/collavre/popup.css +14 -0
  10. data/app/assets/stylesheets/collavre/print.css +0 -1
  11. data/app/assets/stylesheets/collavre/search_popup.css +1 -1
  12. data/app/assets/stylesheets/collavre/secret_fields.css +3 -0
  13. data/app/assets/stylesheets/collavre/workspace.css +307 -0
  14. data/app/channels/collavre/agent_channel.rb +14 -21
  15. data/app/channels/collavre/comments_presence_channel.rb +181 -21
  16. data/app/channels/collavre/inbox_badge_channel.rb +7 -1
  17. data/app/components/collavre/inbox/badge_component.rb +6 -13
  18. data/app/components/collavre/progress_filter_component.html.erb +2 -2
  19. data/app/components/collavre/progress_filter_component.rb +20 -0
  20. data/app/controllers/collavre/admin/settings_controller.rb +4 -1
  21. data/app/controllers/collavre/agent_connections_controller.rb +165 -0
  22. data/app/controllers/collavre/agent_gateways_controller.rb +132 -0
  23. data/app/controllers/collavre/agent_provisioning_controller.rb +79 -0
  24. data/app/controllers/collavre/api/v1/agents_controller.rb +45 -97
  25. data/app/controllers/collavre/application_controller.rb +13 -2
  26. data/app/controllers/collavre/comment_read_pointers_controller.rb +69 -40
  27. data/app/controllers/collavre/comments_controller.rb +45 -131
  28. data/app/controllers/collavre/concerns/shareable.rb +9 -3
  29. data/app/controllers/collavre/creatives_controller.rb +144 -11
  30. data/app/controllers/collavre/desktop_gateway_registrations_controller.rb +173 -0
  31. data/app/controllers/collavre/desktop_setup_controller.rb +119 -0
  32. data/app/controllers/collavre/desktop_sidecar_health_controller.rb +35 -0
  33. data/app/controllers/collavre/features_controller.rb +57 -0
  34. data/app/controllers/collavre/invites_controller.rb +12 -1
  35. data/app/controllers/collavre/llm_models_controller.rb +8 -0
  36. data/app/controllers/collavre/notices_controller.rb +20 -0
  37. data/app/controllers/collavre/tasks_controller.rb +40 -6
  38. data/app/controllers/collavre/topics_controller.rb +130 -53
  39. data/app/controllers/collavre/user_creative_preferences_controller.rb +214 -30
  40. data/app/controllers/concerns/collavre/comments/batch_operations.rb +15 -3
  41. data/app/controllers/concerns/collavre/comments/conversion.rb +1 -1
  42. data/app/controllers/concerns/collavre/users_controller/ai_user_management.rb +118 -8
  43. data/app/controllers/concerns/collavre/users_controller/profile_and_settings.rb +1 -15
  44. data/app/controllers/concerns/collavre/users_controller/registration.rb +2 -2
  45. data/app/errors/collavre/turn_deadline_error.rb +20 -0
  46. data/app/helpers/collavre/application_helper.rb +38 -0
  47. data/app/helpers/collavre/creatives_helper.rb +56 -22
  48. data/app/helpers/collavre/navigation_helper.rb +12 -0
  49. data/app/javascript/collavre.js +1 -2
  50. data/app/javascript/components/InlineLexicalEditor.jsx +30 -21
  51. data/app/javascript/components/__tests__/creative_tree_row_parent_id.test.js +32 -2
  52. data/app/javascript/components/__tests__/creative_tree_row_progress_toggle.test.js +185 -0
  53. data/app/javascript/components/creative_tree_row.js +62 -22
  54. data/app/javascript/controllers/__tests__/agent_connection_controller.test.js +195 -0
  55. data/app/javascript/controllers/__tests__/desktop_proxy_setup_controller.test.js +96 -0
  56. data/app/javascript/controllers/__tests__/index.test.js +42 -0
  57. data/app/javascript/controllers/__tests__/last_visited_creative_controller.test.js +103 -0
  58. data/app/javascript/controllers/__tests__/link_creative_controller.test.js +253 -2
  59. data/app/javascript/controllers/__tests__/progress_filter_controller.test.js +134 -0
  60. data/app/javascript/controllers/__tests__/search_popup_controller.test.js +310 -0
  61. data/app/javascript/controllers/__tests__/topic_list_controller.test.js +111 -0
  62. data/app/javascript/controllers/__tests__/workspace_tree_controller.test.js +1011 -0
  63. data/app/javascript/controllers/agent_connection_controller.js +357 -0
  64. data/app/javascript/controllers/agent_vendor_controller.js +23 -0
  65. data/app/javascript/controllers/comment_controller.js +3 -1
  66. data/app/javascript/controllers/comments/__tests__/feature_cards_controller.test.js +223 -0
  67. data/app/javascript/controllers/comments/__tests__/form_controller_default_placeholder.test.js +83 -0
  68. data/app/javascript/controllers/comments/__tests__/form_controller_draft.test.js +2069 -0
  69. data/app/javascript/controllers/comments/__tests__/form_controller_ime_send.test.js +324 -0
  70. data/app/javascript/controllers/comments/__tests__/form_controller_review.test.js +28 -2
  71. data/app/javascript/controllers/comments/__tests__/form_controller_search_submit.test.js +106 -0
  72. data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft.test.js +134 -0
  73. data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft_creative_switch.test.js +166 -0
  74. data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft_failure.test.js +183 -0
  75. data/app/javascript/controllers/comments/__tests__/list_controller_deep_link_topic.test.js +467 -0
  76. data/app/javascript/controllers/comments/__tests__/list_controller_docked.test.js +115 -0
  77. data/app/javascript/controllers/comments/__tests__/list_controller_prev_message.test.js +245 -0
  78. data/app/javascript/controllers/comments/__tests__/list_controller_read_pointer.test.js +276 -0
  79. data/app/javascript/controllers/comments/__tests__/list_controller_stream_archived.test.js +221 -0
  80. data/app/javascript/controllers/comments/__tests__/list_controller_stream_search.test.js +104 -0
  81. data/app/javascript/controllers/comments/__tests__/placeholder_controller.test.js +79 -0
  82. data/app/javascript/controllers/comments/__tests__/popup_controller.test.js +754 -1
  83. data/app/javascript/controllers/comments/__tests__/presence_controller.test.js +717 -15
  84. data/app/javascript/controllers/comments/__tests__/prev_message_navigator.test.js +129 -0
  85. data/app/javascript/controllers/comments/__tests__/topics_controller_action_errors.test.js +184 -0
  86. data/app/javascript/controllers/comments/__tests__/topics_controller_archived.test.js +879 -0
  87. data/app/javascript/controllers/comments/__tests__/topics_controller_creation_container.test.js +154 -0
  88. data/app/javascript/controllers/comments/__tests__/topics_controller_creation_scroll.test.js +92 -0
  89. data/app/javascript/controllers/comments/__tests__/topics_controller_delete.test.js +130 -0
  90. data/app/javascript/controllers/comments/__tests__/topics_controller_docked.test.js +32 -0
  91. data/app/javascript/controllers/comments/__tests__/topics_controller_last_topic_echo.test.js +1938 -0
  92. data/app/javascript/controllers/comments/__tests__/topics_controller_primary_agent.test.js +256 -0
  93. data/app/javascript/controllers/comments/__tests__/topics_controller_scroll.test.js +182 -0
  94. data/app/javascript/controllers/comments/__tests__/topics_controller_selection_race.test.js +981 -0
  95. data/app/javascript/controllers/comments/__tests__/topics_controller_topic_list.test.js +313 -0
  96. data/app/javascript/controllers/comments/feature_cards_controller.js +83 -0
  97. data/app/javascript/controllers/comments/form_controller.js +795 -14
  98. data/app/javascript/controllers/comments/list_controller.js +338 -52
  99. data/app/javascript/controllers/comments/placeholder_controller.js +30 -0
  100. data/app/javascript/controllers/comments/popup_controller.js +425 -83
  101. data/app/javascript/controllers/comments/presence_controller.js +317 -55
  102. data/app/javascript/controllers/comments/prev_message_navigator.js +94 -0
  103. data/app/javascript/controllers/comments/topics_controller.js +1772 -100
  104. data/app/javascript/controllers/common_popup_controller.js +2 -2
  105. data/app/javascript/controllers/creatives/__tests__/select_mode_controller.test.js +139 -0
  106. data/app/javascript/controllers/creatives/__tests__/tree_controller.test.js +605 -0
  107. data/app/javascript/controllers/creatives/__tests__/write_access_request_controller.test.js +125 -0
  108. data/app/javascript/controllers/creatives/select_mode_controller.js +7 -2
  109. data/app/javascript/controllers/creatives/tree_controller.js +140 -31
  110. data/app/javascript/controllers/creatives/write_access_request_controller.js +58 -0
  111. data/app/javascript/controllers/desktop_proxy_setup_controller.js +51 -0
  112. data/app/javascript/controllers/gateway_check_controller.js +36 -0
  113. data/app/javascript/controllers/index.js +25 -1
  114. data/app/javascript/controllers/last_visited_creative_controller.js +58 -0
  115. data/app/javascript/controllers/link_creative_controller.js +89 -18
  116. data/app/javascript/controllers/progress_filter_controller.js +12 -22
  117. data/app/javascript/controllers/search_popup_controller.js +64 -46
  118. data/app/javascript/controllers/share_user_search_controller.js +2 -2
  119. data/app/javascript/controllers/topic_list_controller.js +44 -7
  120. data/app/javascript/controllers/workspace_tree_controller.js +662 -0
  121. data/app/javascript/creatives/__tests__/tree_renderer.test.js +80 -0
  122. data/app/javascript/creatives/drag_drop/__tests__/drop_completion_empty_state.test.js +98 -0
  123. data/app/javascript/creatives/drag_drop/__tests__/topic_move_released_agent.test.js +125 -0
  124. data/app/javascript/creatives/drag_drop/event_handlers.js +12 -0
  125. data/app/javascript/creatives/tree_renderer.js +65 -22
  126. data/app/javascript/lib/__tests__/chat_drafts.test.js +1458 -0
  127. data/app/javascript/lib/__tests__/common_popup_bounds.test.js +8 -3
  128. data/app/javascript/lib/__tests__/common_popup_placement.test.js +374 -0
  129. data/app/javascript/lib/__tests__/common_popup_reshow.test.js +131 -0
  130. data/app/javascript/lib/__tests__/creative_link_prefetch.test.js +46 -0
  131. data/app/javascript/lib/__tests__/last_visited_creative.test.js +87 -0
  132. data/app/javascript/lib/__tests__/skill_html_entities.test.js +23 -0
  133. data/app/javascript/lib/__tests__/turbo_stream_actions.test.js +190 -0
  134. data/app/javascript/lib/api/__tests__/creatives.test.js +68 -0
  135. data/app/javascript/lib/api/__tests__/csrf_fetch.test.js +27 -0
  136. data/app/javascript/lib/api/__tests__/topics_save_last_topic.test.js +112 -0
  137. data/app/javascript/lib/api/creatives.js +32 -13
  138. data/app/javascript/lib/api/csrf_fetch.js +6 -4
  139. data/app/javascript/lib/api/topics.js +63 -5
  140. data/app/javascript/lib/chat_drafts.js +786 -0
  141. data/app/javascript/lib/common_popup.js +164 -25
  142. data/app/javascript/lib/creative_link_prefetch.js +27 -0
  143. data/app/javascript/lib/last_visited_creative.js +97 -0
  144. data/app/javascript/lib/lexical/__tests__/creative_link_node.test.js +162 -0
  145. data/app/javascript/lib/lexical/__tests__/creative_link_trigger.test.js +133 -0
  146. data/app/javascript/lib/lexical/__tests__/link_toolbar.test.js +62 -0
  147. data/app/javascript/lib/lexical/creative_link_node.js +101 -0
  148. data/app/javascript/lib/lexical/creative_link_trigger.js +98 -0
  149. data/app/javascript/lib/lexical/link_toolbar.js +23 -0
  150. data/app/javascript/lib/turbo_stream_actions.js +23 -16
  151. data/app/javascript/lib/utils/__tests__/filter_navigation.test.js +210 -0
  152. data/app/javascript/lib/utils/__tests__/markdown.test.js +44 -0
  153. data/app/javascript/lib/utils/filter_navigation.js +100 -0
  154. data/app/javascript/modules/__tests__/command_menu_creative_picker.test.js +62 -0
  155. data/app/javascript/modules/__tests__/command_menu_typeahead.test.js +267 -0
  156. data/app/javascript/modules/__tests__/creative_link_picker.test.js +85 -0
  157. data/app/javascript/modules/__tests__/creative_row_editor_archive.test.js +391 -0
  158. data/app/javascript/modules/__tests__/creative_row_editor_delegated_clicks.test.js +158 -0
  159. data/app/javascript/modules/__tests__/creative_row_editor_empty_state_recovery.test.js +519 -0
  160. data/app/javascript/modules/__tests__/creative_row_editor_empty_state_removal.test.js +346 -0
  161. data/app/javascript/modules/__tests__/creative_row_editor_session.test.js +144 -0
  162. data/app/javascript/modules/__tests__/creative_tree_dom.test.js +30 -0
  163. data/app/javascript/modules/__tests__/creative_tree_empty_state.test.js +186 -0
  164. data/app/javascript/modules/__tests__/dom_listener_registry.test.js +67 -0
  165. data/app/javascript/modules/__tests__/support/inline_editor_dom.js +159 -0
  166. data/app/javascript/modules/command_args_form.js +0 -12
  167. data/app/javascript/modules/command_menu.js +80 -38
  168. data/app/javascript/modules/creative_link_picker.js +56 -0
  169. data/app/javascript/modules/creative_row_editor.js +446 -243
  170. data/app/javascript/modules/creative_row_editor_delegated_clicks.js +87 -0
  171. data/app/javascript/modules/creative_tree_dom.js +4 -0
  172. data/app/javascript/modules/creative_tree_empty_state.js +80 -0
  173. data/app/javascript/modules/dom_listener_registry.js +23 -0
  174. data/app/javascript/modules/mention_menu.js +2 -3
  175. data/app/javascript/utils/__tests__/caret_anchor.test.js +75 -0
  176. data/app/javascript/utils/__tests__/chevron_icons.test.js +15 -0
  177. data/app/javascript/utils/caret_position.js +10 -0
  178. data/app/javascript/utils/chevron_icons.js +7 -0
  179. data/app/jobs/collavre/ai_agent_job.rb +262 -72
  180. data/app/jobs/collavre/cancel_offline_delegated_tasks_job.rb +11 -36
  181. data/app/jobs/collavre/comment_badges_broadcast_job.rb +28 -0
  182. data/app/jobs/collavre/comment_link_preview_job.rb +33 -0
  183. data/app/jobs/collavre/comment_notification_job.rb +14 -0
  184. data/app/jobs/collavre/comment_push_delivery_sweep_job.rb +17 -0
  185. data/app/jobs/collavre/compress_job.rb +33 -22
  186. data/app/jobs/collavre/creative_broadcast_job.rb +161 -48
  187. data/app/jobs/collavre/cron_action_job.rb +36 -18
  188. data/app/jobs/collavre/drop_trigger_job.rb +23 -9
  189. data/app/jobs/collavre/merge_comments_job.rb +33 -16
  190. data/app/jobs/collavre/push_notification_job.rb +62 -18
  191. data/app/jobs/collavre/restore_dropped_dispatches_job.rb +17 -0
  192. data/app/jobs/collavre/stuck_detector_job.rb +1 -1
  193. data/app/jobs/collavre/touch_creative_subtree_job.rb +14 -0
  194. data/app/models/collavre/agent_gateway.rb +200 -0
  195. data/app/models/collavre/agent_subscription.rb +4 -4
  196. data/app/models/collavre/agent_workspace.rb +210 -0
  197. data/app/models/collavre/comment/broadcastable.rb +22 -52
  198. data/app/models/collavre/comment/notifiable.rb +154 -77
  199. data/app/models/collavre/comment/topic_membership.rb +41 -0
  200. data/app/models/collavre/comment.rb +336 -31
  201. data/app/models/collavre/comment_notification_delivery.rb +61 -0
  202. data/app/models/collavre/comment_presence_store/cache_batches.rb +64 -0
  203. data/app/models/collavre/comment_presence_store.rb +272 -12
  204. data/app/models/collavre/comment_read_pointer.rb +9 -15
  205. data/app/models/collavre/creative/describable.rb +8 -2
  206. data/app/models/collavre/creative/permissible.rb +11 -0
  207. data/app/models/collavre/creative.rb +29 -4
  208. data/app/models/collavre/creative_share.rb +109 -4
  209. data/app/models/collavre/current.rb +1 -0
  210. data/app/models/collavre/llm_model.rb +62 -0
  211. data/app/models/collavre/system_setting.rb +12 -1
  212. data/app/models/collavre/task.rb +121 -2
  213. data/app/models/collavre/topic.rb +51 -0
  214. data/app/models/collavre/user.rb +101 -41
  215. data/app/models/collavre/user_creative_preference.rb +3 -1
  216. data/app/services/collavre/agent_provisioning/archive.rb +108 -0
  217. data/app/services/collavre/ai_agent/a2a_dispatcher.rb +16 -3
  218. data/app/services/collavre/ai_agent/agent_lifecycle_manager.rb +63 -6
  219. data/app/services/collavre/ai_agent/approval_handler.rb +12 -4
  220. data/app/services/collavre/ai_agent/claude_channel_adapter.rb +10 -5
  221. data/app/services/collavre/ai_agent/merged_trigger_comments.rb +164 -0
  222. data/app/services/collavre/ai_agent/message_builder.rb +103 -16
  223. data/app/services/collavre/ai_agent/session_provisioner.rb +3 -7
  224. data/app/services/collavre/ai_agent/task_claim_service.rb +29 -15
  225. data/app/services/collavre/ai_agent/task_reply_service.rb +67 -0
  226. data/app/services/collavre/ai_agent_service.rb +108 -20
  227. data/app/services/collavre/ai_client.rb +211 -21
  228. data/app/services/collavre/cli_proxy/client.rb +147 -0
  229. data/app/services/collavre/cli_proxy/endpoint_policy.rb +81 -0
  230. data/app/services/collavre/cli_proxy/identity.rb +47 -0
  231. data/app/services/collavre/cli_proxy/safe_net_http_adapter.rb +19 -0
  232. data/app/services/collavre/command_menu_service.rb +0 -10
  233. data/app/services/collavre/comment_move_service.rb +96 -7
  234. data/app/services/collavre/comment_snapshot_restore_service.rb +11 -5
  235. data/app/services/collavre/comments/action_executor.rb +48 -20
  236. data/app/services/collavre/comments/command_processor.rb +0 -1
  237. data/app/services/collavre/comments/list_response.rb +133 -0
  238. data/app/services/collavre/comments/read_pointer_writer.rb +210 -0
  239. data/app/services/collavre/comments/read_receipt_broadcaster.rb +157 -0
  240. data/app/services/collavre/comments/read_receipt_index.rb +152 -0
  241. data/app/services/collavre/comments/topic_command.rb +72 -1
  242. data/app/services/collavre/comments/topic_mutation.rb +20 -0
  243. data/app/services/collavre/creatives/breadcrumb_resolver.rb +1 -1
  244. data/app/services/collavre/creatives/comment_badge_index.rb +299 -27
  245. data/app/services/collavre/creatives/reorderer.rb +15 -3
  246. data/app/services/collavre/creatives/tree_builder.rb +1 -1
  247. data/app/services/collavre/creatives/tree_formatter.rb +5 -6
  248. data/app/services/collavre/creatives/workspace_path_resolver.rb +50 -0
  249. data/app/services/collavre/creatives/workspace_tree_builder.rb +92 -0
  250. data/app/services/collavre/crons/recurring_topic_tasks.rb +23 -0
  251. data/app/services/collavre/home_page_path.rb +55 -0
  252. data/app/services/collavre/html_text.rb +64 -0
  253. data/app/services/collavre/http_client.rb +11 -2
  254. data/app/services/collavre/inbox_reply_service.rb +1 -1
  255. data/app/services/collavre/markdown_converter.rb +13 -1
  256. data/app/services/collavre/orchestration/agent_orchestrator.rb +776 -81
  257. data/app/services/collavre/orchestration/arbiter.rb +48 -5
  258. data/app/services/collavre/orchestration/deferred_trigger_scope.rb +53 -0
  259. data/app/services/collavre/orchestration/delivery_record.rb +792 -0
  260. data/app/services/collavre/orchestration/loop_breaker.rb +10 -4
  261. data/app/services/collavre/orchestration/matcher.rb +132 -17
  262. data/app/services/collavre/orchestration/policy_resolver.rb +58 -10
  263. data/app/services/collavre/orchestration/scheduler.rb +10 -4
  264. data/app/services/collavre/orchestration/scheduling_hooks.rb +10 -0
  265. data/app/services/collavre/orchestration/selection.rb +43 -0
  266. data/app/services/collavre/orchestration/stuck_detector.rb +25 -112
  267. data/app/services/collavre/orchestration/task_coalescer.rb +355 -0
  268. data/app/services/collavre/orchestration/topic_slot.rb +73 -0
  269. data/app/services/collavre/system_events/context_builder.rb +60 -11
  270. data/app/services/collavre/system_events/dispatcher.rb +58 -12
  271. data/app/services/collavre/system_events/envelope.rb +73 -0
  272. data/app/services/collavre/system_events/vocabulary.rb +45 -0
  273. data/app/services/collavre/tools/creative_retrieval_service.rb +1 -1
  274. data/app/services/collavre/tools/cron_create_service.rb +32 -18
  275. data/app/services/collavre/tools/id_list.rb +38 -0
  276. data/app/services/collavre/tools/topic_authorizer.rb +61 -7
  277. data/app/services/collavre/tools/topic_branch_service.rb +85 -0
  278. data/app/services/collavre/tools/topic_create_service.rb +117 -0
  279. data/app/services/collavre/tools/topic_list_service.rb +101 -0
  280. data/app/services/collavre/tools/topic_message_create_service.rb +192 -0
  281. data/app/services/collavre/tools/topic_messages_service.rb +346 -0
  282. data/app/services/collavre/tools/topic_move_service.rb +108 -0
  283. data/app/services/collavre/tools/topic_selection.rb +59 -0
  284. data/app/services/collavre/tools/topic_update_service.rb +165 -0
  285. data/app/services/collavre/topic_branch_service.rb +39 -10
  286. data/app/services/collavre/topics/agent_resolver.rb +87 -0
  287. data/app/services/collavre/topics/char_budget.rb +70 -0
  288. data/app/services/collavre/topics/last_topic_preference_payload.rb +18 -0
  289. data/app/services/collavre/topics/message_page.rb +380 -0
  290. data/app/services/collavre/topics/message_page_markdown.rb +102 -0
  291. data/app/services/collavre/topics/message_scope.rb +61 -0
  292. data/app/services/collavre/topics/message_stats.rb +92 -0
  293. data/app/services/collavre/topics/move_blocker.rb +47 -0
  294. data/app/services/collavre/topics/next_name.rb +35 -0
  295. data/app/services/collavre/topics/pin_rejection.rb +25 -0
  296. data/app/services/collavre/topics/read_pointer_relocator.rb +71 -0
  297. data/app/services/collavre/topics/reserved_name.rb +18 -0
  298. data/app/services/collavre/topics/serializer.rb +92 -0
  299. data/app/services/collavre/topics/topic_destroy.rb +30 -0
  300. data/app/services/collavre/topics/topic_move.rb +80 -0
  301. data/app/services/collavre/topics/topic_move_effects.rb +35 -0
  302. data/app/services/collavre/topics/topic_mutation.rb +27 -0
  303. data/app/views/collavre/admin/integrations/_setting_row.html.erb +5 -8
  304. data/app/views/collavre/admin/settings/_system_tab.html.erb +7 -0
  305. data/app/views/collavre/agent_connections/show.html.erb +77 -0
  306. data/app/views/collavre/agent_gateways/_form.html.erb +71 -0
  307. data/app/views/collavre/agent_gateways/edit.html.erb +2 -0
  308. data/app/views/collavre/agent_gateways/index.html.erb +54 -0
  309. data/app/views/collavre/agent_gateways/new.html.erb +2 -0
  310. data/app/views/collavre/comments/_comments_popup.html.erb +32 -17
  311. data/app/views/collavre/comments/_empty_state.html.erb +33 -0
  312. data/app/views/collavre/comments/_empty_state_minimal.html.erb +7 -0
  313. data/app/views/collavre/comments/_feature_card.html.erb +35 -0
  314. data/app/views/collavre/comments/_list.html.erb +3 -2
  315. data/app/views/collavre/comments/_no_search_results.html.erb +3 -0
  316. data/app/views/collavre/creatives/_add_button.html.erb +17 -19
  317. data/app/views/collavre/creatives/_empty_state.html.erb +109 -0
  318. data/app/views/collavre/creatives/_inline_edit_form.html.erb +5 -5
  319. data/app/views/collavre/creatives/_tree_loading_placeholder.html.erb +16 -0
  320. data/app/views/collavre/creatives/_workspace_tree.html.erb +33 -0
  321. data/app/views/collavre/creatives/index.html.erb +79 -18
  322. data/app/views/collavre/desktop_setup/show.html.erb +114 -0
  323. data/app/views/collavre/features/index.html.erb +42 -0
  324. data/app/views/collavre/features/show.html.erb +52 -0
  325. data/app/views/collavre/invitation_mailer/invite.html.erb +1 -1
  326. data/app/views/collavre/invitation_mailer/invite.text.erb +1 -1
  327. data/app/views/collavre/invites/show.html.erb +14 -1
  328. data/app/views/collavre/landing/show.html.erb +5 -1
  329. data/app/views/collavre/shared/_link_creative_modal.html.erb +4 -1
  330. data/app/views/collavre/shared/navigation/_help_button.html.erb +3 -1
  331. data/app/views/collavre/shared/navigation/_panels.html.erb +1 -8
  332. data/app/views/collavre/shared/navigation/_search_form.html.erb +13 -2
  333. data/app/views/collavre/user_themes/index.html.erb +1 -1
  334. data/app/views/collavre/users/_llm_fields.html.erb +65 -0
  335. data/app/views/collavre/users/_org_chart_node.html.erb +1 -1
  336. data/app/views/collavre/users/edit_ai.html.erb +33 -31
  337. data/app/views/collavre/users/new.html.erb +4 -2
  338. data/app/views/collavre/users/new_ai.html.erb +14 -32
  339. data/app/views/collavre/users/show.html.erb +13 -1
  340. data/app/views/layouts/collavre/landing.html.erb +9 -4
  341. data/config/initializers/ruby_llm_openai_reasoning_content.rb +77 -0
  342. data/config/locales/admin.en.yml +3 -1
  343. data/config/locales/admin.ko.yml +3 -1
  344. data/config/locales/agent_gateway.en.yml +217 -0
  345. data/config/locales/agent_gateway.ko.yml +217 -0
  346. data/config/locales/ai_agent.en.yml +0 -1
  347. data/config/locales/ai_agent.ko.yml +0 -1
  348. data/config/locales/comments.en.yml +72 -30
  349. data/config/locales/comments.ko.yml +72 -30
  350. data/config/locales/creatives.en.yml +32 -1
  351. data/config/locales/creatives.ko.yml +32 -1
  352. data/config/locales/features.en.yml +140 -0
  353. data/config/locales/features.ko.yml +140 -0
  354. data/config/locales/invites.en.yml +3 -0
  355. data/config/locales/invites.ko.yml +3 -0
  356. data/config/locales/landing.en.yml +2 -1
  357. data/config/locales/landing.ko.yml +2 -1
  358. data/config/locales/tools.en.yml +20 -0
  359. data/config/locales/tools.ko.yml +20 -0
  360. data/config/locales/users.en.yml +15 -14
  361. data/config/locales/users.ko.yml +14 -14
  362. data/config/routes.rb +45 -19
  363. data/db/migrate/20250911084338_backfill_creative_in_comment_inbox_items.rb +9 -1
  364. data/db/migrate/20260724000000_add_creative_id_index_to_comments.rb +34 -0
  365. data/db/migrate/20260724000001_add_creative_private_id_index_to_comments.rb +50 -0
  366. data/db/migrate/20260724090000_add_async_comment_notification_state.rb +64 -0
  367. data/db/migrate/20260727000000_add_waiting_notice_scope_to_comments.rb +30 -0
  368. data/db/migrate/20260727010000_add_waiting_notice_scope_to_tasks.rb +26 -0
  369. data/db/migrate/20260805000000_add_creative_workspace_enabled_to_users.rb +5 -0
  370. data/db/migrate/20260807050000_add_dismissed_notices_to_users.rb +5 -0
  371. data/db/migrate/20260809000000_change_creative_workspace_enabled_default_to_true.rb +18 -0
  372. data/db/migrate/20260809000001_create_llm_models.rb +64 -0
  373. data/db/migrate/20260809000002_create_agent_gateways_and_workspaces.rb +35 -0
  374. data/db/migrate/20260809000003_hash_agent_workspace_callback_tokens.rb +44 -0
  375. data/db/migrate/20260809000004_encrypt_agent_workspace_manifest_tokens.rb +59 -0
  376. data/db/migrate/20260810000000_split_agent_workspace_identity_axes.rb +58 -0
  377. data/db/migrate/20260810000001_seed_codex_custom_proxy_llm_models.rb +28 -0
  378. data/db/migrate/20260810010000_add_last_visited_creative_to_users.rb +8 -0
  379. data/db/migrate/20260810020000_add_last_visited_creative_at_to_users.rb +5 -0
  380. data/db/migrate/20260810030000_add_last_visited_creative_navigation_order_to_users.rb +6 -0
  381. data/db/migrate/20260810060000_add_last_visited_creative_issued_sequence_to_users.rb +5 -0
  382. data/db/migrate/20260810100000_add_desktop_managed_to_agent_gateways.rb +40 -0
  383. data/db/migrate/20260810100001_add_desktop_preset_adapter_to_users.rb +38 -0
  384. data/db/migrate/20260811000000_make_agent_gateway_completion_key_nullable.rb +7 -0
  385. data/db/migrate/20260812000000_remove_workflow_columns_from_tasks.rb +101 -0
  386. data/db/migrate/20260812000001_remove_android_devices.rb +24 -0
  387. data/db/migrate/20260812010000_drop_inbox_items.rb +22 -0
  388. data/db/migrate/20260813000000_clear_claude_channel_presence_routing_expressions.rb +16 -0
  389. data/db/migrate/20260813000001_add_last_topic_revision_to_user_creative_preferences.rb +5 -0
  390. data/db/migrate/20260813000002_add_last_topic_save_order_to_user_creative_preferences.rb +6 -0
  391. data/db/migrate/20260813000003_add_last_topic_save_sequences_to_user_creative_preferences.rb +5 -0
  392. data/db/migrate/20260813000004_add_last_topic_save_fences_to_user_creative_preferences.rb +6 -0
  393. data/db/migrate/20260814000000_add_topic_to_comment_read_pointers.rb +79 -0
  394. data/db/migrate/20260814000001_add_creative_topic_private_id_index_to_comments.rb +37 -0
  395. data/db/migrate/20260816000000_add_watermark_index_to_comment_read_pointers.rb +27 -0
  396. data/db/migrate/20260816000001_add_private_recipient_topic_index_to_comments.rb +48 -0
  397. data/db/migrate/20260820000000_add_topic_assigned_at_to_comments.rb +13 -0
  398. data/db/seeds.rb +1 -51
  399. data/lib/collavre/engine.rb +108 -1
  400. data/lib/collavre/feature_card_registry.rb +168 -0
  401. data/lib/collavre/hashed_access_token_lookup.rb +22 -0
  402. data/lib/collavre/sensitive_request_silencer.rb +23 -0
  403. data/lib/collavre/user_extensions.rb +0 -2
  404. data/lib/collavre/version.rb +1 -1
  405. data/lib/collavre.rb +1 -0
  406. data/skills/collavre/SKILL.md +165 -0
  407. data/skills/collavre/package.json +8 -0
  408. data/skills/collavre/references/tool-reference.md +347 -0
  409. data/skills/collavre/scripts/collavre +634 -0
  410. data/skills/collavre/scripts/html_entities.js +24 -0
  411. metadata +228 -30
  412. data/app/controllers/collavre/api/v1/mobile/agent_events_controller.rb +0 -224
  413. data/app/controllers/collavre/api/v1/mobile/base_controller.rb +0 -95
  414. data/app/controllers/collavre/api/v1/mobile/devices_controller.rb +0 -31
  415. data/app/controllers/collavre/api/v1/mobile/voice_commands_controller.rb +0 -25
  416. data/app/controllers/collavre/inbox_items_controller.rb +0 -70
  417. data/app/controllers/collavre/typo_corrections_controller.rb +0 -39
  418. data/app/javascript/lib/__tests__/typo_correction.test.js +0 -192
  419. data/app/javascript/lib/typo_correction.js +0 -146
  420. data/app/javascript/modules/__tests__/typo_corrector.test.js +0 -365
  421. data/app/javascript/modules/creative_guide.js +0 -67
  422. data/app/javascript/modules/typo_corrector.js +0 -534
  423. data/app/jobs/collavre/inbox_summary_job.rb +0 -24
  424. data/app/mailers/collavre/inbox_mailer.rb +0 -19
  425. data/app/models/collavre/inbox_item.rb +0 -89
  426. data/app/services/collavre/comments/concerns/workflow_support.rb +0 -115
  427. data/app/services/collavre/comments/work_command.rb +0 -161
  428. data/app/services/collavre/comments/workflow_executor.rb +0 -276
  429. data/app/services/collavre/gemini_parent_recommender.rb +0 -77
  430. data/app/services/collavre/mobile/event_summarizer.rb +0 -40
  431. data/app/services/collavre/typo_corrector.rb +0 -188
  432. data/app/views/collavre/inbox_items/_item.html.erb +0 -18
  433. data/app/views/collavre/inbox_items/_items.html.erb +0 -3
  434. data/app/views/collavre/inbox_items/_list.html.erb +0 -7
  435. data/app/views/collavre/inbox_items/index.html.erb +0 -1
  436. data/app/views/collavre/inbox_mailer/daily_summary.html.erb +0 -11
  437. data/app/views/collavre/inbox_mailer/daily_summary.text.erb +0 -8
  438. data/app/views/collavre/users/typo_correction.html.erb +0 -50
  439. data/config/locales/mobile.en.yml +0 -16
  440. data/config/locales/mobile.ko.yml +0 -16
@@ -1,11 +1,41 @@
1
+ require "collavre/sensitive_request_silencer"
2
+ require "collavre/hashed_access_token_lookup"
3
+
1
4
  module Collavre
2
5
  class Engine < ::Rails::Engine
6
+ PROVISIONING_CAPABILITY_PATH = %r{
7
+ \A(?:/[^/]+)*/agents/\d+/workspaces/[^/]+(?:/.*)?\z
8
+ }x
9
+
3
10
  isolate_namespace Collavre
4
11
 
5
12
  config.generators do |g|
6
13
  g.test_framework :minitest
7
14
  end
8
15
 
16
+ # Manifest capabilities grant access to workspace callback credentials and
17
+ # therefore must not appear in Rails request logs. This middleware wraps the
18
+ # request logger as well as controller instrumentation while leaving public,
19
+ # content-addressed skill archive requests observable.
20
+ initializer "collavre.silence_provisioning_capability_paths" do |app|
21
+ app.middleware.insert_before(
22
+ Rails::Rack::Logger,
23
+ Collavre::SensitiveRequestSilencer,
24
+ path: PROVISIONING_CAPABILITY_PATH
25
+ )
26
+ end
27
+
28
+ # Workspace callback credentials retain their plaintext only in the
29
+ # encrypted AgentWorkspace column. Doorkeeper stores a one-way digest and
30
+ # resolves presented bearer values through this mixed plain/hashed lookup,
31
+ # preserving existing OAuth tokens without making stored digests usable.
32
+ initializer "collavre.hashed_workspace_callback_tokens" do
33
+ Rails.application.config.to_prepare do
34
+ token_class = Doorkeeper::AccessToken.singleton_class
35
+ token_class.prepend(Collavre::HashedAccessTokenLookup) unless token_class < Collavre::HashedAccessTokenLookup
36
+ end
37
+ end
38
+
9
39
  # Path to engine's JavaScript sources for jsbundling-rails integration
10
40
  def self.javascript_path
11
41
  root.join("app/javascript")
@@ -148,6 +178,83 @@ module Collavre
148
178
  ActiveSupport::Reloader.to_prepare(prepend: true) do
149
179
  Navigation::Registry.instance.reset!
150
180
  Collavre::ViewExtensions.reset!
181
+ Collavre::FeatureCardRegistry.reset!
182
+ end
183
+ end
184
+
185
+ # Register the empty-chat feature discovery cards. Vendor engines may
186
+ # register additional cards from their own initializers (after this one)
187
+ # the same way they register integrations.
188
+ initializer "collavre.feature_cards", after: "collavre.navigation_reset" do
189
+ Rails.application.config.to_prepare do
190
+ Collavre::FeatureCardRegistry.register(:mention_agent, {
191
+ icon: "🤖",
192
+ title_key: "collavre.comments.empty_state.cards.mention_agent.title",
193
+ description_key: "collavre.comments.empty_state.cards.mention_agent.description",
194
+ guide: true
195
+ })
196
+
197
+ Collavre::FeatureCardRegistry.register(:slash_command, {
198
+ icon: "⚡",
199
+ title_key: "collavre.comments.empty_state.cards.slash_command.title",
200
+ description_key: "collavre.comments.empty_state.cards.slash_command.description",
201
+ action: { type: :command_menu, label_key: "collavre.comments.empty_state.cards.slash_command.action" },
202
+ guide: true
203
+ })
204
+
205
+ Collavre::FeatureCardRegistry.register(:chat_context, {
206
+ icon: "🔗",
207
+ title_key: "collavre.comments.empty_state.cards.chat_context.title",
208
+ description_key: "collavre.comments.empty_state.cards.chat_context.description",
209
+ guide: true
210
+ })
211
+
212
+ Collavre::FeatureCardRegistry.register(:automation_trigger, {
213
+ icon: "⚙️",
214
+ title_key: "collavre.comments.empty_state.cards.automation_trigger.title",
215
+ description_key: "collavre.comments.empty_state.cards.automation_trigger.description",
216
+ guide: true
217
+ })
218
+
219
+ Collavre::FeatureCardRegistry.register(:topic_management, {
220
+ icon: "🗂️",
221
+ title_key: "collavre.comments.empty_state.cards.topic_management.title",
222
+ description_key: "collavre.comments.empty_state.cards.topic_management.description",
223
+ action: { type: :topic_list, label_key: "collavre.comments.empty_state.cards.topic_management.action" },
224
+ guide: true
225
+ })
226
+
227
+ Collavre::FeatureCardRegistry.register(:add_user, {
228
+ icon: "👥",
229
+ title_key: "collavre.comments.empty_state.cards.add_user.title",
230
+ description_key: "collavre.comments.empty_state.cards.add_user.description",
231
+ action: { type: :share_modal, label_key: "collavre.comments.empty_state.cards.add_user.action" },
232
+ guide: true
233
+ })
234
+
235
+ Collavre::FeatureCardRegistry.register(:inbox_notifications, {
236
+ icon: "🔔",
237
+ title_key: "collavre.comments.empty_state.cards.inbox_notifications.title",
238
+ description_key: "collavre.comments.empty_state.cards.inbox_notifications.description",
239
+ surfaces: [ :inbox_system ],
240
+ guide: true
241
+ })
242
+
243
+ Collavre::FeatureCardRegistry.register(:inbox_reply, {
244
+ icon: "↩️",
245
+ title_key: "collavre.comments.empty_state.cards.inbox_reply.title",
246
+ description_key: "collavre.comments.empty_state.cards.inbox_reply.description",
247
+ surfaces: [ :inbox_system ],
248
+ guide: true
249
+ })
250
+
251
+ Collavre::FeatureCardRegistry.register(:inbox_source, {
252
+ icon: "🔗",
253
+ title_key: "collavre.comments.empty_state.cards.inbox_source.title",
254
+ description_key: "collavre.comments.empty_state.cards.inbox_source.description",
255
+ surfaces: [ :inbox_system ],
256
+ guide: true
257
+ })
151
258
  end
152
259
  end
153
260
 
@@ -178,7 +285,7 @@ module Collavre
178
285
  key: :home,
179
286
  label: "app.home",
180
287
  type: :button,
181
- path: -> { main_app.root_path },
288
+ path: -> { home_navigation_path }, html_class: "home-nav-button",
182
289
  priority: 110
183
290
  )
184
291
 
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Collavre
4
+ # Registry for the empty-chat "feature discovery" cards shown when a
5
+ # Creative's comment thread has no comments yet. Mirrors the
6
+ # Collavre::IntegrationRegistry pattern so vendor engines can register
7
+ # their own cards later without touching the core empty-state view.
8
+ #
9
+ # @example Registering a card
10
+ # Collavre::FeatureCardRegistry.register(:add_user, {
11
+ # icon: "user-plus",
12
+ # title_key: "collavre.comments.empty_state.cards.add_user.title",
13
+ # description_key: "collavre.comments.empty_state.cards.add_user.description",
14
+ # action: { type: :share_modal },
15
+ # guide: true
16
+ # })
17
+ class FeatureCardRegistry
18
+ include Singleton
19
+
20
+ def initialize
21
+ @cards = {}
22
+ @mutex = Mutex.new
23
+ end
24
+
25
+ # Register a new feature card
26
+ # @param key [Symbol] Unique identifier for the card
27
+ # @param config [Hash] Configuration options
28
+ # @option config [String] :icon Icon identifier (optional)
29
+ # @option config [String] :title_key i18n key for the card title
30
+ # @option config [String] :description_key i18n key for the card description
31
+ # @option config [Array<Symbol>] :surfaces Empty-state surfaces where the card appears
32
+ # (defaults to [:default])
33
+ # @option config [Hash] :action Optional { type:, label_key: } describing a call-to-action button
34
+ # @option config [Boolean] :guide Opt in to the engine-provided public guide page at
35
+ # collavre.feature_path(key). Only set this once the page's copy exists under
36
+ # collavre.features.pages.<key> — FeaturesController#show 404s for cards without it.
37
+ # @option config [String] :guide_url Optional URL overriding the engine guide page, for
38
+ # vendor engines that host their own documentation.
39
+ def register(key, config)
40
+ card = FeatureCard.new(key, config)
41
+ @mutex.synchronize do
42
+ @cards[key.to_sym] = card
43
+ end
44
+ card
45
+ end
46
+
47
+ def unregister(key)
48
+ @mutex.synchronize do
49
+ @cards.delete(key.to_sym)
50
+ end
51
+ end
52
+
53
+ def find(key)
54
+ @mutex.synchronize do
55
+ @cards[key.to_sym]
56
+ end
57
+ end
58
+
59
+ def all
60
+ @mutex.synchronize do
61
+ @cards.values
62
+ end
63
+ end
64
+
65
+ def for(creative:, topic:)
66
+ surface = if creative.inbox? && topic&.name == Creative::SYSTEM_TOPIC_NAME
67
+ :inbox_system
68
+ else
69
+ :default
70
+ end
71
+
72
+ all.select { |card| card.visible_on?(surface) }
73
+ end
74
+
75
+ # Cards that render on the public /features hub — only those opting into the
76
+ # engine-provided guide page, since a vendor card pointing at its own
77
+ # :guide_url has no page here to link to.
78
+ def with_builtin_guide
79
+ all.select(&:builtin_guide?)
80
+ end
81
+
82
+ def each(&block)
83
+ all.each(&block)
84
+ end
85
+
86
+ def any?
87
+ @mutex.synchronize do
88
+ @cards.any?
89
+ end
90
+ end
91
+
92
+ # Clear all cards (useful for testing)
93
+ def reset!
94
+ @mutex.synchronize do
95
+ @cards = {}
96
+ end
97
+ end
98
+
99
+ class << self
100
+ delegate :register, :unregister, :find, :all, :for, :with_builtin_guide, :each, :any?, :reset!, to: :instance
101
+ end
102
+ end
103
+
104
+ # Represents a registered feature card
105
+ class FeatureCard
106
+ # A card with a built-in guide has its key placed in a URL by
107
+ # collavre.feature_path, so it has to satisfy the :key constraint on the
108
+ # features route. config/routes.rb uses this verbatim; Rails rejects anchors
109
+ # in a routing requirement and anchors the segment itself, so the anchored
110
+ # form below is what validation compares against.
111
+ GUIDE_KEY_FORMAT = /[a-z0-9_]+/
112
+ GUIDE_KEY_FORMAT_ANCHORED = /\A#{GUIDE_KEY_FORMAT.source}\z/
113
+
114
+ attr_reader :key, :icon, :title_key, :description_key, :action, :guide_url, :surfaces
115
+
116
+ def initialize(key, config)
117
+ @key = key.to_sym
118
+ @icon = config[:icon]
119
+ @title_key = config[:title_key]
120
+ @description_key = config[:description_key]
121
+ @action = config[:action]
122
+ @guide = config.fetch(:guide, false)
123
+ @guide_url = config[:guide_url]
124
+ @surfaces = Array(config.fetch(:surfaces, :default)).map(&:to_sym).uniq.freeze
125
+
126
+ validate!
127
+ end
128
+
129
+ def guide_url?
130
+ @guide_url.present?
131
+ end
132
+
133
+ # True when this card is served by the engine's own /features/:key page.
134
+ # A card carrying its own :guide_url is documented elsewhere, so the engine
135
+ # neither renders nor routes a page for it.
136
+ def builtin_guide?
137
+ @guide && !guide_url?
138
+ end
139
+
140
+ # True when the empty-state card should render a "learn more" link at all.
141
+ def guide_link?
142
+ builtin_guide? || guide_url?
143
+ end
144
+
145
+ def visible_on?(surface)
146
+ @surfaces.include?(surface.to_sym)
147
+ end
148
+
149
+ private
150
+
151
+ def validate!
152
+ raise ArgumentError, "FeatureCard must have a :title_key" unless @title_key.present?
153
+ raise ArgumentError, "FeatureCard must have a :description_key" unless @description_key.present?
154
+ raise ArgumentError, "FeatureCard must have at least one :surface" if @surfaces.empty?
155
+
156
+ # Reject an unroutable key at registration rather than at render. A key the
157
+ # features route cannot accept would otherwise raise UrlGenerationError from
158
+ # feature_path, turning an extension's typo into a 500 on a user's empty
159
+ # chat and on the public hub. Fail loudly at boot, where the author sees it.
160
+ return unless @guide && !guide_url?
161
+ return if @key.to_s.match?(GUIDE_KEY_FORMAT_ANCHORED)
162
+
163
+ raise ArgumentError,
164
+ "FeatureCard key #{@key.inspect} cannot use the built-in guide page: " \
165
+ "keys must match #{GUIDE_KEY_FORMAT_ANCHORED.inspect}. Supply a :guide_url instead."
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Collavre
6
+ module HashedAccessTokenLookup
7
+ PREFIX = "sha256$"
8
+
9
+ class << self
10
+ def encode(plaintext)
11
+ "#{PREFIX}#{Digest::SHA256.hexdigest(plaintext.to_s)}"
12
+ end
13
+ end
14
+
15
+ def by_token(plaintext)
16
+ value = plaintext.to_s
17
+ return if value.start_with?(PREFIX)
18
+
19
+ super || find_by(token: HashedAccessTokenLookup.encode(value))
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/logger_silence"
4
+
5
+ module Collavre
6
+ # Prevents capability-bearing request paths from reaching any Rails log level.
7
+ # Logger::UNKNOWN + 1 is intentional: the built-in SilenceRequest defaults to
8
+ # ERROR, which still permits exception logs containing the original path.
9
+ class SensitiveRequestSilencer
10
+ SILENCE_LEVEL = ::Logger::UNKNOWN + 1
11
+
12
+ def initialize(app, path:)
13
+ @app = app
14
+ @path = path
15
+ end
16
+
17
+ def call(env)
18
+ return @app.call(env) unless @path === env["PATH_INFO"]
19
+
20
+ Rails.logger.silence(SILENCE_LEVEL) { @app.call(env) }
21
+ end
22
+ end
23
+ end
@@ -10,8 +10,6 @@ module Collavre
10
10
  has_many :collavre_shared_creative_shares, class_name: "Collavre::CreativeShare",
11
11
  foreign_key: :shared_by_id, dependent: :nullify, inverse_of: :shared_by
12
12
  has_many :collavre_topics, class_name: "Collavre::Topic", dependent: :destroy
13
- has_many :collavre_inbox_items, class_name: "Collavre::InboxItem",
14
- foreign_key: :owner_id, dependent: :destroy, inverse_of: :owner
15
13
  has_many :collavre_invitations, class_name: "Collavre::Invitation",
16
14
  foreign_key: :inviter_id, dependent: :destroy, inverse_of: :inviter
17
15
  has_many :collavre_contacts, class_name: "Collavre::Contact", dependent: :destroy
@@ -1,3 +1,3 @@
1
1
  module Collavre
2
- VERSION = "0.24.0"
2
+ VERSION = "0.25.0"
3
3
  end
data/lib/collavre.rb CHANGED
@@ -3,6 +3,7 @@ require "collavre/configuration"
3
3
  require "collavre/engine"
4
4
  require "collavre/user_extensions"
5
5
  require "collavre/integration_registry"
6
+ require "collavre/feature_card_registry"
6
7
  require "collavre/integration_settings"
7
8
  require "collavre/aws_credentials"
8
9
  require "collavre/ses_settings_interceptor"
@@ -0,0 +1,165 @@
1
+ ---
2
+ name: collavre
3
+ description: Manage Collavre Creatives (hierarchical tasks/content blocks) via CLI. Use when creating, retrieving, updating, importing, or batch-operating on Creatives. Creatives are tree-structured items with automatic progress rollup — like a smart to-do list that doubles as a structured document.
4
+ ---
5
+
6
+ # Collavre
7
+
8
+ CLI for managing Collavre Creatives via MCP protocol.
9
+
10
+ ## Setup
11
+
12
+ ```bash
13
+ # Configure (once)
14
+ collavre auth --url https://collavre.example.com --token <oauth_token>
15
+
16
+ # Verify
17
+ collavre list
18
+ ```
19
+
20
+ The `collavre` script lives in `scripts/collavre`. Config is stored at `~/.config/collavre/config.json`.
21
+
22
+ ## Commands
23
+
24
+ ### List root creatives
25
+ ```bash
26
+ collavre list
27
+ collavre list --level 5 # deeper tree
28
+ collavre list --format json # structured output
29
+ ```
30
+
31
+ ### Get a creative subtree
32
+ ```bash
33
+ collavre get 123
34
+ collavre get 123 --level 5 --comments
35
+ ```
36
+
37
+ ### Search
38
+ ```bash
39
+ collavre search "project name"
40
+ collavre search "urgent" --tags "v2"
41
+ ```
42
+
43
+ ### Create
44
+ ```bash
45
+ collavre create --parent 123 --desc "New task"
46
+ collavre create --parent 123 --desc "# Rich content\n\n- point one\n- point two"
47
+ ```
48
+
49
+ ### Update
50
+ ```bash
51
+ collavre update 456 --desc "Updated title"
52
+ collavre update 456 --progress 1.0 # mark complete (leaf only)
53
+ collavre update 456 --parent 789 # move
54
+ ```
55
+
56
+ ### Import markdown
57
+ ```bash
58
+ collavre import --parent 123 --file plan.md
59
+ collavre import --parent 123 --stdin < plan.md
60
+ echo "# Quick\n## Plan" | collavre import --parent 123 --stdin
61
+ ```
62
+
63
+ ### Attach a file
64
+ ```bash
65
+ collavre attach --creative 123 --file ./hero.png # png/mp4/pdf/svg
66
+ ```
67
+ Uploads the raw bytes over a bearer multipart endpoint, embeds the matching
68
+ node (`<img>`/`<video>`/`<a>`) into the creative's description, and attaches the
69
+ blob to `creative.files`. Use this for binary files on disk. For inline
70
+ agent-generated text (markdown/html/svg source), use the
71
+ `creative_attach_files_service` tool with `files: [{ filename, content }]`.
72
+
73
+ ### Batch operations
74
+ ```bash
75
+ collavre batch --file ops.json
76
+ ```
77
+
78
+ ops.json format:
79
+ ```json
80
+ [
81
+ { "action": "create", "parent_id": 100, "description": "Task A" },
82
+ { "action": "update", "id": 200, "progress": 1.0 },
83
+ { "action": "delete", "id": 300 }
84
+ ]
85
+ ```
86
+
87
+ ### Topics and messages
88
+
89
+ Topics are conversation threads on a Creative — and the unit of agent
90
+ concurrency. Tasks in the same topic are serialized (one runs, the rest queue);
91
+ tasks in different topics run in parallel. There is no dedicated CLI verb yet;
92
+ drive them through `collavre tool run`:
93
+
94
+ ```bash
95
+ # See what topics exist and how much conversation each holds
96
+ collavre tool run topic_list --json '{"creative_id": 8849}'
97
+
98
+ # Summarize three topics at once — first page only; offset/limit apply per topic
99
+ collavre tool run topic_messages --json '{"topic_ids": "12,45,78", "limit": 100}'
100
+
101
+ # Continue each topic separately with its own cursor and snapshot id
102
+ collavre tool run topic_messages --json '{"topic_ids": 12, "offset": 100, "cursor": "1770000000000000:989:1770000001000000:1770000002000000", "max_message_id": 9931}'
103
+
104
+ # Continue inside one clipped message, preserving its row cursor
105
+ collavre tool run topic_messages --json '{"topic_ids": 12, "offset": 100, "cursor": "1770000000000000:989:1770000001000000:1770000002000000", "content_offset": 28400, "max_message_id": 9931}'
106
+
107
+ # Fan work out: one topic per unit of work, with an agent pinned to each;
108
+ # keep each returned topic id for the initial-message calls
109
+ collavre tool run topic_create --json '{"creative_id": 8849, "name": "Research", "primary_agent": "Dev1Claude"}'
110
+ collavre tool run topic_create --json '{"creative_id": 8849, "name": "Delivery", "primary_agent": "Dev1Claude"}'
111
+
112
+ # Start the pinned agent independently in both topics
113
+ collavre tool run topic_message_create --json '{"topic_id": 12, "content": "Research the API options and report your recommendation."}'
114
+ collavre tool run topic_message_create --json '{"topic_id": 13, "content": "Build the selected approach and verify it."}'
115
+
116
+ # Put a finished thread away (messages are kept and stay readable by id)
117
+ collavre tool run topic_update --json '{"topic_id": 12, "archived": true}'
118
+
119
+ # Move a complete thread, including its messages and read cursors
120
+ collavre tool run topic_move --json '{"topic_id": 13, "creative_id": 9023}'
121
+
122
+ # Carry the messages that still matter into a fresh topic
123
+ collavre tool run topic_branch --json '{"source_topic_id": 12, "comment_ids": "991,994"}'
124
+ ```
125
+
126
+ `topic_messages` includes each comment image as a URL-bearing marker in the
127
+ message content. Image-only comments are not blank, and a long attachment list
128
+ uses the same `content_offset` continuation as long prose.
129
+
130
+ ### Discover & run tools (meta)
131
+ ```bash
132
+ collavre tool list # list available tools
133
+ collavre tool list --full # list with full details
134
+ collavre tool search "github" # search by name/description
135
+ collavre tool info <tool_name> # show tool parameters
136
+ collavre tool run <tool_name> --json '{"k":"v"}' # run with JSON args
137
+ collavre tool run <tool_name> --key value # run with flag args
138
+ ```
139
+
140
+ ## Key Concepts
141
+
142
+ - **Tree structure**: Creatives nest via `parent_id`. Use `--level` to control depth.
143
+ - **Topics**: conversation threads on a Creative, and the concurrency unit — same
144
+ topic serializes, different topics run in parallel. One topic = one thread
145
+ reaching one conclusion; archive it when it gets there rather than reusing it.
146
+ - **Primary agent**: pinning an agent to a topic is exclusive — that agent alone
147
+ answers there. Use it for a dedicated 1:1 channel; leave it unset for a thread
148
+ several participants should hear.
149
+ - **Starting topic work**: after creating and pinning a topic, call
150
+ `topic_message_create` with its initial instruction. Agent-authored messages
151
+ dispatch as A2A work, so one coordinating agent can start independent work in
152
+ several topics without impersonating a human. Do not call it on the topic the
153
+ caller is currently handling; continue that work in the current turn instead.
154
+ - **Moving topics**: `topic_move` keeps the thread and its messages together,
155
+ but Creative permissions stay with each Creative. Verify that participants
156
+ can access the destination after moving. Wait for or cancel active agent work
157
+ in the topic before moving it, and wait for dropped-message restoration to
158
+ settle. Cancel recurring cron jobs that target the topic and recreate them
159
+ after the move. Topics referenced by a Drop Trigger loop cannot move.
160
+ - **Progress**: Leaf = manual (0.0 or 1.0). Parent = auto-calculated from children.
161
+ - **Import**: Markdown headings/bullets become nested Creatives.
162
+ - **Batch**: All-or-nothing transaction. Requires approval.
163
+ - **Description format**: Written as Markdown (GitHub-Flavored: headings, bold/italic, lists, links, tables, code blocks, task lists). A single newline is a line break. Plain text is stored as-is.
164
+
165
+ For detailed MCP tool parameters, see [references/tool-reference.md](references/tool-reference.md).
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "collavre-cli",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "collavre": "./scripts/collavre"
7
+ }
8
+ }