riffer 0.47.2 → 0.49.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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/rules/comments.md +2 -4
  3. data/.claude/rules/rbs-inline.md +2 -12
  4. data/.release-please-manifest.json +1 -1
  5. data/.rubocop.yml +5 -0
  6. data/CHANGELOG.md +42 -0
  7. data/docs/AGENTS.md +39 -7
  8. data/docs/AGENT_LIFECYCLE.md +14 -16
  9. data/docs/CONFIGURATION.md +23 -34
  10. data/docs/EVALS.md +2 -1
  11. data/docs/MCP.md +0 -4
  12. data/docs/MESSAGES.md +85 -17
  13. data/docs/STREAM_EVENTS.md +8 -4
  14. data/docs/TOOL_ADVANCED.md +1 -3
  15. data/docs/TRACING.md +2 -2
  16. data/docs/providers/AMAZON_BEDROCK.md +32 -1
  17. data/docs/providers/CUSTOM_PROVIDERS.md +56 -4
  18. data/docs/providers/GEMINI.md +1 -1
  19. data/docs/providers/MOCK_PROVIDER.md +17 -0
  20. data/docs/providers/OPENROUTER.md +18 -1
  21. data/docs-site/build.rb +0 -6
  22. data/docs-site/check.rb +1 -5
  23. data/lib/riffer/agent/config.rb +27 -44
  24. data/lib/riffer/agent/context.rb +2 -26
  25. data/lib/riffer/agent/outcome.rb +0 -20
  26. data/lib/riffer/agent/response.rb +4 -35
  27. data/lib/riffer/agent/run.rb +32 -48
  28. data/lib/riffer/agent/serializer.rb +10 -39
  29. data/lib/riffer/agent/session/repair.rb +3 -15
  30. data/lib/riffer/agent/session.rb +26 -38
  31. data/lib/riffer/agent/structured_output/result.rb +0 -8
  32. data/lib/riffer/agent/structured_output.rb +0 -7
  33. data/lib/riffer/agent.rb +31 -127
  34. data/lib/riffer/config/amazon_bedrock.rb +30 -0
  35. data/lib/riffer/config/anthropic.rb +21 -0
  36. data/lib/riffer/config/azure_open_ai.rb +30 -0
  37. data/lib/riffer/config/evals.rb +18 -0
  38. data/lib/riffer/config/files.rb +61 -0
  39. data/lib/riffer/config/gemini.rb +21 -0
  40. data/lib/riffer/config/mcp.rb +31 -0
  41. data/lib/riffer/config/open_ai.rb +30 -0
  42. data/lib/riffer/config/open_router.rb +21 -0
  43. data/lib/riffer/config/pricing/rates.rb +36 -0
  44. data/lib/riffer/config/pricing.rb +64 -0
  45. data/lib/riffer/config/skills.rb +38 -0
  46. data/lib/riffer/config/tracing.rb +43 -0
  47. data/lib/riffer/config.rb +3 -362
  48. data/lib/riffer/evals/evaluator.rb +22 -23
  49. data/lib/riffer/evals/evaluator_runner.rb +0 -15
  50. data/lib/riffer/evals/judge.rb +9 -11
  51. data/lib/riffer/evals/result.rb +1 -11
  52. data/lib/riffer/evals/run_result.rb +0 -12
  53. data/lib/riffer/evals/scenario_result.rb +0 -19
  54. data/lib/riffer/files/downloader.rb +2 -3
  55. data/lib/riffer/files/resolver.rb +5 -10
  56. data/lib/riffer/guardrail.rb +2 -22
  57. data/lib/riffer/guardrails/modification.rb +0 -8
  58. data/lib/riffer/guardrails/result.rb +0 -17
  59. data/lib/riffer/guardrails/runner.rb +2 -11
  60. data/lib/riffer/guardrails/tripwire.rb +0 -8
  61. data/lib/riffer/guardrails.rb +0 -2
  62. data/lib/riffer/helpers/boolean.rb +0 -4
  63. data/lib/riffer/helpers/call_or_value.rb +0 -3
  64. data/lib/riffer/helpers/deep_dup.rb +37 -0
  65. data/lib/riffer/helpers/dependencies.rb +0 -4
  66. data/lib/riffer/helpers/identifier.rb +3 -12
  67. data/lib/riffer/helpers/validate.rb +42 -0
  68. data/lib/riffer/mcp/authenticated_tool.rb +4 -12
  69. data/lib/riffer/mcp/client.rb +0 -7
  70. data/lib/riffer/mcp/manifest.rb +3 -7
  71. data/lib/riffer/mcp/registration.rb +0 -10
  72. data/lib/riffer/mcp/registry.rb +0 -9
  73. data/lib/riffer/mcp/search_tool.rb +0 -4
  74. data/lib/riffer/mcp/tool.rb +1 -4
  75. data/lib/riffer/mcp/tool_factory.rb +3 -6
  76. data/lib/riffer/mcp.rb +2 -23
  77. data/lib/riffer/messages/assistant/reasoning_part.rb +73 -0
  78. data/lib/riffer/messages/assistant/tool_call.rb +55 -0
  79. data/lib/riffer/messages/assistant.rb +43 -15
  80. data/lib/riffer/messages/base.rb +5 -33
  81. data/lib/riffer/messages/system.rb +8 -1
  82. data/lib/riffer/messages/tool.rb +15 -13
  83. data/lib/riffer/messages/{file_part.rb → user/file_part.rb} +16 -40
  84. data/lib/riffer/messages/user.rb +11 -4
  85. data/lib/riffer/params/boolean.rb +1 -5
  86. data/lib/riffer/params/param.rb +15 -31
  87. data/lib/riffer/params.rb +15 -39
  88. data/lib/riffer/providers/amazon_bedrock.rb +104 -44
  89. data/lib/riffer/providers/anthropic.rb +11 -26
  90. data/lib/riffer/providers/azure_open_ai.rb +3 -8
  91. data/lib/riffer/providers/base.rb +30 -32
  92. data/lib/riffer/providers/finish_reason.rb +0 -6
  93. data/lib/riffer/providers/gemini/client.rb +4 -17
  94. data/lib/riffer/providers/gemini.rb +6 -12
  95. data/lib/riffer/providers/mock.rb +18 -27
  96. data/lib/riffer/providers/open_ai.rb +12 -21
  97. data/lib/riffer/providers/open_router.rb +109 -33
  98. data/lib/riffer/providers/repository.rb +2 -14
  99. data/lib/riffer/providers/token_usage.rb +19 -12
  100. data/lib/riffer/registrable.rb +11 -45
  101. data/lib/riffer/runner/fibers.rb +1 -6
  102. data/lib/riffer/runner/sequential.rb +0 -1
  103. data/lib/riffer/runner/threaded.rb +0 -3
  104. data/lib/riffer/runner.rb +0 -3
  105. data/lib/riffer/skills/activate_tool.rb +0 -3
  106. data/lib/riffer/skills/adapter.rb +0 -8
  107. data/lib/riffer/skills/backend.rb +2 -7
  108. data/lib/riffer/skills/config.rb +13 -17
  109. data/lib/riffer/skills/context.rb +0 -29
  110. data/lib/riffer/skills/filesystem_backend.rb +0 -7
  111. data/lib/riffer/skills/frontmatter.rb +2 -16
  112. data/lib/riffer/skills/markdown_adapter.rb +3 -6
  113. data/lib/riffer/skills/xml_adapter.rb +0 -3
  114. data/lib/riffer/stream_events/base.rb +0 -3
  115. data/lib/riffer/stream_events/finish_reason_done.rb +1 -6
  116. data/lib/riffer/stream_events/guardrail_modification.rb +0 -10
  117. data/lib/riffer/stream_events/guardrail_tripwire.rb +0 -10
  118. data/lib/riffer/stream_events/interrupt.rb +2 -12
  119. data/lib/riffer/stream_events/reasoning_delta.rb +0 -3
  120. data/lib/riffer/stream_events/reasoning_done.rb +6 -8
  121. data/lib/riffer/stream_events/skill_activation.rb +0 -3
  122. data/lib/riffer/stream_events/text_delta.rb +0 -2
  123. data/lib/riffer/stream_events/text_done.rb +0 -2
  124. data/lib/riffer/stream_events/token_usage_done.rb +0 -2
  125. data/lib/riffer/stream_events/tool_call_delta.rb +1 -5
  126. data/lib/riffer/stream_events/tool_call_done.rb +0 -5
  127. data/lib/riffer/stream_events/web_search_done.rb +0 -3
  128. data/lib/riffer/stream_events/web_search_status.rb +1 -5
  129. data/lib/riffer/testing/minitest.rb +4 -5
  130. data/lib/riffer/testing.rb +5 -38
  131. data/lib/riffer/tool.rb +2 -28
  132. data/lib/riffer/tools/response.rb +3 -32
  133. data/lib/riffer/tools/runtime/fibers.rb +0 -6
  134. data/lib/riffer/tools/runtime/inline.rb +0 -1
  135. data/lib/riffer/tools/runtime/threaded.rb +0 -6
  136. data/lib/riffer/tools/runtime.rb +8 -30
  137. data/lib/riffer/tools/toolable.rb +0 -37
  138. data/lib/riffer/tracing/capture.rb +5 -9
  139. data/lib/riffer/tracing/no_op.rb +0 -7
  140. data/lib/riffer/tracing/otel.rb +7 -16
  141. data/lib/riffer/tracing/stream_recorder.rb +0 -7
  142. data/lib/riffer/tracing.rb +4 -27
  143. data/lib/riffer/version.rb +1 -1
  144. data/lib/riffer.rb +2 -30
  145. data/sig/generated/riffer/agent/config.rbs +24 -46
  146. data/sig/generated/riffer/agent/context.rbs +2 -26
  147. data/sig/generated/riffer/agent/outcome.rbs +0 -20
  148. data/sig/generated/riffer/agent/response.rbs +4 -27
  149. data/sig/generated/riffer/agent/run.rbs +12 -31
  150. data/sig/generated/riffer/agent/serializer.rbs +2 -27
  151. data/sig/generated/riffer/agent/session/repair.rbs +2 -10
  152. data/sig/generated/riffer/agent/session.rbs +10 -36
  153. data/sig/generated/riffer/agent/structured_output/result.rbs +0 -7
  154. data/sig/generated/riffer/agent/structured_output.rbs +0 -7
  155. data/sig/generated/riffer/agent.rbs +25 -125
  156. data/sig/generated/riffer/config/amazon_bedrock.rbs +21 -0
  157. data/sig/generated/riffer/config/anthropic.rbs +15 -0
  158. data/sig/generated/riffer/config/azure_open_ai.rbs +21 -0
  159. data/sig/generated/riffer/config/evals.rbs +13 -0
  160. data/sig/generated/riffer/config/files.rbs +43 -0
  161. data/sig/generated/riffer/config/gemini.rbs +15 -0
  162. data/sig/generated/riffer/config/mcp.rbs +19 -0
  163. data/sig/generated/riffer/config/open_ai.rbs +21 -0
  164. data/sig/generated/riffer/config/open_router.rbs +15 -0
  165. data/sig/generated/riffer/config/pricing/rates.rbs +19 -0
  166. data/sig/generated/riffer/config/pricing.rbs +31 -0
  167. data/sig/generated/riffer/config/skills.rbs +19 -0
  168. data/sig/generated/riffer/config/tracing.rbs +25 -0
  169. data/sig/generated/riffer/config.rbs +2 -307
  170. data/sig/generated/riffer/evals/evaluator.rbs +12 -21
  171. data/sig/generated/riffer/evals/evaluator_runner.rbs +0 -14
  172. data/sig/generated/riffer/evals/judge.rbs +4 -8
  173. data/sig/generated/riffer/evals/result.rbs +1 -11
  174. data/sig/generated/riffer/evals/run_result.rbs +0 -12
  175. data/sig/generated/riffer/evals/scenario_result.rbs +0 -14
  176. data/sig/generated/riffer/files/resolver.rbs +6 -13
  177. data/sig/generated/riffer/guardrail.rbs +2 -22
  178. data/sig/generated/riffer/guardrails/modification.rbs +0 -6
  179. data/sig/generated/riffer/guardrails/result.rbs +0 -15
  180. data/sig/generated/riffer/guardrails/runner.rbs +0 -11
  181. data/sig/generated/riffer/guardrails/tripwire.rbs +0 -8
  182. data/sig/generated/riffer/guardrails.rbs +0 -2
  183. data/sig/generated/riffer/helpers/boolean.rbs +0 -4
  184. data/sig/generated/riffer/helpers/call_or_value.rbs +0 -3
  185. data/sig/generated/riffer/helpers/deep_dup.rbs +13 -0
  186. data/sig/generated/riffer/helpers/dependencies.rbs +0 -4
  187. data/sig/generated/riffer/helpers/identifier.rbs +0 -12
  188. data/sig/generated/riffer/helpers/validate.rbs +21 -0
  189. data/sig/generated/riffer/mcp/authenticated_tool.rbs +0 -5
  190. data/sig/generated/riffer/mcp/client.rbs +0 -7
  191. data/sig/generated/riffer/mcp/manifest.rbs +3 -7
  192. data/sig/generated/riffer/mcp/registration.rbs +0 -10
  193. data/sig/generated/riffer/mcp/registry.rbs +0 -9
  194. data/sig/generated/riffer/mcp/search_tool.rbs +0 -4
  195. data/sig/generated/riffer/mcp/tool.rbs +0 -4
  196. data/sig/generated/riffer/mcp/tool_factory.rbs +0 -6
  197. data/sig/generated/riffer/mcp.rbs +2 -22
  198. data/sig/generated/riffer/messages/assistant/reasoning_part.rbs +45 -0
  199. data/sig/generated/riffer/messages/assistant/tool_call.rbs +34 -0
  200. data/sig/generated/riffer/messages/assistant.rbs +25 -31
  201. data/sig/generated/riffer/messages/base.rbs +0 -12
  202. data/sig/generated/riffer/messages/system.rbs +4 -1
  203. data/sig/generated/riffer/messages/tool.rbs +4 -10
  204. data/sig/generated/riffer/messages/user/file_part.rbs +76 -0
  205. data/sig/generated/riffer/messages/user.rbs +7 -5
  206. data/sig/generated/riffer/params/boolean.rbs +1 -4
  207. data/sig/generated/riffer/params/param.rbs +7 -31
  208. data/sig/generated/riffer/params.rbs +8 -38
  209. data/sig/generated/riffer/providers/amazon_bedrock.rbs +33 -29
  210. data/sig/generated/riffer/providers/anthropic.rbs +2 -12
  211. data/sig/generated/riffer/providers/azure_open_ai.rbs +0 -8
  212. data/sig/generated/riffer/providers/base.rbs +18 -34
  213. data/sig/generated/riffer/providers/finish_reason.rbs +0 -6
  214. data/sig/generated/riffer/providers/gemini/client.rbs +4 -17
  215. data/sig/generated/riffer/providers/gemini.rbs +4 -10
  216. data/sig/generated/riffer/providers/mock.rbs +6 -27
  217. data/sig/generated/riffer/providers/open_ai.rbs +6 -13
  218. data/sig/generated/riffer/providers/open_router.rbs +37 -18
  219. data/sig/generated/riffer/providers/repository.rbs +2 -14
  220. data/sig/generated/riffer/providers/token_usage.rbs +9 -12
  221. data/sig/generated/riffer/registrable.rbs +0 -45
  222. data/sig/generated/riffer/runner/fibers.rbs +0 -6
  223. data/sig/generated/riffer/runner/sequential.rbs +0 -1
  224. data/sig/generated/riffer/runner/threaded.rbs +0 -3
  225. data/sig/generated/riffer/runner.rbs +0 -3
  226. data/sig/generated/riffer/skills/activate_tool.rbs +0 -3
  227. data/sig/generated/riffer/skills/adapter.rbs +0 -8
  228. data/sig/generated/riffer/skills/backend.rbs +2 -7
  229. data/sig/generated/riffer/skills/config.rbs +6 -17
  230. data/sig/generated/riffer/skills/context.rbs +0 -29
  231. data/sig/generated/riffer/skills/filesystem_backend.rbs +0 -7
  232. data/sig/generated/riffer/skills/frontmatter.rbs +2 -16
  233. data/sig/generated/riffer/skills/markdown_adapter.rbs +0 -6
  234. data/sig/generated/riffer/skills/xml_adapter.rbs +0 -3
  235. data/sig/generated/riffer/stream_events/base.rbs +0 -3
  236. data/sig/generated/riffer/stream_events/finish_reason_done.rbs +1 -6
  237. data/sig/generated/riffer/stream_events/guardrail_modification.rbs +0 -10
  238. data/sig/generated/riffer/stream_events/guardrail_tripwire.rbs +0 -10
  239. data/sig/generated/riffer/stream_events/interrupt.rbs +2 -10
  240. data/sig/generated/riffer/stream_events/reasoning_delta.rbs +0 -3
  241. data/sig/generated/riffer/stream_events/reasoning_done.rbs +4 -6
  242. data/sig/generated/riffer/stream_events/skill_activation.rbs +0 -3
  243. data/sig/generated/riffer/stream_events/text_delta.rbs +0 -2
  244. data/sig/generated/riffer/stream_events/text_done.rbs +0 -2
  245. data/sig/generated/riffer/stream_events/token_usage_done.rbs +0 -2
  246. data/sig/generated/riffer/stream_events/tool_call_delta.rbs +1 -5
  247. data/sig/generated/riffer/stream_events/tool_call_done.rbs +0 -5
  248. data/sig/generated/riffer/stream_events/web_search_done.rbs +0 -3
  249. data/sig/generated/riffer/stream_events/web_search_status.rbs +1 -5
  250. data/sig/generated/riffer/testing.rbs +0 -38
  251. data/sig/generated/riffer/tool.rbs +0 -27
  252. data/sig/generated/riffer/tools/response.rbs +3 -29
  253. data/sig/generated/riffer/tools/runtime/fibers.rbs +0 -5
  254. data/sig/generated/riffer/tools/runtime/inline.rbs +0 -1
  255. data/sig/generated/riffer/tools/runtime/threaded.rbs +0 -5
  256. data/sig/generated/riffer/tools/runtime.rbs +4 -26
  257. data/sig/generated/riffer/tools/toolable.rbs +0 -37
  258. data/sig/generated/riffer/tracing/capture.rbs +6 -9
  259. data/sig/generated/riffer/tracing/no_op.rbs +0 -7
  260. data/sig/generated/riffer/tracing/otel.rbs +7 -16
  261. data/sig/generated/riffer/tracing/stream_recorder.rbs +0 -2
  262. data/sig/generated/riffer/tracing.rbs +4 -23
  263. data/sig/generated/riffer.rbs +2 -29
  264. data/sig/manual/riffer/helpers/deep_dup.rbs +5 -0
  265. data/sig/manual/riffer/helpers/validate.rbs +5 -0
  266. metadata +39 -3
  267. data/sig/generated/riffer/messages/file_part.rbs +0 -101
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5e4c3861f4218fa075b5abd666be63eee6432db05c9a6fff3ab4285748ed7c5
4
- data.tar.gz: a945c6c549c4c18b4e228cbcce1a03a48eb2b263a20bb18c9c1c51088c6a3e8c
3
+ metadata.gz: b1bd95697d75079325d4a057c0f2bb17bc8fee9a3864c611967e78d8e1637ef8
4
+ data.tar.gz: 84ddb8c10cef0b5711b00793b3a19de2713fcf8d7985166dfaa248fa7bbe58f0
5
5
  SHA512:
6
- metadata.gz: 308010c98f1f44a9a13032b39fea4a2636977d9b6f232ccb9aa040ef6485f0b2d51ec3d66a69be0e4a522759cc86e42fa8b94eae6b2446a5c61ec6a3f6388892
7
- data.tar.gz: dd7710eb0ed144e1dfaf49d063e5d4845068e590927092dd5b3f946b0cc0f49533262c3dc83e12dbb87fdc8356eb83890177ccf8eacc485028ca4ce7684ce01e
6
+ metadata.gz: 375f9c10e95e1dbd60da4acd0209bdc5707a30dff00e742815fb874cb00755b187602967433d5677444e790b9d94153c1408e9bc8f91a80e5879aa696de0a550
7
+ data.tar.gz: a05871a1bcd23100fa5d0018438810476560c19f87e82ebaaa6b7322e81b03577f12923845716792deb479c86f59a49127e65b2f862f4a84b95e11d75a6cb942
@@ -6,8 +6,6 @@ paths: ["**/*.rb", "**/*.rake", "**/Gemfile"]
6
6
 
7
7
  A comment exists to explain a **why** when 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 docstrings.
8
8
 
9
- - **Internal and private code** — everything in an application, plus a library's non-exported internals — is self-documenting via clear names and strong types. A comment survives only when it explains something a competent reader cannot recover from the code alone: a non-local constraint, an external-system quirk, a deliberate non-obvious tradeoff. A description of _what_ the code does, or a why that's evident from reading it, gets cut.
10
- - **A published library's public surface** gets one verb-first sentence per exported symbol ("Serializes the definition to JSON."). An optional second sentence is reserved strictly for a why — a non-obvious constraint or rationale — never a second sentence of how. Needing more than one sentence to say _what_ it does is a smell the symbol does too much.
11
- - **Types are not prose's job.** Parameters, return values, and field types live in the type system (TypeScript types, rbs-inline `#:` annotations) — never restated in comments that duplicate them.
12
- - **Markers.** `TODO` / `FIXME` / `HACK` are tracked work and stay; `NOTE` / `REVIEW` meet the same why-bar as any other comment.
9
+ - **The default is no comment.** Names, types, and structure carry the meaning; when they don't, fix them rather than explain them. Delete the comment and read the code cold: if the intent is still recoverable, it stays deleted. Keep only a _why_ the code can't show — a non-local constraint, an external quirk, a deliberate tradeoff, a safety invariant — and put it at the line it explains, not in a header.
10
+
13
11
  - **No history.** A comment describes the present, never how the code got there — no "was X, now Y", no story of the bug that revealed a constraint. State a still-true constraint in the present tense ("the API returns null for empty results — guard").
@@ -26,28 +26,18 @@ module Riffer
26
26
  end
27
27
  ```
28
28
 
29
- ## RDoc Conventions
29
+ ## The `#--` stop directive
30
30
 
31
- **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.
31
+ Place `#--` on the line immediately before a **standalone** `#:` type annotation, even when no comment precedes it. Without it, RDoc treats `#:` as a label-list marker and mangles the method's rendered docs. Inline `#:` on the same line as code (attributes, constants) does not need it.
32
32
 
33
33
  ```ruby
34
- # Serializes the agent definition to a transferable JSON payload.
35
34
  #--
36
35
  #: (Riffer::Agent) -> String
37
36
  def serialize(agent)
38
37
 
39
- # The agent's display name.
40
38
  attr_reader :name #: String
41
39
  ```
42
40
 
43
- **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. 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.
44
-
45
- **Examples.** Include an example only when a **consumer is likely to use the thing themselves** — a public entry point they construct, subclass, or call. Keep them sparing and write them as indented code blocks (2 extra spaces of indent). Usage walkthroughs belong in `docs/`.
46
-
47
- **Inline code formatting.** Use `+word+` for single-word inline code; for multi-word expressions (spaces, colons, brackets) use `<tt>multi word expression</tt>`.
48
-
49
- **Internal APIs.** Mark with `# :nodoc:` to exclude from generated documentation.
50
-
51
41
  ## Optional-dependency types (consumer-safe signatures)
52
42
 
53
43
  `sig/generated/` ships with the gem and is loaded by downstream projects (`rbs collection` / `rbs -r riffer`). rbs-inline copies a method's `#:` signature **verbatim** into the shipped sig, so **never name an optional-dependency type in a `#:` signature** — `OpenAI::*`, `Anthropic::*`, `Aws::*`, `MCP::*`, `Async::*`, `Zeitwerk::*`, etc. A consumer who installs riffer without that gem would hit `Cannot find type`, because those providers are pluggable and the gems ship no usable RBS of their own.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.47.2"
2
+ ".": "0.49.0"
3
3
  }
data/.rubocop.yml CHANGED
@@ -110,3 +110,8 @@ Performance/Sum:
110
110
  - lib/riffer/providers/base.rb
111
111
  - lib/riffer/evals/run_result.rb
112
112
  - lib/riffer/evals/scenario_result.rb
113
+
114
+ # Each accessor carries its own trailing rbs-inline `#:` type; grouping them
115
+ # onto one line drops every type but the first.
116
+ Style/AccessorGrouping:
117
+ EnforcedStyle: separated
data/CHANGELOG.md CHANGED
@@ -5,6 +5,48 @@ 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.49.0](https://github.com/janeapp/riffer/compare/riffer/v0.48.0...riffer/v0.49.0) (2026-09-27)
9
+
10
+
11
+ ### ⚠ BREAKING CHANGES
12
+
13
+ * **config:** provider string settings must be a String or nil; evals.judge_model must be in "provider/model" form; mcp.credentials must respond to #call; mcp.discovery_runner must be a Riffer::Runner. Struct behaviour ([] / to_a / members / value equality) is gone from those sections. Riffer::Tracing.reset! is removed; the tracing backend is read from config on every call instead of being cached.
14
+ * **agent:** Riffer.config.experimental_history_healing is removed, as are Response#healed_tool_call_ids and StreamEvents::Interrupt#healed_tool_call_ids (and its to_h key). Orphan pruning on Agent.new is now always on. Interrupts never fill placeholders; call agent.session.discard_pending_tool_calls instead and use its return value for the filled call ids.
15
+
16
+ ### Features
17
+
18
+ * **agent:** always repair seeded sessions, add discard_pending_tool_calls ([#456](https://github.com/janeapp/riffer/issues/456)) ([b1e8933](https://github.com/janeapp/riffer/commit/b1e89330f2b2f59b74f794d5a21d31b833cb64d8))
19
+ * **amazon_bedrock:** capture and replay reasoning content ([#459](https://github.com/janeapp/riffer/issues/459)) ([5048a8c](https://github.com/janeapp/riffer/commit/5048a8ce2aba35ad7af2eeee018dc381ca77eb00))
20
+ * **open_router:** capture and replay reasoning_details ([#453](https://github.com/janeapp/riffer/issues/453)) ([a3e2523](https://github.com/janeapp/riffer/commit/a3e252324de97d3f03942c60a892126975bb1a60))
21
+
22
+
23
+ ### Code Refactoring
24
+
25
+ * **config:** split Riffer::Config into one file per section ([#458](https://github.com/janeapp/riffer/issues/458)) ([c493684](https://github.com/janeapp/riffer/commit/c4936848c5c34903c160f90786e7493831ab21dd))
26
+
27
+ ## [0.48.0](https://github.com/janeapp/riffer/compare/riffer/v0.47.2...riffer/v0.48.0) (2026-09-23)
28
+
29
+
30
+ ### ⚠ BREAKING CHANGES
31
+
32
+ * **messages:** Riffer::Messages::FilePart is renamed to Riffer::Messages::User::FilePart with no alias. ToolCall no longer responds to Struct-only methods such as [] or to_a; use its readers instead.
33
+
34
+ ### Features
35
+
36
+ * **agent:** inherit configuration on subclass ([#440](https://github.com/janeapp/riffer/issues/440)) ([a45c457](https://github.com/janeapp/riffer/commit/a45c45756108cfac7970e88b9f6f5b64638896c8))
37
+ * identify agents and evals with default tags ([#452](https://github.com/janeapp/riffer/issues/452)) ([22ab54c](https://github.com/janeapp/riffer/commit/22ab54c5225a45c38e255b1a2aa32be0ac28cb12))
38
+ * **messages:** persist reasoning on assistant messages ([#446](https://github.com/janeapp/riffer/issues/446)) ([7fc64ab](https://github.com/janeapp/riffer/commit/7fc64ab416feb47ccff575518378c58bbcc74d63))
39
+
40
+
41
+ ### Bug Fixes
42
+
43
+ * **messages:** preserve tool errors and token usage through from_hash ([#450](https://github.com/janeapp/riffer/issues/450)) ([6e2bd90](https://github.com/janeapp/riffer/commit/6e2bd90926d7c3c071ac95f708875652268c767f))
44
+
45
+
46
+ ### Code Refactoring
47
+
48
+ * **messages:** make nested message objects follow one pattern ([#448](https://github.com/janeapp/riffer/issues/448)) ([e9e63db](https://github.com/janeapp/riffer/commit/e9e63dba16121692f734b476569b062c09cc959d))
49
+
8
50
  ## [0.47.2](https://github.com/janeapp/riffer/compare/riffer/v0.47.1...riffer/v0.47.2) (2026-09-21)
9
51
 
10
52
 
data/docs/AGENTS.md CHANGED
@@ -126,7 +126,7 @@ end
126
126
 
127
127
  ### use_mcp
128
128
 
129
- Loads tools from registered [MCP](MCP.md) servers by tag. Like `uses_tools`, **`use_mcp` is not inherited**—add it on each subclass that should include MCP tools.
129
+ Loads tools from registered [MCP](MCP.md) servers by tag.
130
130
 
131
131
  ### model_options
132
132
 
@@ -251,9 +251,7 @@ A `Hash` param requires a block, and an `Array` param requires a block or `of:`.
251
251
 
252
252
  Structured output is not compatible with streaming — calling `stream` on an agent with structured output configured raises `Riffer::ArgumentError`.
253
253
 
254
- ### tool_runtime (Experimental)
255
-
256
- > **Warning:** This feature is experimental and may be removed or changed without warning in a future release.
254
+ ### tool_runtime
257
255
 
258
256
  Configures how tool calls are executed. Defaults to sequential (inline) execution:
259
257
 
@@ -265,7 +263,7 @@ class MyAgent < Riffer::Agent
265
263
  end
266
264
  ```
267
265
 
268
- Accepts a `Riffer::Tools::Runtime` subclass, a `Riffer::Tools::Runtime` instance, or a `Proc`. When unset, defaults to `Riffer.config.tool_runtime` (captured at agent class definition time). See [Tools — Tool Runtime](TOOL_ADVANCED.md#tool-runtime-experimental) for details.
266
+ Accepts a `Riffer::Tools::Runtime` subclass, a `Riffer::Tools::Runtime` instance, or a `Proc`. When unset, reads `Riffer.config.tool_runtime` at the point of use, so an agent that declares none follows a later change to the global. See [Tools — Tool Runtime](TOOL_ADVANCED.md#tool-runtime) for details.
269
267
 
270
268
  ### guardrail
271
269
 
@@ -304,6 +302,27 @@ MyAgent.config.max_steps # => 8
304
302
 
305
303
  The DSL methods read and mutate this Config in place.
306
304
 
305
+ ### Inheritance
306
+
307
+ A subclass starts from a copy of its parent's Config, so it inherits every setting the parent declared and overrides only what its own body declares:
308
+
309
+ ```ruby
310
+ class BaseAgent < Riffer::Agent
311
+ model 'openai/gpt-5-mini'
312
+ max_steps 8
313
+ end
314
+
315
+ class TerseAgent < BaseAgent
316
+ max_steps 2
317
+ end
318
+
319
+ TerseAgent.config.model # => 'openai/gpt-5-mini'
320
+ TerseAgent.config.max_steps # => 2
321
+ BaseAgent.config.max_steps # => 8
322
+ ```
323
+
324
+ `identifier` is not inherited. A subclass derives its own from its class name, since two classes claiming one identifier would raise `Riffer::DuplicateIdentifierError` at the first lookup.
325
+
307
326
  For advanced composition or testing, build a Config directly and pass it via `config:` to bypass class-level DSL entirely:
308
327
 
309
328
  ```ruby
@@ -372,13 +391,24 @@ agent.generate("Summarize this ticket.",
372
391
  agent.stream("...", tags: {team: "growth", environment: "production"})
373
392
  ```
374
393
 
375
- Keys and values may be `String` or `Symbol`; both are stringified, and entries with a `nil` value are dropped. An omitted or empty `tags:` is a complete no-op.
394
+ Keys and values may be `String` or `Symbol`; both are stringified, and entries with a `nil` value are dropped.
376
395
 
377
396
  Tags propagate to **two** places:
378
397
 
379
398
  1. The provider's native per-request metadata field (see the mapping below).
380
399
  2. Observability — stamped as `riffer.tag.<key>` on **every** span the call emits (`invoke_agent`, `chat`, `execute_tool`, `execute_guardrail`). See [Tracing](TRACING.md).
381
400
 
401
+ ### Default tags
402
+
403
+ Every call also carries two tags riffer adds itself, so a provider can tell who the call is on behalf of:
404
+
405
+ | Tag | Value |
406
+ | ------- | ----------------------------------------------------------------------- |
407
+ | `kind` | `"agent"` (`"judge"` for [evaluator](EVALS.md) judge calls) |
408
+ | `agent` | The agent's `identifier` (the evaluator's `identifier` for judge calls) |
409
+
410
+ A tag you pass with the same key wins. The default tags count towards the provider limits below.
411
+
382
412
  ### Reserved key: `user_id`
383
413
 
384
414
  `user_id` is a reserved tag. Beyond appearing like any other tag, it maps to the provider's native end-user identifier where one exists (see the table).
@@ -393,6 +423,8 @@ Tags propagate to **two** places:
393
423
  | Anthropic | `metadata.user_id` **only** | The only tag forwarded |
394
424
  | Gemini | _(none — observability only)_ | Tag only; no request field |
395
425
 
426
+ If `model_options` already sets the native field (`metadata` or `request_metadata`), the tags are merged into it; a tag wins on a shared key.
427
+
396
428
  **Anthropic silently drops non-`user_id` tags.** The Messages API has no free-form request-metadata field — only `metadata.user_id`. So for Anthropic, `user_id` is forwarded as `metadata: {user_id: …}` and **every other tag is dropped from the request** (it still appears on spans). This is intentional.
397
429
 
398
430
  **Gemini is observability-only.** Riffer's Gemini adapter targets the Gemini Developer API (`generativelanguage.googleapis.com`), whose `generateContent` request has **no** `labels` field — sending unknown fields is rejected. So tags are **not** added to the Gemini request; they propagate to spans only. Native request labels (`labels`, lowercase `[a-z0-9_-]`, ≤63 chars each) are a Vertex AI feature and would arrive with a future Vertex adapter.
@@ -412,5 +444,5 @@ Riffer does not validate tag count, key/value length, or charset — it forwards
412
444
  | Add packaged capabilities | Skills | [Skills](SKILLS.md) |
413
445
  | Control the tool-use loop | Agent Loop | [Agent Loop](AGENT_LOOP.md) |
414
446
  | Human-in-the-loop approval | Interrupts | [Agent Lifecycle](AGENT_LIFECYCLE.md#interrupting-the-agent-loop) |
415
- | Run tools concurrently | Tool Runtime | [Advanced Tools](TOOL_ADVANCED.md#tool-runtime-experimental) |
447
+ | Run tools concurrently | Tool Runtime | [Advanced Tools](TOOL_ADVANCED.md#tool-runtime) |
416
448
  | Stream responses in real time | Streaming | [Agent Lifecycle](AGENT_LIFECYCLE.md#stream) |
@@ -8,7 +8,7 @@
8
8
  Agent.new(session: nil, context: nil)
9
9
  ```
10
10
 
11
- - **`session:`** — an existing `Riffer::Agent::Session`. When given, the agent uses it as-is (no system/skills seeding). Typical use case: cross-process resume from persisted history. With `Riffer.config.experimental_history_healing` on, a provided session is healed at construction time so the `tool_use` ↔ `tool_result` invariant holds before the next inference call.
11
+ - **`session:`** — an existing `Riffer::Agent::Session`. When given, the agent uses it as-is (no system/skills seeding). Typical use case: cross-process resume from persisted history. A provided session is repaired at construction time so the `tool_use` ↔ `tool_result` invariant holds before the next inference call: orphaned `tool_use` exchanges (an assistant `tool_call` with no matching `Tool` result) and parentless `Tool` messages are dropped. Pending tool calls on the **resume boundary** — the last assistant whose tail is purely `Tool` results (or none) — are preserved so `generate`/`stream` can execute them.
12
12
  - **`context:`** — a `Hash` carried for the lifetime of the agent. Used to evaluate Proc-based `instructions`, `model`, `uses_tools`, and skill activation at construction time, and threaded through tool execution and guardrails on every `generate`/`stream` call.
13
13
 
14
14
  When `session:` is omitted, the agent constructs a fresh session and seeds it with `[instruction_message, skills_message].compact` eagerly. To swap context, construct a new agent — context is fixed for the lifetime of an agent instance.
@@ -187,7 +187,7 @@ end
187
187
 
188
188
  There are two ways to resume after an interrupt, depending on whether the agent is still in memory or you're restoring from persisted data.
189
189
 
190
- **In-memory resume** — call `generate` (or `stream`) again. With a prompt, the new user message is appended and the loop runs. Without a prompt, the loop runs against the current session — useful for picking up pending tool calls after the user has approved.
190
+ **In-memory resume** — call `generate` (or `stream`) again. With a prompt, the new user message is appended and the loop runs; results for any pending tool calls are placed ahead of it, directly after the assistant message that requested them. Without a prompt, the loop runs against the current session — useful for picking up pending tool calls after the user has approved.
191
191
 
192
192
  ```ruby
193
193
  agent = MyAgent.new(context: {user_id: 123})
@@ -244,38 +244,36 @@ agent.session.on_message do |msg|
244
244
  end
245
245
  ```
246
246
 
247
- #### Healing pending tool results on interrupt (experimental)
247
+ #### Discarding pending tool calls after an interrupt
248
248
 
249
- When an interrupt fires while the assistant has a `tool_use` block that hasn't been answered yet, the LLM will reject the next request unless every `tool_use` has a matching `tool_result`. By default, the next `generate` call re-executes those pending tools (see "Resuming an Interrupted Loop" above).
249
+ An interrupt only stops the loop. Any `tool_use` the assistant emitted that hasn't been answered yet stays in history, and the next `generate`/`stream` call executes it (see "Resuming an Interrupted Loop" above). This applies to caller-issued `interrupt!` and the built-in `INTERRUPT_MAX_STEPS` ceiling alike.
250
250
 
251
- When the interrupt represents a course-change rather than a pause — e.g. a voice barge-in where the user has moved on — re-execution is the wrong behavior. Opt into history healing to have riffer fill any orphan `tool_use` with a placeholder `Riffer::Messages::Tool` carrying `error_type: :interrupted`, leaving history valid for the next turn:
251
+ When the interrupt represents a course-change rather than a pause — e.g. a voice barge-in or a cancel where the user has moved on — re-execution is the wrong behavior. Call `agent.session.discard_pending_tool_calls` to answer every unanswered `tool_use` with a placeholder `Riffer::Messages::Tool` carrying `error_type: :interrupted`, leaving history valid for the next turn. It returns the filled `call_id`s:
252
252
 
253
253
  ```ruby
254
- Riffer.configure { |c| c.experimental_history_healing = true }
255
-
256
254
  agent.session.on_message do |msg|
257
255
  agent.interrupt!(:user_interrupt) if msg.is_a?(Riffer::Messages::Assistant) && barge_in?
258
256
  end
259
257
 
260
258
  response = agent.generate("Tell me a story")
261
- response.healed_tool_call_ids # => ["call_abc123", ...]
259
+ agent.session.discard_pending_tool_calls # => ["call_abc123", ...]
260
+ agent.generate("Actually, tell me a joke")
262
261
  ```
263
262
 
264
- The placeholder content is fixed: `"Tool call interrupted before completion."` with `error_type: :interrupted`. Each placeholder is inserted immediately after its parent assistant message. The list of filled `call_id`s is exposed on `response.healed_tool_call_ids` (and on `Riffer::StreamEvents::Interrupt#healed_tool_call_ids` when streaming).
263
+ The placeholder content is fixed: `"Tool call interrupted before completion."` with `error_type: :interrupted`. Each placeholder is inserted immediately after its parent assistant message. When nothing is pending, the method returns `[]` and leaves history untouched.
265
264
 
266
- Healing covers all interrupts uniformly — caller-issued `interrupt!` and the built-in `INTERRUPT_MAX_STEPS` ceiling alike. When the flag is off (the default), orphans remain in history and `execute_pending_tool_calls` re-runs them on the next `generate` call.
265
+ The method works purely on the session's current messages, so it also applies when a run was cancelled without going through riffer's interrupt handling (e.g. the surrounding task was stopped with `Async::Stop` or an exception escaped a callback).
267
266
 
268
- If you need finer control over placeholder content (per-call shape, structured metadata, etc.), use the `update` mutator below to upgrade a placeholder after the interrupt returns.
267
+ If you need finer control over placeholder content (per-call shape, structured metadata, etc.), use the `update` mutator below to upgrade a placeholder afterwards.
269
268
 
270
269
  ### Mutating history
271
270
 
272
271
  The session exposes a small set of in-place mutators that enforce the `tool_use` ↔ `tool_result` invariant on every operation. Use these to align history with external state (persisted transcript, partial output that wasn't actually delivered, etc.) without rebuilding the agent.
273
272
 
274
273
  - **`agent.session.update(id:, **attrs)`** — In-place partial update. Looks up by message `id:`; builds a replacement of the same type with `attrs` overlaid on the existing fields. Use this to edit assistant content (`update(id:, content:)`), restate a system message, etc. When the target is an assistant and the update drops entries from `tool_calls`, matching `Tool` children are removed atomically.
275
- - **`agent.session.update(tool_call_id:, **attrs)`** — Same as above but looks up the tool result by `tool_call_id:`. Preserves `name` and `id`. Use this to upgrade an interrupt-time placeholder once the real result is available (`update(tool_call_id:, content:, error: nil, error_type: nil)`).
274
+ - **`agent.session.update(tool_call_id:, **attrs)`** — Same as above but looks up the tool result by `tool_call_id:`. Preserves `name` and `id`. Use this to upgrade a `discard_pending_tool_calls` placeholder once the real result is available (`update(tool_call_id:, content:, error: nil, error_type: nil)`).
276
275
  - **`agent.session.remove(id:)`** — Removes a message; cascades to its `Tool` children when the target carries `tool_calls`. Raises if called on a `Tool` message (use `update(tool_call_id:, ...)` to rewrite a tool result instead).
277
-
278
- Bulk filling of orphan `tool_use` blocks is handled by `Riffer.config.experimental_history_healing` (see "Healing pending tool results on interrupt" above) — there is no public synthesizer hook.
276
+ - **`agent.session.discard_pending_tool_calls`** — Answers every unanswered `tool_use` with an `:interrupted` placeholder result and returns the filled `call_id`s (see "Discarding pending tool calls after an interrupt" above).
279
277
 
280
278
  Lookup patterns that pair with the mutators (via `Enumerable`):
281
279
 
@@ -288,7 +286,7 @@ agent.session.orphaned_tool_call_ids
288
286
 
289
287
  Mutating history while a `stream` enumerator is being consumed is undefined; mutators are intended for use between turns.
290
288
 
291
- Mutators do **not** fire `on_message` — that callback is reserved for messages produced by inference (LLM responses, tool execution results). Healing placeholders bypass `on_message` for the same reason; consumers learn that healing happened via `Response#healed_tool_call_ids` (and `StreamEvents::Interrupt#healed_tool_call_ids`).
289
+ Mutators do **not** fire `on_message` — that callback is reserved for messages produced by inference (LLM responses, tool execution results). Placeholders added by `discard_pending_tool_calls` bypass `on_message` for the same reason; use its return value to learn which calls were filled.
292
290
 
293
291
  ### context
294
292
 
@@ -315,11 +313,11 @@ agent.context[:skills] # the Skills::Context, if skills configured
315
313
  | `content` | `String` | The response text |
316
314
  | `outcome` | `Outcome` | How the run ended — `reason` and optional `detail` (see below) |
317
315
  | `structured_output` | `Hash` / `nil` | Parsed and validated structured output (see below) |
316
+ | `reasoning` | `Array[ReasoningPart]` | The [reasoning parts](MESSAGES.md#reasoning) on the final assistant message (else `[]`) |
318
317
  | `tripwire` | `Tripwire` / `nil` | The guardrail tripwire that blocked the request |
319
318
  | `modified?` | `Boolean` | `true` if a guardrail modified the content |
320
319
  | `modifications` | `Array` | List of guardrail modifications applied |
321
320
  | `messages` | `Array` | Full message history from the conversation |
322
- | `healed_tool_call_ids` | `Array[String]` | `tool_call` ids filled with placeholder results during interrupt healing (else `[]`) |
323
321
  | `token_usage` | `TokenUsage` / `nil` | Aggregate `Riffer::Providers::TokenUsage` across this run's LLM calls (`nil` when none reported) |
324
322
  | `steps` | `Integer` | LLM calls made during this run (`0` when a before-guardrail blocks first); not the session's cumulative count |
325
323
 
@@ -18,6 +18,8 @@ end
18
18
 
19
19
  Providers take no constructor arguments — these settings are the only way to give a provider its credentials.
20
20
 
21
+ Credential and endpoint settings — `amazon_bedrock.api_token` / `.region`, `anthropic.api_key`, `azure_openai.api_key` / `.endpoint`, `gemini.api_key`, `openai.api_key` / `.base_url`, `openrouter.api_key` — accept a `String` or `nil` and raise `Riffer::ArgumentError` naming the setting for anything else. An empty string is accepted; Amazon Bedrock treats it as unset. `client` is not validated (see [Provider Clients](#provider-clients)).
22
+
21
23
  ## Accessing Configuration
22
24
 
23
25
  Access the current configuration via `Riffer.config`:
@@ -101,10 +103,10 @@ The Gemini provider has no vendor SDK, so riffer ships its own transport: `Riffe
101
103
 
102
104
  Optional settings for [MCP server integrations](MCP.md):
103
105
 
104
- | Option | Description |
105
- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
106
- | `credentials` | Optional `Proc` for per-run `tools/call` HTTP headers: `->(manifest:, matched_tags:, context:) { Hash or nil }` |
107
- | `discovery_runner` | `Riffer::Runner` instance for tool discovery (default `Runner::Sequential.new`) |
106
+ | Option | Description |
107
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
108
+ | `credentials` | Optional callable for per-run `tools/call` HTTP headers: `->(manifest:, matched_tags:, context:) { Hash or nil }`. Raises `Riffer::ArgumentError` unless `nil` or it responds to `#call`. |
109
+ | `discovery_runner` | `Riffer::Runner` instance for tool discovery (default `Runner::Sequential.new`). Raises `Riffer::ArgumentError` for anything that isn't a `Riffer::Runner` instance. |
108
110
 
109
111
  ```ruby
110
112
  Riffer.configure do |config|
@@ -116,9 +118,17 @@ end
116
118
 
117
119
  See [MCP](MCP.md) for registration, tags, and agent `use_mcp`.
118
120
 
119
- ### Tool Runtime (Experimental)
121
+ ### Evals
122
+
123
+ ```ruby
124
+ Riffer.configure do |config|
125
+ config.evals.judge_model = "anthropic/claude-opus-4-5-20251101"
126
+ end
127
+ ```
128
+
129
+ `judge_model` is the default model evaluators use as the judge when they don't set their own. It must be a `provider/model` string or `nil` (the default); anything else raises `Riffer::ArgumentError`. See [Evals](EVALS.md).
120
130
 
121
- > **Warning:** This feature is experimental and may be removed or changed without warning in a future release.
131
+ ### Tool Runtime
122
132
 
123
133
  Configure the default tool runtime for all agents:
124
134
 
@@ -134,7 +144,7 @@ end
134
144
  | `Riffer::Tools::Runtime` instance | Custom runtime with specific options |
135
145
  | `Proc` | Dynamic resolution |
136
146
 
137
- Per-agent configuration overrides this global default. See [Advanced Tool Configuration — Tool Runtime](TOOL_ADVANCED.md#tool-runtime-experimental) for details.
147
+ Per-agent configuration overrides this global default. See [Advanced Tool Configuration — Tool Runtime](TOOL_ADVANCED.md#tool-runtime) for details.
138
148
 
139
149
  ### Skills
140
150
 
@@ -176,15 +186,15 @@ Riffer.configure do |config|
176
186
  end
177
187
  ```
178
188
 
179
- | Option | Description |
180
- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
- | `enabled` | The kill switch, consulted on every span — flipping it at runtime takes effect immediately, short-circuiting to a no-op ahead of the backend. Accepts booleans or `'true'`/`'false'`/`'1'`/`'0'`. Defaults to `true`. |
182
- | `capture_messages` | Opt-in capture of full message content on LLM-call spans (`gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.system_instructions`) as GenAI-semconv JSON. Defaults to `false` — message content routinely carries sensitive data. File attachments serialize as metadata-only stubs (media type and name, never bytes), and riffer applies no size limit of its own — cap oversized attributes with the OTEL SDK attribute length limits. |
183
- | `backend` | The backend riffer routes spans through. Assign `Riffer::Tracing::Otel.build` (pass `provider:` to override the global tracer provider — e.g. an in-memory provider in tests), or any object satisfying the duck-typed contract (`in_span` / `current_context` / `with_context`) to route into a non-OTEL system (e.g. Datadog APM). Defaults to `nil` — a no-op. Raises `Riffer::ArgumentError` unless the value is `nil` or responds to `in_span`. See [Tracing → Routing to a non-OpenTelemetry backend](TRACING.md#routing-to-a-non-opentelemetry-backend). |
189
+ | Option | Description |
190
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | `enabled` | The kill switch, consulted on every span — flipping it at runtime takes effect immediately, short-circuiting to a no-op ahead of the backend. Accepts booleans or `'true'`/`'false'`/`'1'`/`'0'`. Defaults to `true`. |
192
+ | `capture_messages` | Opt-in capture of full message content on LLM-call spans (`gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.system_instructions`) as GenAI-semconv JSON. Defaults to `false` — message content routinely carries sensitive data. File attachments serialize as metadata-only stubs (media type and name, never bytes), and riffer applies no size limit of its own — cap oversized attributes with the OTEL SDK attribute length limits. |
193
+ | `backend` | The backend riffer routes spans through. Assign `Riffer::Tracing::Otel.build` (pass `provider:` to override the global tracer provider — e.g. an in-memory provider in tests), or any object satisfying the duck-typed contract (`in_span` / `current_context` / `with_context`) to route into a non-OTEL system (e.g. Datadog APM). Defaults to `nil` — a no-op. Raises `Riffer::ArgumentError` unless the value is `nil` or responds to all of `in_span`, `current_context`, and `with_context`. See [Tracing → Routing to a non-OpenTelemetry backend](TRACING.md#routing-to-a-non-opentelemetry-backend). |
184
194
 
185
195
  ### File Downloads
186
196
 
187
- File-attachment-download policy lives under `config.files`. Before an LLM call, riffer resolves every `Riffer::Messages::FilePart` attached to a user message against the provider's own capability — some providers accept a URL as-is, some need the bytes inline, and some can't take an attachment at all. See [Messages — File Parts](MESSAGES.md#file-parts) for `FilePart` itself and its `sha256:` field.
197
+ File-attachment-download policy lives under `config.files`. Before an LLM call, riffer resolves every `Riffer::Messages::User::FilePart` attached to a user message against the provider's own capability — some providers accept a URL as-is, some need the bytes inline, and some can't take an attachment at all. See [Messages — File Parts](MESSAGES.md#file-parts) for `FilePart` itself and its `sha256:` field.
188
198
 
189
199
  ```ruby
190
200
  Riffer.configure do |config|
@@ -270,27 +280,6 @@ When constructing a `Riffer::Agent::Session` from persisted history with the str
270
280
 
271
281
  See [Messages — IDs](MESSAGES.md#ids) for more details.
272
282
 
273
- ### Experimental: History Healing
274
-
275
- > **Warning:** This feature is experimental and may change without notice.
276
-
277
- Opts the agent into keeping the `tool_use` ↔ `tool_result` invariant intact on its own:
278
-
279
- ```ruby
280
- Riffer.configure do |config|
281
- config.experimental_history_healing = true
282
- end
283
- ```
284
-
285
- When enabled, two repairs run automatically:
286
-
287
- 1. **Seeded session.** Passing a pre-populated `Riffer::Agent::Session` to `Agent.new(session: ...)` silently drops orphaned `tool_use` exchanges (assistant `tool_call` with no matching `Tool` result) and parentless `Tool` messages before the next inference call. Pending tool calls on the **resume boundary** — the last assistant whose tail is purely `Tool` results (or none) — are preserved; `execute_pending_tool_calls` runs them on the next LLM call.
288
- 2. **Interrupts.** Any orphan `tool_use` left when the loop is interrupted (caller-issued `interrupt!` or the built-in `INTERRUPT_MAX_STEPS` ceiling) is filled with a placeholder `Riffer::Messages::Tool` carrying `error_type: :interrupted` and the content `"Tool call interrupted before completion."`. Filled `call_id`s are exposed on `Riffer::Agent::Response#healed_tool_call_ids` (and `Riffer::StreamEvents::Interrupt#healed_tool_call_ids` when streaming).
289
-
290
- Defaults to `false` — pre-healing behavior. Seeded sessions pass through untouched, and orphan `tool_use` left by an interrupt remain in history for `execute_pending_tool_calls` to re-run on the next call.
291
-
292
- There is no per-call override and no customizable placeholder. Callers needing finer control can call `agent.session.update(tool_call_id:, ...)` after the interrupt returns to upgrade a placeholder in place. See [Agent Lifecycle — Healing pending tool results on interrupt](AGENT_LIFECYCLE.md#healing-pending-tool-results-on-interrupt-experimental).
293
-
294
283
  ## Agent-Level Configuration
295
284
 
296
285
  Override global configuration at the agent level:
data/docs/EVALS.md CHANGED
@@ -194,11 +194,12 @@ Class methods:
194
194
  - `instructions(value)` - Evaluation criteria and scoring rubric (enables default `evaluate`)
195
195
  - `higher_is_better(value)` - Whether higher scores are better (default: true)
196
196
  - `judge_model(value)` - Override the global judge model
197
+ - `identifier(value)` - Override the identifier sent with judge calls (default: the snake_cased class name, or `riffer/judge` for an anonymous class)
197
198
 
198
199
  Instance methods:
199
200
 
200
201
  - `evaluate(input:, output:, ground_truth:, messages:)` - Override for custom logic; default calls judge with `instructions`
201
- - `judge` - Returns a Judge instance for LLM-as-judge calls
202
+ - `judge` - Returns a Judge instance for LLM-as-judge calls. Its calls carry the [default tags](AGENTS.md#default-tags) `kind: "judge"` and `agent: <identifier>`
202
203
  - `result(score:, reason:, metadata:, token_usage:)` - Helper to build Result objects
203
204
 
204
205
  ### Advanced: Custom Evaluate Override
data/docs/MCP.md CHANGED
@@ -89,10 +89,6 @@ MCP tools are appended after any tools declared with `uses_tools`.
89
89
 
90
90
  Tool names must be unique across `uses_tools` and all included MCP servers; duplicate names raise `Riffer::ArgumentError` when tools are resolved.
91
91
 
92
- ### Subclassing
93
-
94
- Like [`uses_tools`](AGENTS.md#uses_tools), **`use_mcp` is not inherited** from the superclass. Declare `use_mcp` on each agent class that should load MCP tools.
95
-
96
92
  ## Progressive Tool Discovery
97
93
 
98
94
  Progressive discovery is the default. The `use_mcp` instruction exposes **`mcp_search`** instead of flooding the context with every tool schema up front.