riffer 0.27.2 → 0.29.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/architecture.md +18 -11
  3. data/.agents/code-style.md +1 -1
  4. data/.agents/rbs-inline.md +2 -2
  5. data/.agents/testing.md +9 -5
  6. data/.release-please-manifest.json +1 -1
  7. data/AGENTS.md +17 -10
  8. data/CHANGELOG.md +31 -0
  9. data/README.md +17 -18
  10. data/Steepfile +7 -1
  11. data/docs/03_AGENTS.md +34 -3
  12. data/docs/04_AGENT_LIFECYCLE.md +134 -86
  13. data/docs/05_AGENT_LOOP.md +2 -2
  14. data/docs/06_TOOLS.md +9 -4
  15. data/docs/07_TOOL_ADVANCED.md +23 -19
  16. data/docs/08_MESSAGES.md +28 -31
  17. data/docs/09_STREAM_EVENTS.md +1 -1
  18. data/docs/10_CONFIGURATION.md +25 -15
  19. data/docs/providers/01_PROVIDERS.md +6 -0
  20. data/docs/providers/06_MOCK_PROVIDER.md +2 -1
  21. data/docs/providers/07_CUSTOM_PROVIDERS.md +4 -4
  22. data/docs/providers/08_GEMINI.md +2 -2
  23. data/docs/providers/09_OPENROUTER.md +242 -0
  24. data/lib/riffer/agent/config.rb +173 -0
  25. data/lib/riffer/agent/context.rb +125 -0
  26. data/lib/riffer/agent/response.rb +11 -2
  27. data/lib/riffer/agent/run.rb +308 -0
  28. data/lib/riffer/agent/session/repair.rb +112 -0
  29. data/lib/riffer/agent/session.rb +268 -0
  30. data/lib/riffer/{structured_output → agent/structured_output}/result.rb +1 -1
  31. data/lib/riffer/{structured_output.rb → agent/structured_output.rb} +4 -4
  32. data/lib/riffer/agent.rb +246 -684
  33. data/lib/riffer/config.rb +56 -7
  34. data/lib/riffer/evals/evaluator.rb +13 -3
  35. data/lib/riffer/evals/judge.rb +2 -2
  36. data/lib/riffer/evals/run_result.rb +2 -1
  37. data/lib/riffer/evals/scenario_result.rb +2 -1
  38. data/lib/riffer/guardrails/runner.rb +3 -2
  39. data/lib/riffer/helpers/call_or_value.rb +16 -0
  40. data/lib/riffer/helpers.rb +0 -1
  41. data/lib/riffer/mcp/authenticated_tool.rb +4 -0
  42. data/lib/riffer/mcp/client.rb +1 -1
  43. data/lib/riffer/mcp/registration.rb +2 -3
  44. data/lib/riffer/mcp/registry.rb +3 -1
  45. data/lib/riffer/mcp/tool_factory.rb +5 -0
  46. data/lib/riffer/messages/assistant.rb +9 -3
  47. data/lib/riffer/messages/base.rb +22 -0
  48. data/lib/riffer/messages/converter.rb +6 -6
  49. data/lib/riffer/{file_part.rb → messages/file_part.rb} +5 -5
  50. data/lib/riffer/messages/tool.rb +1 -1
  51. data/lib/riffer/messages/user.rb +4 -4
  52. data/lib/riffer/{boolean.rb → params/boolean.rb} +3 -3
  53. data/lib/riffer/{param.rb → params/param.rb} +6 -6
  54. data/lib/riffer/params.rb +27 -21
  55. data/lib/riffer/providers/amazon_bedrock.rb +19 -20
  56. data/lib/riffer/providers/anthropic.rb +27 -28
  57. data/lib/riffer/providers/base.rb +10 -9
  58. data/lib/riffer/providers/gemini.rb +15 -12
  59. data/lib/riffer/providers/mock.rb +41 -13
  60. data/lib/riffer/providers/open_ai.rb +24 -22
  61. data/lib/riffer/providers/open_router.rb +318 -0
  62. data/lib/riffer/providers/repository.rb +1 -0
  63. data/lib/riffer/{token_usage.rb → providers/token_usage.rb} +4 -4
  64. data/lib/riffer/providers.rb +1 -0
  65. data/lib/riffer/runner/fibers.rb +4 -3
  66. data/lib/riffer/runner/sequential.rb +1 -1
  67. data/lib/riffer/runner/threaded.rb +1 -1
  68. data/lib/riffer/runner.rb +1 -1
  69. data/lib/riffer/skills/activate_tool.rb +4 -3
  70. data/lib/riffer/skills/config.rb +1 -1
  71. data/lib/riffer/skills/context.rb +3 -3
  72. data/lib/riffer/skills/filesystem_backend.rb +7 -5
  73. data/lib/riffer/skills/markdown_adapter.rb +1 -1
  74. data/lib/riffer/skills/xml_adapter.rb +1 -1
  75. data/lib/riffer/stream_events/interrupt.rb +10 -3
  76. data/lib/riffer/stream_events/token_usage_done.rb +2 -2
  77. data/lib/riffer/stream_events/web_search_status.rb +1 -1
  78. data/lib/riffer/tool.rb +3 -3
  79. data/lib/riffer/{tool_runtime → tools/runtime}/fibers.rb +2 -2
  80. data/lib/riffer/{tool_runtime → tools/runtime}/inline.rb +1 -1
  81. data/lib/riffer/{tool_runtime → tools/runtime}/threaded.rb +2 -2
  82. data/lib/riffer/{tool_runtime.rb → tools/runtime.rb} +21 -15
  83. data/lib/riffer/{toolable.rb → tools/toolable.rb} +12 -9
  84. data/lib/riffer/version.rb +1 -1
  85. data/lib/riffer.rb +2 -1
  86. data/sig/generated/riffer/agent/config.rbs +119 -0
  87. data/sig/generated/riffer/agent/context.rbs +91 -0
  88. data/sig/generated/riffer/agent/response.rbs +10 -2
  89. data/sig/generated/riffer/agent/run.rbs +144 -0
  90. data/sig/generated/riffer/agent/session/repair.rbs +51 -0
  91. data/sig/generated/riffer/agent/session.rbs +145 -0
  92. data/sig/generated/riffer/{structured_output → agent/structured_output}/result.rbs +2 -2
  93. data/sig/generated/riffer/{structured_output.rbs → agent/structured_output.rbs} +6 -6
  94. data/sig/generated/riffer/agent.rbs +154 -225
  95. data/sig/generated/riffer/config.rbs +50 -5
  96. data/sig/generated/riffer/evals/judge.rbs +2 -2
  97. data/sig/generated/riffer/helpers/call_or_value.rbs +9 -0
  98. data/sig/generated/riffer/helpers.rbs +0 -1
  99. data/sig/generated/riffer/messages/assistant.rbs +7 -3
  100. data/sig/generated/riffer/messages/base.rbs +18 -0
  101. data/sig/generated/riffer/messages/converter.rbs +4 -4
  102. data/sig/generated/riffer/{file_part.rbs → messages/file_part.rbs} +5 -5
  103. data/sig/generated/riffer/messages/user.rbs +4 -4
  104. data/sig/generated/riffer/params/boolean.rbs +10 -0
  105. data/sig/generated/riffer/{param.rbs → params/param.rbs} +3 -3
  106. data/sig/generated/riffer/params.rbs +15 -15
  107. data/sig/generated/riffer/providers/amazon_bedrock.rbs +22 -22
  108. data/sig/generated/riffer/providers/anthropic.rbs +4 -4
  109. data/sig/generated/riffer/providers/base.rbs +10 -10
  110. data/sig/generated/riffer/providers/gemini.rbs +4 -4
  111. data/sig/generated/riffer/providers/mock.rbs +25 -5
  112. data/sig/generated/riffer/providers/open_ai.rbs +4 -4
  113. data/sig/generated/riffer/providers/open_router.rbs +85 -0
  114. data/sig/generated/riffer/{token_usage.rbs → providers/token_usage.rbs} +5 -5
  115. data/sig/generated/riffer/providers.rbs +1 -0
  116. data/sig/generated/riffer/runner/fibers.rbs +2 -2
  117. data/sig/generated/riffer/runner/sequential.rbs +2 -2
  118. data/sig/generated/riffer/runner/threaded.rbs +2 -2
  119. data/sig/generated/riffer/runner.rbs +2 -2
  120. data/sig/generated/riffer/skills/activate_tool.rbs +4 -3
  121. data/sig/generated/riffer/skills/config.rbs +1 -1
  122. data/sig/generated/riffer/skills/context.rbs +2 -2
  123. data/sig/generated/riffer/stream_events/interrupt.rbs +7 -2
  124. data/sig/generated/riffer/stream_events/token_usage_done.rbs +3 -3
  125. data/sig/generated/riffer/tool.rbs +5 -5
  126. data/sig/generated/riffer/{tool_runtime → tools/runtime}/fibers.rbs +3 -3
  127. data/sig/generated/riffer/{tool_runtime → tools/runtime}/inline.rbs +2 -2
  128. data/sig/generated/riffer/{tool_runtime → tools/runtime}/threaded.rbs +3 -3
  129. data/sig/generated/riffer/{tool_runtime.rbs → tools/runtime.rbs} +19 -13
  130. data/sig/generated/riffer/{toolable.rbs → tools/toolable.rbs} +6 -6
  131. data/sig/stubs/agent_ivars.rbs +7 -0
  132. data/sig/stubs/async.rbs +24 -0
  133. data/sig/stubs/aws-sdk-core/seahorse_request_context.rbs +7 -0
  134. data/sig/stubs/aws-sdk-core/static_token_provider.rbs +5 -0
  135. data/sig/stubs/extend_self.rbs +11 -0
  136. data/sig/stubs/lib_ivars.rbs +101 -0
  137. data/sig/stubs/mcp_sdk.rbs +22 -0
  138. data/sig/stubs/provider_ivars.rbs +36 -0
  139. data/sig/stubs/provider_sdk_methods.rbs +50 -0
  140. data/sig/stubs/zeitwerk.rbs +12 -0
  141. metadata +54 -33
  142. data/lib/riffer/core.rb +0 -28
  143. data/lib/riffer/helpers/validations.rb +0 -18
  144. data/sig/generated/riffer/boolean.rbs +0 -10
  145. data/sig/generated/riffer/core.rbs +0 -19
  146. data/sig/generated/riffer/helpers/validations.rbs +0 -12
@@ -0,0 +1,144 @@
1
+ # Generated from lib/riffer/agent/run.rb with RBS::Inline
2
+
3
+ # Riffer::Agent::Run is the generation loop. A pure module of functions over an
4
+ # +agent+ — Agent owns every per-call value (provider, model, tools, tool
5
+ # runtime, structured output, session, context); Run just orchestrates.
6
+ #
7
+ # Tools and user code see the agent's +context+ (a +Riffer::Agent::Context+)
8
+ # unchanged through the loop, so downstream tool runtimes can read
9
+ # caller-provided keys via <tt>context[:agent]</tt> /
10
+ # <tt>context.dig(:key)</tt>, or the framework built-ins via
11
+ # +context.skills+. Cumulative token usage is updated into
12
+ # +agent.context.token_usage+ as the loop progresses.
13
+ #
14
+ # Riffer::Agent::Run.generate(agent: my_agent, prompt: "Hello")
15
+ # Riffer::Agent::Run.stream(agent: my_agent, prompt: "Hello")
16
+ module Riffer::Agent::Run
17
+ include Riffer::Messages::Converter
18
+
19
+ # Runs the generate loop for the given agent. See Riffer::Agent#generate
20
+ # for prompt/files semantics.
21
+ #
22
+ # --
23
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
24
+ def generate: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
25
+
26
+ # Runs the streaming loop for the given agent. See Riffer::Agent#stream
27
+ # for prompt/files semantics.
28
+ #
29
+ # --
30
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
31
+ def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
32
+
33
+ private
34
+
35
+ # The generation loop. When +stream_yielder+ is provided, per-step events are
36
+ # pushed to it (and +stream+ discards the return value). When +stream_yielder+
37
+ # is +nil+, no events are emitted and +generate+ returns the Response
38
+ # directly. The two modes share every step of the loop — the only
39
+ # divergences are the LLM call shape (atomic vs. accumulated stream)
40
+ # and whether per-step events are emitted.
41
+ #
42
+ # --
43
+ # : (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
44
+ def run_loop: (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
45
+
46
+ # Consumes one provider stream, forwarding every event to +stream_yielder+
47
+ # and folding it into an +Assistant+ message.
48
+ #
49
+ # --
50
+ # : (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
51
+ def accumulate_streamed_response: (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
52
+
53
+ # Appends +new_modifications+ to +all_modifications+ and emits a
54
+ # +GuardrailModification+ event for each one when streaming.
55
+ #
56
+ # --
57
+ # : (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
58
+ def record_modifications!: (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
59
+
60
+ # Emits a +GuardrailTripwire+ event when streaming and returns the
61
+ # short-circuit +Response+ for a tripped guardrail.
62
+ #
63
+ # --
64
+ # : (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
65
+ def tripwire_response: (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
66
+
67
+ # Builds the final +Response+ from the session's last assistant
68
+ # message, validating structured output when configured. +extra+
69
+ # carries the interrupt-only fields (+interrupted:+, +interrupt_reason:+,
70
+ # +healed_tool_call_ids:+) on the interrupt exit path.
71
+ #
72
+ # --
73
+ # : (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
74
+ def final_response: (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
75
+
76
+ # --
77
+ # : (Riffer::Agent) -> Riffer::Messages::Assistant
78
+ def call_llm: (Riffer::Agent) -> Riffer::Messages::Assistant
79
+
80
+ # --
81
+ # : (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
82
+ def call_llm_stream: (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
83
+
84
+ # --
85
+ # : (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
86
+ def execute_tool_calls: (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
87
+
88
+ # Executes tool calls left unfinished by a prior interrupt.
89
+ #
90
+ # Detects gaps between the last assistant message's requested tool calls
91
+ # and the tool result messages that follow it, executing any that are
92
+ # missing. Safe to call unconditionally.
93
+ #
94
+ # --
95
+ # : (Riffer::Agent) -> void
96
+ def execute_pending_tool_calls: (Riffer::Agent) -> void
97
+
98
+ # Runs the +:before+ guardrail phase. Records any modifications into
99
+ # +all_modifications+ (and emits them when streaming). When a tripwire
100
+ # fires, yields the short-circuit +Response+ — the caller's block is
101
+ # expected to +return+ it from +run_loop+.
102
+ #
103
+ # --
104
+ # : (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
105
+ def run_before_guardrails: (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
106
+
107
+ # Runs the +:after+ guardrail phase against the assistant +response+.
108
+ # Records any modifications into +all_modifications+ (and emits them
109
+ # when streaming). When a tripwire fires, yields the short-circuit
110
+ # +Response+ — the caller's block is expected to +return+ it from
111
+ # +run_loop+. Otherwise returns the post-guardrails assistant message.
112
+ #
113
+ # --
114
+ # : (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
115
+ def run_after_guardrails: (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
116
+
117
+ # --
118
+ # : (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
119
+ def validate_structured_output: (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
120
+
121
+ # --
122
+ # : (Riffer::Agent) -> Hash[Symbol, untyped]
123
+ def merged_model_options: (Riffer::Agent) -> Hash[Symbol, untyped]
124
+
125
+ # --
126
+ # : (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
127
+ def build_response: (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
128
+
129
+ # Appends a +User+ message to the session. No-ops when +prompt+ is nil
130
+ # and +files+ is empty (the caller had nothing to add). Raises when
131
+ # +files+ are supplied without a +prompt+ — the provider needs text to
132
+ # anchor the attachments.
133
+ #
134
+ # --
135
+ # : (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
136
+ def append_user_message: (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
137
+
138
+ # Accumulates token usage into +agent.context.token_usage+. Updates the
139
+ # context so cumulative usage persists across every run on the agent.
140
+ #
141
+ # --
142
+ # : (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
143
+ def track_token_usage: (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
144
+ end
@@ -0,0 +1,51 @@
1
+ # Generated from lib/riffer/agent/session/repair.rb with RBS::Inline
2
+
3
+ # Riffer::Agent::Session::Repair holds the pure transformations that keep the
4
+ # +tool_use+ ↔ +tool_result+ invariant on a message array. No state, no
5
+ # instance — module-level functions only. Each entry point is gated by
6
+ # +Riffer.config.experimental_history_healing+: when the flag is off the
7
+ # function returns its input unchanged.
8
+ #
9
+ # Two seams:
10
+ #
11
+ # - +fill_orphans+ — fills orphan +tool_use+ blocks with placeholder
12
+ # results. Used on interrupt (caller-issued or +max_steps+).
13
+ # - +prune_orphans+ — drops orphan +tool_use+ blocks and parentless Tool
14
+ # messages from a caller-provided seed so it is well-formed before the
15
+ # next inference call. Used at construction time when
16
+ # +Riffer::Agent.new(session:)+ receives a session.
17
+ module Riffer::Agent::Session::Repair
18
+ # Placeholder used to fill orphan +tool_use+ blocks. Emitted as the
19
+ # +Riffer::Tools::Response+ body for each filled call_id.
20
+ ORPHAN_PLACEHOLDER: ^(Riffer::Messages::Assistant::ToolCall) -> Riffer::Tools::Response
21
+
22
+ # Fills any orphaned +tool_use+ in +messages+ with the
23
+ # +ORPHAN_PLACEHOLDER+ response. Each placeholder Tool message is
24
+ # inserted immediately after its parent assistant message. Returns
25
+ # +[new_messages, filled_call_ids]+; +filled_call_ids+ is empty when
26
+ # there are no orphans.
27
+ #
28
+ # No-op when +Riffer.config.experimental_history_healing+ is off:
29
+ # returns +[messages, []]+ with the same array reference.
30
+ #
31
+ # --
32
+ # : (Array[Riffer::Messages::Base]) -> [Array[Riffer::Messages::Base], Array[String]]
33
+ def self.fill_orphans: (Array[Riffer::Messages::Base]) -> [ Array[Riffer::Messages::Base], Array[String] ]
34
+
35
+ # Prunes a seeded message array so the +tool_use+ ↔ +tool_result+
36
+ # invariant holds. Drops orphaned tool exchanges (assistant +tool_call+
37
+ # with no matching Tool result) and parentless Tool messages. Returns a
38
+ # new array; the input is not mutated.
39
+ #
40
+ # Pending tool_calls on the resume boundary — the last assistant whose
41
+ # tail is purely Tool results (or empty) — are preserved. They get
42
+ # swept up by +execute_pending_tool_calls+ at the start of the next
43
+ # generate/stream call.
44
+ #
45
+ # No-op when +Riffer.config.experimental_history_healing+ is off:
46
+ # returns +messages+ unchanged.
47
+ #
48
+ # --
49
+ # : (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
50
+ def self.prune_orphans: (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
51
+ end
@@ -0,0 +1,145 @@
1
+ # Generated from lib/riffer/agent/session.rb with RBS::Inline
2
+
3
+ # Riffer::Agent::Session owns the conversation handle for an agent: the message
4
+ # array, the +on_message+ callback list, and the +tool_use+ ↔ +tool_result+
5
+ # invariant that keeps tool calls and their results consistent.
6
+ #
7
+ # Access via +agent.session+. Sessions are constructed by +Riffer::Agent+
8
+ # and live for the lifetime of the agent.
9
+ #
10
+ # agent.session.add(msg) # append + fire callbacks
11
+ # agent.session.set([msg1, msg2]) # bulk replace (silent)
12
+ # agent.session.unset # clear (silent)
13
+ # agent.session.remove(id: "a_1")
14
+ # agent.session.update(id: "a_1", content: "...")
15
+ # agent.session.find { |m| m.id == "a_1" }
16
+ class Riffer::Agent::Session
17
+ include Enumerable[Riffer::Messages::Base]
18
+
19
+ # The message history.
20
+ attr_reader messages: Array[Riffer::Messages::Base]
21
+
22
+ # --
23
+ # : (?messages: Array[Riffer::Messages::Base]) -> void
24
+ def initialize: (?messages: Array[Riffer::Messages::Base]) -> void
25
+
26
+ # Registers a callback invoked once per message appended via +#add+.
27
+ #
28
+ # Callbacks do NOT fire for +#set+, +#unset+, +#remove+, or +#update+.
29
+ # Returns +self+ to allow chaining.
30
+ #
31
+ # Raises Riffer::ArgumentError if no block is given.
32
+ #
33
+ # --
34
+ # : () { (Riffer::Messages::Base) -> void } -> self
35
+ def on_message: () { (Riffer::Messages::Base) -> void } -> self
36
+
37
+ # Appends +message+ and fires every registered callback once with it.
38
+ #
39
+ # Pass +silent: true+ to skip +on_message+ callbacks — used for
40
+ # non-inference inputs like user messages, which subscribers don't
41
+ # expect to observe through the callback channel. Inference-produced
42
+ # messages (Assistant, Tool) always go through +add+ without +silent+.
43
+ #
44
+ # --
45
+ # : (Riffer::Messages::Base, ?silent: bool) -> Riffer::Messages::Base
46
+ def add: (Riffer::Messages::Base, ?silent: bool) -> Riffer::Messages::Base
47
+
48
+ # Replaces the message history wholesale. Does NOT fire +on_message+
49
+ # callbacks; registered callbacks persist across the swap.
50
+ #
51
+ # Used for seeding, guardrail rewrites, and history healing — cases
52
+ # where firing callbacks would double-emit messages that subscribers
53
+ # have already observed (or never produced).
54
+ #
55
+ # --
56
+ # : (Array[Riffer::Messages::Base]) -> self
57
+ def set: (Array[Riffer::Messages::Base]) -> self
58
+
59
+ # Clears the session. Does NOT fire +on_message+ callbacks; registered
60
+ # callbacks persist.
61
+ #
62
+ # --
63
+ # : () -> self
64
+ def unset: () -> self
65
+
66
+ # Removes a message by id. When the target is an assistant message that
67
+ # carries +tool_calls+, every +Riffer::Messages::Tool+ result whose
68
+ # +tool_call_id+ matches one of those calls is removed atomically — keeping
69
+ # the +tool_use+ ↔ +tool_result+ invariant intact.
70
+ #
71
+ # Raises Riffer::ArgumentError when called on a +Riffer::Messages::Tool+
72
+ # message — that would orphan the parent's +tool_use+. Use
73
+ # +#update+ to rewrite a tool result instead.
74
+ #
75
+ # Returns the removed message, or +nil+ when no message has the given id
76
+ # (idempotent).
77
+ #
78
+ # --
79
+ # : (id: String) -> Riffer::Messages::Base?
80
+ def remove: (id: String) -> Riffer::Messages::Base?
81
+
82
+ # Partial in-place update. Looks up a message by either +id:+ or
83
+ # +tool_call_id:+ (exactly one required), constructs a replacement of the
84
+ # same concrete type with +attrs+ overlaid on the existing fields, and
85
+ # swaps it in place.
86
+ #
87
+ # When the target is an assistant message and the update drops one or more
88
+ # entries from +tool_calls+, every +Riffer::Messages::Tool+ result whose
89
+ # +tool_call_id+ matches a dropped call is removed atomically — keeping the
90
+ # +tool_use+ ↔ +tool_result+ invariant intact.
91
+ #
92
+ # Raises Riffer::ArgumentError when neither or both lookup keys are
93
+ # provided, or when no message matches.
94
+ #
95
+ # --
96
+ # : (?id: String?, ?tool_call_id: String?, **untyped) -> Riffer::Messages::Base
97
+ def update: (?id: String?, ?tool_call_id: String?, **untyped) -> Riffer::Messages::Base
98
+
99
+ # Returns the call_ids of every +tool_call+ on any assistant message that
100
+ # has no matching +Riffer::Messages::Tool+ result anywhere in history.
101
+ #
102
+ # Zero-cost validation hook for callers that want to check the
103
+ # +tool_use+ ↔ +tool_result+ invariant before mutating or persisting.
104
+ #
105
+ # --
106
+ # : () -> Array[String]
107
+ def orphaned_tool_call_ids: () -> Array[String]
108
+
109
+ # Returns +[assistant, pending_tool_calls]+ for the last assistant message.
110
+ # When there is no assistant message or no pending calls, the second
111
+ # element is an empty array.
112
+ #
113
+ # --
114
+ # : () -> [Riffer::Messages::Assistant?, Array[Riffer::Messages::Assistant::ToolCall]]
115
+ def pending_tool_calls: () -> [ Riffer::Messages::Assistant?, Array[Riffer::Messages::Assistant::ToolCall] ]
116
+
117
+ # --
118
+ # : () -> Enumerator[Riffer::Messages::Base, self]
119
+ # : () { (Riffer::Messages::Base) -> void } -> untyped
120
+ def each: () -> Enumerator[Riffer::Messages::Base, self]
121
+ | () { (Riffer::Messages::Base) -> void } -> untyped
122
+
123
+ # The number of LLM steps completed in this session, derived from the
124
+ # count of assistant messages. Used by the agent loop to enforce
125
+ # +max_steps+ on resume.
126
+ #
127
+ # --
128
+ # : () -> Integer
129
+ def steps: () -> Integer
130
+
131
+ # The most recent +Riffer::Messages::Assistant+ in the session, or +nil+
132
+ # when none exists.
133
+ #
134
+ # --
135
+ # : () -> Riffer::Messages::Assistant?
136
+ def final_assistant_message: () -> Riffer::Messages::Assistant?
137
+
138
+ private
139
+
140
+ # : (Riffer::Messages::Base, Riffer::Messages::Base) -> void
141
+ def cascade_dropped_tool_calls: (Riffer::Messages::Base, Riffer::Messages::Base) -> void
142
+
143
+ # : (Riffer::Messages::Base, Hash[Symbol, untyped]) -> Riffer::Messages::Base
144
+ def rebuild_message: (Riffer::Messages::Base, Hash[Symbol, untyped]) -> Riffer::Messages::Base
145
+ end
@@ -1,4 +1,4 @@
1
- # Generated from lib/riffer/structured_output/result.rb with RBS::Inline
1
+ # Generated from lib/riffer/agent/structured_output/result.rb with RBS::Inline
2
2
 
3
3
  # Wraps the result of structured output parsing and validation.
4
4
  #
@@ -11,7 +11,7 @@
11
11
  # else
12
12
  # result.error #=> "JSON parse error: ..."
13
13
  # end
14
- class Riffer::StructuredOutput::Result
14
+ class Riffer::Agent::StructuredOutput::Result
15
15
  attr_reader object: Hash[Symbol, untyped]?
16
16
 
17
17
  attr_reader error: String?
@@ -1,14 +1,14 @@
1
- # Generated from lib/riffer/structured_output.rb with RBS::Inline
1
+ # Generated from lib/riffer/agent/structured_output.rb with RBS::Inline
2
2
 
3
- # Riffer::StructuredOutput provides parse/validate for structured JSON
3
+ # Riffer::Agent::StructuredOutput provides parse/validate for structured JSON
4
4
  # responses from LLM providers.
5
5
  #
6
6
  # params = Riffer::Params.new
7
7
  # params.required(:sentiment, String)
8
- # so = Riffer::StructuredOutput.new(params)
8
+ # so = Riffer::Agent::StructuredOutput.new(params)
9
9
  # result = so.parse_and_validate('{"sentiment":"positive","score":0.9}')
10
10
  # result.object #=> {sentiment: "positive", score: 0.9}
11
- class Riffer::StructuredOutput
11
+ class Riffer::Agent::StructuredOutput
12
12
  attr_reader params: Riffer::Params
13
13
 
14
14
  # --
@@ -26,6 +26,6 @@ class Riffer::StructuredOutput
26
26
  # Returns a Result with the validated object on success, or an error message on failure.
27
27
  #
28
28
  # --
29
- # : (String) -> Riffer::StructuredOutput::Result
30
- def parse_and_validate: (String) -> Riffer::StructuredOutput::Result
29
+ # : (String) -> Riffer::Agent::StructuredOutput::Result
30
+ def parse_and_validate: (String) -> Riffer::Agent::StructuredOutput::Result
31
31
  end