riffer 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/code-style.md +63 -4
  3. data/.agents/rbs-inline.md +1 -6
  4. data/.release-please-manifest.json +1 -1
  5. data/AGENTS.md +1 -2
  6. data/CHANGELOG.md +18 -0
  7. data/docs/08_MESSAGES.md +1 -1
  8. data/docs/14_MCP.md +50 -5
  9. data/docs/providers/02_AMAZON_BEDROCK.md +14 -0
  10. data/lib/riffer/agent/config.rb +42 -47
  11. data/lib/riffer/agent/context.rb +70 -50
  12. data/lib/riffer/agent/response.rb +4 -20
  13. data/lib/riffer/agent/run.rb +28 -67
  14. data/lib/riffer/agent/serializer.rb +22 -81
  15. data/lib/riffer/agent/session/repair.rb +14 -40
  16. data/lib/riffer/agent/session.rb +25 -67
  17. data/lib/riffer/agent/structured_output/result.rb +3 -11
  18. data/lib/riffer/agent/structured_output.rb +5 -13
  19. data/lib/riffer/agent.rb +74 -192
  20. data/lib/riffer/config.rb +34 -101
  21. data/lib/riffer/evals/evaluator.rb +7 -27
  22. data/lib/riffer/evals/evaluator_runner.rb +11 -19
  23. data/lib/riffer/evals/judge.rb +4 -25
  24. data/lib/riffer/evals/result.rb +1 -18
  25. data/lib/riffer/evals/run_result.rb +0 -11
  26. data/lib/riffer/evals/scenario_result.rb +0 -14
  27. data/lib/riffer/evals.rb +0 -6
  28. data/lib/riffer/guardrail.rb +4 -27
  29. data/lib/riffer/guardrails/modification.rb +0 -10
  30. data/lib/riffer/guardrails/result.rb +3 -30
  31. data/lib/riffer/guardrails/runner.rb +5 -22
  32. data/lib/riffer/guardrails/tripwire.rb +1 -19
  33. data/lib/riffer/guardrails.rb +2 -4
  34. data/lib/riffer/helpers/call_or_value.rb +4 -3
  35. data/lib/riffer/helpers/class_name_converter.rb +3 -1
  36. data/lib/riffer/helpers/dependencies.rb +5 -7
  37. data/lib/riffer/helpers.rb +0 -5
  38. data/lib/riffer/mcp/authenticated_tool.rb +9 -9
  39. data/lib/riffer/mcp/client.rb +12 -17
  40. data/lib/riffer/mcp/manifest.rb +13 -10
  41. data/lib/riffer/mcp/registration.rb +2 -11
  42. data/lib/riffer/mcp/registry.rb +44 -52
  43. data/lib/riffer/mcp/search_tool.rb +53 -0
  44. data/lib/riffer/mcp/tool_factory.rb +13 -18
  45. data/lib/riffer/mcp.rb +12 -17
  46. data/lib/riffer/messages/assistant.rb +2 -9
  47. data/lib/riffer/messages/base.rb +46 -16
  48. data/lib/riffer/messages/file_part.rb +32 -24
  49. data/lib/riffer/messages/system.rb +0 -5
  50. data/lib/riffer/messages/tool.rb +0 -10
  51. data/lib/riffer/messages/user.rb +0 -10
  52. data/lib/riffer/messages.rb +0 -7
  53. data/lib/riffer/params/boolean.rb +2 -4
  54. data/lib/riffer/params/param.rb +28 -39
  55. data/lib/riffer/params.rb +9 -21
  56. data/lib/riffer/providers/amazon_bedrock.rb +42 -28
  57. data/lib/riffer/providers/anthropic.rb +4 -9
  58. data/lib/riffer/providers/azure_open_ai.rb +3 -19
  59. data/lib/riffer/providers/base.rb +13 -26
  60. data/lib/riffer/providers/gemini.rb +4 -4
  61. data/lib/riffer/providers/mock.rb +6 -26
  62. data/lib/riffer/providers/open_ai.rb +6 -8
  63. data/lib/riffer/providers/open_router.rb +4 -10
  64. data/lib/riffer/providers/repository.rb +4 -3
  65. data/lib/riffer/providers/token_usage.rb +9 -20
  66. data/lib/riffer/providers.rb +0 -8
  67. data/lib/riffer/runner/fibers.rb +10 -16
  68. data/lib/riffer/runner/sequential.rb +1 -4
  69. data/lib/riffer/runner/threaded.rb +3 -14
  70. data/lib/riffer/runner.rb +2 -15
  71. data/lib/riffer/skills/activate_tool.rb +2 -11
  72. data/lib/riffer/skills/adapter.rb +4 -22
  73. data/lib/riffer/skills/backend.rb +7 -21
  74. data/lib/riffer/skills/config.rb +10 -31
  75. data/lib/riffer/skills/context.rb +5 -20
  76. data/lib/riffer/skills/filesystem_backend.rb +7 -25
  77. data/lib/riffer/skills/frontmatter.rb +10 -28
  78. data/lib/riffer/skills/markdown_adapter.rb +2 -9
  79. data/lib/riffer/skills/xml_adapter.rb +2 -8
  80. data/lib/riffer/stream_events/base.rb +1 -6
  81. data/lib/riffer/stream_events/guardrail_modification.rb +1 -8
  82. data/lib/riffer/stream_events/guardrail_tripwire.rb +1 -8
  83. data/lib/riffer/stream_events/interrupt.rb +4 -7
  84. data/lib/riffer/stream_events/reasoning_delta.rb +2 -4
  85. data/lib/riffer/stream_events/reasoning_done.rb +2 -4
  86. data/lib/riffer/stream_events/skill_activation.rb +2 -4
  87. data/lib/riffer/stream_events/text_delta.rb +0 -2
  88. data/lib/riffer/stream_events/text_done.rb +1 -3
  89. data/lib/riffer/stream_events/token_usage_done.rb +1 -8
  90. data/lib/riffer/stream_events/tool_call_delta.rb +2 -3
  91. data/lib/riffer/stream_events/tool_call_done.rb +1 -3
  92. data/lib/riffer/stream_events/web_search_done.rb +1 -3
  93. data/lib/riffer/stream_events/web_search_status.rb +2 -3
  94. data/lib/riffer/stream_events.rb +0 -10
  95. data/lib/riffer/tool.rb +6 -13
  96. data/lib/riffer/tools/response.rb +8 -4
  97. data/lib/riffer/tools/runtime/fibers.rb +0 -3
  98. data/lib/riffer/tools/runtime/inline.rb +1 -4
  99. data/lib/riffer/tools/runtime/threaded.rb +0 -2
  100. data/lib/riffer/tools/runtime.rb +5 -38
  101. data/lib/riffer/tools/toolable.rb +5 -16
  102. data/lib/riffer/tools.rb +0 -4
  103. data/lib/riffer/version.rb +1 -1
  104. data/lib/riffer.rb +7 -8
  105. data/sig/generated/riffer/agent/config.rbs +29 -46
  106. data/sig/generated/riffer/agent/context.rbs +40 -48
  107. data/sig/generated/riffer/agent/response.rbs +4 -20
  108. data/sig/generated/riffer/agent/run.rbs +12 -61
  109. data/sig/generated/riffer/agent/serializer.rbs +21 -80
  110. data/sig/generated/riffer/agent/session/repair.rbs +12 -40
  111. data/sig/generated/riffer/agent/session.rbs +25 -67
  112. data/sig/generated/riffer/agent/structured_output/result.rbs +2 -10
  113. data/sig/generated/riffer/agent/structured_output.rbs +5 -12
  114. data/sig/generated/riffer/agent.rbs +57 -186
  115. data/sig/generated/riffer/config.rbs +34 -100
  116. data/sig/generated/riffer/evals/evaluator.rbs +7 -27
  117. data/sig/generated/riffer/evals/evaluator_runner.rbs +9 -19
  118. data/sig/generated/riffer/evals/judge.rbs +4 -24
  119. data/sig/generated/riffer/evals/result.rbs +1 -17
  120. data/sig/generated/riffer/evals/run_result.rbs +0 -10
  121. data/sig/generated/riffer/evals/scenario_result.rbs +0 -13
  122. data/sig/generated/riffer/evals.rbs +0 -6
  123. data/sig/generated/riffer/guardrail.rbs +4 -27
  124. data/sig/generated/riffer/guardrails/modification.rbs +0 -10
  125. data/sig/generated/riffer/guardrails/result.rbs +3 -30
  126. data/sig/generated/riffer/guardrails/runner.rbs +5 -22
  127. data/sig/generated/riffer/guardrails/tripwire.rbs +1 -19
  128. data/sig/generated/riffer/guardrails.rbs +2 -4
  129. data/sig/generated/riffer/helpers/call_or_value.rbs +4 -3
  130. data/sig/generated/riffer/helpers/class_name_converter.rbs +1 -1
  131. data/sig/generated/riffer/helpers/dependencies.rbs +3 -7
  132. data/sig/generated/riffer/helpers.rbs +0 -5
  133. data/sig/generated/riffer/mcp/authenticated_tool.rbs +5 -4
  134. data/sig/generated/riffer/mcp/client.rbs +10 -16
  135. data/sig/generated/riffer/mcp/manifest.rbs +9 -9
  136. data/sig/generated/riffer/mcp/registration.rbs +2 -10
  137. data/sig/generated/riffer/mcp/registry.rbs +11 -18
  138. data/sig/generated/riffer/mcp/search_tool.rbs +26 -0
  139. data/sig/generated/riffer/mcp/tool_factory.rbs +10 -15
  140. data/sig/generated/riffer/mcp.rbs +10 -17
  141. data/sig/generated/riffer/messages/assistant.rbs +2 -8
  142. data/sig/generated/riffer/messages/base.rbs +11 -16
  143. data/sig/generated/riffer/messages/file_part.rbs +13 -23
  144. data/sig/generated/riffer/messages/system.rbs +0 -4
  145. data/sig/generated/riffer/messages/tool.rbs +0 -9
  146. data/sig/generated/riffer/messages/user.rbs +0 -9
  147. data/sig/generated/riffer/messages.rbs +0 -7
  148. data/sig/generated/riffer/params/boolean.rbs +2 -4
  149. data/sig/generated/riffer/params/param.rbs +21 -39
  150. data/sig/generated/riffer/params.rbs +9 -21
  151. data/sig/generated/riffer/providers/amazon_bedrock.rbs +21 -25
  152. data/sig/generated/riffer/providers/anthropic.rbs +2 -7
  153. data/sig/generated/riffer/providers/azure_open_ai.rbs +3 -18
  154. data/sig/generated/riffer/providers/base.rbs +9 -25
  155. data/sig/generated/riffer/providers/gemini.rbs +0 -2
  156. data/sig/generated/riffer/providers/mock.rbs +6 -26
  157. data/sig/generated/riffer/providers/open_ai.rbs +1 -5
  158. data/sig/generated/riffer/providers/open_router.rbs +4 -10
  159. data/sig/generated/riffer/providers/repository.rbs +2 -3
  160. data/sig/generated/riffer/providers/token_usage.rbs +6 -16
  161. data/sig/generated/riffer/providers.rbs +0 -8
  162. data/sig/generated/riffer/runner/fibers.rbs +8 -15
  163. data/sig/generated/riffer/runner/sequential.rbs +1 -3
  164. data/sig/generated/riffer/runner/threaded.rbs +3 -13
  165. data/sig/generated/riffer/runner.rbs +2 -14
  166. data/sig/generated/riffer/skills/activate_tool.rbs +2 -11
  167. data/sig/generated/riffer/skills/adapter.rbs +4 -22
  168. data/sig/generated/riffer/skills/backend.rbs +7 -21
  169. data/sig/generated/riffer/skills/config.rbs +10 -31
  170. data/sig/generated/riffer/skills/context.rbs +5 -20
  171. data/sig/generated/riffer/skills/filesystem_backend.rbs +7 -24
  172. data/sig/generated/riffer/skills/frontmatter.rbs +10 -27
  173. data/sig/generated/riffer/skills/markdown_adapter.rbs +2 -9
  174. data/sig/generated/riffer/skills/xml_adapter.rbs +2 -8
  175. data/sig/generated/riffer/stream_events/base.rbs +1 -6
  176. data/sig/generated/riffer/stream_events/guardrail_modification.rbs +1 -8
  177. data/sig/generated/riffer/stream_events/guardrail_tripwire.rbs +1 -8
  178. data/sig/generated/riffer/stream_events/interrupt.rbs +4 -7
  179. data/sig/generated/riffer/stream_events/reasoning_delta.rbs +2 -4
  180. data/sig/generated/riffer/stream_events/reasoning_done.rbs +2 -4
  181. data/sig/generated/riffer/stream_events/skill_activation.rbs +2 -4
  182. data/sig/generated/riffer/stream_events/text_delta.rbs +0 -2
  183. data/sig/generated/riffer/stream_events/text_done.rbs +1 -3
  184. data/sig/generated/riffer/stream_events/token_usage_done.rbs +1 -7
  185. data/sig/generated/riffer/stream_events/tool_call_delta.rbs +2 -3
  186. data/sig/generated/riffer/stream_events/tool_call_done.rbs +1 -3
  187. data/sig/generated/riffer/stream_events/web_search_done.rbs +1 -3
  188. data/sig/generated/riffer/stream_events/web_search_status.rbs +2 -3
  189. data/sig/generated/riffer/stream_events.rbs +0 -10
  190. data/sig/generated/riffer/tool.rbs +5 -12
  191. data/sig/generated/riffer/tools/response.rbs +6 -4
  192. data/sig/generated/riffer/tools/runtime/fibers.rbs +0 -3
  193. data/sig/generated/riffer/tools/runtime/inline.rbs +1 -3
  194. data/sig/generated/riffer/tools/runtime/threaded.rbs +0 -2
  195. data/sig/generated/riffer/tools/runtime.rbs +5 -37
  196. data/sig/generated/riffer/tools/toolable.rbs +4 -14
  197. data/sig/generated/riffer/tools.rbs +0 -4
  198. data/sig/generated/riffer.rbs +5 -4
  199. data/sig/manual/riffer/agent/session/repair.rbs +5 -0
  200. data/sig/manual/riffer/evals/evaluator_runner.rbs +5 -0
  201. data/sig/manual/riffer/helpers/class_name_converter.rbs +5 -0
  202. data/sig/manual/riffer/helpers/dependencies.rbs +5 -0
  203. data/sig/manual/riffer/mcp/authenticated_tool.rbs +5 -0
  204. data/sig/manual/riffer/mcp/registry.rbs +5 -0
  205. data/sig/manual/riffer/mcp/tool_factory.rbs +5 -0
  206. data/sig/manual/riffer/mcp.rbs +5 -0
  207. data/sig/manual/riffer/providers/repository.rbs +5 -0
  208. data/sig/manual/riffer.rbs +5 -0
  209. metadata +17 -9
  210. data/.agents/rdoc.md +0 -69
  211. data/lib/riffer/messages/converter.rb +0 -90
  212. data/sig/generated/riffer/messages/converter.rbs +0 -33
  213. data/sig/manual/riffer/tools/toolable.rbs +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3f857f2ab85b78d91685b84c1d239a0409291c6945f9cc7aae0b84a5dead2a6
4
- data.tar.gz: 9ae3efd534a2b099de8268e0b0bc89e2c2682d6a7297ada4409a1056203cf9b3
3
+ metadata.gz: 0cddafa66f3f24980037c23be5e8584ec888b93c40b2b780c99e7205558d2593
4
+ data.tar.gz: 5aea2edfeb9d47a4246b15d3f4e16a7a3c1d7941a473355495267b9eb93bc3cc
5
5
  SHA512:
6
- metadata.gz: bf1b4dbd17abdfb1873f48d7db5b4b13f6ebde5c7e0e40bad73d7e6ca96f51e0d10c1ed9fe50d8be4a0532bcde9d5eb0eebd3ee4d264b195dbbfa12d09e70f9a
7
- data.tar.gz: 0226c06943e53b5386659917746d1cd256d83861ab340bd0377393d8a45310fd2da60dad090e15a4f6a23dfa1e865b4fdc84f3bd6c1e0df94eafdaf850e4b4ae
6
+ metadata.gz: fad79dcfd8036c3249ea17be785cb6acb193e3a1677f23ee052c89daf39fe97840369a4718e65ebb515bbddeb866608a532b8076a89e2f6c724ddd74d2131e85
7
+ data.tar.gz: 0c7b1bdb7383c6f9a2b053d2d811a7c4aa29142f8a44aa3fd07071d88b0dac722268f036c9f45887bbf3b10591cb3067193540c2cff766d6ab91aa4644f0dcb8
@@ -24,11 +24,66 @@ class MyCustomError < Riffer::Error
24
24
  end
25
25
  ```
26
26
 
27
- ## Comments
27
+ ## Comments & Documentation
28
28
 
29
- - Only add comments when the code is ambiguous or not semantically obvious
30
- - Explain **why** something is done, not **what** is being done
31
- - Comments should add value beyond what the code already expresses
29
+ A comment exists to explain a **why** the code itself cannot never a **how**, and never a restatement of what the code already says. This bar governs all prose, from inline `#` comments to RDoc descriptions on the public API. Types are not prose's job: parameters, return values, and attribute/constant types live in rbs-inline `#:` annotations (see [rbs-inline.md](rbs-inline.md)).
30
+
31
+ **What's a comment (in scope):** RDoc descriptions and inline `#` explanations. **Not comments (never touched):** rbs-inline `#:` annotations, magic comments (`frozen_string_literal`, `rbs_inline`), and RDoc directives (`:nodoc:`, `:nocov:`).
32
+
33
+ ### Public surface
34
+
35
+ Everything public — classes, modules, constants, attributes, public methods, and `protected` subclass-contract methods (e.g. the `pass` / `transform` / `block` helpers a custom `Riffer::Guardrail` calls) — gets **at minimum a very brief description**:
36
+
37
+ - **One verb-first sentence, one line.** `Creates a new agent.`, `Serializes the definition to JSON.`
38
+ - An optional **second sentence is reserved strictly for a "why"** — a non-obvious constraint or rationale the code can't convey. Never a second sentence of "how".
39
+ - If a description needs more than one sentence to say _what_ it does, that's a smell the method does too much.
40
+ - **Exempt:** a constant whose name and value already carry the full meaning (`VERSION = "0.30.0"`, `PHASES = %i[before after]`) — describe a constant only when its name doesn't; an empty namespace module (a Zeitwerk placeholder with no usable members of its own, e.g. `module Riffer::Messages; end`) — its children are documented individually; and a constructor (`initialize`) — the class doc and RDoc's `::new` already cover plain construction, so describe it only when it carries a contract or non-obvious construction behavior (a raise, a dup guard).
41
+
42
+ Do not document parameters or return values in prose — the `#:` line is the single source of truth for types. Attributes and constants still carry a brief description on the line above their inline `#:`.
43
+
44
+ ```ruby
45
+ # Serializes the agent definition to a transferable JSON payload.
46
+ #--
47
+ #: (Riffer::Agent) -> String
48
+ def serialize(agent)
49
+
50
+ # The agent's display name.
51
+ attr_reader :name #: String
52
+ ```
53
+
54
+ ### Private methods
55
+
56
+ A comment survives on a private method **only** if it explains a why a competent reader cannot recover from the code and names alone — a non-local constraint, an external-system quirk, a deliberate non-obvious tradeoff. A description of what it does, or a why that's evident from the code, gets cut.
57
+
58
+ ### Inline comments
59
+
60
+ Same bar: kept only to explain the why of something genuinely ambiguous. `TODO` / `FIXME` / `HACK` markers are tracked work and stay; `NOTE` / `REVIEW` are subject to the why-rule.
61
+
62
+ ### No history
63
+
64
+ A comment describes the present, never how the code got there. Change narration — "was X, now Y", "previously used Z" — has no place; the reader cares about what is, not what was. The one thing worth stating is a still-true constraint, and it belongs in the present tense ("the API returns null for empty results — guard"), never told as the story of the bug that revealed it.
65
+
66
+ ### RDoc mechanics
67
+
68
+ **The `#--` stop directive.** Place `#--` on the line immediately before a **standalone** `#:` type annotation. Without it, RDoc treats `#:` as a label-list marker and corrupts the preceding description into a `<pre>` block. Inline `#:` on the same line as code (attributes, constants) does not need it.
69
+
70
+ **Raises.** Document a raise **only when it's part of the caller's contract** — something a caller should reasonably anticipate and handle. Skip programmer-error guards and "should never happen" assertions. Reserved for public methods. When the raise condition merely restates the declared `#:` type, phrase it by intent ("Raises Riffer::ArgumentError on an invalid value") rather than re-listing the type union — but keep the runtime constraints a type can't express (enum value sets, coercion rules, validation failure).
71
+
72
+ ```ruby
73
+ # Builds a param from a schema hash.
74
+ # Raises Riffer::ArgumentError if the schema is missing a +type+.
75
+ ```
76
+
77
+ **Examples.** Include an example only when a **consumer is likely to use the thing themselves** — a public entry point they construct, subclass, or call (an `Agent` subclass, `Riffer::Mcp.register`, the `params` DSL). Skip examples on framework-internal types even though they're technically public (value objects the framework constructs and hands back, internal engines). When included, keep them sparing — only when they teach something the signature can't — and write them as indented code blocks (2 extra spaces of indent). Usage walkthroughs belong in `docs/`.
78
+
79
+ **Inline code formatting.** Use `+word+` for single-word inline code; for multi-word expressions (spaces, colons, brackets) use `<tt>multi word expression</tt>`, e.g. `Equivalent to <tt>throw :riffer_interrupt, reason</tt>`.
80
+
81
+ **Internal APIs.** Mark with `:nodoc:` to exclude from generated documentation:
82
+
83
+ ```ruby
84
+ def internal_method # :nodoc:
85
+ end
86
+ ```
32
87
 
33
88
  ## Hash Key Convention
34
89
 
@@ -37,6 +92,10 @@ end
37
92
  - String keys are only used at serialization boundaries (JSON Schema output, external API payloads)
38
93
  - Do not write dual-access patterns like `hash[:key] || hash["key"]` — normalize to symbol keys at the boundary instead
39
94
 
95
+ ## Reserved Tool Identifiers
96
+
97
+ - Internal-use-only tools use plain descriptive names without a prefix (e.g. `mcp_search`, `mcp_call`, `evaluation`, `skill_activate`)
98
+
40
99
  ## Module Structure
41
100
 
42
101
  ```ruby
@@ -4,12 +4,7 @@ Type annotations are added directly in Ruby source files using [rbs-inline](http
4
4
 
5
5
  ## Magic Comment
6
6
 
7
- Every `lib/**/*.rb` file must include the `rbs_inline: enabled` comment on line 2:
8
-
9
- ```ruby
10
- # frozen_string_literal: true
11
- # rbs_inline: enabled
12
- ```
7
+ rbs-inline only processes a file when `rbs_inline: enabled` is present on line 2. It ships as part of the required header on every `lib/**/*.rb` file — see [Required Header](code-style.md#required-header).
13
8
 
14
9
  ## Annotation Syntax
15
10
 
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.31.0"
2
+ ".": "0.32.0"
3
3
  }
data/AGENTS.md CHANGED
@@ -14,8 +14,7 @@ Ruby gem framework for building AI-powered agents with LLM provider adapters.
14
14
 
15
15
  - [Architecture](.agents/architecture.md) - Core components and project structure
16
16
  - [Testing](.agents/testing.md) - Minitest spec DSL and VCR cassettes
17
- - [Code Style](.agents/code-style.md) - StandardRB and comment conventions
18
- - [RDoc](.agents/rdoc.md) - Documentation format for public APIs
17
+ - [Code Style](.agents/code-style.md) - StandardRB, comment, and RDoc conventions
19
18
  - [Providers](.agents/providers.md) - Adding new LLM provider adapters
20
19
  - [RBS Inline](.agents/rbs-inline.md) - Type annotations with rbs-inline
21
20
 
data/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.32.0](https://github.com/janeapp/riffer/compare/riffer/v0.31.0...riffer/v0.32.0) (2026-06-08)
9
+
10
+
11
+ ### ⚠ BREAKING CHANGES
12
+
13
+ * `TokenUsage#cache_creation_tokens` is renamed to `cache_write_tokens` (also reflected in `TokenUsage#to_h`). Update any code reading that attribute or hash key.
14
+ * apply module/class conventions consistently ([#298](https://github.com/janeapp/riffer/issues/298))
15
+
16
+ ### Features
17
+
18
+ * add Bedrock prompt caching and surface cached tokens ([#300](https://github.com/janeapp/riffer/issues/300)) ([407390b](https://github.com/janeapp/riffer/commit/407390b505a3af6b881c5204d856a8e9ceddbed9))
19
+ * add progressive discovery for MCP tools by default ([0a37485](https://github.com/janeapp/riffer/commit/0a37485dd59e08a0ba0ca8d1046e3313914f72a5))
20
+
21
+
22
+ ### Code Refactoring
23
+
24
+ * apply module/class conventions consistently ([#298](https://github.com/janeapp/riffer/issues/298)) ([76a3131](https://github.com/janeapp/riffer/commit/76a3131f800ba026c07cc194ab2879279134e865))
25
+
8
26
  ## [0.31.0](https://github.com/janeapp/riffer/compare/riffer/v0.30.0...riffer/v0.31.0) (2026-06-03)
9
27
 
10
28
 
data/docs/08_MESSAGES.md CHANGED
@@ -196,7 +196,7 @@ agent = MyAgent.new(session: session)
196
196
  response = agent.generate # session already carries the last user turn
197
197
  ```
198
198
 
199
- `Riffer::Agent::Session.new(messages:)` accepts `Riffer::Messages::Base` objects. If your persistence layer hands back hashes, normalize them first via `Riffer::Messages::Converter#convert_to_message_object` or your own adapter (e.g. jane's `to_riffer`).
199
+ `Riffer::Agent::Session.new(messages:)` accepts `Riffer::Messages::Base` objects. If your persistence layer hands back hashes, normalize them first via `Riffer::Messages::Base.from_hash` or your own adapter.
200
200
 
201
201
  ### Accessing Message History
202
202
 
data/docs/14_MCP.md CHANGED
@@ -36,8 +36,9 @@ When **`Riffer.config.mcp.credentials`** is set to a Proc, each MCP `tools/call`
36
36
  ```ruby
37
37
  Riffer.configure do |config|
38
38
  config.mcp.credentials = lambda do |manifest:, matched_tags:, context:|
39
- # return nil to omit this server's tools for this agent run (at resolve time)
40
- # return Hash<String,String> headers for tools/call (e.g. Authorization)
39
+ # when caller does not have an integration, return nil to omit this server's tools for this run
40
+ # when server is unauthenticated or local, return {} to include this server's tools with no extra headers
41
+ # when authorization headers are required, return Hash<String,String> headers
41
42
  end
42
43
  end
43
44
  ```
@@ -46,9 +47,9 @@ end
46
47
  - **`matched_tags`** — intersection of the agent's `use_mcp` tags and `manifest.tags` for this registration (unioned across multiple `use_mcp` lines).
47
48
  - **`context`** — the same hash passed to `generate` / `stream` for this run.
48
49
 
49
- **Resolve time:** Before tools are exposed to the model, the proc is invoked once per matching registration. If it returns **`nil`**, that server's tools are omitted for this run (e.g. tenant has no integration).
50
+ **Resolve time:** The proc is invoked once per matching registration before tools are exposed to the model.
50
51
 
51
- **Call time:** Authenticated tool wrappers invoke the proc again for each execution. If it returns **`nil`**, `Riffer::Mcp::CredentialsDeniedError` is raised.
52
+ **Call time:** Authenticated tool wrappers invoke the proc again for each execution. If it returns `nil`, then `Riffer::Mcp::CredentialsDeniedError` is raised.
52
53
 
53
54
  If **`credentials` is unset**, discovery and `tools/call` share one client built from `discovery_headers` (same behaviour as a single static token for both list and call).
54
55
 
@@ -71,6 +72,8 @@ class ResearchAgent < Riffer::Agent
71
72
  end
72
73
  ```
73
74
 
75
+ By default, `use_mcp` uses **progressive discovery**. The agent receives `mcp_search` rather than every schema up front. See [Progressive Tool Discovery](#progressive-tool-discovery).
76
+
74
77
  `use_mcp` accepts any tag registered via `Riffer::Mcp.register`. Multiple calls accumulate — the agent receives tools from all matching servers:
75
78
 
76
79
  ```ruby
@@ -90,6 +93,48 @@ Tool names must be unique across `uses_tools` and all included MCP servers; dupl
90
93
 
91
94
  Like [`uses_tools`](03_AGENTS.md#uses_tools), **`use_mcp` is not inherited** from the superclass. Declare `use_mcp` on each agent class that should load MCP tools.
92
95
 
96
+ ## Progressive Tool Discovery
97
+
98
+ Progressive discovery is the default. The `use_mcp` instruction exposes **`mcp_search`** instead of flooding the context with every tool schema up front.
99
+
100
+ ```ruby
101
+ class ResearchAgent < Riffer::Agent
102
+ model "openai/gpt-4o"
103
+
104
+ use_mcp :github # default: tools discoverable on demand
105
+ use_mcp :jira, progressive: false # opt-out: all Jira tools injected directly
106
+ end
107
+ ```
108
+
109
+ Only use `progressive: false` when the server has a small, stable set of tools you always want available.
110
+
111
+ **`mcp_search`** — Search for available tools by name or description.
112
+ - `query` (required, non-empty) — filter by name or description substring.
113
+
114
+ On a successful search, matching tools are injected into the agent's active tool list. The model calls them natively on the next turn — no proxy or JSON-encoded arguments.
115
+
116
+ **Example flow:**
117
+
118
+ 1. Agent starts — only `mcp_search` appears in the tool list.
119
+ 2. LLM calls `mcp_search` with `query: "create pull request"`.
120
+ 3. Riffer injects `github__create_pr` into the active tool list and returns an acknowledgment.
121
+ 4. LLM calls `github__create_pr` directly with its real schema (e.g. `title:`, `body:`, `base:`).
122
+ 5. The provider validates the arguments; the tool executes with all credential handling intact.
123
+
124
+ Injected tools accumulate across turns — tools discovered in one search remain available for subsequent calls without re-searching.
125
+
126
+ **Multiple progressive `use_mcp` calls:** All matching registrations are combined into one `mcp_search`:
127
+
128
+ ```ruby
129
+ use_mcp :connectors_a # both default to progressive
130
+ use_mcp :connectors_b
131
+ # → one mcp_search exposing tools from both registrations
132
+ ```
133
+
134
+ **Credential handling:** Progressive tools follow the same credential rules as regular tools — see [Session credentials callback](#session-credentials-callback).
135
+
136
+ **Tool access in tools:** The full discovery pool is available via `context[:mcp_progressive_tools]` and injected tools via `context[:injected_tools]` (both `Array[Riffer::Tool subclass]`). These keys are framework-managed — treat them as read-only from application code.
137
+
93
138
  ## Unregistering a Server
94
139
 
95
140
  ```ruby
@@ -130,7 +175,7 @@ All inherit from `Riffer::Mcp::Error < Riffer::Error`.
130
175
 
131
176
  - **Tool results:** `tools/call` responses are reduced to joined **text** content from MCP `content` items. Non-text parts (e.g. images, embedded resources) are not surfaced in this release.
132
177
  - **Session credentials:** When `Riffer.config.mcp.credentials` is set, authenticated tool wrappers may build a **new HTTP client per tool invocation** so headers stay fresh; there is no connection pooling in this release.
133
- - **Context window / progressive disclosure:** Discovery registers **all** tools from each server; matching agents see the full set in one shot. There is no built-in lazy listing or prompt-size budgeting yet. Tighter control may require application-level filtering, MCP server design, or future Riffer APIs.
178
+ - **Context window / progressive disclosure:** `use_mcp` defaults to progressive mode tools are discovered via `mcp_search` and injected natively for subsequent calls. Use `progressive: false` to inject all schemas directly. See [Progressive Tool Discovery](#progressive-tool-discovery).
134
179
 
135
180
  ## Requirements
136
181
 
@@ -83,6 +83,20 @@ model_options additional_model_request_fields: {
83
83
  }
84
84
  ```
85
85
 
86
+ ### cache_control
87
+
88
+ Enable prompt caching for models that support it (Claude, Nova). Riffer appends a single Converse `cachePoint` to the stable prefix — after the system array, or after the tools when there is no system prompt — so system instructions and tool definitions are reused across the calls in an agent loop and across conversation turns. The volatile message tail is never cached.
89
+
90
+ ```ruby
91
+ # 5-minute TTL (default)
92
+ model_options cache_control: {type: "ephemeral"}
93
+
94
+ # 1-hour TTL (model-dependent; Bedrock validates support)
95
+ model_options cache_control: {type: "ephemeral", ttl: "1h"}
96
+ ```
97
+
98
+ Caching is opt-in: omit `cache_control` and no cachePoint is sent. The breakpoint is only honored once the prefix clears the model's minimum token count; on models that don't support `cachePoint`, the Converse request errors. Verify hits via `response.token_usage.cache_read_tokens`.
99
+
86
100
  ## Example
87
101
 
88
102
  ```ruby
@@ -2,38 +2,49 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  # Typed configuration object holding every class-level DSL setting on a
5
- # Riffer::Agent subclass.
6
- #
7
- # Each subclass of Riffer::Agent owns one Config, accessible via the class
8
- # method <tt>config</tt>. The class-level DSL (+model+, +instructions+, +uses_tools+,
9
- # etc.) reads and mutates this Config in place. Append-style DSL methods
10
- # (+use_mcp+, +guardrail+) are handled by the +add_mcp+ and +add_guardrail+
11
- # helpers below.
12
- #
13
- # Config stores Procs unresolved. Per-instance resolution happens elsewhere
14
- # (instructions, model, tools, tool runtime, skills).
5
+ # Riffer::Agent subclass. Procs are stored unresolved and resolved per-instance
6
+ # later.
15
7
  class Riffer::Agent::Config
16
8
  DEFAULT_MAX_STEPS = 16 #: Integer
17
9
 
10
+ # The configured agent identifier.
18
11
  attr_reader :identifier #: String?
12
+
13
+ # The configured model.
19
14
  attr_reader :model #: (String | Proc)?
15
+
16
+ # The configured instructions.
20
17
  attr_reader :instructions #: (String | Proc)?
18
+
19
+ # Options passed to the provider client.
21
20
  attr_accessor :provider_options #: Hash[Symbol, untyped]
21
+
22
+ # Options passed to generate_text/stream_text.
22
23
  attr_accessor :model_options #: Hash[Symbol, untyped]
24
+
25
+ # The configured structured-output schema.
23
26
  attr_reader :structured_output #: Riffer::Params?
27
+
28
+ # The maximum number of LLM call steps in the tool-use loop.
24
29
  attr_accessor :max_steps #: Numeric?
30
+
31
+ # The configured tools.
25
32
  attr_accessor :tools_config #: (Array[singleton(Riffer::Tool)] | Proc)?
33
+
34
+ # The accumulated +use_mcp+ tag configurations.
26
35
  attr_reader :mcp_configs #: Array[Hash[Symbol, untyped]]
36
+
37
+ # The configured tool runtime.
27
38
  attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
39
+
40
+ # The configured skills.
28
41
  attr_accessor :skills_config #: Riffer::Skills::Config?
42
+
43
+ # Registered guardrail entries keyed by phase.
29
44
  attr_reader :guardrails #: Hash[Symbol, Array[Hash[Symbol, untyped]]]
30
45
 
31
- # Builds a new Config. All fields are optional; unset fields take the
32
- # documented defaults.
33
- #
34
- # Raises Riffer::ArgumentError if +model+ or +instructions+ is provided
35
- # as a non-String, non-Proc value (or as an empty String).
36
- #
46
+ # Builds a new Config. Raises Riffer::ArgumentError if +model+ or
47
+ # +instructions+ is invalid (e.g. an empty string).
37
48
  #--
38
49
  #: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc), ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
39
50
  def initialize(
@@ -64,18 +75,14 @@ class Riffer::Agent::Config
64
75
  self.tool_runtime = tool_runtime
65
76
  end
66
77
 
67
- # Sets +identifier+. Accepts +nil+ or any value, coerced to String.
68
- #
78
+ # Sets +identifier+, coercing the value to String.
69
79
  #--
70
80
  #: (untyped) -> String?
71
81
  def identifier=(value)
72
82
  @identifier = value&.to_s
73
83
  end
74
84
 
75
- # Sets +structured_output+. Accepts a Riffer::Params instance or +nil+.
76
- #
77
- # Raises Riffer::ArgumentError on any other type.
78
- #
85
+ # Sets +structured_output+. Raises Riffer::ArgumentError on an invalid value.
79
86
  #--
80
87
  #: (Riffer::Params?) -> Riffer::Params?
81
88
  def structured_output=(value)
@@ -83,11 +90,7 @@ class Riffer::Agent::Config
83
90
  @structured_output = value
84
91
  end
85
92
 
86
- # Sets +tool_runtime+. Accepts a Riffer::Tools::Runtime subclass, a
87
- # Riffer::Tools::Runtime instance, or a Proc.
88
- #
89
- # Raises Riffer::ArgumentError on any other type.
90
- #
93
+ # Sets +tool_runtime+. Raises Riffer::ArgumentError on an invalid value.
91
94
  #--
92
95
  #: ((singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
93
96
  def tool_runtime=(value)
@@ -96,10 +99,8 @@ class Riffer::Agent::Config
96
99
  @tool_runtime = value
97
100
  end
98
101
 
99
- # Sets +model+. Accepts a String ("provider/model"), a Proc, or +nil+.
100
- #
101
- # Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
102
- #
102
+ # Sets +model+. Raises Riffer::ArgumentError on an invalid value (e.g. an
103
+ # empty string).
103
104
  #--
104
105
  #: ((String | Proc)?) -> (String | Proc)?
105
106
  def model=(value)
@@ -107,10 +108,8 @@ class Riffer::Agent::Config
107
108
  @model = value
108
109
  end
109
110
 
110
- # Sets +instructions+. Accepts a String, a Proc, or +nil+.
111
- #
112
- # Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
113
- #
111
+ # Sets +instructions+. Raises Riffer::ArgumentError on an invalid value (e.g.
112
+ # an empty string).
114
113
  #--
115
114
  #: ((String | Proc)?) -> (String | Proc)?
116
115
  def instructions=(value)
@@ -121,20 +120,15 @@ class Riffer::Agent::Config
121
120
  # Appends an MCP tag entry to +mcp_configs+.
122
121
  #
123
122
  #--
124
- #: (String | Symbol) -> Array[Hash[Symbol, untyped]]
125
- def add_mcp(tag)
126
- @mcp_configs << {tags: [tag.to_sym]}
123
+ #: (String | Symbol, ?progressive: bool) -> Array[Hash[Symbol, untyped]]
124
+ def add_mcp(tag, progressive: true)
125
+ raise Riffer::ArgumentError, "progressive must be a boolean" unless progressive == true || progressive == false
126
+ @mcp_configs << {tags: [tag.to_sym], progressive: progressive}
127
127
  end
128
128
 
129
- # Appends a guardrail entry to +guardrails+ for the given phase.
130
- #
131
- # [phase] +:before+, +:after+, or +:around+. +:around+ appends to both
132
- # +:before+ and +:after+.
133
- # [klass] the Riffer::Guardrail subclass to register.
134
- # [options] options forwarded to the guardrail at runtime.
135
- #
136
- # Raises Riffer::ArgumentError on an invalid phase or non-Guardrail class.
137
- #
129
+ # Appends a guardrail entry to +guardrails+ for the given phase; +:around+
130
+ # appends to both +:before+ and +:after+. Raises Riffer::ArgumentError unless
131
+ # +phase+ is :before, :after, or :around.
138
132
  #--
139
133
  #: (Symbol, klass: singleton(Riffer::Guardrail), ?options: Hash[Symbol, untyped]) -> void
140
134
  def add_guardrail(phase, klass:, options: {})
@@ -164,6 +158,7 @@ class Riffer::Agent::Config
164
158
 
165
159
  private
166
160
 
161
+ #--
167
162
  #: (untyped, String) -> void
168
163
  def validate_string_or_proc!(value, name)
169
164
  return if value.nil? || value.is_a?(Proc)
@@ -1,38 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
- # Typed value object wrapping the runtime context Hash held by a
5
- # Riffer::Agent. Exposes first-class accessors for the framework-managed
6
- # entries — +skills+ and +token_usage+ and preserves +#[]+ / +#dig+
7
- # reads so tools (which receive +context:+ as a keyword) keep working
8
- # with both built-in and caller-provided keys.
9
- #
10
- # Reserved keys (+:skills+, +:token_usage+) cannot be set by the caller
11
- # at construction; they are owned by Riffer and written through the typed
12
- # setters. Type invariants are enforced on write — +skills+ must be a
13
- # +Riffer::Skills::Context+ (or nil); +token_usage+ must be a
14
- # +Riffer::Providers::TokenUsage+ (or nil).
15
- #
16
- # context = Riffer::Agent::Context.new(user_id: 42)
17
- # context[:user_id] # => 42
18
- # context.skills # => nil
19
- # context.token_usage # => nil
20
- #
4
+ # Typed value object wrapping the runtime context Hash held by a Riffer::Agent.
5
+ # Exposes typed +skills+, +token_usage+, +mcp_progressive_tools+, and
6
+ # +discovered_tools+ accessors while preserving +#[]+ / +#dig+ for caller-provided keys.
21
7
  class Riffer::Agent::Context
22
8
  # @rbs @data: Hash[Symbol, untyped]
23
9
 
24
- # Keys reserved for framework use. Passing any of these to the
25
- # constructor raises +Riffer::ArgumentError+.
26
- RESERVED_KEYS = [:skills, :token_usage].freeze #: Array[Symbol]
10
+ RESERVED_KEYS = [:skills, :token_usage, :mcp_progressive_tools, :discovered_tools].freeze #: Array[Symbol]
27
11
 
28
- # Builds a new context.
29
- #
30
- # [data] caller-provided Hash passed as <tt>Agent.new(context:)</tt>.
31
- # Duped before storage so caller mutations do not affect the
32
- # agent. Must not contain any +RESERVED_KEYS+.
33
- #
34
- # Raises Riffer::ArgumentError when +data+ contains a reserved key.
35
- #
12
+ # Builds a new context. The caller Hash is duped so later caller mutations
13
+ # don't leak in. Raises Riffer::ArgumentError if it contains a reserved key.
36
14
  #--
37
15
  #: (?Hash[Symbol, untyped]) -> void
38
16
  def initialize(data = {})
@@ -45,6 +23,8 @@ class Riffer::Agent::Context
45
23
  @data = data.dup
46
24
  @data[:skills] = nil
47
25
  @data[:token_usage] = nil
26
+ @data[:mcp_progressive_tools] = nil
27
+ @data[:discovered_tools] = nil
48
28
  end
49
29
 
50
30
  # The agent's resolved +Riffer::Skills::Context+, or +nil+ when skills
@@ -56,12 +36,8 @@ class Riffer::Agent::Context
56
36
  @data[:skills]
57
37
  end
58
38
 
59
- # Sets the resolved skills context. Called once by +Riffer::Agent+
60
- # during construction.
61
- #
62
- # Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
63
- # +Riffer::Skills::Context+.
64
- #
39
+ # Sets the resolved skills context. Raises Riffer::ArgumentError on an
40
+ # invalid value.
65
41
  #--
66
42
  #: (Riffer::Skills::Context?) -> Riffer::Skills::Context?
67
43
  def skills=(value)
@@ -81,12 +57,8 @@ class Riffer::Agent::Context
81
57
  @data[:token_usage]
82
58
  end
83
59
 
84
- # Sets the cumulative token usage. Called by +Riffer::Agent::Run+ after
85
- # each LLM response.
86
- #
87
- # Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
88
- # +Riffer::Providers::TokenUsage+.
89
- #
60
+ # Sets the cumulative token usage. Raises Riffer::ArgumentError on an invalid
61
+ # value.
90
62
  #--
91
63
  #: (Riffer::Providers::TokenUsage?) -> Riffer::Providers::TokenUsage?
92
64
  def token_usage=(value)
@@ -97,28 +69,76 @@ class Riffer::Agent::Context
97
69
  @data[:token_usage] = value
98
70
  end
99
71
 
100
- # Hash-style read. Preserved so downstream tool runtimes pulling
101
- # caller-provided keys via <tt>context[:agent]</tt> or
102
- # <tt>context[:tenant]</tt> keep working unchanged.
103
- #
72
+ # Hash-style read, preserved so tools can pull caller-provided keys via
73
+ # <tt>context[:agent]</tt>.
104
74
  #--
105
75
  #: (Symbol) -> untyped
106
76
  def [](key)
107
77
  @data[key]
108
78
  end
109
79
 
110
- # Hash-style dig. Preserved for tools using
111
- # <tt>context&.dig(:user_id)</tt>.
112
- #
80
+ # Auth-wrapped MCP tool classes for progressive discovery, or +nil+.
81
+ #--
82
+ #: () -> Array[singleton(Riffer::Tool)]?
83
+ def mcp_progressive_tools
84
+ @data[:mcp_progressive_tools]
85
+ end
86
+
87
+ # Sets progressive MCP tools. Raises Riffer::ArgumentError on an invalid value.
88
+ #--
89
+ #: (Array[singleton(Riffer::Tool)]?) -> Array[singleton(Riffer::Tool)]?
90
+ def mcp_progressive_tools=(value)
91
+ valid = value.nil? || (
92
+ value.is_a?(Array) &&
93
+ value.all? { |tool| tool.is_a?(Class) && tool < Riffer::Tool }
94
+ )
95
+ unless valid
96
+ raise Riffer::ArgumentError,
97
+ "mcp_progressive_tools must be an Array of Riffer::Tool subclasses or nil, got #{value.class}"
98
+ end
99
+ @data[:mcp_progressive_tools] = value
100
+ end
101
+
102
+ # MCP tool classes discovered during progressive search. Accumulates across
103
+ # +generate+ calls and is merged into the active tool list on every LLM call.
104
+ #--
105
+ #: () -> Array[singleton(Riffer::Tool)]?
106
+ def discovered_tools
107
+ @data[:discovered_tools]
108
+ end
109
+
110
+ # Sets the discovered tools array. Raises Riffer::ArgumentError on an invalid value.
111
+ #--
112
+ #: (Array[singleton(Riffer::Tool)]?) -> Array[singleton(Riffer::Tool)]?
113
+ def discovered_tools=(value)
114
+ valid = value.nil? || (
115
+ value.is_a?(Array) &&
116
+ value.all? { |tool| tool.is_a?(Class) && tool < Riffer::Tool }
117
+ )
118
+ unless valid
119
+ raise Riffer::ArgumentError,
120
+ "discovered_tools must be an Array of Riffer::Tool subclasses or nil, got #{value.class}"
121
+ end
122
+ @data[:discovered_tools] = value
123
+ end
124
+
125
+ # Accumulates newly discovered MCP tool classes, deduplicating by name.
126
+ # Each call extends the existing set; calling multiple times is safe.
127
+ #--
128
+ #: (Array[singleton(Riffer::Tool)]) -> Array[singleton(Riffer::Tool)]
129
+ def discover_tools(tools)
130
+ existing = @data[:discovered_tools] || []
131
+ @data[:discovered_tools] = (existing + tools).uniq(&:name)
132
+ end
133
+
113
134
  #--
114
135
  #: (*Symbol) -> untyped
115
136
  def dig(*keys)
116
137
  @data.dig(*keys)
117
138
  end
118
139
 
119
- # Returns a copy of the underlying Hash. Mutating the result does not
120
- # affect this context.
121
- #
140
+ # Returns a copy of the underlying Hash; mutating it does not affect this
141
+ # context.
122
142
  #--
123
143
  #: () -> Hash[Symbol, untyped]
124
144
  def to_h
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
- # Wraps agent generation responses with optional tripwire information.
5
- #
6
- # When guardrails block execution, the response will contain a tripwire
7
- # with details about the block. The content will be empty for blocked responses.
4
+ # Wraps an agent generation response. When a guardrail blocks execution,
5
+ # +content+ is empty and +tripwire+ carries the block details.
8
6
  #
9
7
  # response = agent.generate("Hello")
10
8
  # if response.blocked?
@@ -33,24 +31,10 @@ class Riffer::Agent::Response
33
31
  # The full message history from the agent conversation.
34
32
  attr_reader :messages #: Array[Riffer::Messages::Base]
35
33
 
36
- # Call ids of tool_use blocks that riffer filled with placeholder
37
- # results during this turn — populated when an interrupt left them
38
- # unanswered and +Riffer.config.experimental_history_healing+ is on.
39
- # Empty otherwise.
34
+ # Call ids of tool_use blocks riffer filled with placeholder results this
35
+ # turn (when an interrupt left them unanswered and history healing is on).
40
36
  attr_reader :healed_tool_call_ids #: Array[String]
41
37
 
42
- # Creates a new response.
43
- #
44
- # [content] the response content.
45
- # [tripwire] optional tripwire for blocked responses.
46
- # [modifications] guardrail modifications applied during processing.
47
- # [interrupted] whether the agent loop was interrupted by a callback.
48
- # [interrupt_reason] optional reason passed via <tt>throw :riffer_interrupt, reason</tt>.
49
- # [structured_output] parsed structured output when structured output is configured.
50
- # [messages] the full message history from the agent conversation.
51
- # [healed_tool_call_ids] call ids filled with placeholder tool results
52
- # when history healing is enabled.
53
- #
54
38
  #--
55
39
  #: (String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?messages: Array[Riffer::Messages::Base], ?healed_tool_call_ids: Array[String]) -> void
56
40
  def initialize(content, tripwire: nil, modifications: [], interrupted: false, interrupt_reason: nil, structured_output: nil, messages: [], healed_tool_call_ids: [])