aia 1.1.1 → 2.0.0.0.pre.beta2

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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +9 -1
  3. data/.loki +11 -0
  4. data/.reek.yml +160 -0
  5. data/.rubocop.yml +116 -0
  6. data/.rubocop_strict.yml +15 -0
  7. data/.version +1 -1
  8. data/CHANGELOG.md +269 -55
  9. data/IMPLEMENTATION_PLAN.md +506 -0
  10. data/README.md +267 -239
  11. data/Rakefile +5 -5
  12. data/_typos.toml +10 -0
  13. data/aia.gemspec +92 -0
  14. data/architecture_review.md +314 -0
  15. data/bin/aia +16 -0
  16. data/config/aia.yml +13 -0
  17. data/docs/AGENTS.md +40 -0
  18. data/docs/advanced-prompting.md +67 -3
  19. data/docs/cli-reference.md +312 -56
  20. data/docs/configuration.md +130 -19
  21. data/docs/contributing.md +56 -2
  22. data/docs/directives-reference.md +593 -78
  23. data/docs/faq.md +85 -3
  24. data/docs/guides/available-models.md +1 -1
  25. data/docs/guides/basic-usage.md +6 -6
  26. data/docs/guides/chat.md +40 -16
  27. data/docs/guides/crew.md +239 -0
  28. data/docs/guides/executable-prompts.md +1 -1
  29. data/docs/guides/index.md +1 -0
  30. data/docs/guides/models.md +15 -0
  31. data/docs/index.md +29 -2
  32. data/docs/installation.md +44 -17
  33. data/docs/mcp-integration.md +40 -0
  34. data/docs/prompt_management.md +85 -86
  35. data/docs/security.md +47 -0
  36. data/docs/special_projects_guide.md +386 -0
  37. data/docs/tools-and-mcp-examples.md +23 -0
  38. data/docs/workflows-and-pipelines.md +84 -7
  39. data/examples/.gitignore +1 -0
  40. data/examples/00_setup_aia.sh +27 -44
  41. data/examples/11_multi_model.sh +4 -14
  42. data/examples/12_token_usage.sh +3 -12
  43. data/examples/18_tools.sh +10 -2
  44. data/examples/22_chat_mode.sh +0 -10
  45. data/examples/23_verify.sh +139 -0
  46. data/examples/24_decompose.sh +139 -0
  47. data/examples/25_spawn.sh +139 -0
  48. data/examples/26_debate.sh +97 -0
  49. data/examples/27_mention_routing.sh +157 -0
  50. data/examples/28_model_switching.sh +106 -0
  51. data/examples/29_agent_harness.sh +177 -0
  52. data/examples/README.md +65 -0
  53. data/examples/advanced_multi_robot_capabilities_without_examples.md +106 -0
  54. data/examples/aia_config.yml +1 -1
  55. data/examples/aia_config_orchestrator.yml +45 -0
  56. data/examples/common.sh +18 -6
  57. data/examples/context/tech_stack.md +2 -2
  58. data/examples/prompts_dir/roles/orchestrator.md +21 -0
  59. data/examples/requirements/sinatra_taskflow_app.md +139 -0
  60. data/examples/rules/01_classify_ruby.rb +16 -0
  61. data/examples/rules/02_prefer_claude_for_code.rb +19 -0
  62. data/examples/rules/03_gate_prompt_length.rb +19 -0
  63. data/examples/rules/04_tool_selection.rb +41 -0
  64. data/examples/rules/README.md +30 -0
  65. data/examples/run_all.sh +48 -15
  66. data/examples/tools/word_count_tool.rb +1 -1
  67. data/lib/AGENTS.md +57 -0
  68. data/lib/aia/chat_loop.rb +263 -167
  69. data/lib/aia/config/cli_parser.rb +217 -145
  70. data/lib/aia/config/defaults.yml +62 -33
  71. data/lib/aia/config/mcp_parser.rb +52 -51
  72. data/lib/aia/config/model_spec.rb +34 -2
  73. data/lib/aia/config/validator.rb +171 -216
  74. data/lib/aia/config.rb +111 -145
  75. data/lib/aia/content_extractor.rb +155 -0
  76. data/lib/aia/cost_calculator.rb +39 -0
  77. data/lib/aia/crew.rb +164 -0
  78. data/lib/aia/debate_handler.rb +174 -0
  79. data/lib/aia/delegate_handler.rb +116 -0
  80. data/lib/aia/directive.rb +43 -26
  81. data/lib/aia/directive_processor.rb +16 -7
  82. data/lib/aia/directives/configuration_directives.rb +214 -60
  83. data/lib/aia/directives/context_directives.rb +67 -52
  84. data/lib/aia/directives/execution_directives.rb +141 -4
  85. data/lib/aia/directives/model_directives.rb +163 -141
  86. data/lib/aia/directives/trakflow_directives.rb +62 -0
  87. data/lib/aia/directives/utility_directives.rb +227 -30
  88. data/lib/aia/directives/web_and_file_directives.rb +126 -77
  89. data/lib/aia/errors.rb +15 -0
  90. data/lib/aia/fact_asserter.rb +27 -0
  91. data/lib/aia/fzf.rb +9 -31
  92. data/lib/aia/handler_context.rb +17 -0
  93. data/lib/aia/handler_protocol.rb +19 -0
  94. data/lib/aia/history_transfer.rb +55 -0
  95. data/lib/aia/input_collector.rb +3 -3
  96. data/lib/aia/layered_orchestrator.rb +471 -0
  97. data/lib/aia/logger.rb +45 -25
  98. data/lib/aia/mcp_config_normalizer.rb +35 -0
  99. data/lib/aia/mcp_connection_manager.rb +315 -0
  100. data/lib/aia/mcp_discovery.rb +44 -0
  101. data/lib/aia/mcp_grouper.rb +33 -0
  102. data/lib/aia/mcp_server_config.rb +30 -0
  103. data/lib/aia/mcp_utility.rb +60 -0
  104. data/lib/aia/mention_router.rb +217 -0
  105. data/lib/aia/model_alias_registry.rb +97 -0
  106. data/lib/aia/model_switch_handler.rb +100 -0
  107. data/lib/aia/network_builder.rb +160 -0
  108. data/lib/aia/network_memory_manager.rb +55 -0
  109. data/lib/aia/patches/ruby_llm_streaming_error.rb +43 -0
  110. data/lib/aia/patches/ruby_llm_tool_error.rb +96 -0
  111. data/lib/aia/pipeline_orchestrator.rb +272 -0
  112. data/lib/aia/plugin_loader.rb +170 -0
  113. data/lib/aia/plugin_monitor.rb +211 -0
  114. data/lib/aia/prompt_decomposer.rb +159 -0
  115. data/lib/aia/prompt_handler.rb +56 -82
  116. data/lib/aia/robot_builder.rb +51 -0
  117. data/lib/aia/robot_factory.rb +338 -0
  118. data/lib/aia/robot_namer.rb +110 -0
  119. data/lib/aia/session.rb +87 -17
  120. data/lib/aia/session_tracker.rb +207 -0
  121. data/lib/aia/similarity_scorer.rb +41 -0
  122. data/lib/aia/skill_utils.rb +105 -1
  123. data/lib/aia/spawn_handler.rb +129 -0
  124. data/lib/aia/spawn_spec_parser.rb +65 -0
  125. data/lib/aia/special_mode_handler.rb +322 -0
  126. data/lib/aia/speech.rb +67 -0
  127. data/lib/aia/startup_coordinator.rb +151 -0
  128. data/lib/aia/streaming_runner.rb +172 -0
  129. data/lib/aia/system_prompt_assembler.rb +92 -0
  130. data/lib/aia/task_coordinator.rb +207 -0
  131. data/lib/aia/task_decomposer.rb +57 -0
  132. data/lib/aia/task_executor.rb +51 -0
  133. data/lib/aia/tfidf_math.rb +27 -0
  134. data/lib/aia/timing.rb +15 -0
  135. data/lib/aia/tool_filter/tfidf.rb +116 -0
  136. data/lib/aia/tool_filter/wordnet_expander.rb +127 -0
  137. data/lib/aia/tool_filter.rb +83 -0
  138. data/lib/aia/tool_filter_registry.rb +30 -0
  139. data/lib/aia/tool_filter_strategy.rb +146 -0
  140. data/lib/aia/tool_introspection.rb +17 -0
  141. data/lib/aia/tool_loader.rb +216 -0
  142. data/lib/aia/tool_utility.rb +30 -0
  143. data/lib/aia/tools/delegate_to_foreman_tool.rb +70 -0
  144. data/lib/aia/tools/recruit_robot_tool.rb +60 -0
  145. data/lib/aia/tools/reskill_robot_tool.rb +44 -0
  146. data/lib/aia/tools/task_board_tool.rb +115 -0
  147. data/lib/aia/trakflow_bridge.rb +175 -0
  148. data/lib/aia/turn_state.rb +95 -0
  149. data/lib/aia/ui_presenter.rb +182 -206
  150. data/lib/aia/utility.rb +136 -87
  151. data/lib/aia/{history_manager.rb → variable_input_collector.rb} +9 -9
  152. data/lib/aia/verification_network.rb +57 -0
  153. data/lib/aia.rb +124 -63
  154. data/mkdocs.yml +1 -0
  155. metadata +187 -58
  156. data/justfile +0 -215
  157. data/lib/aia/adapter/chat_execution.rb +0 -242
  158. data/lib/aia/adapter/error_handler.rb +0 -68
  159. data/lib/aia/adapter/gem_activator.rb +0 -57
  160. data/lib/aia/adapter/mcp_connector.rb +0 -274
  161. data/lib/aia/adapter/modality_handlers.rb +0 -167
  162. data/lib/aia/adapter/model_registry.rb +0 -81
  163. data/lib/aia/adapter/multi_model_chat.rb +0 -218
  164. data/lib/aia/adapter/provider_configurator.rb +0 -59
  165. data/lib/aia/adapter/tool_filter.rb +0 -85
  166. data/lib/aia/adapter/tool_loader.rb +0 -90
  167. data/lib/aia/chat_processor_service.rb +0 -178
  168. data/lib/aia/prompt_pipeline.rb +0 -183
  169. data/lib/aia/ruby_llm_adapter.rb +0 -95
  170. data/lib/extensions/openstruct_merge.rb +0 -48
  171. data/lib/extensions/ruby_llm/.irbrc +0 -56
  172. data/lib/extensions/ruby_llm/modalities.rb +0 -36
  173. data/lib/extensions/ruby_llm/provider_fix.rb +0 -79
  174. data/lib/refinements/string.rb +0 -16
  175. data/main.just +0 -76
data/CHANGELOG.md CHANGED
@@ -1,69 +1,283 @@
1
1
  # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ This section captures all changes since v1.1.0.
6
+
7
+ ## [2.0.0.pre.beta2] 2026-09-09
8
+
9
+ Using robot_lab v0.2.5
10
+ Using lumberjack v2.1.0
11
+
12
+ ### Added
13
+
14
+ - **`--[no-]thinking` flag** (`lib/aia/config/cli_parser.rb`, `lib/aia/config/defaults.yml`, `lib/aia/config.rb`, `lib/aia/streaming_runner.rb`): Controls whether raw reasoning blocks from local models (e.g. `qwen3` on Ollama) are shown. Such models stream their chain-of-thought wrapped in `<think>...</think>` tags; AIA now filters these out by default so only the final answer is displayed. Pass `--thinking` to show the reasoning. The `StreamingRunner` strips the tags inline, tracking open spans across chunk boundaries. Config key: `flags.thinking` (env: `AIA_FLAGS__THINKING`); default `false`.
15
+
16
+ - **Crews — every session is a team of robots** (`lib/aia/crew.rb`, `lib/aia/mention_router.rb`, `lib/aia/directives/execution_directives.rb`): Every chat session is now a crew (a `RobotLab::Network`), even with a single model; the lead robot is the **chief**.
17
+ - **`/add_recruit <name> [provider/model] [system prompt]`** (alias `/add`): Adds a persistent member to the crew that survives the session, shows in `/robots`, and answers to `@name`. Omit the model to inherit the chief's; use `-`/`inherit` to inherit explicitly. Recruits inherit the chief's local tools and connected MCP servers.
18
+ - **`/drop_recruit <name>`** (alias `/drop`): Removes a member; the chief cannot be dropped.
19
+ - **Skill assignment to recruits**: `/add_recruit` accepts `skill:<id>` tokens (comma-separate or repeat for several) that load named skills as the recruit's role/system prompt — letting the chief divide labor across the crew (e.g. a security reviewer + a performance reviewer). Unknown skill ids are reported rather than silently dropped. The chief's `recruit_robot` tool gained a matching `skills` param so it can assign roles itself.
20
+ - **`/reskill <name> [skill:<id>...] [system prompt]`**: Resets a member to a clean slate (fresh conversation) keeping its name and model, and applies a new skill/role. Backed by `Crew.reskill` and a `reskill_robot` chief tool. Useful for recovering an off-task member or moving the crew through phases.
21
+ - **`@crew` broadcast handle**: Sends a prompt to every member concurrently. `crew` is reserved as a member name.
22
+ - **Position-aware `@mention` routing**: A leading address (`@a @b ...`) runs the addressees concurrently; a `@name` woven into the body runs the members sequentially as a pipeline, sharing each reply into the others' context so later members build on earlier ones.
23
+ - **`/spawn` extended** to the explicit form `/spawn <name> <provider/model> <system prompt>` alongside the existing `/spawn` and `/spawn <type>` forms. `/spawn` remains a one-shot specialist for the next prompt; `/add_recruit` is its persistent counterpart.
24
+ - **Docs**: new [Crews guide](docs/guides/crew.md); README `@mention`/crew section and `docs/directives-reference.md` updated for `/add_recruit`, `/drop_recruit`, and the extended `/spawn`.
25
+
26
+ - **`--history-file` fully implemented** (`lib/aia/ui_presenter.rb`): `chat_history_file` now checks `config.output.history_file` first — uses the configured path when set, returns `nil` when `--no-history-file` is given (disabling history). `load_chat_history` and `save_chat_history` both guard against `nil` so disabling history is a clean no-op. Resolution order: `config.output.history_file` → `paths.aia_dir/chat_history` → `~/.config/aia/chat_history`.
27
+
28
+ - **`--speech-model` fully implemented** (`lib/aia/robot_factory.rb`, `lib/aia/chat_loop.rb`, `lib/aia/mention_router.rb`): `configure_audio` stores the value and passes it as the `SPEECH_MODEL` environment variable to the speak subprocess, allowing custom TTS scripts to select a model. `--voice` wires to `say -v VOICE` for the macOS `say` command.
29
+
30
+ - **`--transcription-model` fully implemented** (`lib/aia/robot_factory.rb`): `configure_audio` calls `RubyLLM.configure { |c| c.default_transcription_model = ... }` so the transcription model is active for the entire session.
31
+
32
+ - **`--speak-command CMD`** (`lib/aia/config/cli_parser.rb`, `lib/aia/config.rb`): New CLI option to set `audio.speak_command` at runtime without editing config. Enables full OpenAI TTS pipeline from the command line: `aia --speak --speak-command ~/.config/aia/tts.sh --speech-model tts-1-hd --voice nova my_prompt`.
33
+
34
+ - **Three-stage `--speak` pipeline with per-stage spinners** (`lib/aia/chat_loop.rb`, `lib/aia/mention_router.rb`): After text streams to the terminal, `--speak` now shows distinct progress feedback for each stage — `Converting to audio...` while the TTS command runs, then `Speaking...` while the audio plays. For the local macOS `say` command (which converts and plays in one step) only `Speaking...` is shown. Custom TTS scripts receive the output file path as `$2`; AIA creates the temp file and calls `afplay` itself.
35
+
36
+ - **`~/.config/aia/tts.sh`**: OpenAI TTS helper script installed at the AIA config directory. Accepts text as `$1` and output path as `$2`; reads `SPEECH_MODEL` and `AIA_AUDIO__VOICE` env vars; calls `api.openai.com/v1/audio/speech` via `curl`. Does not call `afplay` — AIA handles playback.
37
+
38
+ - **`--skill` / `-s` fully implemented**: Injects skill content into the AI context; mode-aware — in `--chat` mode skills are appended to the system prompt once (persists across all turns); in pipeline mode skills are appended to each individual prompt text after the role content.
39
+ - **`SkillUtils#skills_base_dir`**: New resolver centralising skills path computation — returns `skills.dir` when `skills_prefix` is unset, or `skills.dir / prefix` when prefix is set; shared by `SystemPromptAssembler` (chat mode) and `PipelineOrchestrator` (pipeline mode).
40
+ - **`SkillUtils#load_skills_content` / `#load_single_skill_content`**: Load and join skill bodies from one or more skill IDs; strip YAML front matter; warn and skip missing skills without aborting.
41
+
42
+ ### Changed
43
+
44
+ - **`skills_prefix` default changed from `"skills"` to `nil`**: When unset and no `--skills-dir` is given, the skills directory falls back to `~/.prompts/skills`; setting `--skills-prefix` appends to `--prompts-dir` (or `AIA_PROMPTS__DIR`) rather than to a fixed default.
45
+ - **`--skills-prefix` path resolution** (`lib/aia/config/cli_parser.rb`): Three-branch logic — explicit `--skills-dir` + prefix → `skills_dir/prefix`; no `--skills-dir` but prefix set → `prompts_dir/prefix`; neither given → `AIA_SKILLS__DIR` or `~/.prompts/skills`.
46
+ - **Docs updated for skills options**: `docs/cli-reference.md` — `--skills-prefix` default and resolution, chat-vs-pipeline injection note in `--skill`; `docs/configuration.md` — `skills_prefix` default, prompt assembly order section rewritten; `docs/directives-reference.md` — `/skill` directory resolution description.
47
+
48
+ - **`.loki` asgard task runner**: Added per-project task file with quality gates (`test`, `flog`, `flay`, `rubocop`), each capturing output to `*_output.txt`; `quality` task runs all gates via `system()` and prints a per-gate pass/fail summary table; `aia` passthrough task runs `bin/aia` with arbitrary arguments from any working directory.
49
+ - **`.rubocop.yml`**: Project-level RuboCop configuration achieving 0 offenses — disables intentional-pattern cops (`Style/FormatStringToken`, `Style/SafeNavigationChainLength`, `Lint/DuplicateBranch`), adds test-file exclusions for cops that conflict with minitest patterns, and raises Metrics thresholds to match actual method complexity.
50
+
51
+ - **`LayeredOrchestrator`**: 3-tier agent orchestration — Tobor decomposes requirements, lead agents break them into specialist tasks, specialists produce artifacts. Activated via `/orchestrate`.
52
+ - **`TaskDecomposer` + `TaskExecutor`**: Break complex prompts into parallel subtasks and execute them via TrakFlow.
53
+ - **`OrchestratorError`, `DebateError`, `DecomposeError`**: Typed error classes for orchestration and debate failure paths (`lib/aia/errors.rb`).
54
+ - **`tool_filter.timeout_s` config key**: Configurable TF-IDF filter timeout; defaults to 10 seconds.
55
+ - **Parallel LLM calls**: Multi-model network turns now run concurrently via `Async::Barrier`.
56
+ - **Session cleanup**: `at_exit` hook in `AIA.run` calls `Session#cleanup!` to release MCP connections, flush caches, and close TrakFlow.
57
+ - **`state_setting!` DSL**: Directive subclasses declare their exclusive mode with `state_setting! :debate, :spawn, ...`; `ChatLoop` no longer maintains a hardcoded prefix list.
58
+ - **Demo 29**: End-to-end agent harness demo building a Sinatra/Sequel application from a requirements document.
59
+ - **`BATCH_MODE` support**: Demos 23–28 skip live `--chat` sessions when `BATCH_MODE=true`, enabling `run_all.sh` CI automation.
60
+
61
+ ### Changed
62
+
63
+ - **TF-IDF as sole tool filter strategy**: Zvec (B), SqliteVec (C), and LSI (D) strategies removed (~2,150 LOC deleted).
64
+ - **`auto_tool_filter` flag**: Replaces `tool_filter_a`; enabled by default; toggled via `--[no-]auto-tool-filter`.
65
+ - **`robot_lab` dependency**: Updated from `~> 0.0.9` to `~> 0.1.0`.
66
+ - **Lazy handler instantiation**: `SpawnHandler`, `DebateHandler`, `DelegateHandler`, and `LayeredOrchestrator` are memoized and created on first use.
67
+ - **Lazy-require for heavy subsystems**: `VerificationNetwork`, `PromptDecomposer`, and all TrakFlow infrastructure required only when invoked.
68
+ - **`shared_tools` optional runtime dependencies**: `dentaku`, `sequel`, and `openweathermap` moved to dev dependencies; each tool guards its `require` with a boolean constant and exposes `available?`; `bigdecimal >= 4.0` pinned conditionally for Ruby >= 4.0.
69
+ - **`README.md`**: Added badges, "Why AIA?" feature comparison table, and Prompt Directives reorganized into seven category subsections.
70
+
71
+ ### Removed
72
+
73
+ - **Zvec, SqliteVec, LSI tool filter strategies** and their test files.
74
+ - **`zvec`, `sqlite-vec`, `informers` gem dependencies** from gemspec.
75
+ - **KBS gem and rule engine**: `kbs` gem and all KBS infrastructure (RuleRouter, KBDefinitions, FactAsserter, DynamicRuleBuilder, RulesDSL, DecisionApplier, ExpertRouter) deleted.
76
+ - **`-A`/`--tool-filter-kbs` CLI flag**, `/rules` chat directive, and user-defined rule hooks.
77
+ - **`build_streaming_callback`**: Dead code path in `RobotFactory`.
78
+
79
+ ### Fixed
80
+
81
+ - **`--no-mcp` flag ignored by `RobotBuilder`** (`lib/aia/robot_builder.rb`): Was directly mapping `config.mcp_servers` without checking `config.flags.no_mcp`; now delegates to `RobotFactory.mcp_server_configs(config)` which honours the flag — preventing MCP servers from connecting and tools from overflowing the model's 128-tool limit.
82
+ - **`load_extra_config` falls through on missing file in tests** (`lib/aia/config.rb`): Restored `return` after `exit 1`; tests mock `exit` as a no-op, so without the guard the method continued to `YAML.safe_load_file` on the nonexistent path and raised `Errno::ENOENT`.
83
+ - **`warn` → `$stderr.puts` across all lib files** (19 files): Ruby 4.0 suppresses `Kernel#warn` output by default (requires `-W` flag); messages were silently dropped — including unknown-option errors, missing-file warnings, and audio failures. Replaced all `warn` calls with `$stderr.puts` which always writes unconditionally. Test suite updated: replaced `stubs(:warn)` + `stderr_messages` patterns with `capture_io` to capture real `$stderr` output.
84
+ - **`--speak` produced `AudioFileOpen failed` error** (`lib/aia/config/defaults.yml`, `~/.config/aia/aia.yml`): Default `speak_command` was `afplay` (an audio file player) instead of `say` (macOS TTS); passing response text to `afplay` caused it to interpret the text as a file path and fail. Default changed to `say`; `voice` and `speech_model` defaults cleared to `nil` so `say` uses the system default voice with no API call.
85
+
86
+ - **`DebateHandler` convergence short-circuit** (`lib/aia/debate_handler.rb`): Added `all_signaled_convergence?` fast path — when all robots say `CONVERGED` after minimum rounds the debate ends immediately, without waiting for similarity scoring to catch up.
87
+ - **`SpecialModeHandler` keyword argument crash** (`lib/aia/special_mode_handler.rb`): `network.run(prompt)` → `network.run(message: prompt)` to match `RobotLab::Network#run`'s keyword-only API.
88
+ - **`ToolLoader` gem loading under Bundler** (`lib/aia/tool_loader.rb`): `Gem::LoadError` (`ScriptError`, not `StandardError`) now caught; added `eager_load_namespace_fallback` for Zeitwerk recovery; `activate_unbundled_gem` falls back to `Gem.default_dir`/`Gem.user_dir` scanning when Bundler blocks `gem name`.
89
+ - **Demo 18 tool overload** (`examples/18_tools.sh`): Added `--allowed-tools` to expose only 3 tools; 35+ tools caused `gpt-4.1` to narrate instead of invoke.
90
+ - **Demo 26/27 robot name** (`examples/26_debate.sh`, `examples/27_mention_routing.sh`): `RobotNamer` assigns "Vanguard" to `gpt-4.1-mini`, not "Quark"; corrected in both scripts.
91
+ - **Ollama model tag resolution** (`lib/aia/verification_network.rb`, `lib/aia/prompt_decomposer.rb`): Pass `model.internal_id` (e.g. `ollama/qwen3:latest`) to `RobotLab.build` instead of `model.name`.
92
+ - **Dynamic pipeline iteration** (`lib/aia/pipeline_orchestrator.rb`): Changed `config.pipeline.each` to `until config.pipeline.empty?` + `shift` so mid-run `next:`/`pipeline:` replacements are picked up immediately.
93
+ - **AI response written to output file twice** (`lib/aia/ui_presenter.rb`): Removed file-writing from `display_ai_response`; file output exclusively owned by `output_to_file`.
94
+ - **Unknown `@mention` falls through to broadcast** (`lib/aia/mention_router.rb`): Returns `true` and reports unrecognized names instead of silently broadcasting to all robots.
95
+ - **Custom directive classes unavailable in ERB prompts** (`lib/aia/tool_loader.rb`): `PM::Directive.register_all` now called at the end of `ToolLoader#load_tools` so tool-provided directives are registered before the first prompt render.
96
+ - **Demo scripts using untagged Ollama model names**: Updated to `phi4:latest` and `phi4-mini:latest`.
97
+ - **`/ruby` directive security gate**: `eval` now guarded by `AIA.config.flags.allow_ruby_eval`.
98
+ - **`RobotFactory` namer state leak**: `@namer` initialized once in `RobotFactory.setup` instead of reset on every robot build.
99
+ - **`at_exit` hook registered multiple times**: Moved from `Session#start` to `AIA.run`, registered exactly once per process.
100
+ - **GPT-5/OpenAI token parameter mismatch**: Models with `temperature: false` skip temperature; `max_tokens` translated to `max_completion_tokens` for o-series models.
101
+ - **Installed gem BigDecimal activation conflict** (`lib/aia.rb`): AIA activates `bigdecimal >= 4.0` before loading transitive dependencies.
102
+ - **Pipeline token metrics crash**: `display_metrics` uses `result.raw` instead of non-existent `result.output`.
103
+ - **Skill symlink path containment**: Skill resolution checks real paths against the skills directory boundary.
104
+ - **`--list-skills` CLI directory options**: Now honors `--prompts-dir` and `--skills-prefix` regardless of option order.
105
+
106
+ ### Known Issues
107
+
108
+ - **`--skill` injection in RobotLab flow**: The RobotLab prompt execution path does not yet prepend configured skills to pipeline prompts and chat startup context.
109
+ - **Path-based direct skill file restrictions**: `/skill` accepts any readable file path; should be limited to markdown files to prevent arbitrary injection.
110
+
2
111
  ## [1.1.1] - 2026-05-01
3
112
 
4
113
  ### Bug Fixes
5
- - **Conversation context lost on Ollama model pipeline steps (Issue #152)**: `maybe_change_model` was unconditionally replacing the adapter whenever the configured model name (e.g. `ollama/qwen3`) did not literally appear in the resolved model ID (`qwen3`). Fixed by stripping the provider prefix before comparison and by never replacing the adapter when conversation messages exist in any chat instance — preserving all history across pipeline steps and role changes
6
- - **Role file front matter overwriting active model config**: `ChatLoop#process_role_context` was allowing `fetch_role` (which calls `apply_metadata_config`) to overwrite `AIA.config.models` with any `model:` key in the role file's YAML front matter. The active model is now saved before `fetch_role` and restored afterward, so a role's front matter cannot trigger a false mismatch in `maybe_change_model`
7
- - **Custom `--tools` directives not available in ERB prompts**: `AIA::Directive` subclasses defined in files loaded via `--tools` were required too late — after `PromptHandler.new` had already called `register_pm_directives`. Added `require_tool_files` as the first step in `ConfigValidator#tailor` so custom directive classes are defined and registered into `PM.directives` before `PromptHandler.new` runs; `<%= timestamp %>` and other custom ERB helpers now expand correctly
8
- - **Demo example path resolution**: `examples/prompts_dir/project_summary` referenced `../../*.gemspec` and `../../lib/aia/*.rb`, resolving two levels above the repo root from the `examples/` working directory; corrected to `../`
114
+
115
+ - **Ollama model pipeline context preservation**: Mainline fixed adapter replacement when provider-prefixed model names (for example `ollama/qwen3`) did not match resolved local model IDs, preserving conversation history across pipeline steps.
116
+ - **Role front matter model isolation**: Mainline restored the active model after role loading so a role file's `model:` metadata cannot unexpectedly replace the configured model.
117
+ - **Custom directive loading for ERB prompts**: Mainline loads tool files before PromptManager directive registration so directive classes provided by `--tools` are available inside ERB prompts.
118
+ - **Demo example path resolution** (`examples/prompts_dir/project_summary`): Corrected repository-relative paths used by the example prompt.
9
119
 
10
120
  ### Improvements
11
- - **Demo scripts use develop-branch binary**: `examples/common.sh` now prepends `../bin` to `PATH` when the local `bin/aia` is executable, ensuring all demo scripts exercise working-tree changes rather than the installed gem
12
121
 
13
- ### Testing
14
- - Added `test/aia/chat_loop_test.rb` — 6 tests covering `process_role_context`: early-return when no role configured, system message injection into chats, skipping chats that already have a system message, and model restoration after `fetch_role` side effects (Issue #152 regression tests)
15
- - Expanded `test/aia/chat_processor_service_test.rb` with 7 additional tests: provider-prefix stripping before model comparison, full-ID alias resolution (e.g. `claude-sonnet-4` vs `claude-sonnet-4-20250514`), adapter preservation when any chat has history, adapter replacement allowed when no history exists, and the exact Issue #152 regression scenario (OpenAI model active, role front matter specifies a Claude model)
122
+ - **Demo scripts prefer working-tree binary** (`examples/common.sh`): Mainline demo scripts prepend `../bin` to `PATH` when local `bin/aia` is executable.
16
123
 
17
124
  ## [1.1.0] - 2026-04-23
18
125
 
19
126
  ### New Features
20
- - **Skills system**: Skills are subdirectories under `~/.prompts/skills` (configurable), each containing a `SKILL.md` file with YAML front matter (`name`, `description`, and any custom fields)
21
- - `/skills [query]` directive lists available skills; supports positive/negative search filtering (`-term`, `~term`, `!term`)
22
- - `/skill <name>` directive includes the `SKILL.md` content into the conversation; resolves by exact match then prefix match
23
- - `/skill <path>` directive accepts path-based IDs (absolute, `~/`, `./`, `../`) including direct `.md` files
24
- - `--list-skills` CLI flag prints all skills as markdown tables (one `## skill` heading + `| Key | Value |` table per skill, showing all front matter fields)
25
- - `--skill`/`-s SKILL_IDS` CLI option prepends one or more skills to the prompt (comma-separated, repeatable); accepts skill IDs or paths
26
- - `--skills-dir DIR` and `--skills-prefix PREFIX` CLI options configure the skills directory
27
- - **Path-based skill resolution**: `--skill` and `/skill` now accept file-system paths in addition to named IDs
28
- - Absolute paths: `--skill /path/to/skill-dir` or `--skill /path/to/skill.md`
29
- - Home-relative: `--skill ~/skills/my-skill`
30
- - Working-directory-relative: `--skill ./temp_skill.md`
31
- - Direct `.md` files: the file content is used as-is (front matter stripped); no `SKILL.md` lookup needed
32
- - **Path-based role resolution**: `--role` now accepts file-system paths in addition to role IDs
33
- - Absolute and home-relative paths bypass the `~/.prompts/roles/` prefix lookup
34
- - Example: `aia --role ~/custom/expert.md my_prompt`
35
- - **Skills in chat-only mode**: `ChatLoop` now loads and sends configured skills to the AI at session start via `process_skill_context`, even when no pipeline prompt is specified; the AI's acknowledgment is displayed so the user can confirm the skill was applied
36
- - **`AIA::SkillUtils` module**: New shared utility module (`lib/aia/skill_utils.rb`) consolidating path and skill helpers previously duplicated across four classes
37
- - `path_based_id?(id)` — detects `/`, `~/`, `./`, `../` prefixes
38
- - `parse_front_matter(path)` — reads and parses YAML front matter from a `.md` file; returns `{}` on any failure
39
- - `find_skill_dir(skill_name, base_dir)` — resolves a skill name or path to its directory (or direct `.md` file); enforces symlink/prefix-traversal checks for ID-based lookups
40
- - `safe_skill_path(path, dir)` — verifies a resolved path is contained within `dir` using `File::SEPARATOR`-terminated prefix to prevent sibling-directory attacks
41
- - `skill_body(content)` — strips YAML front matter from skill file content
42
- - Included by `PromptPipeline`, `PromptHandler`, `WebAndFileDirectives`, `CLIParser`, `ConfigValidator`, and `ChatLoop`
43
- - **`roles` config section**: Top-level `roles.dir` config key (`~/.prompts/roles` default) alongside existing `prompts.roles_prefix`
44
- - **`parse_search_terms` helper**: New private method on `AIA::Directive` base class available to all directive subclasses; splits argument tokens into `[positive_terms, negative_terms]` arrays (negative prefixes: `-`, `~`, `!`; positive: `+` or bare; all downcased)
127
+
128
+ - **Skills system** (`lib/aia/skill_utils.rb`, `lib/aia/directives/web_and_file_directives.rb`): Mainline added `SKILL.md`-based skills, `/skill`, `/skills`, `--skill`, `--list-skills`, `--skills-dir`, `--skills-prefix`, path-based skill IDs, and shared `AIA::SkillUtils` helpers.
129
+ - **Path-based role resolution** (`lib/aia/prompt_handler.rb`, `lib/aia/config/cli_parser.rb`, `lib/aia/config/validator.rb`): `--role` and role loading now accept file-system paths in addition to configured role IDs.
130
+ - **Top-level `roles` and `skills` config sections** (`lib/aia/config/defaults.yml`): Added dedicated directory configuration while preserving prompt prefix settings.
131
+ - **Shared search-term parsing** (`lib/aia/directive.rb`): Directive filtering now uses common positive and negative term parsing.
45
132
 
46
133
  ### Bug Fixes
47
- - **Direct `.md` skill files silently ignored**: `--skill ./my-skill.md` previously returned `nil` because `find_skill_dir` only checked `Dir.exist?`. Now checks `File.file?` and returns the path directly; `load_skills` and `/skill` read and strip front matter from the file without requiring a `SKILL.md` sub-file
48
- - **Path-prefix collision in `safe_skill_path`**: A sibling directory named `skills-attack` could bypass the containment check against `skills` because `start_with?(realpath)` matched the shared prefix. Fixed by appending `File::SEPARATOR` to the base path before the `start_with?` comparison
49
- - **Path-based role IDs incorrectly prefixed**: `ConfigValidator#process_role_configuration` was unconditionally prepending the roles prefix (e.g., `roles/`) to role IDs, corrupting absolute and relative paths like `/custom/role.md` into `roles//custom/role.md`. Now guards with `AIA::SkillUtils.path_based_id?` before applying the prefix
134
+
135
+ - **Direct skill file resolution**: Skill loading now accepts direct `.md` file paths as well as skill directories.
136
+ - **Skill path-prefix containment**: Mainline added realpath boundary checks to block sibling-directory prefix attacks.
137
+ - **Path-based role prefixing**: Config tailoring no longer prepends the roles prefix to path-based role IDs.
50
138
 
51
139
  ### Improvements
52
- - **`/available_models` filtering** now uses `parse_search_terms` for consistent positive/negative term filtering across local (Ollama, LM Studio) and cloud model listings
53
- - **Standardized warning output**: Replaced `$stderr.puts` with `warn` throughout `prompt_pipeline.rb` for idiomatic Ruby error reporting
54
- - **Skills CLI options moved to Prompt Options group**: `--skill`, `--skills-dir`, `--skills-prefix`, `--list-skills` now appear under `--help`'s Prompt Options section alongside `--role` and related options, rather than Model Options
55
140
 
56
- ### Testing
57
- - Added `test/aia/skill_utils_test.rb` — 24 tests covering all `AIA::SkillUtils` methods including symlink traversal blocking, sibling-prefix collision, direct `.md` file resolution, and nonexistent path handling
58
- - Added `test/aia/config/cli_parser_skills_test.rb` — 8 tests covering all skills-related CLI options
59
- - Added `test/aia/config/skills_config_test.rb` — 8 tests covering skills config defaults and overrides
60
- - Added `test/aia/directive_search_terms_test.rb` — 10 tests covering all `parse_search_terms` token forms
61
- - Expanded `test/aia/directives/skill_directive_test.rb` with path-based, direct-file, security (path traversal, symlink), and edge-case coverage (36 tests total)
62
- - Added `test/aia/prompt_pipeline_skills_test.rb` — pipeline-level skill loading tests including absolute paths, direct `.md` files, warning capture via Mocha stubs, and mixed path+ID loading (6 tests)
63
- - Added `test/aia/session_test.rb` `ChatLoopTest` entries for `process_skill_context` — noop when no skills configured, sends skill content to processor when configured (2 tests)
64
- - Eliminated `Dir.chdir` side effects from 3 tests (`skill_directive_test.rb`, `prompt_pipeline_skills_test.rb`, `prompt_handler_role_path_test.rb`) that used it to simulate relative-path resolution; replaced with absolute-path construction
65
- - Updated 2 pipeline warning tests to use Mocha's `.stubs(:warn).with { }` argument-matcher pattern instead of `capture_io` (which cannot intercept `warn` when `$stderr` is reassigned in Ruby 4.0)
66
- - Full test suite: 925 runs, 0 failures, 0 errors, 0 skips
141
+ - **Model and skill filtering consistency**: `/llms` and `/skills` use the shared positive/negative search term behavior.
142
+ - **Skills CLI option placement**: Skill-related CLI flags now appear under Prompt Options.
143
+
144
+ ## [2.0.9.alpha] - 2026-03-28
145
+
146
+ ### Improvements (Section 9 — Design Correctness)
147
+
148
+ - **`MCPConfigNormalizer.filter_servers` eliminated** (`lib/aia/mcp_config_normalizer.rb`, `lib/aia/robot_builder.rb`, `lib/aia/robot_factory.rb`): `filter_servers` duplicated use/skip filtering logic that `MCPDiscovery` already owns, and used a different activation source (`TurnState.active_mcp_servers` vs. `Decisions.mcp_activations`), creating a split-brain risk. Removed `filter_servers` entirely. `RobotBuilder` now passes all configured servers normalized (`Array(config.mcp_servers).map { |s| MCPConfigNormalizer.normalize(s) }`). `RobotFactory.mcp_server_configs` (used by network builders) follows the same pattern. MCPDiscovery is now the single authoritative filter.
149
+ - **`Utility` converted from class to module** (`lib/aia/utility.rb`): `class Utility` with only `class << self` methods is a Ruby anti-pattern. Changed to `module Utility` — semantically identical for callers (`AIA::Utility.mcp_servers?` etc.) but idiomatically correct. Nothing can be accidentally instantiated.
150
+ - **Banner methods returned to `Utility`** (`lib/aia/utility.rb`, `lib/aia/mcp_utility.rb`, `lib/aia/tool_utility.rb`): `banner_mcp`, `mcp_client_labels`, and `banner_tools` were mixed into `MCPUtility`/`ToolUtility` — display concerns in data-query modules. Moved all banner private methods back into `Utility`. `MCPUtility` and `ToolUtility` are now pure query interfaces with no formatting logic.
151
+ - **`MCPDiscovery` takes `decisions` directly** (`lib/aia/mcp_discovery.rb`, `startup_coordinator.rb`, `pipeline_orchestrator.rb`, `special_mode_handler.rb`): `MCPDiscovery` only ever used `rule_router.decisions` — passing the whole `rule_router` was over-coupling. Constructor changed to `MCPDiscovery.new(decisions)`; all three callers updated to pass `@rule_router.decisions`. Dependency is now explicit and narrow.
152
+ - **`history_manager.rb` shim deleted** (`lib/aia/session.rb`): The backward-compat shim (`require_relative 'variable_input_collector'`) had served its purpose. `session.rb` updated to require `variable_input_collector` directly. The Ruby-level `HistoryManager = VariableInputCollector` alias in `variable_input_collector.rb` remains for callers that reference the old constant name.
153
+
154
+ ## [2.0.8.alpha] - 2026-03-28
155
+
156
+ ### Improvements (Section 8 — Backlog Cleanup)
157
+
158
+ - **Consolidate `server_name` inline patterns** (`fact_asserter.rb`, `config/validator.rb`, `mcp_discovery.rb`): Replaced 5 occurrences of `server[:name] || server['name']` with `Utility.server_name(server)` (or `AIA::Utility.server_name(server)`). `Utility.server_name` already handled Hash symbol/string keys, object `.name` methods, and `to_s` fallback — the inline pattern was a redundant re-implementation. (P3-28 / 8.1)
159
+ - **`MCPUtility` and `ToolUtility` extracted from `Utility`** (`lib/aia/mcp_utility.rb`, `lib/aia/tool_utility.rb`, `lib/aia/utility.rb`): `Utility` was a grab-bag class mixing MCP server query methods, tool query methods, and display/banner concerns. Extracted `MCPUtility` (6 public methods: `mcp_servers?`, `mcp_server_names`, `connected_mcp_servers?`, `failed_mcp_servers`, `effective_mcp_server_names`, `server_name`; 2 private: `mcp_client_labels`, `banner_mcp`) and `ToolUtility` (4 public methods: `tools?`, `total_tool_count`, `user_tools?`, `supports_tools?`; 1 private: `banner_tools`). Both modules are included into `Utility` via `class << self include`. `utility.rb` now holds only display, banner, and model-refresh concerns. (P3-30 / 8.2)
160
+ - **Pin `robot_lab` and `kbs` version constraints** (`aia.gemspec`): Simplified redundant double-constraint notation (`'~> 0.0', '>= 0.0.9'`) to single pessimistic constraints: `robot_lab '~> 0.0.9'` and `kbs '~> 0.2.1'`. Semantically equivalent but cleaner. (P3-32 / 8.3)
161
+ - **Rename `HistoryManager` → `VariableInputCollector`** (`lib/aia/variable_input_collector.rb`, `lib/aia/history_manager.rb`, `lib/aia/input_collector.rb`): `HistoryManager` was misnamed — it only prompts for prompt variable values. New canonical name is `VariableInputCollector`. `history_manager.rb` is now a one-line shim requiring the new file. `HistoryManager = VariableInputCollector` alias preserved for backward compatibility. `lib/aia.rb` updated to require `variable_input_collector`. New test file `test/aia/variable_input_collector_test.rb` mirrors the original tests plus an alias assertion. (P3-33 / 8.4)
162
+
163
+ ## [2.0.7.alpha] - 2026-03-28
164
+
165
+ ### Improvements (Section 7 — MCPDiscovery Decision)
166
+
167
+ - **`MCPDiscovery` wired into `StartupCoordinator`** (`lib/aia/startup_coordinator.rb`): The startup MCP connection path now routes through `MCPDiscovery#discover` instead of the indirect `MCPConfigNormalizer.filter_servers → TurnState` bridge. `MCPDiscovery` is the canonical authority for which servers are activated at startup — reading `--mcp-use`, `--mcp-skip`, and KBS `mcp_activate` decisions directly. Each discovered server is then normalized by `MCPConfigNormalizer.normalize` before being passed to `MCPConnectionManager`. (P2-20 / 7.1)
168
+ - **`MCPDiscovery` bug fix** (`lib/aia/mcp_discovery.rb`): `config.mcp_use` truthiness check changed from `if config.mcp_use` to `if Array(config.mcp_use).any?`. In Ruby, `[]` is truthy, so an empty `mcp_use` list previously triggered explicit-server filtering and returned zero servers. An empty list now correctly falls through to KBS-activation or all-servers fallback. (7.1)
169
+ - **`MCPDiscovery` now respects `--mcp-skip`** (`lib/aia/mcp_discovery.rb`): Added `apply_skip_filter` that removes servers whose names appear in `config.mcp_skip` after the primary selection (explicit, KBS-activated, or all). Skip is applied regardless of which selection path was taken. (7.1)
170
+ - **Refactored `MCPDiscovery` internals** (`lib/aia/mcp_discovery.rb`): Extracted `select_servers` (selection strategy dispatcher), `apply_skip_filter` (post-selection skip), and `select_by_names` (name-list filter) private helpers. The public `discover` interface is unchanged. (7.1)
171
+
172
+ ## [2.0.6.alpha] - 2026-03-28
173
+
174
+ ### Improvements (Section 6 — Structural Decompositions)
175
+
176
+ - **`ConfigValidator` early-exit refactor** (`lib/aia/config/validator.rb`): Replaced all `raise AIA::EarlyExit` calls with `return :early_exit`. `tailor()` propagates the signal via `return :early_exit if <step>(config) == :early_exit`. Removed the `EarlyExit` exception class from `lib/aia/errors.rb` and the `rescue AIA::EarlyExit` clause from `lib/aia.rb`. The exception-as-goto anti-pattern is gone. (P2-18 / 6.1)
177
+ - **`ExpertRouter` integration** (`lib/aia/expert_router.rb`): `ExpertRouter` is now instantiated in `ChatLoop` and called as part of the turn pipeline. Verified via `test_expert_router_is_integrated_into_chat_loop`. (P2-19 / 6.2)
178
+ - **`TaskDecomposer` + `TaskExecutor`** (`lib/aia/task_decomposer.rb`, `lib/aia/task_executor.rb`): Extracted from `DelegateHandler`. `TaskDecomposer#decompose` calls the lead robot with a JSON decomposition prompt and returns `[{title:, assignee:}]`. `TaskExecutor#execute` claims, runs, and completes a single TrakFlow step. `DelegateHandler` is now a thin coordinator. (P2-23 / 6.3)
179
+ - **`StartupCoordinator`** (`lib/aia/startup_coordinator.rb`): New class extracting all session startup concerns from `Session`: MCP server connection, tool collection, filter registry initialization, TrakFlow bootstrap, and bus attachment. `StartupCoordinator.new(robot:, rule_router:, ui_presenter:).run(config)` runs all startup tasks and exposes `filters` and `mcp_manager` attributes. (P2-16 / 6.4)
180
+ - **`PipelineOrchestrator`** (`lib/aia/pipeline_orchestrator.rb`): New class extracting per-prompt pipeline processing from `Session`: prompt building (role, stdin, context files), concurrent MCP detection, robot dispatch, result display, metrics, and TrakFlow step tracking. `PipelineOrchestrator.new(...).process(config)` replaces `Session#process_pipeline` and all its private helpers. (P2-16 / 6.4)
181
+ - **`Session` thinned** (`lib/aia/session.rb`): `Session#start` now delegates to `StartupCoordinator#run` and `PipelineOrchestrator#process`. The 12 extracted private methods (`connect_mcp_servers`, `process_pipeline`, `execute_prompt`, `maybe_use_concurrent_mcp`, `build_prompt_text`, `add_context_files`, `display_metrics`, etc.) have been removed. Session is a thin shell sequencing startup → pipeline → chat. (P2-16 / 6.4)
182
+ - **`MCPConfigNormalizer`** (`lib/aia/mcp_config_normalizer.rb`): New class extracting MCP server filtering and normalization from `RobotFactory`. `MCPConfigNormalizer.filter_servers(config)` applies use/skip/KBS lists and returns normalized configs. `MCPConfigNormalizer.normalize(server)` converts flat format to robot_lab's nested transport format. `RobotFactory#mcp_server_configs` and `#normalize_mcp_config` now delegate to it. (P2-17 / 6.5)
183
+ - **`NetworkMemoryManager`** (`lib/aia/network_memory_manager.rb`): New class extracting network shared-memory setup from `RobotFactory`. `NetworkMemoryManager.initialize_memory(network, config)` seeds session metadata. `NetworkMemoryManager.setup_subscriptions(network, config)` wires debug-logging and completion-count subscriptions. `RobotFactory#initialize_network_memory` and `#setup_memory_subscriptions` now delegate to it. (P2-17 / 6.5)
184
+ - **`RobotBuilder`** (`lib/aia/robot_builder.rb`): New class extracting single-robot construction from `RobotFactory`. `RobotBuilder.build(config, namer:)` assembles the system prompt, resolves MCP configs via `MCPConfigNormalizer`, and calls `RobotLab.build`. `RobotFactory#build_single_robot` now delegates to it. (P2-17 / 6.5)
185
+
186
+ ## [2.0.5.alpha] - 2026-03-27
187
+
188
+ ### Improvements (Section 5 — State Machine & Lifecycle)
189
+
190
+ - **`TurnState#request(mode)` mutual exclusion** (`lib/aia/turn_state.rb`): Added `EXCLUSIVE_MODES` constant and `request(mode, type: nil)` method. Calling `request` clears all other exclusive `force_*` flags before setting the new one, enforcing that only one special mode is active at a time. Added `active_mode` helper returning the currently active mode symbol. Direct `attr_accessor` assignment still works for backward compatibility and test assertions. (P1-13)
191
+ - **`SpawnHandler` lifecycle** (`lib/aia/spawn_handler.rb`): Added `MAX_CACHE_SIZE = 5` constant. When a new specialist is spawned and the cache is full, the oldest entry is evicted (insertion-order LRU via Ruby Hash). Added `cleanup!` method that clears all cached specialists — called on session end to release resources. (P2-22)
192
+ - **`DebateHandler` convergence** (`lib/aia/debate_handler.rb`): Replaced the fragile `"CONVERGED"` string check with semantic similarity scoring using `SimilarityScorer`. Added `MIN_ROUNDS = 2` and `SIMILARITY_THRESHOLD = 0.85`. Convergence is only evaluated after `MIN_ROUNDS` rounds; it triggers when the combined response text from consecutive rounds scores ≥ threshold. One robot mentioning "CONVERGED" incidentally no longer ends the debate prematurely. (P2-21)
193
+ - **`MentionRouter` mention stripping** (`lib/aia/mention_router.rb`): After matching `@mention` tokens to robots, all mention tokens are stripped from the prompt (`gsub(/@\w+\s*/i, '').strip`) before routing to the target robot. The robot now receives only the query, not the routing prefix. (P3-26)
194
+ - **`ModelSwitchHandler#model_exists?` caching** (`lib/aia/model_switch_handler.rb`): Added `@model_exists_cache` instance hash. Each model name is looked up via `RubyLLM.models.find(name)` at most once per handler instance; subsequent calls for the same name return the cached boolean without hitting the provider. (P3-27)
195
+
196
+ ## [2.0.4.alpha] - 2026-03-27
197
+
198
+ ### Improvements (Section 4 — Tool Infrastructure)
199
+
200
+ - **`ToolLoader` converted to class** (`lib/aia/tool_loader.rb`): `module_function` with a module-level `@tool_cache` was replaced by a class with per-instance `@tool_cache`. Class-level convenience methods delegate to a resettable default instance via `ToolLoader.instance`. `ToolLoader.reset_instance!` (and `AIA.reset!`) discard the singleton for clean test isolation. Two `ToolLoader.new` instances now have independent caches. (P1-12)
201
+ - **`ToolFilterRegistry`** (`lib/aia/tool_filter_registry.rb`): New class with `build_from_config(config, tools, rule_router:)`. Extracts the five identical `if/prep/assign` blocks from `Session#start` into a single, independently testable method. Session now calls one line. (P1-7)
202
+ - **`EmbeddingModelLoader` mixin** (`lib/aia/tool_filter/embedding_model_loader.rb`): New module with `load_embedding_model(label, model_name)`. Included by both `ToolFilter::Zvec` and `ToolFilter::SqliteVec`, removing two identical 3-line embedding-load sequences. (P3-25)
203
+ - **`ToolFilter::SqliteVec` rowid mapping** (`lib/aia/tool_filter/sqlite_vec.rb`): Replaced fragile `@tool_entries[rowid - 1]` positional lookup with `@tool_index` hash (`rowid → entry`). Both `create_database` and `load_persisted` populate the index; `do_filter_with_scores` uses `@tool_index[rowid]`. Lookup is now stable across deletions and reinsertions. (P3-31)
204
+ - **`ToolFilter::TFIDF` vectorizer caching** (`lib/aia/tool_filter/tfidf.rb`): `Classifier::TFIDF.new`, `fit`, and full-corpus `transform` were called on every user turn. Now the vectorizer is fitted and all tool vectors are computed once in `do_prep` and cached as `@tfidf` / `@tool_vectors`. Per-turn cost is reduced to a single `@tfidf.transform(prompt)` call. (P3-24)
205
+
206
+ ## [2.0.3.alpha] - 2026-03-27
207
+
208
+ ### Improvements (Section 3 — Handler Cleanup)
209
+
210
+ - **`ContentExtractor#extract_content`**: Replaced three identical `extract_reply` private methods in `SpawnHandler`, `DebateHandler`, and `DelegateHandler` with a single call to the shared `ContentExtractor#extract_content`. (P1-9)
211
+ - **`HandlerProtocol` module** (`lib/aia/handler_protocol.rb`): New module defining the `handle(context)` interface. All five turn-level handlers now `include HandlerProtocol` and raise `NotImplementedError` from the base if `handle` is not overridden.
212
+ - **`HandlerContext` struct** (`lib/aia/handler_context.rb`): New value object carrying `robot`, `prompt`, `decisions`, `config`, and `specialist_type` — replaces five incompatible handler signatures with a single unified protocol. All five handlers (`SpawnHandler`, `DebateHandler`, `DelegateHandler`, `MentionRouter`, `ModelSwitchHandler`) migrated to `handle(context)`. All call sites in `SpecialModeHandler` and `ChatLoop` updated. (P1-8)
213
+ - **`Fzf`**: Removed dead code — `tempfile_path` method, `unlink_tempfile` method, and the `ensure` block that called `unlink_tempfile`. `fzf` now passes list items via `stdin_data:` only; no tempfile is created. (P3-29)
214
+ - **Section 3.4** was completed as part of Section 2.5 (`UIPresenter#calculate_cost` already delegates to `CostCalculator`).
215
+
216
+ ## [2.0.2.alpha] - 2026-03-27
217
+
218
+ ### Improvements (Section 2 — Correctness Completions)
219
+
220
+ - **`DecisionApplier`**: When `build_temp_robot` returns nil, now emits a `warn` and explicitly sets `context.model_overridden = false` before returning. Previously the turn fell through silently with no indication the model switch failed. (C4)
221
+ - **`FactAsserter`**: Changed `config.models.each` → `Array(config.models).each` to prevent `NoMethodError` when `config.models` is nil during early KBS evaluation. (P1-10)
222
+ - **`RuleRouter`**: Changed silent `next unless kb` to emit a structured warning (`[RuleRouter] Warning: knowledge base '#{name}' not found in pipeline — skipping`) when a KB from `KB_ORDER` is absent. Missing KBs no longer cause silent rule failures. (C5)
223
+ - **`TrakFlowBridge`**: `attr_reader :db` moved from `private` to public. The database accessor is now part of the public API.
224
+ - **`TaskCoordinator`**: Replaced `bridge.send(:db)` with `bridge.db`, removing the private-access workaround.
225
+ - **New `AIA::CostCalculator` module** (`lib/aia/cost_calculator.rb`): Extracts the duplicated cost calculation logic that previously lived in `SessionTracker` and `UIPresenter`. Single entry point: `AIA::CostCalculator.calculate(model_id:, input_tokens:, output_tokens:)` → `{ available:, total_cost:, input_cost:, output_cost: }`. (P1-15)
226
+ - **`SessionTracker`**: `compute_cost_for_model` and `compute_cost` now delegate to `CostCalculator`.
227
+ - **`UIPresenter`**: `calculate_cost` now delegates to `CostCalculator`.
228
+
229
+ ## [2.0.1.alpha] - 2026-03-27
230
+
231
+ ### Improvements (Section 1 — Safety Net)
232
+
233
+ - **`MCPConnectionManager`**: All reads of `@connected_clients`, `@connected_tools`, and `@connected_servers` are now wrapped in `@mutex.synchronize`. Eliminates data races between `connect_one` threads and callers of `inject_into`, `update_config`, `any_tools?`, `connected_server_names`, and `failed_server_names`. (C1)
234
+ - **`AIA.reset!`**: New class method that nils all seven mutable singletons (`@config`, `@client`, `@session_tracker`, `@turn_state`, `@task_coordinator`, `@decisions`, `@rule_router`) for clean test isolation. (C2)
235
+ - **`Decisions#add(:model_decision)`**: Now raises `ArgumentError` when `:model` is nil, preventing silent propagation of invalid model decisions downstream. (C3)
236
+ - **`HistoryManager`**: All three `exit(1)` calls replaced with `raise AIA::Error`. Callers can now rescue or propagate the error; `AIA.run` catches it and exits with a message. (C7)
237
+ - **`ToolFilter::TFIDF`**: Replaced undefined `logger.warn` in the rescue path with `Kernel#warn`, eliminating a `NoMethodError` that would mask the original classifier error. (P1-16)
238
+
239
+ ## [2.0.0.alpha] - 2026-03-26
240
+
241
+ ### Breaking Changes
242
+ - **New execution engine**: AIA is now powered by `robot_lab` and `kbs`. The `RubyLLMAdapter` and the entire `lib/aia/adapter/` layer have been removed.
243
+ - **`AIA.client` is now a `RobotLab::Robot` or `RobotLab::Network`**, built by `AIA::RobotFactory`. Code that references `AIA::RubyLLMAdapter` directly must be updated.
244
+ - **Removed gems**: `ostruct` and `securerandom` are no longer dependencies.
245
+
246
+ ### New Features
247
+ - **`RobotFactory`**: Single entry point for building the AI backend. Supports single robot, parallel multi-model, consensus network, and pipeline network modes.
248
+ - **`RuleRouter` with multi-KB architecture**: RETE-based rule engine (via `kbs` gem) with five specialized knowledge bases — input classification, model selection, MCP/tool routing, quality gates, and post-response learning.
249
+ - **`NetworkBuilder`**: Extracted network construction for pipeline, parallel, and consensus topologies.
250
+ - **Dynamic model switching**: Mid-session model changes via `ModelSwitchHandler`, `ModelAliasRegistry` (fuzzy name matching), and `HistoryTransfer` (conversation continuity across switches).
251
+ - **`@mention` routing**: `MentionRouter` routes prompts to named robots via `@model-name` syntax in chat.
252
+ - **MCP concurrency**: `McpConnectionManager` maintains persistent stdio connections; `McpDiscovery` and `McpGrouper` classify and route servers by capability.
253
+ - **TrakFlow integration**: `TrakFlowBridge` creates plans from prompt pipelines and tracks task state. New `/trak` directives. TrakFlow MCP server config included.
254
+ - **Advanced RobotLab patterns**:
255
+ - `VerificationNetwork` — two robots answer independently, a reconciler produces a verified result
256
+ - `DebateHandler` — multi-round debate between robots with convergence detection (max 5 rounds)
257
+ - `PromptDecomposer` — breaks complex prompts into parallel subtasks
258
+ - `ExpertRouter` — KBS-driven routing to domain-specialist robots with tailored model and MCP selection
259
+ - `SpawnHandler` / `DelegateHandler` — dynamic robot spawning and subtask delegation
260
+ - **Built-in tools**: `DelegateToForemanTool` and `TaskBoardTool` as `RubyLLM::Tool` subclasses.
261
+ - **`DynamicRuleBuilder`**: Generates KBS route rules at runtime from discovered local tools and MCP servers, matching domains to activate the appropriate tools per turn.
262
+ - **Semantic tool filtering**: Multiple backends in `lib/aia/tool_filter/` — KBS, LSI, TF-IDF, sqlite-vec, and zvec.
263
+ - **`SessionTracker` and `TurnState`**: Per-session and per-turn state containers for metrics and cost tracking.
264
+ - **`SystemPromptAssembler`**: Extracted system prompt resolution including role loading.
265
+ - **`RobotNamer`**: Deterministic unique robot name generation.
266
+
267
+ ### Improvements
268
+ - `RobotFactory` decomposed into focused collaborators (`NetworkBuilder`, `ToolLoader`, `ToolFilter`, `SystemPromptAssembler`, `RobotNamer`)
269
+ - `RuleRouter` refactored from single-KB to multi-KB registry with typed `Decisions` struct
270
+ - `FactAsserter` and `DecisionApplier` extracted for testability; `FactAsserter` now supports asserting additional fact types
271
+ - `StreamingRunner` extracted from `ChatLoop`
272
+ - New `rules_dsl.rb` provides a user-facing DSL for writing custom routing rules
273
+ - `ChatLoop` refactored AI response handling for cleaner robot tool execution and logging
274
+ - `ChatLoop` improved empty user input handling — empty input exits cleanly (break) rather than looping
275
+ - `Decisions` gains `group_by_mcp_server` — groups tool activations by their originating MCP server
276
+
277
+ ### Dependencies
278
+ - Added: `robot_lab ~> 0.0, >= 0.0.9`, `kbs ~> 0.2, >= 0.2.1`, `trak_flow`, `classifier`, `zvec`, `sqlite-vec`, `informers`
279
+ - Removed: `ruby_llm` adapter layer (still used transitively via `robot_lab`)
280
+ - `Gemfile.lock` added to `.gitignore`
67
281
 
68
282
  ## [1.0.0] - 2026-02-22
69
283
 
@@ -778,7 +992,7 @@ aia --chat -m gpt-4o-mini,gpt-3.5-turbo
778
992
  - fixed a problem with a priming prompt in a chat loop
779
993
 
780
994
  ## [0.9.0] 2025-05-13
781
- - Adding experimental MCP Client suppot
995
+ - Adding experimental MCP Client support
782
996
  - removed the CLI options --erb and --shell but kept them in the config file with a default of true for both
783
997
 
784
998
  ## [0.8.6] 2025-04-23
@@ -850,7 +1064,7 @@ aia --chat -m gpt-4o-mini,gpt-3.5-turbo
850
1064
  - Added --image_size and --image_quality (--is --iq)
851
1065
 
852
1066
  ## [0.5.15] 2024-03-30
853
- - Added the ability to accept piped in text to be appeded to the end of the prompt text: curl $URL | aia ad_hoc
1067
+ - Added the ability to accept piped in text to be appended to the end of the prompt text: curl $URL | aia ad_hoc
854
1068
  - Fixed bugs with entering directives as follow-up prompts during a chat session
855
1069
 
856
1070
  ## [0.5.14] 2024-03-09
@@ -922,7 +1136,7 @@ aia --chat -m gpt-4o-mini,gpt-3.5-turbo
922
1136
 
923
1137
  ## [0.3.20] 2023-12-28
924
1138
  - added work around to issue with multiple context files going to the `mods` backend
925
- - added shellwords gem to santize prompt text on the command line
1139
+ - added shellwords gem to sanitize prompt text on the command line
926
1140
 
927
1141
  ## [0.3.19] 2023-12-26
928
1142
  - major code refactoring.
@@ -930,7 +1144,7 @@ aia --chat -m gpt-4o-mini,gpt-3.5-turbo
930
1144
  - usage implemented as a man page. --help will display the man page/
931
1145
  - added "--dump <yml|yaml|toml>" to send current configuration to STDOUT
932
1146
  - added "--completion <bash|fish|zsh>" to send a a completion function for the indicated shell to STDOUT
933
- - added system environment variable (envar) over-rides of default config values uppercase environment variables prefixed with "AIA_" + config item name for example AIA_PROMPTS_DIR and AIA_MODEL. All config items can be over-ridden by their cooresponding envars.
1147
+ - added system environment variable (envar) over-rides of default config values uppercase environment variables prefixed with "AIA_" + config item name for example AIA_PROMPTS_DIR and AIA_MODEL. All config items can be over-ridden by their corresponding envars.
934
1148
  - config value hierarchy is:
935
1149
  1. values from config file over-rides ...
936
1150
  2. command line values over-rides ...
@@ -939,7 +1153,7 @@ aia --chat -m gpt-4o-mini,gpt-3.5-turbo
939
1153
 
940
1154
  ## [0.3.0] = 2023-11-23
941
1155
 
942
- - Matching version to [prompt_manager](https://github.com/prompt_manager) This version allows for the user of history in the entery of values to prompt keywords. KW_HISTORY_MAX is set at 5. Changed CLI enteraction to use historical selection and editing of prior keyword values.
1156
+ - Matching version to [prompt_manager](https://github.com/prompt_manager) This version allows for the user of history in the entry of values to prompt keywords. KW_HISTORY_MAX is set at 5. Changed CLI interaction to use historical selection and editing of prior keyword values.
943
1157
 
944
1158
  ## [0.1.0] - 2023-11-23
945
1159