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
@@ -18,8 +18,12 @@
18
18
  # @!attribute level
19
19
  # @return [Integer] compression level (1 = from raw messages, 2 = from L1 snapshots)
20
20
  # @!attribute token_count
21
- # @return [Integer] cached token count of the summary text
21
+ # @return [Integer] token count of the summary text. Seeded with a
22
+ # local estimate on create and later refined by {CountTokensJob}
23
+ # using the real Anthropic tokenizer.
22
24
  class Snapshot < ApplicationRecord
25
+ include TokenEstimation
26
+
23
27
  belongs_to :session
24
28
 
25
29
  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
@@ -30,12 +34,17 @@ class Snapshot < ApplicationRecord
30
34
  validates :from_message_id, presence: true
31
35
  validates :to_message_id, presence: true
32
36
  validates :level, presence: true, numericality: {greater_than: 0}
33
- validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
37
+ validates :token_count, numericality: {greater_than_or_equal_to: 0}
34
38
  validate :from_message_id_not_after_to_message_id
35
39
 
36
40
  scope :for_level, ->(level) { where(level: level) }
37
41
  scope :chronological, -> { order(:from_message_id) }
38
42
 
43
+ # @return [String] summary text used for token estimation and remote counting
44
+ def tokenization_text
45
+ text.to_s
46
+ end
47
+
39
48
  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
40
49
  # Used to determine which L1 snapshots are still "live" in the viewport.
41
50
  scope :not_covered_by_l2, -> {
@@ -48,29 +57,10 @@ class Snapshot < ApplicationRecord
48
57
  )
49
58
  }
50
59
 
51
- # Snapshots whose source messages have fully evicted from the sliding window.
52
- # A snapshot is visible when its entire message range precedes the first
53
- # message currently in the viewport.
54
- #
55
- # @param first_message_id [Integer] the first message ID in the sliding window
56
- scope :source_messages_evicted, ->(first_message_id) {
57
- where("to_message_id < ?", first_message_id)
58
- }
59
-
60
- # @return [Integer] token cost, using cached count or heuristic estimate
61
- def token_cost
62
- token_count.positive? ? token_count : estimate_tokens
63
- end
64
-
65
60
  private
66
61
 
67
62
  def from_message_id_not_after_to_message_id
68
63
  return unless from_message_id && to_message_id
69
64
  errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
70
65
  end
71
-
72
- # @return [Integer] estimated token count (at least 1)
73
- def estimate_tokens
74
- [(text.bytesize / Message::BYTES_PER_TOKEN.to_f).ceil, 1].max
75
- end
76
66
  end
data/bin/inspect-cassette CHANGED
@@ -106,6 +106,8 @@ puts " (#{omitted} non-LLM HTTP requests omitted)" if omitted > 0
106
106
  puts
107
107
 
108
108
  seen_messages = 0
109
+ previous_first = nil
110
+
109
111
  llm_interactions.each_with_index do |interaction, round|
110
112
  request_body = JSON.parse(interaction["request"]["body"]["string"])
111
113
  messages = request_body["messages"] || []
@@ -114,17 +116,28 @@ llm_interactions.each_with_index do |interaction, round|
114
116
  status = interaction["response"]["status"]["code"]
115
117
  model = response_body["model"] || request_body["model"]
116
118
 
119
+ # Detect a new conversation: cassettes can interleave multiple phantom
120
+ # sessions (Mneme, Melete, Aoide's drain) that each keep their own
121
+ # independent message history. When a new round's first message differs
122
+ # from the prior round's, or the round has fewer messages than we've
123
+ # already shown, we've crossed into a new conversation — rewind the
124
+ # dedup counter so we render from the top again.
125
+ first = messages.first
126
+ if round > 0 && (previous_first != first || messages.size < seen_messages)
127
+ puts "─── new conversation ───"
128
+ puts
129
+ seen_messages = 0
130
+ end
131
+ previous_first = first
132
+
117
133
  puts "── Round #{round + 1} (#{status} #{model}) ──"
118
134
  puts
119
135
 
120
- # Print only messages we haven't shown yet
121
- new_messages = messages[seen_messages..]
122
- new_messages.each do |msg|
136
+ messages[seen_messages..].to_a.each do |msg|
123
137
  puts format_message(msg)
124
138
  puts
125
139
  end
126
140
 
127
- # Print the assistant response from this round
128
141
  if response_body["content"]
129
142
  assistant_msg = {"role" => "assistant", "content" => response_body["content"]}
130
143
  puts format_message(assistant_msg)
@@ -8,6 +8,7 @@ require "active_record/railtie"
8
8
  require "active_job/railtie"
9
9
  require "action_cable/engine"
10
10
  require "rails/test_unit/railtie"
11
+ require "aasm"
11
12
  require "draper"
12
13
  require "solid_cable"
13
14
  require "solid_queue"
@@ -3,17 +3,84 @@
3
3
  # Registers global EventBus subscribers at boot time.
4
4
  # Subscribers registered here receive all events regardless of which
5
5
  # process emitted them (brain server, background job, etc.).
6
+ #
7
+ # Three event layers:
8
+ # 1. Domain events (anima.agent_message, anima.system_message,
9
+ # anima.goal.created, anima.goal.updated, etc.) — raw intent
10
+ # 2. Lifecycle events (anima.message.created) — emitted after persistence
11
+ # 3. Drain pipeline events (anima.session.start_melete, start_mneme,
12
+ # start_processing, llm_responded, tool_executed) — the event-driven
13
+ # agent loop that promotes PendingMessages into the conversation,
14
+ # calls the LLM, and dispatches tool execution (epic #427). Pipeline
15
+ # order: Melete first; Mneme conditional on a goal change during the
16
+ # Melete run; Processing always closes the chain.
17
+ #
18
+ # Persister bridges layer 1 → 2 by creating Message records whose
19
+ # after_create_commit emits MessageCreated events.
20
+ MESSAGE_LIFECYCLE_FILTER = ->(event) { event[:name].start_with?("anima.message.") }
21
+ MESSAGE_CREATED_FILTER = ->(event) { event[:name] == "anima.message.created" }
22
+ EVICTION_FILTER = ->(event) { event[:name] == "anima.eviction.completed" }
23
+ SUBAGENT_EVICTED_FILTER = ->(event) { event[:name] == "anima.subagent.evicted" }
24
+ ACTIVE_STATE_TRIGGER_FILTER = ->(event) {
25
+ %w[anima.skill.activated anima.workflow.activated anima.eviction.completed].include?(event[:name])
26
+ }
27
+ SESSION_STATE_FILTER = ->(event) { event[:name] == "anima.session.state_changed" }
28
+ AUTHENTICATION_REQUIRED_FILTER = ->(event) { event[:name] == "anima.authentication_required" }
29
+ START_MNEME_FILTER = ->(event) { event[:name] == "anima.session.start_mneme" }
30
+ START_MELETE_FILTER = ->(event) { event[:name] == "anima.session.start_melete" }
31
+ START_PROCESSING_FILTER = ->(event) { event[:name] == "anima.session.start_processing" }
32
+ LLM_RESPONDED_FILTER = ->(event) { event[:name] == "anima.session.llm_responded" }
33
+ TOOL_EXECUTED_FILTER = ->(event) { event[:name] == "anima.session.tool_executed" }
34
+
6
35
  Rails.application.config.after_initialize do
36
+ # SessionStateBroadcaster also runs in tests — job/channel specs assert
37
+ # ActionCable broadcasts, which now flow through this subscriber.
38
+ Events::Bus.subscribe(Events::Subscribers::SessionStateBroadcaster.new, &SESSION_STATE_FILTER)
39
+
40
+ # AuthenticationBroadcaster turns provider auth failures into a
41
+ # conversation-visible system_message plus a client-side action frame.
42
+ Events::Bus.subscribe(Events::Subscribers::AuthenticationBroadcaster.new, &AUTHENTICATION_REQUIRED_FILTER)
43
+
44
+ # Drain pipeline — registered in all environments so job/channel specs
45
+ # can drive the full event-driven loop end-to-end.
46
+ Events::Bus.subscribe(Events::Subscribers::MnemeKickoff.new, &START_MNEME_FILTER)
47
+ Events::Bus.subscribe(Events::Subscribers::MeleteKickoff.new, &START_MELETE_FILTER)
48
+ Events::Bus.subscribe(Events::Subscribers::DrainKickoff.new, &START_PROCESSING_FILTER)
49
+ Events::Bus.subscribe(Events::Subscribers::LLMResponseHandler.new, &LLM_RESPONDED_FILTER)
50
+ Events::Bus.subscribe(Events::Subscribers::ToolResponseCreator.new, &TOOL_EXECUTED_FILTER)
51
+
7
52
  unless Rails.env.test?
53
+ # --- Domain event subscribers (layer 1) ---
54
+
8
55
  # Global persister handles events from all sessions (brain server, background jobs).
9
- # Skips non-pending user messages — those are persisted by their callers
10
- # (SessionChannel#speak for idle sessions, AgentLoop#process for direct usage).
56
+ # Skips user messages — those are promoted from PendingMessage by {DrainJob}.
11
57
  Events::Bus.subscribe(Events::Subscribers::Persister.new)
12
58
 
13
59
  # Bridges transient events (e.g. BounceBack) to ActionCable for client delivery.
14
60
  Events::Bus.subscribe(Events::Subscribers::TransientBroadcaster.new)
15
61
 
16
- # Routes text messages between parent and sub-agent sessions via @mentions.
17
- Events::Bus.subscribe(Events::Subscribers::SubagentMessageRouter.new)
62
+ # --- Lifecycle event subscribers (layer 2) ---
63
+
64
+ # Broadcasts message creates and updates to connected WebSocket clients.
65
+ Events::Bus.subscribe(Events::Subscribers::MessageBroadcaster.new, &MESSAGE_LIFECYCLE_FILTER)
66
+
67
+ # Routes agent_message Messages between parent and sub-agent sessions
68
+ # via @mentions. Hangs off the Message persistence lifecycle, so every
69
+ # persisted agent_message is a routing opportunity.
70
+ Events::Bus.subscribe(Events::Subscribers::SubagentMessageRouter.new, &MESSAGE_CREATED_FILTER)
71
+
72
+ # Checks whether Mneme should run after each persisted message.
73
+ Events::Bus.subscribe(Events::Subscribers::MnemeScheduler.new, &MESSAGE_CREATED_FILTER)
74
+
75
+ # Broadcasts eviction cutoff to clients after Mneme advances the boundary.
76
+ Events::Bus.subscribe(Events::Subscribers::EvictionBroadcaster.new, &EVICTION_FILTER)
77
+
78
+ # Broadcasts sub-agent HUD removal when eviction takes the last trace
79
+ # of a sub-agent past the Mneme boundary.
80
+ Events::Bus.subscribe(Events::Subscribers::SubagentVisibilityBroadcaster.new, &SUBAGENT_EVICTED_FILTER)
81
+
82
+ # Rebroadcasts active skills/workflow on every event that can change
83
+ # the set: skill activation, workflow activation, or eviction.
84
+ Events::Bus.subscribe(Events::Subscribers::ActiveStateBroadcaster.new, &ACTIVE_STATE_TRIGGER_FILTER)
18
85
  end
19
86
  end
@@ -4,6 +4,8 @@
4
4
  Rails.autoloaders.each do |autoloader|
5
5
  autoloader.inflector.inflect(
6
6
  "llm" => "LLM",
7
- "tui" => "TUI"
7
+ "tui" => "TUI",
8
+ "llm_response_handler" => "LLMResponseHandler",
9
+ "llm_responded" => "LLMResponded"
8
10
  )
9
11
  end
@@ -1,9 +1,9 @@
1
- CREATE TABLE IF NOT EXISTS "solid_cable_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "channel" blob(1024) NOT NULL, "channel_hash" integer(8) NOT NULL, "created_at" datetime(6) NOT NULL, "payload" blob(536870912) NOT NULL);
1
+ CREATE TABLE "solid_cable_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "channel" blob(1024) NOT NULL, "channel_hash" integer(8) NOT NULL, "created_at" datetime(6) NOT NULL, "payload" blob(536870912) NOT NULL);
2
2
  CREATE INDEX "index_solid_cable_messages_on_channel" ON "solid_cable_messages" ("channel");
3
3
  CREATE INDEX "index_solid_cable_messages_on_channel_hash" ON "solid_cable_messages" ("channel_hash");
4
4
  CREATE INDEX "index_solid_cable_messages_on_created_at" ON "solid_cable_messages" ("created_at");
5
- CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
6
- CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
5
+ CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
6
+ CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
7
7
  INSERT INTO "schema_migrations" (version) VALUES
8
8
  ('1');
9
9
 
@@ -0,0 +1,5 @@
1
+ class RemoveViewportMessageIdsFromSessions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ remove_column :sessions, :viewport_message_ids, :json, default: "[]", null: false
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class RemoveMnemeSnapshotPointerColumnsFromSessions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ remove_column :sessions, :mneme_snapshot_first_message_id, :integer
4
+ remove_column :sessions, :mneme_snapshot_last_message_id, :integer
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddTokenCountToPinnedMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :pinned_messages, :token_count, :integer, null: false, default: 0
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class RemoveActiveSkillsAndWorkflowFromSessions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ remove_column :sessions, :active_skills, :json
4
+ remove_column :sessions, :active_workflow, :string
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class ReplaceProcessingWithAasmState < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :sessions, :aasm_state, :string, default: "idle", null: false
4
+ remove_column :sessions, :processing, :boolean, default: false, null: false
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class AddKindAndMessageTypeToPendingMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :pending_messages, :kind, :string, default: "active", null: false
4
+ add_column :pending_messages, :message_type, :string
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class AddDrainFieldsToPendingMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :pending_messages, :tool_use_id, :string
4
+ add_column :pending_messages, :success, :boolean
5
+ add_column :pending_messages, :bounce_back, :boolean, default: false, null: false
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class DropPendingMessagesKindDefault < ActiveRecord::Migration[8.1]
2
+ def change
3
+ change_column_default :pending_messages, :kind, from: "active", to: nil
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class AddDrainIndexesToPendingMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_index :pending_messages, [:session_id, :message_type, :created_at],
4
+ name: "index_pending_messages_on_drain_ordering"
5
+ add_index :pending_messages, [:session_id, :tool_use_id],
6
+ name: "index_pending_messages_on_session_and_tool_use"
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Sub-agent sessions track presence in the parent's HUD panel through the
4
+ # +hud_visible+ boolean. Mneme flips it to +false+ when viewport eviction
5
+ # removes every trace of the sub-agent (spawn pair + response pairs). The
6
+ # +spawn_tool_use_id+ column lets the trace query find the spawn pair
7
+ # directly instead of string-matching the +spawn_subagent+ tool_input.
8
+ class AddHudVisibilityToSessions < ActiveRecord::Migration[8.1]
9
+ def change
10
+ add_column :sessions, :hud_visible, :boolean, default: true, null: false
11
+ add_column :sessions, :spawn_tool_use_id, :string
12
+
13
+ add_index :sessions, [:parent_session_id, :hud_visible]
14
+ end
15
+ end
@@ -1,61 +1,61 @@
1
- CREATE TABLE IF NOT EXISTS "solid_queue_jobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "active_job_id" varchar, "arguments" text, "class_name" varchar NOT NULL, "concurrency_key" varchar, "created_at" datetime(6) NOT NULL, "finished_at" datetime(6), "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, "scheduled_at" datetime(6), "updated_at" datetime(6) NOT NULL);
1
+ CREATE TABLE "solid_queue_jobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "active_job_id" varchar, "arguments" text, "class_name" varchar NOT NULL, "concurrency_key" varchar, "created_at" datetime(6) NOT NULL, "finished_at" datetime(6), "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, "scheduled_at" datetime(6), "updated_at" datetime(6) NOT NULL);
2
2
  CREATE INDEX "index_solid_queue_jobs_on_active_job_id" ON "solid_queue_jobs" ("active_job_id");
3
3
  CREATE INDEX "index_solid_queue_jobs_on_class_name" ON "solid_queue_jobs" ("class_name");
4
4
  CREATE INDEX "index_solid_queue_jobs_on_finished_at" ON "solid_queue_jobs" ("finished_at");
5
5
  CREATE INDEX "index_solid_queue_jobs_for_filtering" ON "solid_queue_jobs" ("queue_name", "finished_at");
6
6
  CREATE INDEX "index_solid_queue_jobs_for_alerting" ON "solid_queue_jobs" ("scheduled_at", "finished_at");
7
- CREATE TABLE IF NOT EXISTS "solid_queue_pauses" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "queue_name" varchar NOT NULL);
7
+ CREATE TABLE "solid_queue_pauses" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "queue_name" varchar NOT NULL);
8
8
  CREATE UNIQUE INDEX "index_solid_queue_pauses_on_queue_name" ON "solid_queue_pauses" ("queue_name");
9
- CREATE TABLE IF NOT EXISTS "solid_queue_processes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "hostname" varchar, "kind" varchar NOT NULL, "last_heartbeat_at" datetime(6) NOT NULL, "metadata" text, "name" varchar NOT NULL, "pid" integer NOT NULL, "supervisor_id" bigint);
9
+ CREATE TABLE "solid_queue_processes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "hostname" varchar, "kind" varchar NOT NULL, "last_heartbeat_at" datetime(6) NOT NULL, "metadata" text, "name" varchar NOT NULL, "pid" integer NOT NULL, "supervisor_id" bigint);
10
10
  CREATE INDEX "index_solid_queue_processes_on_last_heartbeat_at" ON "solid_queue_processes" ("last_heartbeat_at");
11
11
  CREATE UNIQUE INDEX "index_solid_queue_processes_on_name_and_supervisor_id" ON "solid_queue_processes" ("name", "supervisor_id");
12
12
  CREATE INDEX "index_solid_queue_processes_on_supervisor_id" ON "solid_queue_processes" ("supervisor_id");
13
- CREATE TABLE IF NOT EXISTS "solid_queue_recurring_tasks" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "arguments" text, "class_name" varchar, "command" varchar(2048), "created_at" datetime(6) NOT NULL, "description" text, "key" varchar NOT NULL, "priority" integer DEFAULT 0, "queue_name" varchar, "schedule" varchar NOT NULL, "static" boolean DEFAULT TRUE NOT NULL, "updated_at" datetime(6) NOT NULL);
13
+ CREATE TABLE "solid_queue_recurring_tasks" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "arguments" text, "class_name" varchar, "command" varchar(2048), "created_at" datetime(6) NOT NULL, "description" text, "key" varchar NOT NULL, "priority" integer DEFAULT 0, "queue_name" varchar, "schedule" varchar NOT NULL, "static" boolean DEFAULT TRUE NOT NULL, "updated_at" datetime(6) NOT NULL);
14
14
  CREATE UNIQUE INDEX "index_solid_queue_recurring_tasks_on_key" ON "solid_queue_recurring_tasks" ("key");
15
15
  CREATE INDEX "index_solid_queue_recurring_tasks_on_static" ON "solid_queue_recurring_tasks" ("static");
16
- CREATE TABLE IF NOT EXISTS "solid_queue_semaphores" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "expires_at" datetime(6) NOT NULL, "key" varchar NOT NULL, "updated_at" datetime(6) NOT NULL, "value" integer DEFAULT 1 NOT NULL);
16
+ CREATE TABLE "solid_queue_semaphores" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "expires_at" datetime(6) NOT NULL, "key" varchar NOT NULL, "updated_at" datetime(6) NOT NULL, "value" integer DEFAULT 1 NOT NULL);
17
17
  CREATE INDEX "index_solid_queue_semaphores_on_expires_at" ON "solid_queue_semaphores" ("expires_at");
18
18
  CREATE INDEX "index_solid_queue_semaphores_on_key_and_value" ON "solid_queue_semaphores" ("key", "value");
19
19
  CREATE UNIQUE INDEX "index_solid_queue_semaphores_on_key" ON "solid_queue_semaphores" ("key");
20
- CREATE TABLE IF NOT EXISTS "solid_queue_blocked_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "concurrency_key" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "expires_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, CONSTRAINT "fk_rails_4cd34e2228"
20
+ CREATE TABLE "solid_queue_blocked_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "concurrency_key" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "expires_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, CONSTRAINT "fk_rails_4cd34e2228"
21
21
  FOREIGN KEY ("job_id")
22
22
  REFERENCES "solid_queue_jobs" ("id")
23
23
  ON DELETE CASCADE);
24
24
  CREATE INDEX "index_solid_queue_blocked_executions_for_release" ON "solid_queue_blocked_executions" ("concurrency_key", "priority", "job_id");
25
25
  CREATE INDEX "index_solid_queue_blocked_executions_for_maintenance" ON "solid_queue_blocked_executions" ("expires_at", "concurrency_key");
26
26
  CREATE UNIQUE INDEX "index_solid_queue_blocked_executions_on_job_id" ON "solid_queue_blocked_executions" ("job_id");
27
- CREATE TABLE IF NOT EXISTS "solid_queue_claimed_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "process_id" bigint, CONSTRAINT "fk_rails_9cfe4d4944"
27
+ CREATE TABLE "solid_queue_claimed_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "process_id" bigint, CONSTRAINT "fk_rails_9cfe4d4944"
28
28
  FOREIGN KEY ("job_id")
29
29
  REFERENCES "solid_queue_jobs" ("id")
30
30
  ON DELETE CASCADE);
31
31
  CREATE UNIQUE INDEX "index_solid_queue_claimed_executions_on_job_id" ON "solid_queue_claimed_executions" ("job_id");
32
32
  CREATE INDEX "index_solid_queue_claimed_executions_on_process_id_and_job_id" ON "solid_queue_claimed_executions" ("process_id", "job_id");
33
- CREATE TABLE IF NOT EXISTS "solid_queue_failed_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "error" text, "job_id" bigint NOT NULL, CONSTRAINT "fk_rails_39bbc7a631"
33
+ CREATE TABLE "solid_queue_failed_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "error" text, "job_id" bigint NOT NULL, CONSTRAINT "fk_rails_39bbc7a631"
34
34
  FOREIGN KEY ("job_id")
35
35
  REFERENCES "solid_queue_jobs" ("id")
36
36
  ON DELETE CASCADE);
37
37
  CREATE UNIQUE INDEX "index_solid_queue_failed_executions_on_job_id" ON "solid_queue_failed_executions" ("job_id");
38
- CREATE TABLE IF NOT EXISTS "solid_queue_ready_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, CONSTRAINT "fk_rails_81fcbd66af"
38
+ CREATE TABLE "solid_queue_ready_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, CONSTRAINT "fk_rails_81fcbd66af"
39
39
  FOREIGN KEY ("job_id")
40
40
  REFERENCES "solid_queue_jobs" ("id")
41
41
  ON DELETE CASCADE);
42
42
  CREATE UNIQUE INDEX "index_solid_queue_ready_executions_on_job_id" ON "solid_queue_ready_executions" ("job_id");
43
43
  CREATE INDEX "index_solid_queue_poll_all" ON "solid_queue_ready_executions" ("priority", "job_id");
44
44
  CREATE INDEX "index_solid_queue_poll_by_queue" ON "solid_queue_ready_executions" ("queue_name", "priority", "job_id");
45
- CREATE TABLE IF NOT EXISTS "solid_queue_recurring_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "run_at" datetime(6) NOT NULL, "task_key" varchar NOT NULL, CONSTRAINT "fk_rails_318a5533ed"
45
+ CREATE TABLE "solid_queue_recurring_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "run_at" datetime(6) NOT NULL, "task_key" varchar NOT NULL, CONSTRAINT "fk_rails_318a5533ed"
46
46
  FOREIGN KEY ("job_id")
47
47
  REFERENCES "solid_queue_jobs" ("id")
48
48
  ON DELETE CASCADE);
49
49
  CREATE UNIQUE INDEX "index_solid_queue_recurring_executions_on_job_id" ON "solid_queue_recurring_executions" ("job_id");
50
50
  CREATE UNIQUE INDEX "index_solid_queue_recurring_executions_on_task_key_and_run_at" ON "solid_queue_recurring_executions" ("task_key", "run_at");
51
- CREATE TABLE IF NOT EXISTS "solid_queue_scheduled_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, "scheduled_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c4316f352d"
51
+ CREATE TABLE "solid_queue_scheduled_executions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "job_id" bigint NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "queue_name" varchar NOT NULL, "scheduled_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c4316f352d"
52
52
  FOREIGN KEY ("job_id")
53
53
  REFERENCES "solid_queue_jobs" ("id")
54
54
  ON DELETE CASCADE);
55
55
  CREATE UNIQUE INDEX "index_solid_queue_scheduled_executions_on_job_id" ON "solid_queue_scheduled_executions" ("job_id");
56
56
  CREATE INDEX "index_solid_queue_dispatch_all" ON "solid_queue_scheduled_executions" ("scheduled_at", "priority", "job_id");
57
- CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
58
- CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
57
+ CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
58
+ CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
59
59
  INSERT INTO "schema_migrations" (version) VALUES
60
60
  ('1');
61
61
 
data/db/structure.sql CHANGED
@@ -1,46 +1,48 @@
1
- CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
2
- CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
3
- CREATE TABLE IF NOT EXISTS "goals" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "session_id" integer NOT NULL, "parent_goal_id" integer, "description" text NOT NULL, "status" varchar DEFAULT 'active' NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "completed_at" datetime(6), "evicted_at" datetime(6), CONSTRAINT "fk_rails_874b7534ae"
4
- FOREIGN KEY ("session_id")
5
- REFERENCES "sessions" ("id")
6
- , CONSTRAINT "fk_rails_feeb9df31e"
1
+ CREATE TABLE "secrets" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "key" varchar NOT NULL, "namespace" varchar NOT NULL, "updated_at" datetime(6) NOT NULL, "value" text NOT NULL);
2
+ CREATE UNIQUE INDEX "index_secrets_on_namespace_and_key" ON "secrets" ("namespace", "key");
3
+ CREATE TABLE "goals" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "completed_at" datetime(6), "created_at" datetime(6) NOT NULL, "description" text NOT NULL, "evicted_at" datetime(6), "parent_goal_id" integer, "session_id" integer NOT NULL, "status" varchar DEFAULT 'active' NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_feeb9df31e"
7
4
  FOREIGN KEY ("parent_goal_id")
8
5
  REFERENCES "goals" ("id")
6
+ , CONSTRAINT "fk_rails_874b7534ae"
7
+ FOREIGN KEY ("session_id")
8
+ REFERENCES "sessions" ("id")
9
9
  );
10
- CREATE INDEX "index_goals_on_session_id" ON "goals" ("session_id");
11
10
  CREATE INDEX "index_goals_on_parent_goal_id" ON "goals" ("parent_goal_id");
12
11
  CREATE INDEX "index_goals_on_session_id_and_status" ON "goals" ("session_id", "status");
13
- CREATE TABLE IF NOT EXISTS "messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "session_id" integer NOT NULL, "message_type" varchar NOT NULL, "payload" json DEFAULT '{}' NOT NULL, "timestamp" integer(8) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "token_count" integer DEFAULT 0 NOT NULL, "tool_use_id" varchar, "status" varchar, "api_metrics" json, CONSTRAINT "fk_rails_1ee2a92df0"
12
+ CREATE INDEX "index_goals_on_session_id" ON "goals" ("session_id");
13
+ CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
14
+ CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
15
+ CREATE TABLE "messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "api_metrics" json, "created_at" datetime(6) NOT NULL, "message_type" varchar NOT NULL, "payload" json DEFAULT '{}' NOT NULL, "session_id" integer NOT NULL, "status" varchar, "timestamp" integer(8) NOT NULL, "token_count" integer DEFAULT 0 NOT NULL, "tool_use_id" varchar, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_1ee2a92df0"
14
16
  FOREIGN KEY ("session_id")
15
17
  REFERENCES "sessions" ("id")
16
18
  );
17
19
  CREATE INDEX "index_messages_on_session_id_and_status" ON "messages" ("session_id", "status");
18
- CREATE INDEX "index_messages_on_tool_use_id" ON "messages" ("tool_use_id");
19
20
  CREATE INDEX "index_messages_on_session_id" ON "messages" ("session_id");
21
+ CREATE INDEX "index_messages_on_tool_use_id" ON "messages" ("tool_use_id");
20
22
  CREATE INDEX "index_messages_on_message_type" ON "messages" ("message_type");
21
23
  CREATE INDEX "index_messages_on_session_id_and_message_type" ON "messages" ("session_id", "message_type");
22
- CREATE TABLE IF NOT EXISTS "pinned_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "message_id" integer NOT NULL, "display_text" text NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_4a5f237c43"
24
+ CREATE TABLE "pinned_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "display_text" text NOT NULL, "message_id" integer NOT NULL, "updated_at" datetime(6) NOT NULL, "token_count" integer DEFAULT 0 NOT NULL, CONSTRAINT "fk_rails_4a5f237c43"
23
25
  FOREIGN KEY ("message_id")
24
26
  REFERENCES "messages" ("id")
25
27
  );
26
28
  CREATE UNIQUE INDEX "index_pinned_messages_on_message_id" ON "pinned_messages" ("message_id");
27
- CREATE TABLE IF NOT EXISTS "goal_pinned_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "goal_id" integer NOT NULL, "pinned_message_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_fb51bfeebe"
28
- FOREIGN KEY ("pinned_message_id")
29
- REFERENCES "pinned_messages" ("id")
30
- , CONSTRAINT "fk_rails_689fd4bf8a"
29
+ CREATE TABLE "goal_pinned_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "goal_id" integer NOT NULL, "pinned_message_id" integer NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_689fd4bf8a"
31
30
  FOREIGN KEY ("goal_id")
32
31
  REFERENCES "goals" ("id")
32
+ , CONSTRAINT "fk_rails_fb51bfeebe"
33
+ FOREIGN KEY ("pinned_message_id")
34
+ REFERENCES "pinned_messages" ("id")
33
35
  );
34
36
  CREATE INDEX "index_goal_pinned_messages_on_goal_id" ON "goal_pinned_messages" ("goal_id");
35
- CREATE INDEX "index_goal_pinned_messages_on_pinned_message_id" ON "goal_pinned_messages" ("pinned_message_id");
36
37
  CREATE UNIQUE INDEX "index_goal_pinned_messages_on_goal_id_and_pinned_message_id" ON "goal_pinned_messages" ("goal_id", "pinned_message_id");
37
- CREATE TABLE IF NOT EXISTS "snapshots" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "session_id" integer NOT NULL, "text" text NOT NULL, "from_message_id" integer NOT NULL, "to_message_id" integer NOT NULL, "level" integer DEFAULT 1 NOT NULL, "token_count" integer DEFAULT 0 NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_eb2ad51db9"
38
+ CREATE INDEX "index_goal_pinned_messages_on_pinned_message_id" ON "goal_pinned_messages" ("pinned_message_id");
39
+ CREATE TABLE "snapshots" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "from_message_id" integer NOT NULL, "level" integer DEFAULT 1 NOT NULL, "session_id" integer NOT NULL, "text" text NOT NULL, "to_message_id" integer NOT NULL, "token_count" integer DEFAULT 0 NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_eb2ad51db9"
38
40
  FOREIGN KEY ("session_id")
39
41
  REFERENCES "sessions" ("id")
40
42
  );
41
- CREATE INDEX "index_snapshots_on_session_id" ON "snapshots" ("session_id");
42
- CREATE INDEX "index_snapshots_on_session_id_and_level" ON "snapshots" ("session_id", "level");
43
43
  CREATE INDEX "index_snapshots_on_session_and_event_range" ON "snapshots" ("session_id", "from_message_id", "to_message_id");
44
+ CREATE INDEX "index_snapshots_on_session_id_and_level" ON "snapshots" ("session_id", "level");
45
+ CREATE INDEX "index_snapshots_on_session_id" ON "snapshots" ("session_id");
44
46
  CREATE VIRTUAL TABLE messages_fts USING fts5(
45
47
  searchable_text,
46
48
  content='',
@@ -48,10 +50,10 @@ CREATE VIRTUAL TABLE messages_fts USING fts5(
48
50
  tokenize='porter unicode61'
49
51
  )
50
52
  /* messages_fts(searchable_text) */;
51
- CREATE TABLE IF NOT EXISTS 'messages_fts_data'(id INTEGER PRIMARY KEY, block BLOB);
52
- CREATE TABLE IF NOT EXISTS 'messages_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
53
- CREATE TABLE IF NOT EXISTS 'messages_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER);
54
- CREATE TABLE IF NOT EXISTS 'messages_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;
53
+ CREATE TABLE 'messages_fts_data'(id INTEGER PRIMARY KEY, block BLOB);
54
+ CREATE TABLE 'messages_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
55
+ CREATE TABLE 'messages_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER);
56
+ CREATE TABLE 'messages_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;
55
57
  CREATE TRIGGER messages_fts_insert AFTER INSERT ON messages
56
58
  WHEN NEW.message_type IN ('user_message', 'agent_message', 'system_message')
57
59
  OR (NEW.message_type = 'tool_call' AND json_extract(NEW.payload, '$.tool_name') = 'think')
@@ -73,20 +75,31 @@ WHEN OLD.message_type IN ('user_message', 'agent_message', 'system_message')
73
75
  BEGIN
74
76
  DELETE FROM messages_fts WHERE rowid = OLD.id;
75
77
  END;
76
- CREATE TABLE IF NOT EXISTS "secrets" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "namespace" varchar NOT NULL, "key" varchar NOT NULL, "value" text NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
77
- CREATE UNIQUE INDEX "index_secrets_on_namespace_and_key" ON "secrets" ("namespace", "key");
78
- CREATE INDEX "index_goals_on_evicted_at" ON "goals" ("evicted_at");
79
- CREATE TABLE IF NOT EXISTS "pending_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "session_id" integer NOT NULL, "content" text NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "source_type" varchar DEFAULT 'user' NOT NULL, "source_name" varchar, CONSTRAINT "fk_rails_007242365b"
80
- FOREIGN KEY ("session_id")
81
- REFERENCES "sessions" ("id")
82
- );
83
- CREATE INDEX "index_pending_messages_on_session_id" ON "pending_messages" ("session_id");
84
- CREATE TABLE IF NOT EXISTS "sessions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "view_mode" varchar DEFAULT 'basic' NOT NULL, "processing" boolean DEFAULT FALSE NOT NULL, "parent_session_id" integer, "prompt" text, "granted_tools" text, "name" varchar, "viewport_message_ids" json DEFAULT '[]' NOT NULL, "active_skills" json DEFAULT '[]' NOT NULL, "active_workflow" varchar, "interrupt_requested" boolean DEFAULT FALSE NOT NULL, "mneme_boundary_message_id" integer, "mneme_snapshot_first_message_id" integer, "mneme_snapshot_last_message_id" integer, "initial_cwd" varchar, CONSTRAINT "fk_rails_045409ac27"
78
+ CREATE TABLE "sessions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime(6) NOT NULL, "granted_tools" text, "interrupt_requested" boolean DEFAULT FALSE NOT NULL, "mneme_boundary_message_id" integer, "name" varchar, "parent_session_id" integer, "prompt" text, "recalled_message_ids" json DEFAULT '[]' NOT NULL, "updated_at" datetime(6) NOT NULL, "view_mode" varchar DEFAULT 'basic' NOT NULL, "initial_cwd" varchar, "aasm_state" varchar DEFAULT 'idle' NOT NULL, "hud_visible" boolean DEFAULT TRUE NOT NULL, "spawn_tool_use_id" varchar, CONSTRAINT "fk_rails_045409ac27"
85
79
  FOREIGN KEY ("parent_session_id")
86
80
  REFERENCES "sessions" ("id")
87
81
  );
82
+ CREATE UNIQUE INDEX "index_sessions_on_parent_and_name_unique" ON "sessions" ("parent_session_id", "name") WHERE name IS NOT NULL;
88
83
  CREATE INDEX "index_sessions_on_parent_session_id" ON "sessions" ("parent_session_id");
84
+ CREATE TABLE "pending_messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "content" text NOT NULL, "created_at" datetime(6) NOT NULL, "session_id" integer NOT NULL, "source_name" varchar, "source_type" varchar DEFAULT 'user' NOT NULL, "updated_at" datetime(6) NOT NULL, "kind" varchar NOT NULL, "message_type" varchar, "tool_use_id" varchar, "success" boolean, "bounce_back" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_007242365b"
85
+ FOREIGN KEY ("session_id")
86
+ REFERENCES "sessions" ("id")
87
+ );
88
+ CREATE INDEX "index_pending_messages_on_session_id" ON "pending_messages" ("session_id");
89
+ CREATE INDEX "index_pending_messages_on_drain_ordering" ON "pending_messages" ("session_id", "message_type", "created_at");
90
+ CREATE INDEX "index_pending_messages_on_session_and_tool_use" ON "pending_messages" ("session_id", "tool_use_id");
91
+ CREATE INDEX "index_sessions_on_parent_session_id_and_hud_visible" ON "sessions" ("parent_session_id", "hud_visible");
89
92
  INSERT INTO "schema_migrations" (version) VALUES
93
+ ('20260420100000'),
94
+ ('20260419140000'),
95
+ ('20260419130000'),
96
+ ('20260419120000'),
97
+ ('20260418150323'),
98
+ ('20260412110625'),
99
+ ('20260411172926'),
100
+ ('20260411120553'),
101
+ ('20260407180400'),
102
+ ('20260407170803'),
90
103
  ('20260403080031'),
91
104
  ('20260401210935'),
92
105
  ('20260401180000'),
@@ -97,7 +97,7 @@ module Agents
97
97
  def validate_tools!(agent_name, tools)
98
98
  return if tools.empty?
99
99
 
100
- unknown = tools - AgentLoop::STANDARD_TOOLS_BY_NAME.keys
100
+ unknown = tools - Tools::Registry::STANDARD_TOOLS_BY_NAME.keys
101
101
  return if unknown.empty?
102
102
 
103
103
  raise InvalidDefinitionError, "Unknown tools in '#{agent_name}': #{unknown.join(", ")}"