protege 0.1.0.alpha.2 → 0.1.0.alpha.7
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +21 -0
- data/Rakefile +1 -3
- data/app/assets/builds/protege.css +76 -83
- data/app/controllers/concerns/protege/agent_scoped.rb +22 -0
- data/app/controllers/concerns/protege/agent_toolkit_scoped.rb +22 -0
- data/app/controllers/protege/access_rules_controller.rb +13 -13
- data/app/controllers/protege/agent_toolkit_rules_controller.rb +62 -0
- data/app/controllers/protege/agent_toolkits_controller.rb +44 -0
- data/app/controllers/protege/agents/agent_toolkits_controller.rb +65 -0
- data/app/controllers/protege/agents_controller.rb +118 -0
- data/app/controllers/protege/archives_controller.rb +18 -18
- data/app/controllers/protege/home_controller.rb +1 -1
- data/app/controllers/protege/messages_controller.rb +1 -1
- data/app/controllers/protege/replies_controller.rb +3 -3
- data/app/controllers/protege/responsibilities_controller.rb +7 -7
- data/app/controllers/protege/threads_controller.rb +4 -4
- data/app/controllers/protege/toolkits_controller.rb +84 -0
- data/app/helpers/protege/agents_helper.rb +75 -0
- data/app/helpers/protege/application_helper.rb +3 -2
- data/app/helpers/protege/components/badges_helper.rb +11 -2
- data/app/helpers/protege/components/forms_helper.rb +47 -12
- data/app/helpers/protege/components/typography_helper.rb +1 -1
- data/app/helpers/protege/messages_helper.rb +2 -2
- data/app/helpers/protege/responsibilities_helper.rb +2 -2
- data/app/helpers/protege/threads_helper.rb +11 -11
- data/app/helpers/protege/toolkits_helper.rb +66 -0
- data/app/hooks/protege/event_logger_hook.rb +8 -8
- data/app/jobs/protege/console_inference_job.rb +8 -8
- data/app/jobs/protege/inference_job.rb +11 -11
- data/app/jobs/protege/responsibility_job.rb +10 -10
- data/app/mailboxes/protege/agent_mailbox.rb +54 -29
- data/app/mailers/protege/alert_mailer.rb +41 -0
- data/app/mailers/protege/application_mailer.rb +9 -8
- data/app/models/concerns/protege/broadcastable_agent.rb +50 -0
- data/app/models/concerns/protege/broadcastable_toolkit.rb +49 -0
- data/app/models/concerns/protege/toolable.rb +108 -0
- data/app/models/protege/access_rule.rb +12 -12
- data/app/models/protege/{persona.rb → agent.rb} +45 -45
- data/app/models/protege/agent_toolkit.rb +50 -0
- data/app/models/protege/agent_toolkit_rule.rb +25 -0
- data/app/models/protege/application_record.rb +1 -1
- data/app/models/protege/email_thread.rb +16 -13
- data/app/models/protege/message.rb +36 -20
- data/app/models/protege/responsibility.rb +12 -12
- data/app/models/protege/tool_use.rb +6 -6
- data/app/models/protege/toolkit.rb +115 -0
- data/app/models/protege/trace.rb +1 -1
- data/app/providers/protege/open_router_provider.rb +17 -0
- data/app/resolvers/README.md +4 -4
- data/app/resolvers/protege/load_file_resolver.rb +5 -5
- data/app/resolvers/protege/load_text_resolver.rb +3 -3
- data/app/resolvers/protege/thread_history_resolver.rb +1 -1
- data/app/services/protege/message_search.rb +2 -2
- data/app/services/protege/system_toolkits.rb +67 -0
- data/app/tools/protege/create_file_tool.rb +2 -0
- data/app/tools/protege/read_attachment_tool.rb +3 -1
- data/app/tools/protege/search_emails_tool.rb +15 -13
- data/app/tools/protege/send_email_tool.rb +60 -18
- data/app/tools/protege/web_fetch_tool.rb +2 -0
- data/app/tools/protege/web_search_tool.rb +3 -1
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app/views/protege/access_rules/create.turbo_stream.slim +3 -3
- data/app/views/protege/agent_toolkit_rules/create.turbo_stream.slim +6 -0
- data/app/views/protege/agent_toolkits/_gate_rule.html.slim +7 -0
- data/app/views/protege/agent_toolkits/_gate_rule_form.html.slim +8 -0
- data/app/views/protege/agent_toolkits/_gate_rules.html.slim +19 -0
- data/app/views/protege/agent_toolkits/show.html.slim +17 -0
- data/app/views/protege/{personas → agents}/_access_rule.html.slim +1 -1
- data/app/views/protege/{personas → agents}/_access_rule_form.html.slim +1 -1
- data/app/views/protege/{personas → agents}/_access_rules.html.slim +5 -5
- data/app/views/protege/agents/_actions.html.slim +15 -0
- data/app/views/protege/agents/_agent_sidebar_item.html.slim +5 -0
- data/app/views/protege/agents/_agent_toolkit.html.slim +7 -0
- data/app/views/protege/agents/_agent_toolkit_form.html.slim +7 -0
- data/app/views/protege/agents/_form.html.slim +25 -0
- data/app/views/protege/agents/_sidebar.html.slim +11 -0
- data/app/views/protege/agents/_toolkits.html.slim +19 -0
- data/app/views/protege/agents/agent_toolkits/create.turbo_stream.slim +6 -0
- data/app/views/protege/agents/edit.html.slim +7 -0
- data/app/views/protege/agents/index.html.slim +6 -0
- data/app/views/protege/agents/new.html.slim +7 -0
- data/app/views/protege/agents/show.html.slim +29 -0
- data/app/views/protege/alert_mailer/inference_failed.text.erb +4 -0
- data/app/views/protege/email_domains/show.html.slim +1 -1
- data/app/views/protege/home/show.html.slim +13 -13
- data/app/views/protege/responsibilities/_form.html.slim +2 -2
- data/app/views/protege/responsibilities/_responsibility_sidebar_item.html.slim +1 -1
- data/app/views/protege/responsibilities/new.html.slim +1 -1
- data/app/views/protege/responsibilities/show.html.slim +2 -2
- data/app/views/protege/responsibility_runs/show.html.slim +1 -1
- data/app/views/protege/shared/_header.html.slim +2 -1
- data/app/views/protege/shared/_hotkeys.html.slim +15 -11
- data/app/views/protege/threads/new.html.slim +2 -2
- data/app/views/protege/toolkits/_form.html.slim +13 -0
- data/app/views/protege/toolkits/_sidebar.html.slim +11 -0
- data/app/views/protege/toolkits/_toolkit_sidebar_item.html.slim +5 -0
- data/app/views/protege/toolkits/edit.html.slim +7 -0
- data/app/views/protege/toolkits/index.html.slim +6 -0
- data/app/views/protege/toolkits/new.html.slim +7 -0
- data/app/views/protege/toolkits/show.html.slim +13 -0
- data/config/routes.rb +12 -1
- data/db/migrate/20260707120000_replace_persona_active_with_archived_at.rb +1 -1
- data/db/migrate/20260708120000_add_disabled_tool_ids_to_personas.rb +1 -1
- data/db/migrate/20260728140000_scope_message_and_thread_uniqueness_per_persona.rb +16 -0
- data/db/migrate/20260812000001_create_protege_toolkits.rb +37 -0
- data/db/migrate/20260815000001_remove_disabled_tool_ids_from_personas.rb +21 -0
- data/db/migrate/20260815000002_add_key_to_protege_toolkits.rb +22 -0
- data/db/migrate/20260815100000_rename_personas_to_agents.rb +24 -0
- data/lib/generators/protege/agent/agent_generator.rb +35 -0
- data/lib/generators/protege/{persona/templates/persona.rb.tt → agent/templates/agent.rb.tt} +9 -9
- data/lib/generators/protege/extension_naming.rb +1 -1
- data/lib/generators/protege/hook/templates/hook.rb.tt +2 -2
- data/lib/generators/protege/install/install_generator.rb +16 -11
- data/lib/generators/protege/install/templates/initializer.rb.tt +17 -6
- data/lib/generators/protege/postfix/templates/deploy/mail/MAIL.md +2 -2
- data/lib/generators/protege/resolver/resolver_generator.rb +2 -2
- data/lib/generators/protege/resolver/templates/resolver.rb.tt +4 -4
- data/lib/generators/protege/tool/templates/tool.rb.tt +7 -5
- data/lib/protege/configuration.rb +43 -8
- data/lib/protege/engine.rb +3 -1
- data/lib/protege/errors/tool_not_available_error.rb +7 -6
- data/lib/protege/events/event.rb +2 -2
- data/lib/protege/events/inference_chunk_event.rb +1 -1
- data/lib/protege/events/inference_completed_event.rb +1 -1
- data/lib/protege/events/inference_failed_event.rb +1 -1
- data/lib/protege/events/inference_generated_event.rb +1 -1
- data/lib/protege/events/inference_max_turns_reached_event.rb +1 -1
- data/lib/protege/events/inference_started_event.rb +1 -1
- data/lib/protege/events/loop_run_completed_event.rb +1 -1
- data/lib/protege/events/loop_run_enqueued_event.rb +1 -1
- data/lib/protege/events/loop_run_failed_event.rb +1 -1
- data/lib/protege/events/loop_run_started_event.rb +1 -1
- data/lib/protege/events/tool_call_completed_event.rb +1 -1
- data/lib/protege/events/tool_call_failed_event.rb +1 -1
- data/lib/protege/events/tool_call_started_event.rb +1 -1
- data/lib/protege/events/tool_calls_received_event.rb +1 -1
- data/lib/protege/extensions/hook.rb +1 -0
- data/lib/protege/extensions/hook_mixin.rb +1 -1
- data/lib/protege/extensions/manifest.rb +67 -0
- data/lib/protege/extensions/manifested.rb +81 -0
- data/lib/protege/extensions/provider.rb +1 -0
- data/lib/protege/extensions/provider_mixin.rb +9 -17
- data/lib/protege/extensions/resolver.rb +2 -1
- data/lib/protege/extensions/resolver_mixin.rb +2 -2
- data/lib/protege/extensions/tool.rb +1 -0
- data/lib/protege/extensions/tool_mixin.rb +16 -40
- data/lib/protege/gateway/access_control.rb +19 -19
- data/lib/protege/gateway/access_policy.rb +2 -2
- data/lib/protege/gateway/mail/outbound.rb +22 -5
- data/lib/protege/gateway.rb +48 -37
- data/lib/protege/loop/scheduler.rb +1 -1
- data/lib/protege/orchestrator/context.rb +8 -8
- data/lib/protege/orchestrator/harness.rb +26 -24
- data/lib/protege/orchestrator/reply_context.rb +3 -3
- data/lib/protege/orchestrator/reply_harness.rb +8 -8
- data/lib/protege/orchestrator/resolver_chain.rb +3 -3
- data/lib/protege/orchestrator/responsibility_context.rb +4 -4
- data/lib/protege/orchestrator/responsibility_harness.rb +8 -8
- data/lib/protege/orchestrator.rb +7 -7
- data/lib/protege/subscribers/alerter.rb +117 -0
- data/lib/protege/subscribers/tracing.rb +1 -1
- data/lib/protege/version.rb +1 -1
- data/lib/protege.rb +2 -2
- data/lib/tasks/protege_tasks.rake +12 -0
- metadata +66 -23
- data/app/controllers/concerns/protege/persona_scoped.rb +0 -22
- data/app/controllers/protege/personas_controller.rb +0 -118
- data/app/helpers/protege/personas_helper.rb +0 -90
- data/app/models/concerns/protege/broadcastable_persona.rb +0 -50
- data/app/models/concerns/protege/tool_scoped.rb +0 -88
- data/app/views/protege/personas/_actions.html.slim +0 -15
- data/app/views/protege/personas/_form.html.slim +0 -29
- data/app/views/protege/personas/_persona_sidebar_item.html.slim +0 -5
- data/app/views/protege/personas/_sidebar.html.slim +0 -11
- data/app/views/protege/personas/edit.html.slim +0 -7
- data/app/views/protege/personas/index.html.slim +0 -6
- data/app/views/protege/personas/new.html.slim +0 -7
- data/app/views/protege/personas/show.html.slim +0 -37
- data/lib/generators/protege/persona/persona_generator.rb +0 -35
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protege
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0.alpha.
|
|
4
|
+
version: 0.1.0.alpha.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sebastian Scholl
|
|
@@ -205,14 +205,17 @@ dependencies:
|
|
|
205
205
|
- - "~>"
|
|
206
206
|
- !ruby/object:Gem::Version
|
|
207
207
|
version: '3.0'
|
|
208
|
-
description: Protege
|
|
209
|
-
|
|
208
|
+
description: 'Protege gives your Rails app AI teammates: inbound mail via Action Mailbox,
|
|
209
|
+
replies over SMTP, tools that act on your own models, cron-scheduled responsibilities
|
|
210
|
+
dispatched through Active Job, and a built-in dashboard to manage and observe it
|
|
211
|
+
all — in process, no separate service.'
|
|
210
212
|
email:
|
|
211
213
|
- sebscholl@gmail.com
|
|
212
214
|
executables: []
|
|
213
215
|
extensions: []
|
|
214
216
|
extra_rdoc_files: []
|
|
215
217
|
files:
|
|
218
|
+
- MIT-LICENSE
|
|
216
219
|
- README.md
|
|
217
220
|
- Rakefile
|
|
218
221
|
- app/assets/builds/protege.css
|
|
@@ -224,22 +227,28 @@ files:
|
|
|
224
227
|
- app/assets/javascripts/protege/theme.js
|
|
225
228
|
- app/assets/stylesheets/protege/application.css
|
|
226
229
|
- app/assets/tailwind/application.css
|
|
227
|
-
- app/controllers/concerns/protege/
|
|
230
|
+
- app/controllers/concerns/protege/agent_scoped.rb
|
|
231
|
+
- app/controllers/concerns/protege/agent_toolkit_scoped.rb
|
|
228
232
|
- app/controllers/concerns/protege/thread_scoped.rb
|
|
229
233
|
- app/controllers/protege/access_rules_controller.rb
|
|
234
|
+
- app/controllers/protege/agent_toolkit_rules_controller.rb
|
|
235
|
+
- app/controllers/protege/agent_toolkits_controller.rb
|
|
236
|
+
- app/controllers/protege/agents/agent_toolkits_controller.rb
|
|
237
|
+
- app/controllers/protege/agents_controller.rb
|
|
230
238
|
- app/controllers/protege/application_controller.rb
|
|
231
239
|
- app/controllers/protege/archives_controller.rb
|
|
232
240
|
- app/controllers/protege/email_domains_controller.rb
|
|
233
241
|
- app/controllers/protege/home_controller.rb
|
|
234
242
|
- app/controllers/protege/messages_controller.rb
|
|
235
243
|
- app/controllers/protege/mta_domains_syncs_controller.rb
|
|
236
|
-
- app/controllers/protege/personas_controller.rb
|
|
237
244
|
- app/controllers/protege/replies_controller.rb
|
|
238
245
|
- app/controllers/protege/responsibilities_controller.rb
|
|
239
246
|
- app/controllers/protege/responsibility_runs_controller.rb
|
|
240
247
|
- app/controllers/protege/threads_controller.rb
|
|
248
|
+
- app/controllers/protege/toolkits_controller.rb
|
|
241
249
|
- app/controllers/protege/traces/searches_controller.rb
|
|
242
250
|
- app/controllers/protege/traces_controller.rb
|
|
251
|
+
- app/helpers/protege/agents_helper.rb
|
|
243
252
|
- app/helpers/protege/application_helper.rb
|
|
244
253
|
- app/helpers/protege/components/badges_helper.rb
|
|
245
254
|
- app/helpers/protege/components/buttons_helper.rb
|
|
@@ -258,9 +267,9 @@ files:
|
|
|
258
267
|
- app/helpers/protege/components/typography_helper.rb
|
|
259
268
|
- app/helpers/protege/email_domains_helper.rb
|
|
260
269
|
- app/helpers/protege/messages_helper.rb
|
|
261
|
-
- app/helpers/protege/personas_helper.rb
|
|
262
270
|
- app/helpers/protege/responsibilities_helper.rb
|
|
263
271
|
- app/helpers/protege/threads_helper.rb
|
|
272
|
+
- app/helpers/protege/toolkits_helper.rb
|
|
264
273
|
- app/helpers/protege/trace_helper.rb
|
|
265
274
|
- app/hooks/protege/event_logger_hook.rb
|
|
266
275
|
- app/jobs/protege/application_job.rb
|
|
@@ -270,23 +279,28 @@ files:
|
|
|
270
279
|
- app/jobs/protege/responsibility_job.rb
|
|
271
280
|
- app/jobs/protege/responsibility_tick_job.rb
|
|
272
281
|
- app/mailboxes/protege/agent_mailbox.rb
|
|
282
|
+
- app/mailers/protege/alert_mailer.rb
|
|
273
283
|
- app/mailers/protege/application_mailer.rb
|
|
284
|
+
- app/models/concerns/protege/broadcastable_agent.rb
|
|
274
285
|
- app/models/concerns/protege/broadcastable_email_domain.rb
|
|
275
286
|
- app/models/concerns/protege/broadcastable_message.rb
|
|
276
|
-
- app/models/concerns/protege/broadcastable_persona.rb
|
|
277
287
|
- app/models/concerns/protege/broadcastable_responsibility.rb
|
|
278
288
|
- app/models/concerns/protege/broadcastable_thread.rb
|
|
279
|
-
- app/models/concerns/protege/
|
|
289
|
+
- app/models/concerns/protege/broadcastable_toolkit.rb
|
|
290
|
+
- app/models/concerns/protege/toolable.rb
|
|
280
291
|
- app/models/protege/access_rule.rb
|
|
292
|
+
- app/models/protege/agent.rb
|
|
293
|
+
- app/models/protege/agent_toolkit.rb
|
|
294
|
+
- app/models/protege/agent_toolkit_rule.rb
|
|
281
295
|
- app/models/protege/application_record.rb
|
|
282
296
|
- app/models/protege/current.rb
|
|
283
297
|
- app/models/protege/email_domain.rb
|
|
284
298
|
- app/models/protege/email_thread.rb
|
|
285
299
|
- app/models/protege/message.rb
|
|
286
|
-
- app/models/protege/persona.rb
|
|
287
300
|
- app/models/protege/responsibility.rb
|
|
288
301
|
- app/models/protege/responsibility_run.rb
|
|
289
302
|
- app/models/protege/tool_use.rb
|
|
303
|
+
- app/models/protege/toolkit.rb
|
|
290
304
|
- app/models/protege/trace.rb
|
|
291
305
|
- app/providers/protege/open_router_provider.rb
|
|
292
306
|
- app/resolvers/README.md
|
|
@@ -296,6 +310,7 @@ files:
|
|
|
296
310
|
- app/resolvers/protege/thread_history_resolver.rb
|
|
297
311
|
- app/services/protege/introspection_tool_call_presentation.rb
|
|
298
312
|
- app/services/protege/message_search.rb
|
|
313
|
+
- app/services/protege/system_toolkits.rb
|
|
299
314
|
- app/services/protege/trace_search.rb
|
|
300
315
|
- app/subscribers/protege/introspection_broadcaster.rb
|
|
301
316
|
- app/tools/protege/create_file_tool.rb
|
|
@@ -304,8 +319,30 @@ files:
|
|
|
304
319
|
- app/tools/protege/send_email_tool.rb
|
|
305
320
|
- app/tools/protege/web_fetch_tool.rb
|
|
306
321
|
- app/tools/protege/web_search_tool.rb
|
|
322
|
+
- app/views/layouts/mailer.text.erb
|
|
307
323
|
- app/views/layouts/protege/application.html.slim
|
|
308
324
|
- app/views/protege/access_rules/create.turbo_stream.slim
|
|
325
|
+
- app/views/protege/agent_toolkit_rules/create.turbo_stream.slim
|
|
326
|
+
- app/views/protege/agent_toolkits/_gate_rule.html.slim
|
|
327
|
+
- app/views/protege/agent_toolkits/_gate_rule_form.html.slim
|
|
328
|
+
- app/views/protege/agent_toolkits/_gate_rules.html.slim
|
|
329
|
+
- app/views/protege/agent_toolkits/show.html.slim
|
|
330
|
+
- app/views/protege/agents/_access_rule.html.slim
|
|
331
|
+
- app/views/protege/agents/_access_rule_form.html.slim
|
|
332
|
+
- app/views/protege/agents/_access_rules.html.slim
|
|
333
|
+
- app/views/protege/agents/_actions.html.slim
|
|
334
|
+
- app/views/protege/agents/_agent_sidebar_item.html.slim
|
|
335
|
+
- app/views/protege/agents/_agent_toolkit.html.slim
|
|
336
|
+
- app/views/protege/agents/_agent_toolkit_form.html.slim
|
|
337
|
+
- app/views/protege/agents/_form.html.slim
|
|
338
|
+
- app/views/protege/agents/_sidebar.html.slim
|
|
339
|
+
- app/views/protege/agents/_toolkits.html.slim
|
|
340
|
+
- app/views/protege/agents/agent_toolkits/create.turbo_stream.slim
|
|
341
|
+
- app/views/protege/agents/edit.html.slim
|
|
342
|
+
- app/views/protege/agents/index.html.slim
|
|
343
|
+
- app/views/protege/agents/new.html.slim
|
|
344
|
+
- app/views/protege/agents/show.html.slim
|
|
345
|
+
- app/views/protege/alert_mailer/inference_failed.text.erb
|
|
309
346
|
- app/views/protege/email_domains/_email_domain_sidebar_item.html.slim
|
|
310
347
|
- app/views/protege/email_domains/_form.html.slim
|
|
311
348
|
- app/views/protege/email_domains/_sidebar.html.slim
|
|
@@ -316,17 +353,6 @@ files:
|
|
|
316
353
|
- app/views/protege/messages/_message.html.slim
|
|
317
354
|
- app/views/protege/messages/_sidebar.html.slim
|
|
318
355
|
- app/views/protege/messages/index.html.slim
|
|
319
|
-
- app/views/protege/personas/_access_rule.html.slim
|
|
320
|
-
- app/views/protege/personas/_access_rule_form.html.slim
|
|
321
|
-
- app/views/protege/personas/_access_rules.html.slim
|
|
322
|
-
- app/views/protege/personas/_actions.html.slim
|
|
323
|
-
- app/views/protege/personas/_form.html.slim
|
|
324
|
-
- app/views/protege/personas/_persona_sidebar_item.html.slim
|
|
325
|
-
- app/views/protege/personas/_sidebar.html.slim
|
|
326
|
-
- app/views/protege/personas/edit.html.slim
|
|
327
|
-
- app/views/protege/personas/index.html.slim
|
|
328
|
-
- app/views/protege/personas/new.html.slim
|
|
329
|
-
- app/views/protege/personas/show.html.slim
|
|
330
356
|
- app/views/protege/responsibilities/_form.html.slim
|
|
331
357
|
- app/views/protege/responsibilities/_responsibility_sidebar_item.html.slim
|
|
332
358
|
- app/views/protege/responsibilities/_sidebar.html.slim
|
|
@@ -349,6 +375,13 @@ files:
|
|
|
349
375
|
- app/views/protege/threads/index.html.slim
|
|
350
376
|
- app/views/protege/threads/new.html.slim
|
|
351
377
|
- app/views/protege/threads/show.html.slim
|
|
378
|
+
- app/views/protege/toolkits/_form.html.slim
|
|
379
|
+
- app/views/protege/toolkits/_sidebar.html.slim
|
|
380
|
+
- app/views/protege/toolkits/_toolkit_sidebar_item.html.slim
|
|
381
|
+
- app/views/protege/toolkits/edit.html.slim
|
|
382
|
+
- app/views/protege/toolkits/index.html.slim
|
|
383
|
+
- app/views/protege/toolkits/new.html.slim
|
|
384
|
+
- app/views/protege/toolkits/show.html.slim
|
|
352
385
|
- app/views/protege/traces/_sidebar.html.slim
|
|
353
386
|
- app/views/protege/traces/index.html.slim
|
|
354
387
|
- app/views/protege/traces/searches/_sidebar.html.slim
|
|
@@ -359,13 +392,18 @@ files:
|
|
|
359
392
|
- db/migrate/20260706120130_create_protege_traces.rb
|
|
360
393
|
- db/migrate/20260707120000_replace_persona_active_with_archived_at.rb
|
|
361
394
|
- db/migrate/20260708120000_add_disabled_tool_ids_to_personas.rb
|
|
395
|
+
- db/migrate/20260728140000_scope_message_and_thread_uniqueness_per_persona.rb
|
|
396
|
+
- db/migrate/20260812000001_create_protege_toolkits.rb
|
|
397
|
+
- db/migrate/20260815000001_remove_disabled_tool_ids_from_personas.rb
|
|
398
|
+
- db/migrate/20260815000002_add_key_to_protege_toolkits.rb
|
|
399
|
+
- db/migrate/20260815100000_rename_personas_to_agents.rb
|
|
400
|
+
- lib/generators/protege/agent/agent_generator.rb
|
|
401
|
+
- lib/generators/protege/agent/templates/agent.rb.tt
|
|
362
402
|
- lib/generators/protege/extension_naming.rb
|
|
363
403
|
- lib/generators/protege/hook/hook_generator.rb
|
|
364
404
|
- lib/generators/protege/hook/templates/hook.rb.tt
|
|
365
405
|
- lib/generators/protege/install/install_generator.rb
|
|
366
406
|
- lib/generators/protege/install/templates/initializer.rb.tt
|
|
367
|
-
- lib/generators/protege/persona/persona_generator.rb
|
|
368
|
-
- lib/generators/protege/persona/templates/persona.rb.tt
|
|
369
407
|
- lib/generators/protege/postfix/postfix_generator.rb
|
|
370
408
|
- lib/generators/protege/postfix/templates/deploy/mail/Dockerfile
|
|
371
409
|
- lib/generators/protege/postfix/templates/deploy/mail/KeyTable
|
|
@@ -425,6 +463,8 @@ files:
|
|
|
425
463
|
- lib/protege/events/tool_calls_received_event.rb
|
|
426
464
|
- lib/protege/extensions/hook.rb
|
|
427
465
|
- lib/protege/extensions/hook_mixin.rb
|
|
466
|
+
- lib/protege/extensions/manifest.rb
|
|
467
|
+
- lib/protege/extensions/manifested.rb
|
|
428
468
|
- lib/protege/extensions/provider.rb
|
|
429
469
|
- lib/protege/extensions/provider_mixin.rb
|
|
430
470
|
- lib/protege/extensions/resolver.rb
|
|
@@ -455,6 +495,7 @@ files:
|
|
|
455
495
|
- lib/protege/orchestrator/resolver_chain.rb
|
|
456
496
|
- lib/protege/orchestrator/responsibility_context.rb
|
|
457
497
|
- lib/protege/orchestrator/responsibility_harness.rb
|
|
498
|
+
- lib/protege/subscribers/alerter.rb
|
|
458
499
|
- lib/protege/subscribers/hook_dispatcher.rb
|
|
459
500
|
- lib/protege/subscribers/tracing.rb
|
|
460
501
|
- lib/protege/types/document_part.rb
|
|
@@ -473,6 +514,8 @@ metadata:
|
|
|
473
514
|
homepage_uri: https://github.com/sebscholl/protege
|
|
474
515
|
source_code_uri: https://github.com/sebscholl/protege
|
|
475
516
|
bug_tracker_uri: https://github.com/sebscholl/protege/issues
|
|
517
|
+
documentation_uri: https://docs.protege.bot
|
|
518
|
+
changelog_uri: https://github.com/sebscholl/protege/blob/main/CHANGELOG.md
|
|
476
519
|
rubygems_mfa_required: 'true'
|
|
477
520
|
rdoc_options: []
|
|
478
521
|
require_paths:
|
|
@@ -490,5 +533,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
490
533
|
requirements: []
|
|
491
534
|
rubygems_version: 3.6.9
|
|
492
535
|
specification_version: 4
|
|
493
|
-
summary:
|
|
536
|
+
summary: Rails engine for email-native AI agents.
|
|
494
537
|
test_files: []
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Protege
|
|
4
|
-
# Shared concern for controllers nested under a persona.
|
|
5
|
-
# Sets +@persona+ from +params[:persona_id]+.
|
|
6
|
-
module PersonaScoped
|
|
7
|
-
extend ActiveSupport::Concern
|
|
8
|
-
|
|
9
|
-
included do
|
|
10
|
-
before_action :set_persona
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
# Load the +Persona+ named by +params[:persona_id]+ into +@persona+.
|
|
16
|
-
#
|
|
17
|
-
# @return [Protege::Persona] the loaded persona
|
|
18
|
-
def set_persona
|
|
19
|
-
@persona = Persona.find(params[:persona_id])
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Protege
|
|
4
|
-
# Manages persona records — listing, creating, viewing, editing, and permanent deletion.
|
|
5
|
-
#
|
|
6
|
-
# A persona is the identity behind an inbox; the controller surfaces the full CRUD the dashboard
|
|
7
|
-
# needs. +destroy+ is the hard, irreversible removal (cascading all the persona's data); the
|
|
8
|
-
# reversible, history-preserving alternative is archiving — see +ArchivesController+.
|
|
9
|
-
class PersonasController < ApplicationController
|
|
10
|
-
before_action :set_persona, only: %i[show edit update destroy]
|
|
11
|
-
|
|
12
|
-
# List every persona. Serves +GET /personas+.
|
|
13
|
-
#
|
|
14
|
-
# @return [void]
|
|
15
|
-
def index
|
|
16
|
-
@personas = Persona.all
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Show a single persona. Serves +GET /personas/:id+.
|
|
20
|
-
#
|
|
21
|
-
# @return [void]
|
|
22
|
-
def show; end
|
|
23
|
-
|
|
24
|
-
# Render the form for creating a persona. Serves +GET /personas/new+.
|
|
25
|
-
#
|
|
26
|
-
# @return [void]
|
|
27
|
-
def new
|
|
28
|
-
@persona = Persona.new
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Render the form for editing a persona. Serves +GET /personas/:id/edit+.
|
|
32
|
-
#
|
|
33
|
-
# @return [void]
|
|
34
|
-
def edit; end
|
|
35
|
-
|
|
36
|
-
# Create a persona from the form's split email fields. Serves +POST /personas+.
|
|
37
|
-
#
|
|
38
|
-
# On failure, re-render the form with validation errors.
|
|
39
|
-
#
|
|
40
|
-
# @return [void]
|
|
41
|
-
def create
|
|
42
|
-
@persona = Persona.new(create_params)
|
|
43
|
-
|
|
44
|
-
if @persona.save
|
|
45
|
-
redirect_to persona_path(@persona), notice: 'Persona created.'
|
|
46
|
-
else
|
|
47
|
-
render :new, status: :unprocessable_entity
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Update an existing persona. Serves +PATCH /personas/:id+.
|
|
52
|
-
#
|
|
53
|
-
# On failure, re-render the edit form with validation errors.
|
|
54
|
-
#
|
|
55
|
-
# @return [void]
|
|
56
|
-
def update
|
|
57
|
-
if @persona.update(update_params)
|
|
58
|
-
redirect_to persona_path(@persona), notice: 'Persona updated.'
|
|
59
|
-
else
|
|
60
|
-
render :edit, status: :unprocessable_entity
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Permanently delete a persona and everything it owns. Serves +DELETE /personas/:id+.
|
|
65
|
-
#
|
|
66
|
-
# This is the hard, irreversible removal (the reversible path is Archive — see +ArchivesController+):
|
|
67
|
-
# the model cascades the persona's threads, messages, responsibilities, access rules, and tool-use
|
|
68
|
-
# history. +BroadcastablePersona+ removes its sidebar row.
|
|
69
|
-
#
|
|
70
|
-
# @return [void]
|
|
71
|
-
def destroy
|
|
72
|
-
@persona.destroy!
|
|
73
|
-
|
|
74
|
-
redirect_to personas_path, notice: 'Persona deleted.'
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
# Load the +Persona+ named by +params[:id]+ into +@persona+.
|
|
80
|
-
#
|
|
81
|
-
# @return [Protege::Persona] the loaded persona
|
|
82
|
-
def set_persona
|
|
83
|
-
@persona = Persona.find(params[:id])
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Permit creation attributes and fold the split email fields into a single address.
|
|
87
|
-
#
|
|
88
|
-
# @return [ActionController::Parameters] the permitted attributes plus +:email_address+
|
|
89
|
-
def create_params
|
|
90
|
-
params.require(:persona)
|
|
91
|
-
.permit(:type, :name, :instructions)
|
|
92
|
-
.merge(email_address: compose_email_address)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Permit the editable attributes and recompose the address from the split email fields. Changing the
|
|
96
|
-
# address is allowed — only mail to the *old* address stops routing here; all history keys off
|
|
97
|
-
# +persona_id+, not the address. +disabled_tool_ids+ is the tool checklist submitted straight from
|
|
98
|
-
# the form (the model strips the hidden blank); a PATCH without that field leaves the scope untouched.
|
|
99
|
-
#
|
|
100
|
-
# @return [ActionController::Parameters] the permitted attributes plus +:email_address+
|
|
101
|
-
def update_params
|
|
102
|
-
params.require(:persona)
|
|
103
|
-
.permit(:name, :instructions, disabled_tool_ids: [])
|
|
104
|
-
.merge(email_address: compose_email_address)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Combine the split prefix + domain fields from the form into a single
|
|
108
|
-
# email address.
|
|
109
|
-
#
|
|
110
|
-
# @return [String] the +prefix@domain+ address with surrounding whitespace stripped
|
|
111
|
-
def compose_email_address
|
|
112
|
-
params[:persona].values_at(:email_prefix, :email_domain)
|
|
113
|
-
.map(&:to_s)
|
|
114
|
-
.map(&:strip)
|
|
115
|
-
.join('@')
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Protege
|
|
4
|
-
# View helpers for persona pages — selectors, labels, status, and resolver display.
|
|
5
|
-
module PersonasHelper
|
|
6
|
-
# List the loaded +Persona+ subclasses for the new-persona type selector.
|
|
7
|
-
#
|
|
8
|
-
# @return [Array<Class>] the +Persona+ descendant classes
|
|
9
|
-
def available_persona_types
|
|
10
|
-
Persona.descendants
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# List active email domains for the persona form's domain selector.
|
|
14
|
-
#
|
|
15
|
-
# @return [ActiveRecord::Relation<Protege::EmailDomain>] domains eligible for selection
|
|
16
|
-
def available_email_domains
|
|
17
|
-
EmailDomain.all
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Fetch every +Persona+ ordered by name for sidebar listing.
|
|
21
|
-
#
|
|
22
|
-
# @return [ActiveRecord::Relation<Protege::Persona>] personas ordered alphabetically
|
|
23
|
-
def all_personas
|
|
24
|
-
Persona.order(:name)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Format a persona's name together with its type for display.
|
|
28
|
-
#
|
|
29
|
-
# persona_label(persona) # => "Bob <Agent>"
|
|
30
|
-
#
|
|
31
|
-
# @param persona [Protege::Persona] the persona to label
|
|
32
|
-
# @return [String] the +"name <Type>"+ label
|
|
33
|
-
def persona_label(persona)
|
|
34
|
-
"#{persona.name} <#{persona.class.display_name}>"
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Render a status dot reflecting whether a persona is available.
|
|
38
|
-
#
|
|
39
|
-
# @param persona [Protege::Persona] the persona whose availability to show
|
|
40
|
-
# @return [ActiveSupport::SafeBuffer] a green dot when active, red otherwise
|
|
41
|
-
def persona_status(persona)
|
|
42
|
-
if persona.active?
|
|
43
|
-
status_dot('Available', :green)
|
|
44
|
-
else
|
|
45
|
-
status_dot('Unavailable', :red)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Format a persona's resolver chain as a hash suitable for +info_card+ (rendered full-width, since
|
|
50
|
-
# the resolver class names are long). Each entry maps the resolver's short class name to a summary
|
|
51
|
-
# of its positional args, keyword args, and whether it was given a block.
|
|
52
|
-
#
|
|
53
|
-
# @param persona [Protege::Persona] the persona whose reply (+message_resolvers+) chain to format
|
|
54
|
-
# @return [Hash{String => String}] resolver-name => configuration summary
|
|
55
|
-
def resolver_pairs(persona)
|
|
56
|
-
pairs = persona.message_resolvers.entries.each_with_object({}) do |(klass, args, kwargs, block), accumulator|
|
|
57
|
-
accumulator[klass.name.demodulize] = resolver_config(args, kwargs, block)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
pairs.any? ? pairs : { 'Resolvers' => 'None configured' }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# The tool classes a persona's operator may toggle — the checkbox universe on the persona form.
|
|
64
|
-
#
|
|
65
|
-
# This is the persona's *grant* (its code-declared ceiling), or every registered tool when no grant
|
|
66
|
-
# was declared. It is deliberately the grant rather than the effective set: the operator toggles
|
|
67
|
-
# within what the code allows, so a tool disabled in the DB still appears (unchecked) and can be
|
|
68
|
-
# switched back on, while a tool outside the grant is never offered.
|
|
69
|
-
#
|
|
70
|
-
# @param persona [Protege::Persona] the persona being edited
|
|
71
|
-
# @return [Array<Class>] the toggleable tool classes
|
|
72
|
-
def persona_tool_options(persona)
|
|
73
|
-
grant = persona.class.tool_grant
|
|
74
|
-
|
|
75
|
-
grant ? ToolMixin.registered.select { grant.include?(_1.id) } : ToolMixin.registered
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
# Summarize a resolver entry's positional args, keyword args, and whether it was given a block.
|
|
81
|
-
#
|
|
82
|
-
# @return [String] a comma-joined summary, or "" when the resolver takes no configuration
|
|
83
|
-
def resolver_config(args, kwargs, block)
|
|
84
|
-
parts = args.map(&:inspect)
|
|
85
|
-
parts += kwargs.map { |key, value| "#{key}: #{value.inspect}" }
|
|
86
|
-
parts << '{ … }' if block
|
|
87
|
-
parts.join(', ')
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Protege
|
|
4
|
-
# Drives real-time Turbo Stream broadcasting for +Persona+ records — keeping the Personas sidebar
|
|
5
|
-
# live: the row is appended on create, replaced on update, and removed on destroy. First-page viewers
|
|
6
|
-
# subscribe via +turbo_stream_from "protege:personas"+ (see +SidebarHelper#sidebar_stream+).
|
|
7
|
-
module BroadcastablePersona
|
|
8
|
-
extend ActiveSupport::Concern
|
|
9
|
-
|
|
10
|
-
# Turbo Stream channel for all +Persona+ records.
|
|
11
|
-
# @return [String]
|
|
12
|
-
STREAM = 'protege:personas'
|
|
13
|
-
|
|
14
|
-
included do
|
|
15
|
-
after_create_commit :broadcast_persona_created
|
|
16
|
-
after_update_commit :broadcast_persona_updated
|
|
17
|
-
after_destroy_commit :broadcast_persona_removed
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
# Append this persona's row to the Personas sidebar on creation.
|
|
23
|
-
#
|
|
24
|
-
# @return [void]
|
|
25
|
-
def broadcast_persona_created
|
|
26
|
-
broadcast_append_to STREAM,
|
|
27
|
-
target: 'personas_list',
|
|
28
|
-
partial: 'protege/personas/persona_sidebar_item',
|
|
29
|
-
locals: { persona: self }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Replace this persona's sidebar row in place when its attributes change.
|
|
33
|
-
#
|
|
34
|
-
# @return [void]
|
|
35
|
-
def broadcast_persona_updated
|
|
36
|
-
broadcast_replace_to STREAM,
|
|
37
|
-
target: ActionView::RecordIdentifier.dom_id(self),
|
|
38
|
-
partial: 'protege/personas/persona_sidebar_item',
|
|
39
|
-
locals: { persona: self }
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Remove this persona's sidebar row on destruction.
|
|
43
|
-
#
|
|
44
|
-
# @return [void]
|
|
45
|
-
def broadcast_persona_removed
|
|
46
|
-
broadcast_remove_to STREAM,
|
|
47
|
-
target: ActionView::RecordIdentifier.dom_id(self)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Protege
|
|
4
|
-
# Per-persona tool scoping — mixed into +Persona+ to bound which tools an agent may use.
|
|
5
|
-
#
|
|
6
|
-
# A persona's effective tools are the code-declared *grant* (the ceiling, set by the {ClassMethods#tools}
|
|
7
|
-
# macro) minus the operator's +disabled_tool_ids+ override, and never wider than the grant. Two layers,
|
|
8
|
-
# deliberately different in reach:
|
|
9
|
-
#
|
|
10
|
-
# * The grant lives on a +class_attribute+ so it survives Active Record's schema-loading reloads (the
|
|
11
|
-
# same reason +Persona._display_name+ is one). Unlike the resolver chains — per-subclass +@ivar+s that
|
|
12
|
-
# never inherit — the grant *inherits* down the STI tree, so a base persona narrows a whole family's
|
|
13
|
-
# ceiling unless a subclass declares its own. A nil grant means "every registered tool", so an
|
|
14
|
-
# unconfigured persona keeps the full catalogue (the pre-scoping behaviour).
|
|
15
|
-
# * +disabled_tool_ids+ is a dashboard-editable JSON array on the row (text column; SQLite has no native
|
|
16
|
-
# JSON type), normalised to a clean string array so it is never nil. It can only *subtract* from the
|
|
17
|
-
# grant, never add to it.
|
|
18
|
-
#
|
|
19
|
-
# {#available_tools} resolves both layers against the live registry, so a stale grant or disabled id —
|
|
20
|
-
# naming a tool since deleted from the code — is simply skipped rather than conjuring or breaking a tool.
|
|
21
|
-
module ToolScoped
|
|
22
|
-
extend ActiveSupport::Concern
|
|
23
|
-
|
|
24
|
-
included do
|
|
25
|
-
serialize :disabled_tool_ids, coder: JSON
|
|
26
|
-
before_validation :normalize_disabled_tool_ids
|
|
27
|
-
|
|
28
|
-
# Backing store for the code-declared grant (tool ids, or nil for "all tools"). Inherits down STI.
|
|
29
|
-
class_attribute :_tool_grant, instance_accessor: false
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Class-level DSL for declaring the grant.
|
|
33
|
-
class_methods do
|
|
34
|
-
# Declare the tools this persona subclass may use — the code-side ceiling of its tool scope.
|
|
35
|
-
#
|
|
36
|
-
# A DSL macro (positional by convention, like +message_resolver+): given tool ids it records them as
|
|
37
|
-
# symbols. Omitting it leaves the grant nil ("every registered tool"). The operator can only narrow
|
|
38
|
-
# this grant at runtime via +disabled_tool_ids+, never widen it.
|
|
39
|
-
#
|
|
40
|
-
# @param ids [Array<Symbol, String>] the tool ids the persona is allowed to use
|
|
41
|
-
# @return [Array<Symbol>] the recorded grant
|
|
42
|
-
def tools(*ids)
|
|
43
|
-
self._tool_grant = ids.map(&:to_sym)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Return this subclass's declared tool grant, or nil when none was declared ("all tools").
|
|
47
|
-
#
|
|
48
|
-
# @return [Array<Symbol>, nil] the granted tool ids, or nil for the full catalogue
|
|
49
|
-
def tool_grant
|
|
50
|
-
_tool_grant
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Return the tool classes this persona may currently use — its effective, scoped catalogue.
|
|
55
|
-
#
|
|
56
|
-
# Computed as one filter over the live registry (+ToolMixin.registered+): drop anything the operator
|
|
57
|
-
# has switched off in +disabled_tool_ids+, then keep only what the subclass's grant permits (or
|
|
58
|
-
# everything, when the grant is nil). Iterating the registry rather than the grant means a grant id
|
|
59
|
-
# with no matching registered tool is simply skipped — a stale scope can never conjure or break a tool.
|
|
60
|
-
#
|
|
61
|
-
# @return [Array<Class>] the tool classes offered to the model for this persona
|
|
62
|
-
def available_tools
|
|
63
|
-
disabled = disabled_tool_ids.map(&:to_sym)
|
|
64
|
-
grant = self.class.tool_grant
|
|
65
|
-
|
|
66
|
-
ToolMixin.registered
|
|
67
|
-
.reject { |tool| disabled.include?(tool.id) }
|
|
68
|
-
.select { |tool| grant.nil? || grant.include?(tool.id) }
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# The ids of this persona's effective tools — a convenience over {#available_tools} for UI and tests.
|
|
72
|
-
#
|
|
73
|
-
# @return [Array<Symbol>] the effective tool ids
|
|
74
|
-
def available_tool_ids
|
|
75
|
-
available_tools.map(&:id)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
# Coerce +disabled_tool_ids+ to a deduped, blank-stripped array of strings so the JSON column stays
|
|
81
|
-
# clean regardless of how it was assigned (symbols, blanks, nils, duplicates) and is never nil.
|
|
82
|
-
#
|
|
83
|
-
# @return [void]
|
|
84
|
-
def normalize_disabled_tool_ids
|
|
85
|
-
self.disabled_tool_ids = Array(disabled_tool_ids).map(&:to_s).reject(&:blank?).uniq
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/ The persona show page's action bar — Edit, Archive/Unarchive, and permanent Delete — plus an "Archived"
|
|
2
|
-
/ badge when soft-removed. Wrapped in a dom_id so Archive/Unarchive can replace it in place over Turbo
|
|
3
|
-
/ (see ArchivesController). The Delete button opens the confirm dialog rendered by the show template.
|
|
4
|
-
div id=dom_id(persona, :actions) class="flex items-center gap-3"
|
|
5
|
-
- if persona.archived?
|
|
6
|
-
= badge("Archived")
|
|
7
|
-
|
|
8
|
-
= ui_link("Edit", edit_persona_path(persona))
|
|
9
|
-
|
|
10
|
-
- if persona.archived?
|
|
11
|
-
= action_button("Unarchive", persona_archive_path(persona), method: :delete)
|
|
12
|
-
- else
|
|
13
|
-
= action_button("Archive", persona_archive_path(persona), method: :post, variant: :secondary)
|
|
14
|
-
|
|
15
|
-
= button("Delete", type: "button", variant: :danger, onclick: show_dialog("delete-persona"))
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
- url = persona.new_record? ? personas_path : persona_path(persona)
|
|
2
|
-
- method = persona.new_record? ? :post : :patch
|
|
3
|
-
|
|
4
|
-
= form_with url:, method:, scope: :persona, class: "space-y-5" do |f|
|
|
5
|
-
= form_errors(persona)
|
|
6
|
-
|
|
7
|
-
= form_field(f, :name, placeholder: "e.g. alice")
|
|
8
|
-
|
|
9
|
-
= form_email_address(persona, available_email_domains)
|
|
10
|
-
|
|
11
|
-
- unless persona.new_record?
|
|
12
|
-
= hint("Changing the address only affects new mail — messages to the old address will no longer reach this persona. Existing threads and history are unaffected.")
|
|
13
|
-
|
|
14
|
-
- if persona.new_record?
|
|
15
|
-
= form_select(f, :type, available_persona_types.map { |k| [k.display_name, k.name] }, label: "Type")
|
|
16
|
-
|
|
17
|
-
= form_text_area(f, :instructions, \
|
|
18
|
-
label: "Instructions", \
|
|
19
|
-
rows: 10, \
|
|
20
|
-
hint: "The agent's system prompt — who it is and how it behaves. Only sent to the model when a resolver (such as LoadText) includes it in the agent's context.")
|
|
21
|
-
|
|
22
|
-
- unless persona.new_record?
|
|
23
|
-
- tool_id = ->(tool) { tool.id.to_s }
|
|
24
|
-
= form_checklist(f, :disabled_tool_ids, persona_tool_options(persona), label: "Blocked Tools", value: tool_id, text: tool_id, hint: "Disabled tools aren't offered to the agent; everything else stays available.")
|
|
25
|
-
|
|
26
|
-
.flex.items-center.gap-3.pt-2
|
|
27
|
-
= button(persona.new_record? ? "Create persona" : "Save changes", type: :submit)
|
|
28
|
-
|
|
29
|
-
= ui_link("Cancel", persona.new_record? ? personas_path : persona_path(persona))
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
- pagy, personas = paginate(all_personas)
|
|
2
|
-
|
|
3
|
-
= sidebar_content do
|
|
4
|
-
= sidebar_header("Personas (#{pagy.count})", new_path: new_persona_path)
|
|
5
|
-
|
|
6
|
-
= sidebar_stream("protege:personas", pagy: pagy)
|
|
7
|
-
|
|
8
|
-
= sidebar_body(personas, empty: "No personas yet", id: "personas_list") do |persona|
|
|
9
|
-
= render "persona_sidebar_item", persona: persona, active: local_assigns[:current] == persona
|
|
10
|
-
|
|
11
|
-
= sidebar_pagination(pagy)
|