anima-core 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.reek.yml +23 -26
- data/README.md +118 -104
- data/agents/thoughts-analyzer.md +12 -7
- data/anima-core.gemspec +1 -0
- data/app/channels/session_channel.rb +38 -58
- data/app/decorators/agent_message_decorator.rb +7 -2
- data/app/decorators/message_decorator.rb +31 -100
- data/app/decorators/pending_from_melete_decorator.rb +36 -0
- data/app/decorators/pending_from_melete_goal_decorator.rb +13 -0
- data/app/decorators/pending_from_melete_skill_decorator.rb +19 -0
- data/app/decorators/pending_from_melete_workflow_decorator.rb +13 -0
- data/app/decorators/pending_from_mneme_decorator.rb +44 -0
- data/app/decorators/pending_message_decorator.rb +94 -0
- data/app/decorators/pending_subagent_decorator.rb +46 -0
- data/app/decorators/pending_tool_response_decorator.rb +51 -0
- data/app/decorators/pending_user_message_decorator.rb +22 -0
- data/app/decorators/system_message_decorator.rb +5 -0
- data/app/decorators/tool_call_decorator.rb +16 -5
- data/app/decorators/tool_response_decorator.rb +2 -2
- data/app/decorators/user_message_decorator.rb +7 -2
- data/app/jobs/count_tokens_job.rb +23 -0
- data/app/jobs/drain_job.rb +169 -0
- data/app/jobs/melete_enrichment_job/goal_change_listener.rb +52 -0
- data/app/jobs/melete_enrichment_job.rb +48 -0
- data/app/jobs/mneme_enrichment_job.rb +46 -0
- data/app/jobs/tool_execution_job.rb +87 -0
- data/app/models/concerns/token_estimation.rb +54 -0
- data/app/models/goal.rb +23 -11
- data/app/models/message.rb +46 -48
- data/app/models/pending_message.rb +407 -12
- data/app/models/pinned_message.rb +8 -3
- data/app/models/session.rb +660 -566
- data/app/models/snapshot.rb +11 -21
- data/bin/inspect-cassette +157 -0
- data/bin/release +212 -0
- data/bin/with-llms +20 -0
- data/config/application.rb +1 -0
- data/config/database.yml +1 -0
- data/config/initializers/event_subscribers.rb +71 -4
- data/config/initializers/inflections.rb +3 -1
- data/db/cable_structure.sql +9 -0
- data/db/migrate/20260330120000_add_source_to_pending_messages.rb +8 -0
- data/db/migrate/20260401180000_add_api_metrics_to_messages.rb +7 -0
- data/db/migrate/20260401210935_remove_recalled_message_ids_from_sessions.rb +5 -0
- data/db/migrate/20260403080031_add_initial_cwd_to_sessions.rb +5 -0
- data/db/migrate/20260407170803_remove_viewport_message_ids_from_sessions.rb +5 -0
- data/db/migrate/20260407180400_remove_mneme_snapshot_pointer_columns_from_sessions.rb +6 -0
- data/db/migrate/20260411120553_add_token_count_to_pinned_messages.rb +5 -0
- data/db/migrate/20260411172926_remove_active_skills_and_workflow_from_sessions.rb +6 -0
- data/db/migrate/20260412110625_replace_processing_with_aasm_state.rb +6 -0
- data/db/migrate/20260418150323_add_kind_and_message_type_to_pending_messages.rb +6 -0
- data/db/migrate/20260419120000_add_drain_fields_to_pending_messages.rb +7 -0
- data/db/migrate/20260419130000_drop_pending_messages_kind_default.rb +5 -0
- data/db/migrate/20260419140000_add_drain_indexes_to_pending_messages.rb +8 -0
- data/db/migrate/20260420100000_add_hud_visibility_to_sessions.rb +15 -0
- data/db/queue_structure.sql +61 -0
- data/db/structure.sql +133 -0
- data/lib/agents/registry.rb +1 -1
- data/lib/anima/cli.rb +41 -13
- data/lib/anima/installer.rb +13 -0
- data/lib/anima/settings.rb +16 -36
- data/lib/anima/version.rb +1 -1
- data/lib/events/authentication_required.rb +24 -0
- data/lib/events/bounce_back.rb +4 -4
- data/lib/events/eviction_completed.rb +28 -0
- data/lib/events/goal_created.rb +28 -0
- data/lib/events/goal_updated.rb +32 -0
- data/lib/events/llm_responded.rb +35 -0
- data/lib/events/message_created.rb +27 -0
- data/lib/events/message_updated.rb +25 -0
- data/lib/events/session_state_changed.rb +30 -0
- data/lib/events/skill_activated.rb +28 -0
- data/lib/events/start_melete.rb +36 -0
- data/lib/events/start_mneme.rb +33 -0
- data/lib/events/start_processing.rb +32 -0
- data/lib/events/subagent_evicted.rb +31 -0
- data/lib/events/subscribers/active_state_broadcaster.rb +27 -0
- data/lib/events/subscribers/authentication_broadcaster.rb +34 -0
- data/lib/events/subscribers/drain_kickoff.rb +20 -0
- data/lib/events/subscribers/eviction_broadcaster.rb +26 -0
- data/lib/events/subscribers/llm_response_handler.rb +111 -0
- data/lib/events/subscribers/melete_kickoff.rb +24 -0
- data/lib/events/subscribers/message_broadcaster.rb +34 -0
- data/lib/events/subscribers/mneme_kickoff.rb +24 -0
- data/lib/events/subscribers/mneme_scheduler.rb +21 -0
- data/lib/events/subscribers/persister.rb +8 -9
- data/lib/events/subscribers/session_state_broadcaster.rb +33 -0
- data/lib/events/subscribers/subagent_message_router.rb +28 -34
- data/lib/events/subscribers/subagent_visibility_broadcaster.rb +33 -0
- data/lib/events/subscribers/tool_response_creator.rb +33 -0
- data/lib/events/subscribers/transient_broadcaster.rb +1 -1
- data/lib/events/tool_executed.rb +34 -0
- data/lib/events/workflow_activated.rb +27 -0
- data/lib/llm/client.rb +46 -199
- data/lib/mcp/client_manager.rb +41 -46
- data/lib/mcp/stdio_transport.rb +9 -5
- data/lib/{analytical_brain → melete}/runner.rb +73 -68
- data/lib/{analytical_brain → melete}/tools/activate_skill.rb +3 -3
- data/lib/{analytical_brain → melete}/tools/assign_nickname.rb +3 -3
- data/lib/{analytical_brain → melete}/tools/everything_is_ready.rb +2 -2
- data/lib/{analytical_brain → melete}/tools/finish_goal.rb +6 -3
- data/lib/melete/tools/goal_messaging.rb +29 -0
- data/lib/{analytical_brain → melete}/tools/read_workflow.rb +4 -4
- data/lib/{analytical_brain → melete}/tools/rename_session.rb +3 -3
- data/lib/{analytical_brain → melete}/tools/set_goal.rb +6 -2
- data/lib/{analytical_brain → melete}/tools/update_goal.rb +9 -5
- data/lib/{analytical_brain.rb → melete.rb} +6 -3
- data/lib/mneme/base_runner.rb +121 -0
- data/lib/mneme/l2_runner.rb +14 -20
- data/lib/mneme/recall_runner.rb +132 -0
- data/lib/mneme/runner.rb +123 -165
- data/lib/mneme/search.rb +104 -62
- data/lib/mneme/tools/nothing_to_surface.rb +25 -0
- data/lib/mneme/tools/save_snapshot.rb +2 -10
- data/lib/mneme/tools/surface_memory.rb +89 -0
- data/lib/mneme.rb +11 -5
- data/lib/providers/anthropic.rb +112 -7
- data/lib/shell_session.rb +290 -432
- data/lib/skills/definition.rb +2 -2
- data/lib/skills/registry.rb +1 -1
- data/lib/tools/base.rb +16 -1
- data/lib/tools/bash.rb +25 -55
- data/lib/tools/edit.rb +2 -0
- data/lib/tools/mark_goal_completed.rb +4 -5
- data/lib/tools/read.rb +2 -0
- data/lib/tools/registry.rb +85 -4
- data/lib/tools/response_truncator.rb +1 -1
- data/lib/tools/{recall.rb → search_messages.rb} +19 -21
- data/lib/tools/spawn_specialist.rb +22 -14
- data/lib/tools/spawn_subagent.rb +30 -20
- data/lib/tools/subagent_prompts.rb +17 -19
- data/lib/tools/think.rb +1 -1
- data/lib/tools/{remember.rb → view_messages.rb} +10 -10
- data/lib/tools/write.rb +2 -0
- data/lib/tui/app.rb +393 -149
- data/lib/tui/braille_spinner.rb +7 -7
- data/lib/tui/cable_client.rb +9 -16
- data/lib/tui/decorators/base_decorator.rb +47 -6
- data/lib/tui/decorators/bash_decorator.rb +1 -1
- data/lib/tui/decorators/edit_decorator.rb +4 -2
- data/lib/tui/decorators/read_decorator.rb +4 -2
- data/lib/tui/decorators/think_decorator.rb +2 -2
- data/lib/tui/decorators/web_get_decorator.rb +1 -1
- data/lib/tui/decorators/write_decorator.rb +4 -2
- data/lib/tui/flash.rb +19 -14
- data/lib/tui/formatting.rb +20 -9
- data/lib/tui/input_buffer.rb +6 -6
- data/lib/tui/message_store.rb +165 -28
- data/lib/tui/performance_logger.rb +2 -3
- data/lib/tui/screens/chat.rb +149 -79
- data/lib/tui/settings.rb +93 -0
- data/lib/workflows/definition.rb +3 -3
- data/lib/workflows/registry.rb +1 -1
- data/skills/github.md +38 -0
- data/templates/config.toml +16 -32
- data/templates/tui.toml +209 -0
- data/workflows/review_pr.md +18 -14
- metadata +98 -29
- data/app/jobs/agent_request_job.rb +0 -199
- data/app/jobs/analytical_brain_job.rb +0 -33
- data/app/jobs/count_message_tokens_job.rb +0 -39
- data/app/jobs/passive_recall_job.rb +0 -29
- data/app/models/concerns/message/broadcasting.rb +0 -85
- data/config/initializers/fts5_schema_dump.rb +0 -21
- data/lib/agent_loop.rb +0 -186
- data/lib/analytical_brain/tools/deactivate_skill.rb +0 -39
- data/lib/analytical_brain/tools/deactivate_workflow.rb +0 -34
- data/lib/environment_probe.rb +0 -232
- data/lib/events/agent_message.rb +0 -11
- data/lib/events/subscribers/message_collector.rb +0 -64
- data/lib/events/tool_call.rb +0 -31
- data/lib/events/tool_response.rb +0 -33
- data/lib/mneme/compressed_viewport.rb +0 -200
- data/lib/mneme/passive_recall.rb +0 -69
|
@@ -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
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
CREATE INDEX "index_solid_queue_jobs_on_active_job_id" ON "solid_queue_jobs" ("active_job_id");
|
|
3
|
+
CREATE INDEX "index_solid_queue_jobs_on_class_name" ON "solid_queue_jobs" ("class_name");
|
|
4
|
+
CREATE INDEX "index_solid_queue_jobs_on_finished_at" ON "solid_queue_jobs" ("finished_at");
|
|
5
|
+
CREATE INDEX "index_solid_queue_jobs_for_filtering" ON "solid_queue_jobs" ("queue_name", "finished_at");
|
|
6
|
+
CREATE INDEX "index_solid_queue_jobs_for_alerting" ON "solid_queue_jobs" ("scheduled_at", "finished_at");
|
|
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
|
+
CREATE UNIQUE INDEX "index_solid_queue_pauses_on_queue_name" ON "solid_queue_pauses" ("queue_name");
|
|
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
|
+
CREATE INDEX "index_solid_queue_processes_on_last_heartbeat_at" ON "solid_queue_processes" ("last_heartbeat_at");
|
|
11
|
+
CREATE UNIQUE INDEX "index_solid_queue_processes_on_name_and_supervisor_id" ON "solid_queue_processes" ("name", "supervisor_id");
|
|
12
|
+
CREATE INDEX "index_solid_queue_processes_on_supervisor_id" ON "solid_queue_processes" ("supervisor_id");
|
|
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
|
+
CREATE UNIQUE INDEX "index_solid_queue_recurring_tasks_on_key" ON "solid_queue_recurring_tasks" ("key");
|
|
15
|
+
CREATE INDEX "index_solid_queue_recurring_tasks_on_static" ON "solid_queue_recurring_tasks" ("static");
|
|
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
|
+
CREATE INDEX "index_solid_queue_semaphores_on_expires_at" ON "solid_queue_semaphores" ("expires_at");
|
|
18
|
+
CREATE INDEX "index_solid_queue_semaphores_on_key_and_value" ON "solid_queue_semaphores" ("key", "value");
|
|
19
|
+
CREATE UNIQUE INDEX "index_solid_queue_semaphores_on_key" ON "solid_queue_semaphores" ("key");
|
|
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
|
+
FOREIGN KEY ("job_id")
|
|
22
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
23
|
+
ON DELETE CASCADE);
|
|
24
|
+
CREATE INDEX "index_solid_queue_blocked_executions_for_release" ON "solid_queue_blocked_executions" ("concurrency_key", "priority", "job_id");
|
|
25
|
+
CREATE INDEX "index_solid_queue_blocked_executions_for_maintenance" ON "solid_queue_blocked_executions" ("expires_at", "concurrency_key");
|
|
26
|
+
CREATE UNIQUE INDEX "index_solid_queue_blocked_executions_on_job_id" ON "solid_queue_blocked_executions" ("job_id");
|
|
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
|
+
FOREIGN KEY ("job_id")
|
|
29
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
30
|
+
ON DELETE CASCADE);
|
|
31
|
+
CREATE UNIQUE INDEX "index_solid_queue_claimed_executions_on_job_id" ON "solid_queue_claimed_executions" ("job_id");
|
|
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 "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
|
+
FOREIGN KEY ("job_id")
|
|
35
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
36
|
+
ON DELETE CASCADE);
|
|
37
|
+
CREATE UNIQUE INDEX "index_solid_queue_failed_executions_on_job_id" ON "solid_queue_failed_executions" ("job_id");
|
|
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
|
+
FOREIGN KEY ("job_id")
|
|
40
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
41
|
+
ON DELETE CASCADE);
|
|
42
|
+
CREATE UNIQUE INDEX "index_solid_queue_ready_executions_on_job_id" ON "solid_queue_ready_executions" ("job_id");
|
|
43
|
+
CREATE INDEX "index_solid_queue_poll_all" ON "solid_queue_ready_executions" ("priority", "job_id");
|
|
44
|
+
CREATE INDEX "index_solid_queue_poll_by_queue" ON "solid_queue_ready_executions" ("queue_name", "priority", "job_id");
|
|
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
|
+
FOREIGN KEY ("job_id")
|
|
47
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
48
|
+
ON DELETE CASCADE);
|
|
49
|
+
CREATE UNIQUE INDEX "index_solid_queue_recurring_executions_on_job_id" ON "solid_queue_recurring_executions" ("job_id");
|
|
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 "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
|
+
FOREIGN KEY ("job_id")
|
|
53
|
+
REFERENCES "solid_queue_jobs" ("id")
|
|
54
|
+
ON DELETE CASCADE);
|
|
55
|
+
CREATE UNIQUE INDEX "index_solid_queue_scheduled_executions_on_job_id" ON "solid_queue_scheduled_executions" ("job_id");
|
|
56
|
+
CREATE INDEX "index_solid_queue_dispatch_all" ON "solid_queue_scheduled_executions" ("scheduled_at", "priority", "job_id");
|
|
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
|
+
INSERT INTO "schema_migrations" (version) VALUES
|
|
60
|
+
('1');
|
|
61
|
+
|
data/db/structure.sql
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
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"
|
|
4
|
+
FOREIGN KEY ("parent_goal_id")
|
|
5
|
+
REFERENCES "goals" ("id")
|
|
6
|
+
, CONSTRAINT "fk_rails_874b7534ae"
|
|
7
|
+
FOREIGN KEY ("session_id")
|
|
8
|
+
REFERENCES "sessions" ("id")
|
|
9
|
+
);
|
|
10
|
+
CREATE INDEX "index_goals_on_parent_goal_id" ON "goals" ("parent_goal_id");
|
|
11
|
+
CREATE INDEX "index_goals_on_session_id_and_status" ON "goals" ("session_id", "status");
|
|
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"
|
|
16
|
+
FOREIGN KEY ("session_id")
|
|
17
|
+
REFERENCES "sessions" ("id")
|
|
18
|
+
);
|
|
19
|
+
CREATE INDEX "index_messages_on_session_id_and_status" ON "messages" ("session_id", "status");
|
|
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");
|
|
22
|
+
CREATE INDEX "index_messages_on_message_type" ON "messages" ("message_type");
|
|
23
|
+
CREATE INDEX "index_messages_on_session_id_and_message_type" ON "messages" ("session_id", "message_type");
|
|
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"
|
|
25
|
+
FOREIGN KEY ("message_id")
|
|
26
|
+
REFERENCES "messages" ("id")
|
|
27
|
+
);
|
|
28
|
+
CREATE UNIQUE INDEX "index_pinned_messages_on_message_id" ON "pinned_messages" ("message_id");
|
|
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"
|
|
30
|
+
FOREIGN KEY ("goal_id")
|
|
31
|
+
REFERENCES "goals" ("id")
|
|
32
|
+
, CONSTRAINT "fk_rails_fb51bfeebe"
|
|
33
|
+
FOREIGN KEY ("pinned_message_id")
|
|
34
|
+
REFERENCES "pinned_messages" ("id")
|
|
35
|
+
);
|
|
36
|
+
CREATE INDEX "index_goal_pinned_messages_on_goal_id" ON "goal_pinned_messages" ("goal_id");
|
|
37
|
+
CREATE UNIQUE INDEX "index_goal_pinned_messages_on_goal_id_and_pinned_message_id" ON "goal_pinned_messages" ("goal_id", "pinned_message_id");
|
|
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"
|
|
40
|
+
FOREIGN KEY ("session_id")
|
|
41
|
+
REFERENCES "sessions" ("id")
|
|
42
|
+
);
|
|
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");
|
|
46
|
+
CREATE VIRTUAL TABLE messages_fts USING fts5(
|
|
47
|
+
searchable_text,
|
|
48
|
+
content='',
|
|
49
|
+
contentless_delete=1,
|
|
50
|
+
tokenize='porter unicode61'
|
|
51
|
+
)
|
|
52
|
+
/* messages_fts(searchable_text) */;
|
|
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;
|
|
57
|
+
CREATE TRIGGER messages_fts_insert AFTER INSERT ON messages
|
|
58
|
+
WHEN NEW.message_type IN ('user_message', 'agent_message', 'system_message')
|
|
59
|
+
OR (NEW.message_type = 'tool_call' AND json_extract(NEW.payload, '$.tool_name') = 'think')
|
|
60
|
+
BEGIN
|
|
61
|
+
INSERT INTO messages_fts(rowid, searchable_text)
|
|
62
|
+
VALUES (
|
|
63
|
+
NEW.id,
|
|
64
|
+
CASE
|
|
65
|
+
WHEN NEW.message_type IN ('user_message', 'agent_message', 'system_message')
|
|
66
|
+
THEN json_extract(NEW.payload, '$.content')
|
|
67
|
+
WHEN NEW.message_type = 'tool_call'
|
|
68
|
+
THEN json_extract(NEW.payload, '$.tool_input.thoughts')
|
|
69
|
+
END
|
|
70
|
+
);
|
|
71
|
+
END;
|
|
72
|
+
CREATE TRIGGER messages_fts_delete AFTER DELETE ON messages
|
|
73
|
+
WHEN OLD.message_type IN ('user_message', 'agent_message', 'system_message')
|
|
74
|
+
OR (OLD.message_type = 'tool_call' AND json_extract(OLD.payload, '$.tool_name') = 'think')
|
|
75
|
+
BEGIN
|
|
76
|
+
DELETE FROM messages_fts WHERE rowid = OLD.id;
|
|
77
|
+
END;
|
|
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"
|
|
79
|
+
FOREIGN KEY ("parent_session_id")
|
|
80
|
+
REFERENCES "sessions" ("id")
|
|
81
|
+
);
|
|
82
|
+
CREATE UNIQUE INDEX "index_sessions_on_parent_and_name_unique" ON "sessions" ("parent_session_id", "name") WHERE name IS NOT NULL;
|
|
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");
|
|
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'),
|
|
103
|
+
('20260403080031'),
|
|
104
|
+
('20260401210935'),
|
|
105
|
+
('20260401180000'),
|
|
106
|
+
('20260330120000'),
|
|
107
|
+
('20260329120000'),
|
|
108
|
+
('20260328152142'),
|
|
109
|
+
('20260328100000'),
|
|
110
|
+
('20260326180000'),
|
|
111
|
+
('20260321140100'),
|
|
112
|
+
('20260321140000'),
|
|
113
|
+
('20260321120000'),
|
|
114
|
+
('20260321080000'),
|
|
115
|
+
('20260316094817'),
|
|
116
|
+
('20260315191105'),
|
|
117
|
+
('20260315144837'),
|
|
118
|
+
('20260315140843'),
|
|
119
|
+
('20260315100000'),
|
|
120
|
+
('20260314150000'),
|
|
121
|
+
('20260314140000'),
|
|
122
|
+
('20260314112417'),
|
|
123
|
+
('20260314075248'),
|
|
124
|
+
('20260313020000'),
|
|
125
|
+
('20260313010000'),
|
|
126
|
+
('20260312170000'),
|
|
127
|
+
('20260308160000'),
|
|
128
|
+
('20260308150000'),
|
|
129
|
+
('20260308140000'),
|
|
130
|
+
('20260308130000'),
|
|
131
|
+
('20260308124203'),
|
|
132
|
+
('20260308124202');
|
|
133
|
+
|
data/lib/agents/registry.rb
CHANGED
|
@@ -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 -
|
|
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(", ")}"
|
data/lib/anima/cli.rb
CHANGED
|
@@ -39,21 +39,29 @@ module Anima
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
require_relative "config_migrator"
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
result = Spinner.run("Migrating brain configuration...") do
|
|
43
44
|
Anima::ConfigMigrator.new.run
|
|
44
45
|
end
|
|
45
|
-
|
|
46
|
-
case result.status
|
|
47
|
-
when :not_found
|
|
46
|
+
if result.status == :not_found
|
|
48
47
|
say "Config file not found. Run 'anima install' first.", :red
|
|
49
48
|
exit 1
|
|
50
|
-
when :up_to_date
|
|
51
|
-
say " Config is already up to date."
|
|
52
|
-
when :updated
|
|
53
|
-
result.additions.each do |addition|
|
|
54
|
-
say " added [#{addition.section}] #{addition.key} = #{addition.value.inspect}"
|
|
55
|
-
end
|
|
56
49
|
end
|
|
50
|
+
report_migration("Config", result)
|
|
51
|
+
|
|
52
|
+
tui_config_path = File.join(Anima::ConfigMigrator::ANIMA_HOME, "tui.toml")
|
|
53
|
+
tui_template = File.expand_path("../../templates/tui.toml", __dir__)
|
|
54
|
+
unless File.exist?(tui_config_path)
|
|
55
|
+
File.write(tui_config_path, File.read(tui_template))
|
|
56
|
+
say " created #{tui_config_path} (new in this version)"
|
|
57
|
+
end
|
|
58
|
+
tui_result = Spinner.run("Migrating TUI configuration...") do
|
|
59
|
+
Anima::ConfigMigrator.new(
|
|
60
|
+
config_path: tui_config_path,
|
|
61
|
+
template_path: tui_template
|
|
62
|
+
).run
|
|
63
|
+
end
|
|
64
|
+
report_migration("TUI config", tui_result)
|
|
57
65
|
|
|
58
66
|
restart_service_if_active
|
|
59
67
|
end
|
|
@@ -83,13 +91,15 @@ module Anima
|
|
|
83
91
|
end
|
|
84
92
|
|
|
85
93
|
desc "tui", "Launch the Anima terminal interface"
|
|
86
|
-
option :host, desc: "Brain server address (default: #{DEFAULT_HOST})"
|
|
87
|
-
option :debug, type: :boolean, default: false, desc: "Enable performance logging
|
|
94
|
+
option :host, desc: "Brain server address (default: from tui.toml or #{DEFAULT_HOST})"
|
|
95
|
+
option :debug, type: :boolean, default: false, desc: "Enable performance logging"
|
|
88
96
|
def tui
|
|
89
97
|
require "ratatui_ruby"
|
|
98
|
+
require_relative "../tui/settings"
|
|
90
99
|
require_relative "../tui/app"
|
|
91
100
|
|
|
92
|
-
|
|
101
|
+
TUI::Settings.load!
|
|
102
|
+
host = options[:host] || TUI::Settings.connection_default_host
|
|
93
103
|
|
|
94
104
|
say "Connecting to brain at #{host}...", :cyan
|
|
95
105
|
|
|
@@ -111,6 +121,24 @@ module Anima
|
|
|
111
121
|
|
|
112
122
|
private
|
|
113
123
|
|
|
124
|
+
# Reports the outcome of a config migration to the user.
|
|
125
|
+
#
|
|
126
|
+
# @param label [String] human-readable config name (e.g. "Config", "TUI config")
|
|
127
|
+
# @param result [Anima::ConfigMigrator::Result] migration outcome
|
|
128
|
+
# @return [void]
|
|
129
|
+
def report_migration(label, result)
|
|
130
|
+
case result.status
|
|
131
|
+
when :not_found
|
|
132
|
+
say "#{label} file not found. Run 'anima install' first.", :red
|
|
133
|
+
when :up_to_date
|
|
134
|
+
say " #{label} is already up to date."
|
|
135
|
+
when :updated
|
|
136
|
+
result.additions.each do |addition|
|
|
137
|
+
say " added [#{addition.section}] #{addition.key} = #{addition.value.inspect}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
114
142
|
# Restarts the systemd user service so updated code takes effect.
|
|
115
143
|
# Without this, the service continues running the old gem version
|
|
116
144
|
# until manually restarted (see #269).
|
data/lib/anima/installer.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Anima
|
|
|
31
31
|
create_directories
|
|
32
32
|
create_soul_file
|
|
33
33
|
create_settings_config
|
|
34
|
+
create_tui_config
|
|
34
35
|
create_mcp_config
|
|
35
36
|
generate_credentials
|
|
36
37
|
create_systemd_service
|
|
@@ -68,6 +69,18 @@ module Anima
|
|
|
68
69
|
say " created #{config_path}"
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
# Creates ~/.anima/tui.toml — TUI-specific presentation settings.
|
|
73
|
+
# Separate from config.toml because the TUI is a standalone client
|
|
74
|
+
# process with no Rails dependency.
|
|
75
|
+
def create_tui_config
|
|
76
|
+
config_path = anima_home.join("tui.toml")
|
|
77
|
+
return if config_path.exist?
|
|
78
|
+
|
|
79
|
+
template = File.read(File.join(TEMPLATE_DIR, "tui.toml"))
|
|
80
|
+
config_path.write(template)
|
|
81
|
+
say " created #{config_path}"
|
|
82
|
+
end
|
|
83
|
+
|
|
71
84
|
def create_mcp_config
|
|
72
85
|
config_path = anima_home.join("mcp.toml")
|
|
73
86
|
return if config_path.exist?
|
data/lib/anima/settings.rb
CHANGED
|
@@ -96,6 +96,15 @@ module Anima
|
|
|
96
96
|
# @return [Integer]
|
|
97
97
|
def thinking_budget = get("llm", "thinking_budget")
|
|
98
98
|
|
|
99
|
+
# Model for sub-agent sessions. Sonnet is cost-effective for focused tasks.
|
|
100
|
+
# @return [String] Anthropic model identifier
|
|
101
|
+
def subagent_model = get("llm", "subagent_model")
|
|
102
|
+
|
|
103
|
+
# Context window budget for sub-agent sessions.
|
|
104
|
+
# Smaller than main to keep sub-agents out of the "dumb zone".
|
|
105
|
+
# @return [Integer]
|
|
106
|
+
def subagent_token_budget = get("llm", "subagent_token_budget")
|
|
107
|
+
|
|
99
108
|
# ─── Timeouts (seconds) ────────────────────────────────────────
|
|
100
109
|
|
|
101
110
|
# LLM API request timeout.
|
|
@@ -179,26 +188,12 @@ module Anima
|
|
|
179
188
|
value
|
|
180
189
|
end
|
|
181
190
|
|
|
182
|
-
# Regenerate session name every N messages.
|
|
183
|
-
# @return [Integer]
|
|
184
|
-
def name_generation_interval = get("session", "name_generation_interval")
|
|
185
|
-
|
|
186
191
|
# ─── Paths ───────────────────────────────────────────────────────
|
|
187
192
|
|
|
188
193
|
# Path to the soul file — the agent's self-authored identity.
|
|
189
194
|
# @return [String] absolute path
|
|
190
195
|
def soul_path = get("paths", "soul")
|
|
191
196
|
|
|
192
|
-
# ─── Environment ──────────────────────────────────────────────
|
|
193
|
-
|
|
194
|
-
# Filenames to scan for in the working directory.
|
|
195
|
-
# @return [Array<String>]
|
|
196
|
-
def project_files_whitelist = get("environment", "project_files")
|
|
197
|
-
|
|
198
|
-
# Maximum directory depth for project file scanning.
|
|
199
|
-
# @return [Integer]
|
|
200
|
-
def project_files_max_depth = get("environment", "project_files_max_depth")
|
|
201
|
-
|
|
202
197
|
# ─── GitHub ─────────────────────────────────────────────────────
|
|
203
198
|
|
|
204
199
|
# Repository for feature requests (+owner/repo+ format).
|
|
@@ -210,30 +205,15 @@ module Anima
|
|
|
210
205
|
# @return [String]
|
|
211
206
|
def github_label = get("github", "label")
|
|
212
207
|
|
|
213
|
-
# ───
|
|
208
|
+
# ─── Melete ─────────────────────────────────────────
|
|
214
209
|
|
|
215
|
-
# Maximum tokens per
|
|
210
|
+
# Maximum tokens per Melete response.
|
|
216
211
|
# @return [Integer]
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
# Run the analytical brain synchronously before the main agent on user messages.
|
|
220
|
-
# @return [Boolean]
|
|
221
|
-
def analytical_brain_blocking_on_user_message = get("analytical_brain", "blocking_on_user_message")
|
|
222
|
-
|
|
223
|
-
# Run the analytical brain asynchronously after the main agent completes.
|
|
224
|
-
# @return [Boolean]
|
|
225
|
-
def analytical_brain_blocking_on_agent_message = get("analytical_brain", "blocking_on_agent_message")
|
|
226
|
-
|
|
227
|
-
# Number of recent messages to include in the analytical brain's context window.
|
|
228
|
-
# @return [Integer]
|
|
229
|
-
def analytical_brain_message_window = get("analytical_brain", "message_window")
|
|
230
|
-
|
|
231
|
-
# ─── Goals ──────────────────────────────────────────────────────
|
|
212
|
+
def melete_max_tokens = get("melete", "max_tokens")
|
|
232
213
|
|
|
233
|
-
# Number of
|
|
234
|
-
# before a completed goal is automatically evicted from context.
|
|
214
|
+
# Number of recent messages to include in Melete's context window.
|
|
235
215
|
# @return [Integer]
|
|
236
|
-
def
|
|
216
|
+
def melete_message_window = get("melete", "message_window")
|
|
237
217
|
|
|
238
218
|
# ─── Mneme (Memory Department) ────────────────────────────────
|
|
239
219
|
|
|
@@ -241,9 +221,9 @@ module Anima
|
|
|
241
221
|
# @return [Integer]
|
|
242
222
|
def mneme_max_tokens = get("mneme", "max_tokens")
|
|
243
223
|
|
|
244
|
-
# Fraction of the main
|
|
224
|
+
# Fraction of the main token budget for Mneme's eviction zone.
|
|
245
225
|
# @return [Float]
|
|
246
|
-
def
|
|
226
|
+
def eviction_fraction = get("mneme", "eviction_fraction")
|
|
247
227
|
|
|
248
228
|
# Fraction of the main viewport token budget reserved for L1 snapshots.
|
|
249
229
|
# @return [Float]
|
data/lib/anima/version.rb
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted when the Anthropic provider rejects the configured token with
|
|
5
|
+
# an authentication error. Surfaces to the TUI via
|
|
6
|
+
# {Events::Subscribers::AuthenticationBroadcaster} so the user is
|
|
7
|
+
# prompted for a new token — and to the conversation as a system
|
|
8
|
+
# message so the failure is visible in history.
|
|
9
|
+
#
|
|
10
|
+
# Not persisted — not included in {Message::TYPES}.
|
|
11
|
+
class AuthenticationRequired < Base
|
|
12
|
+
TYPE = "authentication_required"
|
|
13
|
+
|
|
14
|
+
# @param session_id [Integer] session the failure is scoped to
|
|
15
|
+
# @param content [String] human-readable error text from the provider
|
|
16
|
+
def initialize(session_id:, content:)
|
|
17
|
+
super(content: content, session_id: session_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def type
|
|
21
|
+
TYPE
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/events/bounce_back.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Events
|
|
4
|
-
# Transient failure event emitted
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
4
|
+
# Transient failure event emitted by {DrainJob} when the first LLM
|
|
5
|
+
# call on a bounce-back-flagged user_message fails. The promoted
|
|
6
|
+
# Message is destroyed so the TUI can remove the phantom and restore
|
|
7
|
+
# the user's text to the input field.
|
|
8
8
|
#
|
|
9
9
|
# Not persisted — not included in {Message::TYPES}.
|
|
10
10
|
class BounceBack < Base
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted after Mneme advances the boundary past an eviction zone.
|
|
5
|
+
# Subscribers broadcast the cutoff to connected clients so they can
|
|
6
|
+
# drop messages below it.
|
|
7
|
+
class EvictionCompleted
|
|
8
|
+
TYPE = "eviction.completed"
|
|
9
|
+
|
|
10
|
+
attr_reader :session_id, :evict_above_id
|
|
11
|
+
|
|
12
|
+
# @param session_id [Integer] the session whose boundary advanced
|
|
13
|
+
# @param evict_above_id [Integer] last message ID in the evicted zone;
|
|
14
|
+
# clients drop all messages with id <= this value
|
|
15
|
+
def initialize(session_id:, evict_above_id:)
|
|
16
|
+
@session_id = session_id
|
|
17
|
+
@evict_above_id = evict_above_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def event_name
|
|
21
|
+
"#{Bus::NAMESPACE}.#{TYPE}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
{type: TYPE, session_id:, evict_above_id:}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted after a {Goal} record is committed for the first time.
|
|
5
|
+
# The drain pipeline's {MeleteEnrichmentJob} subscribes for the
|
|
6
|
+
# duration of one Melete run so a fresh goal triggers Mneme recall
|
|
7
|
+
# against the updated goal set before the user's message reaches the LLM.
|
|
8
|
+
class GoalCreated
|
|
9
|
+
TYPE = "goal.created"
|
|
10
|
+
|
|
11
|
+
attr_reader :session_id, :goal_id
|
|
12
|
+
|
|
13
|
+
# @param session_id [Integer] session that owns the goal
|
|
14
|
+
# @param goal_id [Integer] the newly created goal
|
|
15
|
+
def initialize(session_id:, goal_id:)
|
|
16
|
+
@session_id = session_id
|
|
17
|
+
@goal_id = goal_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def event_name
|
|
21
|
+
"#{Bus::NAMESPACE}.#{TYPE}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
{type: TYPE, session_id:, goal_id:}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted after a {Goal}'s description is changed and the change is
|
|
5
|
+
# committed. Status-only updates (finish_goal, cascade completion,
|
|
6
|
+
# mark_goal_completed) do not emit — a completed goal carries no new
|
|
7
|
+
# search seed for Mneme.
|
|
8
|
+
#
|
|
9
|
+
# The drain pipeline's {MeleteEnrichmentJob} subscribes for the
|
|
10
|
+
# duration of one Melete run so a refined goal triggers Mneme recall
|
|
11
|
+
# against the updated wording before the user's message reaches the LLM.
|
|
12
|
+
class GoalUpdated
|
|
13
|
+
TYPE = "goal.updated"
|
|
14
|
+
|
|
15
|
+
attr_reader :session_id, :goal_id
|
|
16
|
+
|
|
17
|
+
# @param session_id [Integer] session that owns the goal
|
|
18
|
+
# @param goal_id [Integer] the updated goal
|
|
19
|
+
def initialize(session_id:, goal_id:)
|
|
20
|
+
@session_id = session_id
|
|
21
|
+
@goal_id = goal_id
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def event_name
|
|
25
|
+
"#{Bus::NAMESPACE}.#{TYPE}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_h
|
|
29
|
+
{type: TYPE, session_id:, goal_id:}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted by the drain loop after a single LLM round-trip completes.
|
|
5
|
+
# Carries the raw Anthropic response so downstream subscribers can
|
|
6
|
+
# persist messages, transition session state, and dispatch tool
|
|
7
|
+
# execution when the response is a +tool_use+.
|
|
8
|
+
#
|
|
9
|
+
# The drain loop hands off via this event — it does not persist
|
|
10
|
+
# Messages or release the session itself. Single responsibility:
|
|
11
|
+
# one subscriber pumps PendingMessages into the LLM, another owns
|
|
12
|
+
# the aftermath.
|
|
13
|
+
class LLMResponded
|
|
14
|
+
TYPE = "session.llm_responded"
|
|
15
|
+
|
|
16
|
+
attr_reader :session_id, :response, :api_metrics
|
|
17
|
+
|
|
18
|
+
# @param session_id [Integer] session that made the LLM call
|
|
19
|
+
# @param response [Hash] raw Anthropic response (with +content+ and +stop_reason+)
|
|
20
|
+
# @param api_metrics [Hash, nil] rate-limit and usage metrics from the provider
|
|
21
|
+
def initialize(session_id:, response:, api_metrics: nil)
|
|
22
|
+
@session_id = session_id
|
|
23
|
+
@response = response
|
|
24
|
+
@api_metrics = api_metrics
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def event_name
|
|
28
|
+
"#{Bus::NAMESPACE}.#{TYPE}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_h
|
|
32
|
+
{type: TYPE, session_id:, response:, api_metrics:}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
# Emitted after a Message record is committed to the database.
|
|
5
|
+
# Subscribers react to persisted messages — not to raw domain events.
|
|
6
|
+
#
|
|
7
|
+
# Carries the Message record directly so subscribers don't need to
|
|
8
|
+
# look it up again.
|
|
9
|
+
class MessageCreated
|
|
10
|
+
TYPE = "message.created"
|
|
11
|
+
|
|
12
|
+
attr_reader :message
|
|
13
|
+
|
|
14
|
+
# @param message [Message] the persisted message record
|
|
15
|
+
def initialize(message)
|
|
16
|
+
@message = message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def event_name
|
|
20
|
+
"#{Bus::NAMESPACE}.#{TYPE}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_h
|
|
24
|
+
{type: TYPE, message:}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|