anima-core 1.4.0 → 1.5.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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +18 -20
  3. data/README.md +61 -95
  4. data/agents/thoughts-analyzer.md +12 -7
  5. data/anima-core.gemspec +1 -0
  6. data/app/channels/session_channel.rb +38 -58
  7. data/app/decorators/agent_message_decorator.rb +7 -2
  8. data/app/decorators/message_decorator.rb +31 -100
  9. data/app/decorators/pending_from_melete_decorator.rb +36 -0
  10. data/app/decorators/pending_from_melete_goal_decorator.rb +13 -0
  11. data/app/decorators/pending_from_melete_skill_decorator.rb +19 -0
  12. data/app/decorators/pending_from_melete_workflow_decorator.rb +13 -0
  13. data/app/decorators/pending_from_mneme_decorator.rb +44 -0
  14. data/app/decorators/pending_message_decorator.rb +94 -0
  15. data/app/decorators/pending_subagent_decorator.rb +46 -0
  16. data/app/decorators/pending_tool_response_decorator.rb +51 -0
  17. data/app/decorators/pending_user_message_decorator.rb +22 -0
  18. data/app/decorators/system_message_decorator.rb +5 -0
  19. data/app/decorators/tool_call_decorator.rb +13 -2
  20. data/app/decorators/tool_response_decorator.rb +2 -2
  21. data/app/decorators/user_message_decorator.rb +7 -2
  22. data/app/jobs/count_tokens_job.rb +23 -0
  23. data/app/jobs/drain_job.rb +169 -0
  24. data/app/jobs/melete_enrichment_job/goal_change_listener.rb +52 -0
  25. data/app/jobs/melete_enrichment_job.rb +48 -0
  26. data/app/jobs/mneme_enrichment_job.rb +46 -0
  27. data/app/jobs/tool_execution_job.rb +87 -0
  28. data/app/models/concerns/token_estimation.rb +54 -0
  29. data/app/models/goal.rb +21 -10
  30. data/app/models/message.rb +47 -36
  31. data/app/models/pending_message.rb +276 -29
  32. data/app/models/pinned_message.rb +8 -3
  33. data/app/models/session.rb +468 -432
  34. data/app/models/snapshot.rb +11 -21
  35. data/bin/inspect-cassette +17 -4
  36. data/config/application.rb +1 -0
  37. data/config/initializers/event_subscribers.rb +71 -4
  38. data/config/initializers/inflections.rb +3 -1
  39. data/db/cable_structure.sql +3 -3
  40. data/db/migrate/20260407170803_remove_viewport_message_ids_from_sessions.rb +5 -0
  41. data/db/migrate/20260407180400_remove_mneme_snapshot_pointer_columns_from_sessions.rb +6 -0
  42. data/db/migrate/20260411120553_add_token_count_to_pinned_messages.rb +5 -0
  43. data/db/migrate/20260411172926_remove_active_skills_and_workflow_from_sessions.rb +6 -0
  44. data/db/migrate/20260412110625_replace_processing_with_aasm_state.rb +6 -0
  45. data/db/migrate/20260418150323_add_kind_and_message_type_to_pending_messages.rb +6 -0
  46. data/db/migrate/20260419120000_add_drain_fields_to_pending_messages.rb +7 -0
  47. data/db/migrate/20260419130000_drop_pending_messages_kind_default.rb +5 -0
  48. data/db/migrate/20260419140000_add_drain_indexes_to_pending_messages.rb +8 -0
  49. data/db/migrate/20260420100000_add_hud_visibility_to_sessions.rb +15 -0
  50. data/db/queue_structure.sql +13 -13
  51. data/db/structure.sql +44 -31
  52. data/lib/agents/registry.rb +1 -1
  53. data/lib/anima/settings.rb +7 -33
  54. data/lib/anima/version.rb +1 -1
  55. data/lib/events/authentication_required.rb +24 -0
  56. data/lib/events/bounce_back.rb +4 -4
  57. data/lib/events/eviction_completed.rb +28 -0
  58. data/lib/events/goal_created.rb +28 -0
  59. data/lib/events/goal_updated.rb +32 -0
  60. data/lib/events/llm_responded.rb +35 -0
  61. data/lib/events/message_created.rb +27 -0
  62. data/lib/events/message_updated.rb +25 -0
  63. data/lib/events/session_state_changed.rb +30 -0
  64. data/lib/events/skill_activated.rb +28 -0
  65. data/lib/events/start_melete.rb +36 -0
  66. data/lib/events/start_mneme.rb +33 -0
  67. data/lib/events/start_processing.rb +32 -0
  68. data/lib/events/subagent_evicted.rb +31 -0
  69. data/lib/events/subscribers/active_state_broadcaster.rb +27 -0
  70. data/lib/events/subscribers/authentication_broadcaster.rb +34 -0
  71. data/lib/events/subscribers/drain_kickoff.rb +20 -0
  72. data/lib/events/subscribers/eviction_broadcaster.rb +26 -0
  73. data/lib/events/subscribers/llm_response_handler.rb +111 -0
  74. data/lib/events/subscribers/melete_kickoff.rb +24 -0
  75. data/lib/events/subscribers/message_broadcaster.rb +34 -0
  76. data/lib/events/subscribers/mneme_kickoff.rb +24 -0
  77. data/lib/events/subscribers/mneme_scheduler.rb +21 -0
  78. data/lib/events/subscribers/persister.rb +6 -8
  79. data/lib/events/subscribers/session_state_broadcaster.rb +33 -0
  80. data/lib/events/subscribers/subagent_message_router.rb +26 -29
  81. data/lib/events/subscribers/subagent_visibility_broadcaster.rb +33 -0
  82. data/lib/events/subscribers/tool_response_creator.rb +33 -0
  83. data/lib/events/subscribers/transient_broadcaster.rb +1 -1
  84. data/lib/events/tool_executed.rb +34 -0
  85. data/lib/events/workflow_activated.rb +27 -0
  86. data/lib/llm/client.rb +41 -201
  87. data/lib/mcp/client_manager.rb +41 -46
  88. data/lib/mcp/stdio_transport.rb +9 -5
  89. data/lib/{analytical_brain → melete}/runner.rb +63 -68
  90. data/lib/{analytical_brain → melete}/tools/activate_skill.rb +1 -1
  91. data/lib/{analytical_brain → melete}/tools/assign_nickname.rb +2 -2
  92. data/lib/{analytical_brain → melete}/tools/everything_is_ready.rb +2 -2
  93. data/lib/{analytical_brain → melete}/tools/finish_goal.rb +3 -3
  94. data/lib/{analytical_brain → melete}/tools/goal_messaging.rb +4 -3
  95. data/lib/{analytical_brain → melete}/tools/read_workflow.rb +2 -2
  96. data/lib/{analytical_brain → melete}/tools/rename_session.rb +3 -3
  97. data/lib/{analytical_brain → melete}/tools/set_goal.rb +1 -1
  98. data/lib/{analytical_brain → melete}/tools/update_goal.rb +4 -4
  99. data/lib/{analytical_brain.rb → melete.rb} +6 -3
  100. data/lib/mneme/base_runner.rb +121 -0
  101. data/lib/mneme/l2_runner.rb +14 -20
  102. data/lib/mneme/recall_runner.rb +132 -0
  103. data/lib/mneme/runner.rb +118 -171
  104. data/lib/mneme/search.rb +104 -62
  105. data/lib/mneme/tools/nothing_to_surface.rb +25 -0
  106. data/lib/mneme/tools/save_snapshot.rb +2 -10
  107. data/lib/mneme/tools/surface_memory.rb +89 -0
  108. data/lib/mneme.rb +11 -5
  109. data/lib/shell_session.rb +287 -612
  110. data/lib/skills/definition.rb +2 -2
  111. data/lib/skills/registry.rb +1 -1
  112. data/lib/tools/base.rb +16 -0
  113. data/lib/tools/bash.rb +25 -57
  114. data/lib/tools/edit.rb +2 -0
  115. data/lib/tools/read.rb +2 -0
  116. data/lib/tools/registry.rb +79 -3
  117. data/lib/tools/{recall.rb → search_messages.rb} +19 -21
  118. data/lib/tools/spawn_specialist.rb +16 -10
  119. data/lib/tools/spawn_subagent.rb +20 -14
  120. data/lib/tools/subagent_prompts.rb +4 -4
  121. data/lib/tools/think.rb +1 -1
  122. data/lib/tools/{remember.rb → view_messages.rb} +10 -10
  123. data/lib/tools/write.rb +2 -0
  124. data/lib/tui/app.rb +5 -4
  125. data/lib/tui/braille_spinner.rb +7 -7
  126. data/lib/tui/decorators/base_decorator.rb +24 -3
  127. data/lib/tui/message_store.rb +93 -44
  128. data/lib/tui/screens/chat.rb +94 -20
  129. data/lib/tui/settings.rb +9 -2
  130. data/lib/workflows/definition.rb +3 -3
  131. data/lib/workflows/registry.rb +1 -1
  132. data/skills/github.md +38 -0
  133. data/templates/config.toml +4 -23
  134. data/workflows/review_pr.md +18 -14
  135. metadata +86 -28
  136. data/app/jobs/agent_request_job.rb +0 -199
  137. data/app/jobs/analytical_brain_job.rb +0 -33
  138. data/app/jobs/count_message_tokens_job.rb +0 -39
  139. data/app/jobs/passive_recall_job.rb +0 -24
  140. data/app/models/concerns/message/broadcasting.rb +0 -86
  141. data/lib/agent_loop.rb +0 -215
  142. data/lib/analytical_brain/tools/deactivate_skill.rb +0 -40
  143. data/lib/analytical_brain/tools/deactivate_workflow.rb +0 -35
  144. data/lib/events/agent_message.rb +0 -25
  145. data/lib/events/subscribers/message_collector.rb +0 -64
  146. data/lib/events/tool_call.rb +0 -31
  147. data/lib/events/tool_response.rb +0 -33
  148. data/lib/mneme/compressed_viewport.rb +0 -204
  149. data/lib/mneme/passive_recall.rb +0 -138
@@ -102,16 +102,6 @@ max_tool_response_chars = 6_000
102
102
  # ~24000 chars ≈ ~8000 tokens.
103
103
  max_subagent_response_chars = 24_000
104
104
 
105
- # ─── Environment ──────────────────────────────────────────────
106
-
107
- [environment]
108
-
109
- # Files to scan for in the working directory (at root and up to project_files_max_depth subdirectories deep).
110
- project_files = ["CLAUDE.md", "AGENTS.md", "README.md", "CONTRIBUTING.md"]
111
-
112
- # Maximum directory depth for project file scanning.
113
- project_files_max_depth = 3
114
-
115
105
  # ─── GitHub ─────────────────────────────────────────────────────
116
106
 
117
107
  [github]
@@ -137,25 +127,16 @@ soul = "{{ANIMA_HOME}}/soul.md"
137
127
  # View mode for new sessions: "basic", "verbose", or "debug".
138
128
  default_view_mode = "basic"
139
129
 
140
- # Regenerate session name every N messages.
141
- name_generation_interval = 30
142
130
 
143
- # ─── Analytical Brain ─────────────────────────────────────────
131
+ # ─── Melete ─────────────────────────────────────────
144
132
 
145
- [analytical_brain]
133
+ [melete]
146
134
 
147
- # Maximum tokens per analytical brain response.
135
+ # Maximum tokens per Melete response.
148
136
  # Must accommodate multiple tool calls (rename + goals + skills + ready).
149
137
  max_tokens = 4096
150
138
 
151
- # Run the analytical brain synchronously before the main agent on user messages.
152
- # Ensures activated skills are available for the current response.
153
- blocking_on_user_message = true
154
-
155
- # Run the analytical brain asynchronously after the main agent completes.
156
- blocking_on_agent_message = false
157
-
158
- # Number of recent messages to include in the analytical brain's context window.
139
+ # Number of recent messages to include in Melete's context window.
159
140
  message_window = 20
160
141
 
161
142
  # ─── Mneme (Memory Department) ──────────────────────────────────
@@ -124,37 +124,38 @@ Read the diff from: /tmp/pr_<number>_diff.txt
124
124
  - REST conventions and route design
125
125
  - ActiveRecord patterns (associations, validations placement)
126
126
  - Service object patterns and naming
127
+ - Security-adjacent AR patterns: raw SQL interpolation, mass assignment gaps, missing tenant/org scoping on shared-model queries
127
128
 
128
129
  Output: List findings tagged [major], [minor], or [nit] with file:line references."
129
130
  ```
130
131
 
131
- #### Subagent 2: SecurityHawk
132
+ #### Subagent 2: TicketDelivery
132
133
 
133
134
  ```
134
- Prompt: "Review PR #<number> for security vulnerabilities.
135
+ Prompt: "Your role: verify this PR delivers the ticket. Code-quality subagents judge how the work was done; you judge whether the work was done.
135
136
 
136
137
  Read the diff from: /tmp/pr_<number>_diff.txt
137
138
 
138
- *Critical:* Activate the `activerecord` skill for SQL injection prevention patterns.
139
+ The ticket defines 'done'. The PR description is how the author frames their work useful context, not authority. When the two disagree, the ticket wins and the disagreement itself is a finding.
139
140
 
140
- ## Ticket Context
141
- <ticket title, acceptance criteria, business context>
141
+ ## Ticket (verbatim — do not summarize)
142
+ <full ticket title, description, every Task, every Acceptance Criterion>
143
+
144
+ ## PR description (verbatim)
145
+ <PR body from Step 1>
142
146
 
143
147
  ## Historical Context
144
148
  <output from thoughts-analyzer>
145
149
 
146
150
  <any additional instructions from user input>
147
151
 
148
- ## Focus Areas
149
- - SQL injection (raw queries, interpolation in where clauses)
150
- - XSS vulnerabilities (unescaped output, html_safe misuse)
151
- - CSRF protection gaps
152
- - Mass assignment vulnerabilities (permit params)
153
- - Authentication/authorization bypasses
154
- - Secrets or credentials in code
155
- - Insecure direct object references
152
+ ## How to work
156
153
 
157
- Output: List findings tagged [major], [minor], or [nit] with file:line references."
154
+ Map each requirement in the ticket — Tasks, Acceptance Criteria, named targets — to evidence in the diff. For each, produce one line: ✅ delivered, ⚠️ partial, or missing, with file:line references.
155
+
156
+ A requirement is delivered when the code does what the ticket asked for in meaning, not merely in mention. Match semantics against the ticket's verbs: 'add Y' needs Y; 'replace X with Y' needs Y and no X. When the ticket lists multiple targets, verify each separately.
157
+
158
+ Output: the verification table first. Then findings tagged [major], [minor], or [nit] with file:line references."
158
159
  ```
159
160
 
160
161
  #### Subagent 3: PerfPro
@@ -182,6 +183,7 @@ Read the diff from: /tmp/pr_<number>_diff.txt
182
183
  - Memory bloat (loading large datasets)
183
184
  - Missing caching opportunities
184
185
  - Background job considerations (should this be async?)
186
+ - Cross-tenant data leakage in aggregation (missing organization_id scope on joins, unscoped WHERE in reports)
185
187
 
186
188
  Output: List findings tagged [major], [minor], or [nit] with file:line references."
187
189
  ```
@@ -210,6 +212,7 @@ Read the diff from: /tmp/pr_<number>_diff.txt
210
212
  - Test isolation issues (shared state, missing cleanup)
211
213
  - Assertion quality (testing behavior vs implementation)
212
214
  - Missing edge case coverage
215
+ - Missing coverage for authorization boundaries (cross-org access denial, role-based access denied, unauthenticated request rejected)
213
216
 
214
217
  Output: List findings tagged [major], [minor], or [nit] with file:line references."
215
218
  ```
@@ -235,6 +238,7 @@ Read the diff from: /tmp/pr_<number>_diff.txt
235
238
  - Complex logic lacking explanatory comments
236
239
  - Misleading or outdated comments
237
240
  - Magic numbers or strings needing constants
241
+ - Secrets, tokens, or credentials appearing in logs, comments, error messages, or test fixtures; permission-gating magic constants that should be named
238
242
 
239
243
  Output: List findings tagged [major], [minor], or [nit] with file:line references."
240
244
  ```
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anima-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yevhenii Hurin
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: aasm
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.5'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.5'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: certifi
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -260,20 +274,31 @@ files:
260
274
  - app/decorators/agent_message_decorator.rb
261
275
  - app/decorators/application_decorator.rb
262
276
  - app/decorators/message_decorator.rb
277
+ - app/decorators/pending_from_melete_decorator.rb
278
+ - app/decorators/pending_from_melete_goal_decorator.rb
279
+ - app/decorators/pending_from_melete_skill_decorator.rb
280
+ - app/decorators/pending_from_melete_workflow_decorator.rb
281
+ - app/decorators/pending_from_mneme_decorator.rb
282
+ - app/decorators/pending_message_decorator.rb
283
+ - app/decorators/pending_subagent_decorator.rb
284
+ - app/decorators/pending_tool_response_decorator.rb
285
+ - app/decorators/pending_user_message_decorator.rb
263
286
  - app/decorators/system_message_decorator.rb
264
287
  - app/decorators/tool_call_decorator.rb
265
288
  - app/decorators/tool_decorator.rb
266
289
  - app/decorators/tool_response_decorator.rb
267
290
  - app/decorators/user_message_decorator.rb
268
291
  - app/decorators/web_get_tool_decorator.rb
269
- - app/jobs/agent_request_job.rb
270
- - app/jobs/analytical_brain_job.rb
271
292
  - app/jobs/application_job.rb
272
- - app/jobs/count_message_tokens_job.rb
293
+ - app/jobs/count_tokens_job.rb
294
+ - app/jobs/drain_job.rb
295
+ - app/jobs/melete_enrichment_job.rb
296
+ - app/jobs/melete_enrichment_job/goal_change_listener.rb
297
+ - app/jobs/mneme_enrichment_job.rb
273
298
  - app/jobs/mneme_job.rb
274
- - app/jobs/passive_recall_job.rb
299
+ - app/jobs/tool_execution_job.rb
275
300
  - app/models/application_record.rb
276
- - app/models/concerns/message/broadcasting.rb
301
+ - app/models/concerns/token_estimation.rb
277
302
  - app/models/goal.rb
278
303
  - app/models/goal_pinned_message.rb
279
304
  - app/models/message.rb
@@ -337,27 +362,23 @@ files:
337
362
  - db/migrate/20260401180000_add_api_metrics_to_messages.rb
338
363
  - db/migrate/20260401210935_remove_recalled_message_ids_from_sessions.rb
339
364
  - db/migrate/20260403080031_add_initial_cwd_to_sessions.rb
365
+ - db/migrate/20260407170803_remove_viewport_message_ids_from_sessions.rb
366
+ - db/migrate/20260407180400_remove_mneme_snapshot_pointer_columns_from_sessions.rb
367
+ - db/migrate/20260411120553_add_token_count_to_pinned_messages.rb
368
+ - db/migrate/20260411172926_remove_active_skills_and_workflow_from_sessions.rb
369
+ - db/migrate/20260412110625_replace_processing_with_aasm_state.rb
370
+ - db/migrate/20260418150323_add_kind_and_message_type_to_pending_messages.rb
371
+ - db/migrate/20260419120000_add_drain_fields_to_pending_messages.rb
372
+ - db/migrate/20260419130000_drop_pending_messages_kind_default.rb
373
+ - db/migrate/20260419140000_add_drain_indexes_to_pending_messages.rb
374
+ - db/migrate/20260420100000_add_hud_visibility_to_sessions.rb
340
375
  - db/queue_schema.rb
341
376
  - db/queue_structure.sql
342
377
  - db/seeds.rb
343
378
  - db/structure.sql
344
379
  - exe/anima
345
- - lib/agent_loop.rb
346
380
  - lib/agents/definition.rb
347
381
  - lib/agents/registry.rb
348
- - lib/analytical_brain.rb
349
- - lib/analytical_brain/runner.rb
350
- - lib/analytical_brain/tools/activate_skill.rb
351
- - lib/analytical_brain/tools/assign_nickname.rb
352
- - lib/analytical_brain/tools/deactivate_skill.rb
353
- - lib/analytical_brain/tools/deactivate_workflow.rb
354
- - lib/analytical_brain/tools/everything_is_ready.rb
355
- - lib/analytical_brain/tools/finish_goal.rb
356
- - lib/analytical_brain/tools/goal_messaging.rb
357
- - lib/analytical_brain/tools/read_workflow.rb
358
- - lib/analytical_brain/tools/rename_session.rb
359
- - lib/analytical_brain/tools/set_goal.rb
360
- - lib/analytical_brain/tools/update_goal.rb
361
382
  - lib/anima.rb
362
383
  - lib/anima/cli.rb
363
384
  - lib/anima/cli/mcp.rb
@@ -368,34 +389,70 @@ files:
368
389
  - lib/anima/spinner.rb
369
390
  - lib/anima/version.rb
370
391
  - lib/credential_store.rb
371
- - lib/events/agent_message.rb
392
+ - lib/events/authentication_required.rb
372
393
  - lib/events/base.rb
373
394
  - lib/events/bounce_back.rb
374
395
  - lib/events/bus.rb
396
+ - lib/events/eviction_completed.rb
397
+ - lib/events/goal_created.rb
398
+ - lib/events/goal_updated.rb
399
+ - lib/events/llm_responded.rb
400
+ - lib/events/message_created.rb
401
+ - lib/events/message_updated.rb
402
+ - lib/events/session_state_changed.rb
403
+ - lib/events/skill_activated.rb
404
+ - lib/events/start_melete.rb
405
+ - lib/events/start_mneme.rb
406
+ - lib/events/start_processing.rb
407
+ - lib/events/subagent_evicted.rb
375
408
  - lib/events/subscriber.rb
376
- - lib/events/subscribers/message_collector.rb
409
+ - lib/events/subscribers/active_state_broadcaster.rb
410
+ - lib/events/subscribers/authentication_broadcaster.rb
411
+ - lib/events/subscribers/drain_kickoff.rb
412
+ - lib/events/subscribers/eviction_broadcaster.rb
413
+ - lib/events/subscribers/llm_response_handler.rb
414
+ - lib/events/subscribers/melete_kickoff.rb
415
+ - lib/events/subscribers/message_broadcaster.rb
416
+ - lib/events/subscribers/mneme_kickoff.rb
417
+ - lib/events/subscribers/mneme_scheduler.rb
377
418
  - lib/events/subscribers/persister.rb
419
+ - lib/events/subscribers/session_state_broadcaster.rb
378
420
  - lib/events/subscribers/subagent_message_router.rb
421
+ - lib/events/subscribers/subagent_visibility_broadcaster.rb
422
+ - lib/events/subscribers/tool_response_creator.rb
379
423
  - lib/events/subscribers/transient_broadcaster.rb
380
424
  - lib/events/system_message.rb
381
- - lib/events/tool_call.rb
382
- - lib/events/tool_response.rb
425
+ - lib/events/tool_executed.rb
383
426
  - lib/events/user_message.rb
427
+ - lib/events/workflow_activated.rb
384
428
  - lib/llm/client.rb
385
429
  - lib/mcp/client_manager.rb
386
430
  - lib/mcp/config.rb
387
431
  - lib/mcp/health_check.rb
388
432
  - lib/mcp/secrets.rb
389
433
  - lib/mcp/stdio_transport.rb
434
+ - lib/melete.rb
435
+ - lib/melete/runner.rb
436
+ - lib/melete/tools/activate_skill.rb
437
+ - lib/melete/tools/assign_nickname.rb
438
+ - lib/melete/tools/everything_is_ready.rb
439
+ - lib/melete/tools/finish_goal.rb
440
+ - lib/melete/tools/goal_messaging.rb
441
+ - lib/melete/tools/read_workflow.rb
442
+ - lib/melete/tools/rename_session.rb
443
+ - lib/melete/tools/set_goal.rb
444
+ - lib/melete/tools/update_goal.rb
390
445
  - lib/mneme.rb
391
- - lib/mneme/compressed_viewport.rb
446
+ - lib/mneme/base_runner.rb
392
447
  - lib/mneme/l2_runner.rb
393
- - lib/mneme/passive_recall.rb
448
+ - lib/mneme/recall_runner.rb
394
449
  - lib/mneme/runner.rb
395
450
  - lib/mneme/search.rb
396
451
  - lib/mneme/tools/attach_messages_to_goals.rb
397
452
  - lib/mneme/tools/everything_ok.rb
453
+ - lib/mneme/tools/nothing_to_surface.rb
398
454
  - lib/mneme/tools/save_snapshot.rb
455
+ - lib/mneme/tools/surface_memory.rb
399
456
  - lib/providers/anthropic.rb
400
457
  - lib/shell_session.rb
401
458
  - lib/skills/definition.rb
@@ -408,14 +465,14 @@ files:
408
465
  - lib/tools/mcp_tool.rb
409
466
  - lib/tools/open_issue.rb
410
467
  - lib/tools/read.rb
411
- - lib/tools/recall.rb
412
468
  - lib/tools/registry.rb
413
- - lib/tools/remember.rb
414
469
  - lib/tools/response_truncator.rb
470
+ - lib/tools/search_messages.rb
415
471
  - lib/tools/spawn_specialist.rb
416
472
  - lib/tools/spawn_subagent.rb
417
473
  - lib/tools/subagent_prompts.rb
418
474
  - lib/tools/think.rb
475
+ - lib/tools/view_messages.rb
419
476
  - lib/tools/web_get.rb
420
477
  - lib/tools/write.rb
421
478
  - lib/tui/app.rb
@@ -540,6 +597,7 @@ files:
540
597
  - skills/draper-decorators/references/patterns.md
541
598
  - skills/draper-decorators/references/testing.md
542
599
  - skills/gh-issue.md
600
+ - skills/github.md
543
601
  - skills/mcp-server/SKILL.md
544
602
  - skills/mcp-server/examples/dynamic_tools.rb
545
603
  - skills/mcp-server/examples/file_manager_tool.rb
@@ -1,199 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Executes an LLM agent loop as a background job with retry logic
4
- # for transient failures (network errors, rate limits, server errors).
5
- #
6
- # Supports two modes:
7
- #
8
- # **Immediate Persist (message_id provided):** The user message was already
9
- # persisted and broadcast by the caller (e.g. {SessionChannel#speak}).
10
- # The job verifies LLM delivery — if the first API call fails, the
11
- # message is deleted and a {Events::BounceBack} is emitted so clients
12
- # can restore the text to the input field.
13
- #
14
- # **Standard (no message_id):** Processes already-persisted messages (e.g.
15
- # after pending message promotion). Uses ActiveJob retry/discard for
16
- # error handling.
17
- #
18
- # @example Immediate Persist — message already saved by SessionChannel
19
- # AgentRequestJob.perform_later(session.id, message_id: 42)
20
- #
21
- # @example Standard — pending message processing
22
- # AgentRequestJob.perform_later(session.id)
23
- class AgentRequestJob < ApplicationJob
24
- queue_as :default
25
-
26
- # ActionCable action signaling clients to prompt for an API token.
27
- AUTH_REQUIRED_ACTION = "authentication_required"
28
-
29
- # Standard path only — immediate persist handles its own errors.
30
- retry_on Providers::Anthropic::TransientError,
31
- wait: :polynomially_longer, attempts: 5 do |job, error|
32
- Events::Bus.emit(Events::SystemMessage.new(
33
- content: "Failed after multiple retries: #{error.message}",
34
- session_id: job.arguments.first
35
- ))
36
- end
37
-
38
- discard_on ActiveRecord::RecordNotFound
39
- discard_on Providers::Anthropic::AuthenticationError do |job, error|
40
- session_id = job.arguments.first
41
- Events::Bus.emit(Events::SystemMessage.new(
42
- content: "Authentication failed: #{error.message}",
43
- session_id: session_id
44
- ))
45
- ActionCable.server.broadcast(
46
- "session_#{session_id}",
47
- {"action" => AUTH_REQUIRED_ACTION, "message" => error.message}
48
- )
49
- end
50
-
51
- # @param session_id [Integer] ID of the session to process
52
- # @param message_id [Integer, nil] ID of a pre-persisted user message (triggers delivery verification)
53
- def perform(session_id, message_id: nil)
54
- session = Session.find(session_id)
55
-
56
- # Atomic: only one job processes a session at a time.
57
- return unless claim_processing(session_id)
58
-
59
- run_analytical_brain_blocking(session)
60
-
61
- agent_loop = AgentLoop.new(session: session)
62
-
63
- if message_id
64
- deliver_persisted_message(session, message_id, agent_loop)
65
- else
66
- agent_loop.run
67
- end
68
-
69
- # Process any pending messages that arrived after the last tool round.
70
- loop do
71
- promoted = session.promote_pending_messages!
72
- break if promoted[:texts].empty? && promoted[:pairs].empty?
73
- agent_loop.run
74
- end
75
-
76
- session.schedule_analytical_brain!
77
- ensure
78
- release_processing(session_id)
79
- clear_interrupt(session_id)
80
- agent_loop&.finalize
81
- end
82
-
83
- private
84
-
85
- # Verifies LLM delivery for a pre-persisted user message.
86
- #
87
- # The message was already created and broadcast by the caller, so
88
- # the user sees their message immediately. This method makes the
89
- # first LLM API call — if it fails, the message is deleted and a
90
- # {Events::BounceBack} notifies clients to remove the phantom
91
- # message and restore the text to the input field. For
92
- # {Providers::Anthropic::AuthenticationError}, an additional
93
- # +authentication_required+ broadcast prompts the client to show
94
- # the token entry dialog.
95
- #
96
- # Unlike the standard path (which uses +retry_on+ / +discard_on+),
97
- # all errors here are caught and swallowed after emitting a
98
- # BounceBack — the job completes normally so ActiveJob does not
99
- # retry a message the user will re-send manually.
100
- #
101
- # After successful delivery, continues the agent loop (tool
102
- # execution, subsequent API calls).
103
- #
104
- # @param session [Session] the conversation session
105
- # @param message_id [Integer] database ID of the pre-persisted user message
106
- # @param agent_loop [AgentLoop] agent loop instance (reused for continuation)
107
- def deliver_persisted_message(session, message_id, agent_loop)
108
- message = Message.find_by(id: message_id, session_id: session.id)
109
- # Message may have been deleted between SessionChannel#speak and job
110
- # execution (e.g. user recalled the message). Exit silently — there
111
- # is nothing to deliver or bounce back.
112
- return unless message
113
-
114
- content = message.payload["content"]
115
-
116
- begin
117
- agent_loop.deliver!
118
- rescue => error
119
- message.destroy!
120
- Events::Bus.emit(Events::BounceBack.new(
121
- content: content,
122
- error: error.message,
123
- session_id: session.id,
124
- message_id: message_id
125
- ))
126
- broadcast_auth_required(session.id, error) if error.is_a?(Providers::Anthropic::AuthenticationError)
127
- return
128
- end
129
-
130
- agent_loop.run
131
- end
132
-
133
- def broadcast_auth_required(session_id, error)
134
- ActionCable.server.broadcast(
135
- "session_#{session_id}",
136
- {"action" => AUTH_REQUIRED_ACTION, "message" => error.message}
137
- )
138
- end
139
-
140
- # Runs the analytical brain synchronously before the main agent loop.
141
- # Respects the blocking_on_user_message setting and session guards
142
- # (skips sub-agents and sessions with too few messages).
143
- def run_analytical_brain_blocking(session)
144
- return unless Anima::Settings.analytical_brain_blocking_on_user_message
145
- return if session.sub_agent?
146
-
147
- AnalyticalBrain::Runner.new(session).call
148
- rescue => error
149
- # The analytical brain is best-effort: skill activation enhances the
150
- # response but the main agent must still reply even if it fails.
151
- msg = "FAILED (blocking) session=#{session.id}: #{error.class}: #{error.message}"
152
- Rails.logger.error("Analytical brain #{msg}")
153
- AnalyticalBrain.logger.error("#{msg}\n#{error.backtrace&.first(10)&.join("\n")}")
154
- end
155
-
156
- # Sets the session's processing flag atomically. Returns true if this
157
- # job claimed the lock, false if another job already holds it.
158
- # Broadcasts +session_state: llm_generating+ and the state change to
159
- # the parent session's HUD.
160
- def claim_processing(session_id)
161
- claimed = Session.where(id: session_id, processing: false).update_all(processing: true) == 1
162
- if claimed
163
- session = Session.find_by(id: session_id)
164
- session&.broadcast_session_state("llm_generating")
165
- session&.broadcast_children_update_to_parent
166
- end
167
- claimed
168
- end
169
-
170
- # Clears the processing flag so the session can accept new jobs.
171
- # Broadcasts +session_state: idle+ to the session stream (replaces
172
- # the old +processing_stopped+ action) and +children_updated+ to the
173
- # parent session's HUD.
174
- def release_processing(session_id)
175
- Session.where(id: session_id).update_all(processing: false)
176
- session = Session.find_by(id: session_id)
177
- session&.broadcast_session_state("idle")
178
- session&.broadcast_children_update_to_parent
179
- end
180
-
181
- # Safety-net clearing of the interrupt flag.
182
- def clear_interrupt(session_id)
183
- Session.where(id: session_id, interrupt_requested: true).update_all(interrupt_requested: false)
184
- end
185
-
186
- # Emits a system message before each retry so the user sees
187
- # "retrying..." instead of nothing.
188
- def retry_job(options = {})
189
- error = options[:error]
190
- wait = options[:wait]
191
-
192
- Events::Bus.emit(Events::SystemMessage.new(
193
- content: "#{error.message} — retrying in #{wait.to_i}s...",
194
- session_id: arguments.first
195
- ))
196
-
197
- super
198
- end
199
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Runs the analytical brain — a phantom LLM loop that observes the main
4
- # session and performs background maintenance (currently: session naming).
5
- #
6
- # Replaces {GenerateSessionNameJob} with a tool-based architecture that
7
- # future tickets will expand with skill activation, goal tracking, etc.
8
- #
9
- # Scheduling guards live in {Session#schedule_analytical_brain!} — this
10
- # job always runs when called.
11
- #
12
- # @example
13
- # AnalyticalBrainJob.perform_later(session.id)
14
- class AnalyticalBrainJob < ApplicationJob
15
- queue_as :default
16
-
17
- retry_on Providers::Anthropic::TransientError,
18
- wait: :polynomially_longer, attempts: 3
19
-
20
- discard_on ActiveRecord::RecordNotFound
21
- discard_on Providers::Anthropic::AuthenticationError
22
-
23
- # @param session_id [Integer] the main Session to analyze
24
- def perform(session_id)
25
- brain_log = AnalyticalBrain.logger
26
- session = Session.find(session_id)
27
- brain_log.info("async job started for session=#{session_id}")
28
- AnalyticalBrain::Runner.new(session).call
29
- rescue => error
30
- brain_log.error("FAILED (async) session=#{session_id}: #{error.class}: #{error.message}")
31
- raise
32
- end
33
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Counts tokens in a message's payload via the Anthropic API and
4
- # caches the result on the message record. Enqueued automatically
5
- # after each LLM message is created.
6
- class CountMessageTokensJob < ApplicationJob
7
- queue_as :default
8
-
9
- retry_on Providers::Anthropic::Error, wait: :polynomially_longer, attempts: 3
10
- discard_on ActiveRecord::RecordNotFound
11
-
12
- # @param message_id [Integer] the Message record to count tokens for
13
- def perform(message_id)
14
- message = Message.find(message_id)
15
- return if already_counted?(message)
16
-
17
- provider = Providers::Anthropic.new
18
- api_messages = [{role: message.api_role, content: message.payload["content"].to_s}]
19
-
20
- token_count = provider.count_tokens(
21
- model: Anima::Settings.model,
22
- messages: api_messages
23
- )
24
-
25
- # Guard against parallel jobs: reload and re-check before writing.
26
- # Uses update! (not update_all) so {Message::Broadcasting} after_update_commit
27
- # broadcasts the updated token count to connected clients.
28
- message.reload
29
- return if already_counted?(message)
30
-
31
- message.update!(token_count: token_count)
32
- end
33
-
34
- private
35
-
36
- def already_counted?(message)
37
- message.token_count > 0
38
- end
39
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Runs passive recall after goal updates — searches message history for
4
- # context relevant to active goals and injects phantom tool_call/tool_response
5
- # pairs into the session's message stream.
6
- #
7
- # Phantom pairs ride the conveyor belt like regular messages, getting
8
- # cached, evicted, and compressed by Mneme naturally.
9
- #
10
- # @example
11
- # PassiveRecallJob.perform_later(session.id)
12
- class PassiveRecallJob < ApplicationJob
13
- queue_as :default
14
-
15
- discard_on ActiveRecord::RecordNotFound
16
-
17
- # @param session_id [Integer]
18
- def perform(session_id)
19
- session = Session.find(session_id)
20
- count = Mneme::PassiveRecall.new(session).call
21
-
22
- Mneme.logger.info("session=#{session_id} — passive recall injected #{count} phantom pairs") if count > 0
23
- end
24
- end