anima-core 1.2.0 → 1.4.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +14 -8
  3. data/README.md +96 -23
  4. data/agents/codebase-analyzer.md +1 -1
  5. data/agents/codebase-pattern-finder.md +1 -1
  6. data/agents/documentation-researcher.md +1 -1
  7. data/agents/thoughts-analyzer.md +1 -1
  8. data/agents/web-search-researcher.md +2 -2
  9. data/app/channels/session_channel.rb +53 -35
  10. data/app/decorators/tool_call_decorator.rb +7 -7
  11. data/app/decorators/user_message_decorator.rb +3 -17
  12. data/app/jobs/agent_request_job.rb +15 -6
  13. data/app/jobs/passive_recall_job.rb +6 -11
  14. data/app/models/concerns/message/broadcasting.rb +1 -0
  15. data/app/models/goal.rb +14 -0
  16. data/app/models/message.rb +13 -31
  17. data/app/models/pending_message.rb +191 -0
  18. data/app/models/secret.rb +72 -0
  19. data/app/models/session.rb +480 -271
  20. data/bin/inspect-cassette +144 -0
  21. data/bin/release +212 -0
  22. data/bin/with-llms +20 -0
  23. data/config/database.yml +1 -0
  24. data/config/environments/test.rb +5 -0
  25. data/config/initializers/time_nanoseconds.rb +11 -0
  26. data/db/cable_structure.sql +9 -0
  27. data/db/migrate/20260328100000_create_secrets.rb +15 -0
  28. data/db/migrate/20260328152142_add_evicted_at_to_goals.rb +6 -0
  29. data/db/migrate/20260329120000_create_pending_messages.rb +11 -0
  30. data/db/migrate/20260330120000_add_source_to_pending_messages.rb +8 -0
  31. data/db/migrate/20260401180000_add_api_metrics_to_messages.rb +7 -0
  32. data/db/migrate/20260401210935_remove_recalled_message_ids_from_sessions.rb +5 -0
  33. data/db/migrate/20260403080031_add_initial_cwd_to_sessions.rb +5 -0
  34. data/db/queue_structure.sql +61 -0
  35. data/db/structure.sql +120 -0
  36. data/lib/agent_loop.rb +53 -51
  37. data/lib/agents/definition.rb +1 -1
  38. data/lib/analytical_brain/runner.rb +19 -6
  39. data/lib/analytical_brain/tools/activate_skill.rb +2 -2
  40. data/lib/analytical_brain/tools/assign_nickname.rb +1 -1
  41. data/lib/analytical_brain/tools/deactivate_skill.rb +2 -1
  42. data/lib/analytical_brain/tools/deactivate_workflow.rb +2 -1
  43. data/lib/analytical_brain/tools/finish_goal.rb +3 -0
  44. data/lib/analytical_brain/tools/goal_messaging.rb +28 -0
  45. data/lib/analytical_brain/tools/read_workflow.rb +2 -2
  46. data/lib/analytical_brain/tools/set_goal.rb +5 -1
  47. data/lib/analytical_brain/tools/update_goal.rb +5 -1
  48. data/lib/anima/cli/mcp/secrets.rb +4 -4
  49. data/lib/anima/cli/mcp.rb +4 -4
  50. data/lib/anima/cli.rb +41 -13
  51. data/lib/anima/installer.rb +20 -1
  52. data/lib/anima/settings.rb +37 -2
  53. data/lib/anima/version.rb +1 -1
  54. data/lib/anima.rb +1 -1
  55. data/lib/credential_store.rb +17 -66
  56. data/lib/events/agent_message.rb +14 -0
  57. data/lib/events/base.rb +1 -1
  58. data/lib/events/subscribers/persister.rb +12 -18
  59. data/lib/events/subscribers/subagent_message_router.rb +18 -9
  60. data/lib/events/user_message.rb +2 -13
  61. data/lib/llm/client.rb +91 -50
  62. data/lib/mcp/config.rb +2 -2
  63. data/lib/mcp/secrets.rb +7 -8
  64. data/lib/mneme/compressed_viewport.rb +9 -5
  65. data/lib/mneme/passive_recall.rb +85 -16
  66. data/lib/mneme/runner.rb +15 -4
  67. data/lib/providers/anthropic.rb +112 -7
  68. data/lib/shell_session.rb +239 -18
  69. data/lib/tools/base.rb +22 -0
  70. data/lib/tools/bash.rb +61 -7
  71. data/lib/tools/edit.rb +2 -2
  72. data/lib/tools/mark_goal_completed.rb +85 -0
  73. data/lib/tools/read.rb +2 -1
  74. data/lib/tools/recall.rb +98 -0
  75. data/lib/tools/registry.rb +41 -7
  76. data/lib/tools/remember.rb +1 -1
  77. data/lib/tools/response_truncator.rb +70 -0
  78. data/lib/tools/spawn_specialist.rb +11 -8
  79. data/lib/tools/spawn_subagent.rb +19 -13
  80. data/lib/tools/subagent_prompts.rb +41 -5
  81. data/lib/tools/think.rb +23 -0
  82. data/lib/tools/write.rb +1 -1
  83. data/lib/tui/app.rb +545 -137
  84. data/lib/tui/braille_spinner.rb +152 -0
  85. data/lib/tui/cable_client.rb +13 -20
  86. data/lib/tui/decorators/base_decorator.rb +40 -11
  87. data/lib/tui/decorators/bash_decorator.rb +3 -3
  88. data/lib/tui/decorators/edit_decorator.rb +7 -4
  89. data/lib/tui/decorators/read_decorator.rb +6 -8
  90. data/lib/tui/decorators/think_decorator.rb +4 -6
  91. data/lib/tui/decorators/web_get_decorator.rb +4 -3
  92. data/lib/tui/decorators/write_decorator.rb +7 -4
  93. data/lib/tui/flash.rb +19 -14
  94. data/lib/tui/formatting.rb +33 -0
  95. data/lib/tui/input_buffer.rb +6 -6
  96. data/lib/tui/message_store.rb +159 -27
  97. data/lib/tui/performance_logger.rb +2 -3
  98. data/lib/tui/screens/chat.rb +302 -103
  99. data/lib/tui/settings.rb +86 -0
  100. data/skills/activerecord/SKILL.md +1 -1
  101. data/skills/dragonruby/SKILL.md +1 -1
  102. data/skills/draper-decorators/SKILL.md +1 -1
  103. data/skills/gh-issue.md +1 -1
  104. data/skills/mcp-server/SKILL.md +1 -1
  105. data/skills/ratatui-ruby/SKILL.md +1 -1
  106. data/skills/rspec/SKILL.md +1 -1
  107. data/templates/config.toml +30 -1
  108. data/templates/tui.toml +209 -0
  109. metadata +24 -3
  110. data/config/initializers/fts5_schema_dump.rb +0 -21
  111. data/lib/environment_probe.rb +0 -232
@@ -1,23 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tools
4
- # Shared prompt fragments and nickname logic for tools that spawn sub-agent sessions.
4
+ # Shared prompt fragments and spawn logic for tools that create sub-agent sessions.
5
5
  # Included by {SpawnSubagent} and {SpawnSpecialist} to avoid duplication.
6
6
  module SubagentPrompts
7
- COMMUNICATION_INSTRUCTION = "Your text messages are automatically forwarded to the parent agent. " \
8
- "When you finish, write your final summary and stop — no special tool needed. " \
9
- "If you need clarification, just ask the parent can reply."
7
+ # Prepended to every sub-agent's stored prompt after nickname assignment.
8
+ # Establishes identity before any other instruction.
9
+ IDENTITY_TEMPLATE = "You are %s, a sub-agent of the primary agent."
10
+
11
+ COMMUNICATION_INSTRUCTION = "Your messages reach the parent automatically. " \
12
+ "Ask if you need clarification — the parent can reply."
10
13
 
11
14
  private
12
15
 
13
- # Runs the analytical brain synchronously to assign a nickname.
16
+ # Creates the sub-agent's Goal from the task description, inserts the
17
+ # task as the first user message, and pins it to the Goal so it survives
18
+ # viewport eviction for as long as the Goal is active.
19
+ #
20
+ # @param child [Session] the newly created child session
21
+ # @param task [String] the task description
22
+ # @return [void]
23
+ def create_goal_with_pinned_task(child, task)
24
+ goal = child.goals.create!(description: task)
25
+ message = child.create_user_message(task)
26
+ pin = PinnedMessage.create!(
27
+ message: message,
28
+ display_text: task.truncate(PinnedMessage::MAX_DISPLAY_TEXT_LENGTH)
29
+ )
30
+ GoalPinnedMessage.create!(goal: goal, pinned_message: pin)
31
+ end
32
+
33
+ # Runs the analytical brain synchronously to assign a nickname,
34
+ # then prepends identity context to the stored prompt.
14
35
  # Falls back to a sequential "agent-N" name on any failure.
36
+ # Identity injection runs in +ensure+ so it applies to both
37
+ # brain-assigned and fallback nicknames.
15
38
  def assign_nickname_via_brain(child)
16
39
  AnalyticalBrain::Runner.new(child).call
17
40
  child.reload
18
41
  rescue => error
19
42
  Rails.logger.warn("Sub-agent nickname assignment failed: #{error.message}")
20
43
  child.update!(name: fallback_nickname)
44
+ ensure
45
+ inject_identity_context(child)
46
+ end
47
+
48
+ # Prepends identity context (nickname + sub-agent status) to the child's
49
+ # stored prompt. Called after nickname assignment so the sub-agent knows
50
+ # who it is from first token.
51
+ #
52
+ # @param child [Session] the child session with a nickname already set
53
+ # @return [void]
54
+ def inject_identity_context(child)
55
+ identity = format(IDENTITY_TEMPLATE, child.name)
56
+ child.update!(prompt: "#{identity}\n#{child.prompt}")
21
57
  end
22
58
 
23
59
  def fallback_nickname
data/lib/tools/think.rb CHANGED
@@ -13,6 +13,10 @@ module Tools
13
13
  # - **inner** (default) — silent reasoning, visible only in verbose/debug
14
14
  # - **aloud** — narration shown in all view modes with a thought bubble
15
15
  #
16
+ # The +maxLength+ on thoughts is controlled by +thinking_budget+ in settings.
17
+ # Sub-agents receive half the main agent's budget — their tasks are scoped
18
+ # and less complex, so runaway reasoning is a stronger signal of confusion.
19
+ #
16
20
  # @example Silent planning between tool calls
17
21
  # think(thoughts: "Three auth failures — likely a config issue, not individual tests.")
18
22
  #
@@ -23,6 +27,8 @@ module Tools
23
27
 
24
28
  def self.description = "Think out loud or silently."
25
29
 
30
+ # Schema is static — maxLength is injected at runtime by the registry
31
+ # via {#dynamic_schema} when session context is available.
26
32
  def self.input_schema
27
33
  {
28
34
  type: "object",
@@ -38,6 +44,23 @@ module Tools
38
44
  }
39
45
  end
40
46
 
47
+ # @param session [Session, nil] current session for budget calculation
48
+ def initialize(session: nil, **)
49
+ @session = session
50
+ end
51
+
52
+ # Returns the tool schema with a thinking budget applied as maxLength
53
+ # on the thoughts property. Sub-agents get half the budget.
54
+ #
55
+ # @return [Hash] Anthropic tool schema with maxLength constraint
56
+ def dynamic_schema
57
+ schema = self.class.schema.deep_dup
58
+ budget = Anima::Settings.thinking_budget
59
+ budget /= 2 if @session&.sub_agent?
60
+ schema[:input_schema][:properties][:thoughts][:maxLength] = budget
61
+ schema
62
+ end
63
+
41
64
  # @param input [Hash] with "thoughts" and optional "visibility"
42
65
  # @return [String] acknowledgement — the value is in the call, not the result
43
66
  def execute(input)
data/lib/tools/write.rb CHANGED
@@ -15,7 +15,7 @@ module Tools
15
15
  # tool.execute("path" => "README.md", "content" => "# Title\n")
16
16
  # # => "Wrote 9 bytes to /home/user/project/README.md"
17
17
  class Write < Base
18
- def self.tool_name = "write"
18
+ def self.tool_name = "write_file"
19
19
 
20
20
  def self.description = "Write file."
21
21