protege 0.1.0.alpha.2 → 0.1.0.alpha.8
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/dev_setup.rb +110 -0
- data/app/services/protege/message_search.rb +2 -2
- data/app/services/protege/system_toolkits.rb +80 -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 +19 -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 +40 -0
- metadata +67 -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.8
|
|
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 agents: 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
|
|
@@ -294,8 +308,10 @@ files:
|
|
|
294
308
|
- app/resolvers/protege/load_record_resolver.rb
|
|
295
309
|
- app/resolvers/protege/load_text_resolver.rb
|
|
296
310
|
- app/resolvers/protege/thread_history_resolver.rb
|
|
311
|
+
- app/services/protege/dev_setup.rb
|
|
297
312
|
- app/services/protege/introspection_tool_call_presentation.rb
|
|
298
313
|
- app/services/protege/message_search.rb
|
|
314
|
+
- app/services/protege/system_toolkits.rb
|
|
299
315
|
- app/services/protege/trace_search.rb
|
|
300
316
|
- app/subscribers/protege/introspection_broadcaster.rb
|
|
301
317
|
- app/tools/protege/create_file_tool.rb
|
|
@@ -304,8 +320,30 @@ files:
|
|
|
304
320
|
- app/tools/protege/send_email_tool.rb
|
|
305
321
|
- app/tools/protege/web_fetch_tool.rb
|
|
306
322
|
- app/tools/protege/web_search_tool.rb
|
|
323
|
+
- app/views/layouts/mailer.text.erb
|
|
307
324
|
- app/views/layouts/protege/application.html.slim
|
|
308
325
|
- app/views/protege/access_rules/create.turbo_stream.slim
|
|
326
|
+
- app/views/protege/agent_toolkit_rules/create.turbo_stream.slim
|
|
327
|
+
- app/views/protege/agent_toolkits/_gate_rule.html.slim
|
|
328
|
+
- app/views/protege/agent_toolkits/_gate_rule_form.html.slim
|
|
329
|
+
- app/views/protege/agent_toolkits/_gate_rules.html.slim
|
|
330
|
+
- app/views/protege/agent_toolkits/show.html.slim
|
|
331
|
+
- app/views/protege/agents/_access_rule.html.slim
|
|
332
|
+
- app/views/protege/agents/_access_rule_form.html.slim
|
|
333
|
+
- app/views/protege/agents/_access_rules.html.slim
|
|
334
|
+
- app/views/protege/agents/_actions.html.slim
|
|
335
|
+
- app/views/protege/agents/_agent_sidebar_item.html.slim
|
|
336
|
+
- app/views/protege/agents/_agent_toolkit.html.slim
|
|
337
|
+
- app/views/protege/agents/_agent_toolkit_form.html.slim
|
|
338
|
+
- app/views/protege/agents/_form.html.slim
|
|
339
|
+
- app/views/protege/agents/_sidebar.html.slim
|
|
340
|
+
- app/views/protege/agents/_toolkits.html.slim
|
|
341
|
+
- app/views/protege/agents/agent_toolkits/create.turbo_stream.slim
|
|
342
|
+
- app/views/protege/agents/edit.html.slim
|
|
343
|
+
- app/views/protege/agents/index.html.slim
|
|
344
|
+
- app/views/protege/agents/new.html.slim
|
|
345
|
+
- app/views/protege/agents/show.html.slim
|
|
346
|
+
- app/views/protege/alert_mailer/inference_failed.text.erb
|
|
309
347
|
- app/views/protege/email_domains/_email_domain_sidebar_item.html.slim
|
|
310
348
|
- app/views/protege/email_domains/_form.html.slim
|
|
311
349
|
- app/views/protege/email_domains/_sidebar.html.slim
|
|
@@ -316,17 +354,6 @@ files:
|
|
|
316
354
|
- app/views/protege/messages/_message.html.slim
|
|
317
355
|
- app/views/protege/messages/_sidebar.html.slim
|
|
318
356
|
- 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
357
|
- app/views/protege/responsibilities/_form.html.slim
|
|
331
358
|
- app/views/protege/responsibilities/_responsibility_sidebar_item.html.slim
|
|
332
359
|
- app/views/protege/responsibilities/_sidebar.html.slim
|
|
@@ -349,6 +376,13 @@ files:
|
|
|
349
376
|
- app/views/protege/threads/index.html.slim
|
|
350
377
|
- app/views/protege/threads/new.html.slim
|
|
351
378
|
- app/views/protege/threads/show.html.slim
|
|
379
|
+
- app/views/protege/toolkits/_form.html.slim
|
|
380
|
+
- app/views/protege/toolkits/_sidebar.html.slim
|
|
381
|
+
- app/views/protege/toolkits/_toolkit_sidebar_item.html.slim
|
|
382
|
+
- app/views/protege/toolkits/edit.html.slim
|
|
383
|
+
- app/views/protege/toolkits/index.html.slim
|
|
384
|
+
- app/views/protege/toolkits/new.html.slim
|
|
385
|
+
- app/views/protege/toolkits/show.html.slim
|
|
352
386
|
- app/views/protege/traces/_sidebar.html.slim
|
|
353
387
|
- app/views/protege/traces/index.html.slim
|
|
354
388
|
- app/views/protege/traces/searches/_sidebar.html.slim
|
|
@@ -359,13 +393,18 @@ files:
|
|
|
359
393
|
- db/migrate/20260706120130_create_protege_traces.rb
|
|
360
394
|
- db/migrate/20260707120000_replace_persona_active_with_archived_at.rb
|
|
361
395
|
- db/migrate/20260708120000_add_disabled_tool_ids_to_personas.rb
|
|
396
|
+
- db/migrate/20260728140000_scope_message_and_thread_uniqueness_per_persona.rb
|
|
397
|
+
- db/migrate/20260812000001_create_protege_toolkits.rb
|
|
398
|
+
- db/migrate/20260815000001_remove_disabled_tool_ids_from_personas.rb
|
|
399
|
+
- db/migrate/20260815000002_add_key_to_protege_toolkits.rb
|
|
400
|
+
- db/migrate/20260815100000_rename_personas_to_agents.rb
|
|
401
|
+
- lib/generators/protege/agent/agent_generator.rb
|
|
402
|
+
- lib/generators/protege/agent/templates/agent.rb.tt
|
|
362
403
|
- lib/generators/protege/extension_naming.rb
|
|
363
404
|
- lib/generators/protege/hook/hook_generator.rb
|
|
364
405
|
- lib/generators/protege/hook/templates/hook.rb.tt
|
|
365
406
|
- lib/generators/protege/install/install_generator.rb
|
|
366
407
|
- 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
408
|
- lib/generators/protege/postfix/postfix_generator.rb
|
|
370
409
|
- lib/generators/protege/postfix/templates/deploy/mail/Dockerfile
|
|
371
410
|
- lib/generators/protege/postfix/templates/deploy/mail/KeyTable
|
|
@@ -425,6 +464,8 @@ files:
|
|
|
425
464
|
- lib/protege/events/tool_calls_received_event.rb
|
|
426
465
|
- lib/protege/extensions/hook.rb
|
|
427
466
|
- lib/protege/extensions/hook_mixin.rb
|
|
467
|
+
- lib/protege/extensions/manifest.rb
|
|
468
|
+
- lib/protege/extensions/manifested.rb
|
|
428
469
|
- lib/protege/extensions/provider.rb
|
|
429
470
|
- lib/protege/extensions/provider_mixin.rb
|
|
430
471
|
- lib/protege/extensions/resolver.rb
|
|
@@ -455,6 +496,7 @@ files:
|
|
|
455
496
|
- lib/protege/orchestrator/resolver_chain.rb
|
|
456
497
|
- lib/protege/orchestrator/responsibility_context.rb
|
|
457
498
|
- lib/protege/orchestrator/responsibility_harness.rb
|
|
499
|
+
- lib/protege/subscribers/alerter.rb
|
|
458
500
|
- lib/protege/subscribers/hook_dispatcher.rb
|
|
459
501
|
- lib/protege/subscribers/tracing.rb
|
|
460
502
|
- lib/protege/types/document_part.rb
|
|
@@ -473,6 +515,8 @@ metadata:
|
|
|
473
515
|
homepage_uri: https://github.com/sebscholl/protege
|
|
474
516
|
source_code_uri: https://github.com/sebscholl/protege
|
|
475
517
|
bug_tracker_uri: https://github.com/sebscholl/protege/issues
|
|
518
|
+
documentation_uri: https://docs.protege.bot
|
|
519
|
+
changelog_uri: https://github.com/sebscholl/protege/blob/main/CHANGELOG.md
|
|
476
520
|
rubygems_mfa_required: 'true'
|
|
477
521
|
rdoc_options: []
|
|
478
522
|
require_paths:
|
|
@@ -490,5 +534,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
490
534
|
requirements: []
|
|
491
535
|
rubygems_version: 3.6.9
|
|
492
536
|
specification_version: 4
|
|
493
|
-
summary:
|
|
537
|
+
summary: Rails engine for email-native AI agents.
|
|
494
538
|
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)
|