anima-core 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) 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 +474 -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/aoide/phantom_call_filter.rb +49 -0
  56. data/lib/{analytical_brain.rb → aoide.rb} +6 -3
  57. data/lib/events/authentication_required.rb +24 -0
  58. data/lib/events/bounce_back.rb +4 -4
  59. data/lib/events/eviction_completed.rb +28 -0
  60. data/lib/events/goal_created.rb +28 -0
  61. data/lib/events/goal_updated.rb +32 -0
  62. data/lib/events/llm_responded.rb +35 -0
  63. data/lib/events/message_created.rb +27 -0
  64. data/lib/events/message_updated.rb +25 -0
  65. data/lib/events/session_state_changed.rb +30 -0
  66. data/lib/events/skill_activated.rb +28 -0
  67. data/lib/events/start_melete.rb +36 -0
  68. data/lib/events/start_mneme.rb +33 -0
  69. data/lib/events/start_processing.rb +32 -0
  70. data/lib/events/subagent_evicted.rb +31 -0
  71. data/lib/events/subscribers/active_state_broadcaster.rb +27 -0
  72. data/lib/events/subscribers/authentication_broadcaster.rb +34 -0
  73. data/lib/events/subscribers/drain_kickoff.rb +20 -0
  74. data/lib/events/subscribers/eviction_broadcaster.rb +26 -0
  75. data/lib/events/subscribers/llm_response_handler.rb +145 -0
  76. data/lib/events/subscribers/melete_kickoff.rb +24 -0
  77. data/lib/events/subscribers/message_broadcaster.rb +34 -0
  78. data/lib/events/subscribers/mneme_kickoff.rb +24 -0
  79. data/lib/events/subscribers/mneme_scheduler.rb +21 -0
  80. data/lib/events/subscribers/persister.rb +6 -8
  81. data/lib/events/subscribers/session_state_broadcaster.rb +33 -0
  82. data/lib/events/subscribers/subagent_message_router.rb +26 -29
  83. data/lib/events/subscribers/subagent_visibility_broadcaster.rb +33 -0
  84. data/lib/events/subscribers/tool_response_creator.rb +33 -0
  85. data/lib/events/subscribers/transient_broadcaster.rb +1 -1
  86. data/lib/events/tool_executed.rb +34 -0
  87. data/lib/events/workflow_activated.rb +27 -0
  88. data/lib/llm/client.rb +41 -201
  89. data/lib/mcp/client_manager.rb +41 -46
  90. data/lib/mcp/stdio_transport.rb +9 -5
  91. data/lib/{analytical_brain → melete}/runner.rb +63 -68
  92. data/lib/{analytical_brain → melete}/tools/activate_skill.rb +1 -1
  93. data/lib/{analytical_brain → melete}/tools/assign_nickname.rb +2 -2
  94. data/lib/{analytical_brain → melete}/tools/everything_is_ready.rb +2 -2
  95. data/lib/{analytical_brain → melete}/tools/finish_goal.rb +3 -3
  96. data/lib/{analytical_brain → melete}/tools/goal_messaging.rb +4 -3
  97. data/lib/{analytical_brain → melete}/tools/read_workflow.rb +2 -2
  98. data/lib/{analytical_brain → melete}/tools/rename_session.rb +3 -3
  99. data/lib/{analytical_brain → melete}/tools/set_goal.rb +1 -1
  100. data/lib/{analytical_brain → melete}/tools/update_goal.rb +4 -4
  101. data/lib/melete.rb +26 -0
  102. data/lib/mneme/base_runner.rb +121 -0
  103. data/lib/mneme/l2_runner.rb +14 -20
  104. data/lib/mneme/recall_runner.rb +132 -0
  105. data/lib/mneme/runner.rb +118 -171
  106. data/lib/mneme/search.rb +104 -62
  107. data/lib/mneme/tools/nothing_to_surface.rb +25 -0
  108. data/lib/mneme/tools/save_snapshot.rb +2 -10
  109. data/lib/mneme/tools/surface_memory.rb +89 -0
  110. data/lib/mneme.rb +11 -5
  111. data/lib/shell_session.rb +303 -612
  112. data/lib/skills/definition.rb +2 -2
  113. data/lib/skills/registry.rb +1 -1
  114. data/lib/tools/base.rb +16 -0
  115. data/lib/tools/bash.rb +25 -57
  116. data/lib/tools/edit.rb +2 -0
  117. data/lib/tools/read.rb +2 -0
  118. data/lib/tools/registry.rb +79 -3
  119. data/lib/tools/{recall.rb → search_messages.rb} +19 -21
  120. data/lib/tools/spawn_specialist.rb +20 -10
  121. data/lib/tools/spawn_subagent.rb +24 -14
  122. data/lib/tools/subagent_prompts.rb +15 -4
  123. data/lib/tools/think.rb +1 -1
  124. data/lib/tools/{remember.rb → view_messages.rb} +10 -10
  125. data/lib/tools/write.rb +2 -0
  126. data/lib/tui/app.rb +5 -4
  127. data/lib/tui/braille_spinner.rb +7 -7
  128. data/lib/tui/decorators/base_decorator.rb +24 -3
  129. data/lib/tui/message_store.rb +93 -44
  130. data/lib/tui/screens/chat.rb +94 -20
  131. data/lib/tui/settings.rb +9 -2
  132. data/lib/workflows/definition.rb +3 -3
  133. data/lib/workflows/registry.rb +1 -1
  134. data/skills/github.md +38 -0
  135. data/templates/config.toml +4 -23
  136. data/workflows/review_pr.md +18 -14
  137. metadata +88 -28
  138. data/app/jobs/agent_request_job.rb +0 -199
  139. data/app/jobs/analytical_brain_job.rb +0 -33
  140. data/app/jobs/count_message_tokens_job.rb +0 -39
  141. data/app/jobs/passive_recall_job.rb +0 -24
  142. data/app/models/concerns/message/broadcasting.rb +0 -86
  143. data/lib/agent_loop.rb +0 -215
  144. data/lib/analytical_brain/tools/deactivate_skill.rb +0 -40
  145. data/lib/analytical_brain/tools/deactivate_workflow.rb +0 -35
  146. data/lib/events/agent_message.rb +0 -25
  147. data/lib/events/subscribers/message_collector.rb +0 -64
  148. data/lib/events/tool_call.rb +0 -31
  149. data/lib/events/tool_response.rb +0 -33
  150. data/lib/mneme/compressed_viewport.rb +0 -204
  151. data/lib/mneme/passive_recall.rb +0 -138
data/skills/github.md ADDED
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: github
3
+ description: "GitHub operations — issues, pull requests, releases, CI runs, comments. Activate when an issue or PR number comes up, or when the conversation turns to repo state."
4
+ ---
5
+
6
+ # GitHub
7
+
8
+ `gh` is installed and authenticated.
9
+
10
+ ## Sub-issues — REST only
11
+
12
+ Not in top-level `gh` commands. Use the issue's *global ID* (not its number):
13
+
14
+ ```bash
15
+ REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
16
+ ISSUE_ID=$(gh api repos/$REPO/issues/42 --jq '.id')
17
+
18
+ # add
19
+ gh api repos/$REPO/issues/36/sub_issues -X POST -F sub_issue_id=$ISSUE_ID
20
+
21
+ # list
22
+ gh api repos/$REPO/issues/36/sub_issues
23
+
24
+ # reorder (move one to sit after another)
25
+ gh api repos/$REPO/issues/36/sub_issues/priority -X PATCH \
26
+ -F sub_issue_id=$ISSUE_ID -F after_id=$AFTER_ID
27
+ ```
28
+
29
+ ## Reading JSON
30
+
31
+ `gh` JSON output is token-expensive. Pipe through `toon` — a lossless LLM-optimized format that round-trips back to JSON:
32
+
33
+ ```bash
34
+ gh pr view 459 --json title,body,reviewDecision | toon
35
+ gh api repos/$REPO/pulls/459/comments | toon
36
+ ```
37
+
38
+ Skip `toon` when the output is going into further tooling that needs raw JSON (jq, scripts).
@@ -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.1
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
@@ -367,35 +388,73 @@ files:
367
388
  - lib/anima/settings.rb
368
389
  - lib/anima/spinner.rb
369
390
  - lib/anima/version.rb
391
+ - lib/aoide.rb
392
+ - lib/aoide/phantom_call_filter.rb
370
393
  - lib/credential_store.rb
371
- - lib/events/agent_message.rb
394
+ - lib/events/authentication_required.rb
372
395
  - lib/events/base.rb
373
396
  - lib/events/bounce_back.rb
374
397
  - lib/events/bus.rb
398
+ - lib/events/eviction_completed.rb
399
+ - lib/events/goal_created.rb
400
+ - lib/events/goal_updated.rb
401
+ - lib/events/llm_responded.rb
402
+ - lib/events/message_created.rb
403
+ - lib/events/message_updated.rb
404
+ - lib/events/session_state_changed.rb
405
+ - lib/events/skill_activated.rb
406
+ - lib/events/start_melete.rb
407
+ - lib/events/start_mneme.rb
408
+ - lib/events/start_processing.rb
409
+ - lib/events/subagent_evicted.rb
375
410
  - lib/events/subscriber.rb
376
- - lib/events/subscribers/message_collector.rb
411
+ - lib/events/subscribers/active_state_broadcaster.rb
412
+ - lib/events/subscribers/authentication_broadcaster.rb
413
+ - lib/events/subscribers/drain_kickoff.rb
414
+ - lib/events/subscribers/eviction_broadcaster.rb
415
+ - lib/events/subscribers/llm_response_handler.rb
416
+ - lib/events/subscribers/melete_kickoff.rb
417
+ - lib/events/subscribers/message_broadcaster.rb
418
+ - lib/events/subscribers/mneme_kickoff.rb
419
+ - lib/events/subscribers/mneme_scheduler.rb
377
420
  - lib/events/subscribers/persister.rb
421
+ - lib/events/subscribers/session_state_broadcaster.rb
378
422
  - lib/events/subscribers/subagent_message_router.rb
423
+ - lib/events/subscribers/subagent_visibility_broadcaster.rb
424
+ - lib/events/subscribers/tool_response_creator.rb
379
425
  - lib/events/subscribers/transient_broadcaster.rb
380
426
  - lib/events/system_message.rb
381
- - lib/events/tool_call.rb
382
- - lib/events/tool_response.rb
427
+ - lib/events/tool_executed.rb
383
428
  - lib/events/user_message.rb
429
+ - lib/events/workflow_activated.rb
384
430
  - lib/llm/client.rb
385
431
  - lib/mcp/client_manager.rb
386
432
  - lib/mcp/config.rb
387
433
  - lib/mcp/health_check.rb
388
434
  - lib/mcp/secrets.rb
389
435
  - lib/mcp/stdio_transport.rb
436
+ - lib/melete.rb
437
+ - lib/melete/runner.rb
438
+ - lib/melete/tools/activate_skill.rb
439
+ - lib/melete/tools/assign_nickname.rb
440
+ - lib/melete/tools/everything_is_ready.rb
441
+ - lib/melete/tools/finish_goal.rb
442
+ - lib/melete/tools/goal_messaging.rb
443
+ - lib/melete/tools/read_workflow.rb
444
+ - lib/melete/tools/rename_session.rb
445
+ - lib/melete/tools/set_goal.rb
446
+ - lib/melete/tools/update_goal.rb
390
447
  - lib/mneme.rb
391
- - lib/mneme/compressed_viewport.rb
448
+ - lib/mneme/base_runner.rb
392
449
  - lib/mneme/l2_runner.rb
393
- - lib/mneme/passive_recall.rb
450
+ - lib/mneme/recall_runner.rb
394
451
  - lib/mneme/runner.rb
395
452
  - lib/mneme/search.rb
396
453
  - lib/mneme/tools/attach_messages_to_goals.rb
397
454
  - lib/mneme/tools/everything_ok.rb
455
+ - lib/mneme/tools/nothing_to_surface.rb
398
456
  - lib/mneme/tools/save_snapshot.rb
457
+ - lib/mneme/tools/surface_memory.rb
399
458
  - lib/providers/anthropic.rb
400
459
  - lib/shell_session.rb
401
460
  - lib/skills/definition.rb
@@ -408,14 +467,14 @@ files:
408
467
  - lib/tools/mcp_tool.rb
409
468
  - lib/tools/open_issue.rb
410
469
  - lib/tools/read.rb
411
- - lib/tools/recall.rb
412
470
  - lib/tools/registry.rb
413
- - lib/tools/remember.rb
414
471
  - lib/tools/response_truncator.rb
472
+ - lib/tools/search_messages.rb
415
473
  - lib/tools/spawn_specialist.rb
416
474
  - lib/tools/spawn_subagent.rb
417
475
  - lib/tools/subagent_prompts.rb
418
476
  - lib/tools/think.rb
477
+ - lib/tools/view_messages.rb
419
478
  - lib/tools/web_get.rb
420
479
  - lib/tools/write.rb
421
480
  - lib/tui/app.rb
@@ -540,6 +599,7 @@ files:
540
599
  - skills/draper-decorators/references/patterns.md
541
600
  - skills/draper-decorators/references/testing.md
542
601
  - skills/gh-issue.md
602
+ - skills/github.md
543
603
  - skills/mcp-server/SKILL.md
544
604
  - skills/mcp-server/examples/dynamic_tools.rb
545
605
  - 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