riffer 0.41.0 → 0.43.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/{AGENTS.md → .claude/CLAUDE.md} +0 -8
  3. data/.claude/rules/comments.md +13 -0
  4. data/{.agents → .claude/rules}/rbs-inline.md +28 -104
  5. data/.release-please-manifest.json +1 -1
  6. data/CHANGELOG.md +22 -0
  7. data/docs/AGENTS.md +24 -3
  8. data/docs/AGENT_LIFECYCLE.md +2 -2
  9. data/docs/AGENT_LOOP.md +6 -4
  10. data/docs/TOOLS.md +22 -2
  11. data/docs/TOOL_ADVANCED.md +20 -7
  12. data/docs/TRACING.md +5 -4
  13. data/lib/riffer/agent/run.rb +2 -4
  14. data/lib/riffer/agent.rb +3 -17
  15. data/lib/riffer/guardrail.rb +1 -1
  16. data/lib/riffer/helpers/identifier.rb +41 -0
  17. data/lib/riffer/messages/tool.rb +2 -1
  18. data/lib/riffer/providers/anthropic.rb +4 -0
  19. data/lib/riffer/providers/base.rb +4 -1
  20. data/lib/riffer/registrable.rb +81 -0
  21. data/lib/riffer/tool.rb +25 -9
  22. data/lib/riffer/tools/response.rb +10 -5
  23. data/lib/riffer/tools/runtime.rb +20 -11
  24. data/lib/riffer/tools/toolable.rb +2 -3
  25. data/lib/riffer/version.rb +1 -1
  26. data/lib/riffer.rb +6 -1
  27. data/sig/generated/riffer/agent.rbs +2 -12
  28. data/sig/generated/riffer/helpers/identifier.rbs +19 -0
  29. data/sig/generated/riffer/messages/tool.rbs +2 -1
  30. data/sig/generated/riffer/providers/base.rbs +2 -0
  31. data/sig/generated/riffer/registrable.rbs +51 -0
  32. data/sig/generated/riffer/tool.rbs +6 -5
  33. data/sig/generated/riffer/tools/response.rbs +8 -4
  34. data/sig/generated/riffer/tools/runtime.rbs +5 -4
  35. data/sig/generated/riffer/tools/toolable.rbs +3 -1
  36. data/sig/generated/riffer.rbs +7 -1
  37. data/sig/manual/riffer/agent.rbs +7 -0
  38. data/sig/manual/riffer/helpers/identifier.rbs +5 -0
  39. data/sig/manual/riffer/tool.rbs +7 -0
  40. metadata +11 -11
  41. data/.agents/architecture.md +0 -265
  42. data/.agents/code-style.md +0 -110
  43. data/.agents/providers.md +0 -54
  44. data/.agents/testing.md +0 -60
  45. data/CLAUDE.md +0 -1
  46. data/lib/riffer/helpers/class_name_converter.rb +0 -22
  47. data/sig/generated/riffer/helpers/class_name_converter.rbs +0 -12
  48. data/sig/manual/riffer/helpers/class_name_converter.rbs +0 -5
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ # Registry of a class's named direct subclasses, keyed by identifier. Extend it
5
+ # onto a base class to look up subclasses in constant time via +find+ and +all+.
6
+ #
7
+ # class Riffer::Tool
8
+ # extend Riffer::Registrable
9
+ # end
10
+ #
11
+ # Riffer::Tool.find("weather_tool") # => WeatherTool
12
+ #
13
+ # @rbs module-self Class
14
+ module Riffer::Registrable
15
+ # @rbs @identifier_registry: Hash[String, Class]?
16
+
17
+ # Finds a registered subclass by identifier, or +nil+ when none matches.
18
+ # Only *named direct* subclasses are registered: grandchildren are not
19
+ # visible to a grandparent's +find+ (call +find+ on their direct parent
20
+ # instead), anonymous classes are never registered, and duplicate identifiers
21
+ # raise Riffer::DuplicateIdentifierError at first lookup.
22
+ #
23
+ #--
24
+ #: (String | Symbol) -> Class?
25
+ def find(identifier)
26
+ identifier_registry[identifier.to_s]
27
+ end
28
+
29
+ # Returns all registered subclasses. Only *named direct* subclasses are
30
+ # registered: grandchildren are not included (call +all+ on their direct
31
+ # parent instead), anonymous classes are never registered, and duplicate
32
+ # identifiers raise Riffer::DuplicateIdentifierError at first lookup.
33
+ #
34
+ #--
35
+ #: () -> Array[Class]
36
+ def all
37
+ identifier_registry.values
38
+ end
39
+
40
+ private
41
+
42
+ # Ruby invokes +inherited+ with +self+ bound to the direct superclass — the
43
+ # only registry the new subclass joins — so busting self's memo is exactly
44
+ # sufficient.
45
+ #--
46
+ #: (Class) -> void
47
+ def inherited(subclass)
48
+ super
49
+ @identifier_registry = nil
50
+ end
51
+
52
+ #--
53
+ #: () -> Hash[String, Class]
54
+ def identifier_registry
55
+ @identifier_registry ||= build_identifier_registry
56
+ end
57
+
58
+ #--
59
+ #: () -> Hash[String, Class]
60
+ def build_identifier_registry
61
+ registry = {} #: Hash[String, Class]
62
+ subclasses.each_with_object(registry) do |subclass, acc|
63
+ # Anonymous classes are skipped even with an explicit identifier — the
64
+ # MCP factory and serializer shells synthesize short-lived anonymous
65
+ # classes whose registration would flake with GC timing.
66
+ next if Riffer::Helpers::Identifier.for(subclass).empty?
67
+
68
+ candidate = subclass #: untyped
69
+ key = candidate.identifier.to_s
70
+ next if key.strip.empty?
71
+
72
+ existing = acc[key]
73
+ if existing
74
+ raise Riffer::DuplicateIdentifierError,
75
+ "Duplicate identifier #{key.inspect} for #{existing} and #{subclass}"
76
+ end
77
+
78
+ acc[key] = subclass
79
+ end.freeze
80
+ end
81
+ end
data/lib/riffer/tool.rb CHANGED
@@ -21,6 +21,7 @@ require "timeout"
21
21
  #
22
22
  class Riffer::Tool
23
23
  extend Riffer::Tools::Toolable
24
+ extend Riffer::Registrable
24
25
 
25
26
  kind :tool
26
27
 
@@ -55,19 +56,23 @@ class Riffer::Tool
55
56
  Riffer::Tools::Response.error(message, type: type)
56
57
  end
57
58
 
58
- # Executes the tool with validation and timeout (used by Agent).
59
- #
60
- # Raises Riffer::ValidationError if validation fails.
61
- # Raises Riffer::TimeoutError if execution exceeds the configured timeout.
62
- # Raises Riffer::Error if the tool does not return a Response object.
59
+ # Executes the tool with validation and timeout, folding every +StandardError+
60
+ # into an error Response. Anything outside +StandardError+ — an unimplemented
61
+ # +#call+ above all — still propagates, because a broken tool is a broken
62
+ # deploy rather than a bad request.
63
63
  #
64
64
  #--
65
65
  #: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
66
66
  def call_with_validation(context:, **kwargs)
67
67
  params_builder = self.class.params
68
- validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
69
68
 
70
- result = Timeout.timeout(self.class.timeout) do
69
+ begin
70
+ validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
71
+ rescue Riffer::ValidationError => e
72
+ return Riffer::Tools::Response.error(e.message, type: :validation_error)
73
+ end
74
+
75
+ result = Timeout.timeout(self.class.timeout, Riffer::TimeoutError) do
71
76
  call(context: context, **validated_args) #: untyped
72
77
  end
73
78
 
@@ -76,7 +81,18 @@ class Riffer::Tool
76
81
  end
77
82
 
78
83
  result
79
- rescue Timeout::Error
80
- raise Riffer::TimeoutError, "Tool execution timed out after #{self.class.timeout} seconds"
84
+ rescue Riffer::TimeoutError
85
+ Riffer::Tools::Response.error(
86
+ "Tool execution timed out after #{self.class.timeout} seconds",
87
+ type: :timeout_error,
88
+ )
89
+ rescue Riffer::ToolExecutionError => e
90
+ Riffer::Tools::Response.error(e.message, type: :execution_error)
91
+ rescue StandardError => e
92
+ Riffer::Tools::Response.error(
93
+ "Error executing tool: #{e.class}: #{e.message}",
94
+ type: :unhandled_error,
95
+ exception: e,
96
+ )
81
97
  end
82
98
  end
@@ -28,6 +28,10 @@ class Riffer::Tools::Response
28
28
  # The error type, or +nil+ on success.
29
29
  attr_reader :error_type #: Symbol?
30
30
 
31
+ # The exception an unhandled failure was folded from, or +nil+. Kept out of
32
+ # every serialized form so it never reaches an LLM or a message payload.
33
+ attr_reader :exception #: Exception?
34
+
31
35
  # Creates a success response.
32
36
  #
33
37
  # Raises Riffer::ArgumentError if format is invalid.
@@ -62,9 +66,9 @@ class Riffer::Tools::Response
62
66
  # Creates an error response.
63
67
  #
64
68
  #--
65
- #: (String, ?type: Symbol) -> Riffer::Tools::Response
66
- def self.error(message, type: :execution_error)
67
- new(content: message, success: false, error_message: message, error_type: type)
69
+ #: (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
70
+ def self.error(message, type: :execution_error, exception: nil)
71
+ new(content: message, success: false, error_message: message, error_type: type, exception: exception)
68
72
  end
69
73
 
70
74
  # Returns true if the tool execution succeeded.
@@ -88,11 +92,12 @@ class Riffer::Tools::Response
88
92
  private
89
93
 
90
94
  #--
91
- #: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
92
- def initialize(content:, success:, error_message: nil, error_type: nil)
95
+ #: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
96
+ def initialize(content:, success:, error_message: nil, error_type: nil, exception: nil)
93
97
  @content = content
94
98
  @success = success
95
99
  @error_message = error_message
96
100
  @error_type = error_type
101
+ @exception = exception
97
102
  end
98
103
  end
@@ -88,19 +88,20 @@ class Riffer::Tools::Runtime
88
88
  tool_instance = tool_class.new
89
89
  arguments = parse_arguments(tool_call.arguments)
90
90
 
91
+ unless arguments.is_a?(Hash)
92
+ return Riffer::Tools::Response.error(
93
+ "Invalid JSON in tool arguments: expected an object, got #{arguments.class}",
94
+ type: :validation_error,
95
+ )
96
+ end
97
+
91
98
  tool_instance.call_with_validation(context: context, **arguments)
92
- rescue Riffer::TimeoutError => e
93
- Riffer::Tools::Response.error(e.message, type: :timeout_error)
94
- rescue Riffer::ValidationError => e
95
- Riffer::Tools::Response.error(e.message, type: :validation_error)
96
- rescue Riffer::ToolExecutionError => e
97
- Riffer::Tools::Response.error(e.message, type: :execution_error)
98
- rescue RuntimeError => e
99
- Riffer::Tools::Response.error("Error executing tool: #{e.message}", type: :execution_error)
99
+ rescue JSON::ParserError => e
100
+ Riffer::Tools::Response.error("Invalid JSON in tool arguments: #{e.message}", type: :validation_error)
100
101
  end
101
102
 
102
103
  #--
103
- #: (String?) -> Hash[Symbol, untyped]
104
+ #: (String?) -> untyped
104
105
  def parse_arguments(arguments)
105
106
  return {} if arguments.nil? || arguments.empty?
106
107
 
@@ -144,13 +145,21 @@ class Riffer::Tools::Runtime
144
145
  tags.transform_keys { |key| "riffer.tag.#{key}" }
145
146
  end
146
147
 
147
- # A returned error Response is a handled outcome, so its status stays unset —
148
- # an error span status is reserved for a raised exception.
148
+ # A deliberate error Response is a handled outcome, so its status stays unset.
149
+ # An error status is reserved for a Response carrying the exception it was
150
+ # folded from — the tool failed for a reason nobody anticipated.
149
151
  #--
150
152
  #: ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tools::Response) -> void
151
153
  def record_tool_outcome(span, result)
152
154
  error_type = result.error_type
153
155
  span.set_attribute("error.type", error_type.to_s) if error_type
156
+
157
+ exception = result.exception
158
+ if exception
159
+ span.record_exception(exception)
160
+ span.error!(exception.message)
161
+ end
162
+
154
163
  capture_tool_result(span, result)
155
164
  end
156
165
 
@@ -15,6 +15,7 @@
15
15
  # end
16
16
  # end
17
17
  #
18
+ # @rbs module-self Module
18
19
  module Riffer::Tools::Toolable
19
20
  # @rbs self.@extenders: Array[Module]?
20
21
  # @rbs @description: String?
@@ -57,9 +58,7 @@ module Riffer::Tools::Toolable
57
58
  #--
58
59
  #: (?String?) -> String
59
60
  def identifier(value = nil)
60
- if value.nil?
61
- return @identifier || Riffer::Helpers::ClassNameConverter.convert(Module.instance_method(:name).bind_call(self))
62
- end
61
+ return @identifier || Riffer::Helpers::Identifier.for(self) if value.nil?
63
62
 
64
63
  @identifier = value.to_s
65
64
  end
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.41.0" #: String
5
+ VERSION = "0.43.0" #: String
6
6
  end
data/lib/riffer.rb CHANGED
@@ -26,12 +26,17 @@ module Riffer
26
26
  # Raised when tool parameter validation fails.
27
27
  class ValidationError < Error; end
28
28
 
29
- # Raised when tool execution times out.
29
+ # Raised inside a tool's +call+ when execution exceeds the configured
30
+ # timeout. Rescue it in the tool to clean up; otherwise it becomes a
31
+ # +:timeout_error+ response.
30
32
  class TimeoutError < Error; end
31
33
 
32
34
  # Raised when a tool encounters an expected execution error.
33
35
  class ToolExecutionError < Error; end
34
36
 
37
+ # Raised when two registered subclasses share the same identifier.
38
+ class DuplicateIdentifierError < Error; end
39
+
35
40
  # Returns the Riffer configuration.
36
41
  #
37
42
  #--
@@ -11,6 +11,8 @@
11
11
  # agent = MyAgent.new
12
12
  # agent.generate('Hello!')
13
13
  class Riffer::Agent
14
+ extend Riffer::Registrable
15
+
14
16
  self.@config: Riffer::Agent::Config?
15
17
 
16
18
  INTERRUPT_MAX_STEPS: Symbol
@@ -104,18 +106,6 @@ class Riffer::Agent
104
106
  # : () ?{ (Riffer::Skills::Config) [self: Riffer::Skills::Config] -> void } -> Riffer::Skills::Config?
105
107
  def self.skills: () ?{ (Riffer::Skills::Config) [self: Riffer::Skills::Config] -> void } -> Riffer::Skills::Config?
106
108
 
107
- # Finds an agent class by identifier.
108
- #
109
- # --
110
- # : (String) -> singleton(Riffer::Agent)?
111
- def self.find: (String) -> singleton(Riffer::Agent)?
112
-
113
- # Returns all agent subclasses.
114
- #
115
- # --
116
- # : () -> Array[singleton(Riffer::Agent)]
117
- def self.all: () -> Array[singleton(Riffer::Agent)]
118
-
119
109
  # Generates a response using a new agent instance.
120
110
  # --
121
111
  # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
@@ -0,0 +1,19 @@
1
+ # Generated from lib/riffer/helpers/identifier.rb with RBS::Inline
2
+
3
+ # Helper module for deriving snake_case identifiers from class names.
4
+ module Riffer::Helpers::Identifier
5
+ # Derives a snake_case identifier from a class name string.
6
+ #
7
+ # --
8
+ # : (String?) -> String
9
+ def derive: (String?) -> String
10
+
11
+ # Derives and memoizes the identifier for a class or module. Anonymous
12
+ # classes return "" without caching, so a class named later still derives its
13
+ # real identifier — a guard that must travel with the cache, so callers never
14
+ # memoize their own.
15
+ #
16
+ # --
17
+ # : (Module) -> String
18
+ def for: (Module) -> String
19
+ end
@@ -11,7 +11,8 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
11
11
  # The error message if the tool execution failed.
12
12
  attr_reader error: String?
13
13
 
14
- # The type of error (:unknown_tool, :validation_error, :execution_error, :timeout_error).
14
+ # The type of error (:unknown_tool, :validation_error, :execution_error,
15
+ # :timeout_error, :unhandled_error).
15
16
  attr_reader error_type: Symbol?
16
17
 
17
18
  # --
@@ -5,6 +5,8 @@
5
5
  # +extract_token_usage+, +extract_content+, +extract_tool_calls+) and the base
6
6
  # class orchestrates them.
7
7
  class Riffer::Providers::Base
8
+ self.@semconv_provider_name: String?
9
+
8
10
  @client: untyped
9
11
 
10
12
  @current_model: String?
@@ -0,0 +1,51 @@
1
+ # Generated from lib/riffer/registrable.rb with RBS::Inline
2
+
3
+ # Registry of a class's named direct subclasses, keyed by identifier. Extend it
4
+ # onto a base class to look up subclasses in constant time via +find+ and +all+.
5
+ #
6
+ # class Riffer::Tool
7
+ # extend Riffer::Registrable
8
+ # end
9
+ #
10
+ # Riffer::Tool.find("weather_tool") # => WeatherTool
11
+ #
12
+ # @rbs module-self Class
13
+ module Riffer::Registrable : Class
14
+ @identifier_registry: Hash[String, Class]?
15
+
16
+ # Finds a registered subclass by identifier, or +nil+ when none matches.
17
+ # Only *named direct* subclasses are registered: grandchildren are not
18
+ # visible to a grandparent's +find+ (call +find+ on their direct parent
19
+ # instead), anonymous classes are never registered, and duplicate identifiers
20
+ # raise Riffer::DuplicateIdentifierError at first lookup.
21
+ #
22
+ # --
23
+ # : (String | Symbol) -> Class?
24
+ def find: (String | Symbol) -> Class?
25
+
26
+ # Returns all registered subclasses. Only *named direct* subclasses are
27
+ # registered: grandchildren are not included (call +all+ on their direct
28
+ # parent instead), anonymous classes are never registered, and duplicate
29
+ # identifiers raise Riffer::DuplicateIdentifierError at first lookup.
30
+ #
31
+ # --
32
+ # : () -> Array[Class]
33
+ def all: () -> Array[Class]
34
+
35
+ private
36
+
37
+ # Ruby invokes +inherited+ with +self+ bound to the direct superclass — the
38
+ # only registry the new subclass joins — so busting self's memo is exactly
39
+ # sufficient.
40
+ # --
41
+ # : (Class) -> void
42
+ def inherited: (Class) -> void
43
+
44
+ # --
45
+ # : () -> Hash[String, Class]
46
+ def identifier_registry: () -> Hash[String, Class]
47
+
48
+ # --
49
+ # : () -> Hash[String, Class]
50
+ def build_identifier_registry: () -> Hash[String, Class]
51
+ end
@@ -18,6 +18,8 @@
18
18
  class Riffer::Tool
19
19
  extend Riffer::Tools::Toolable
20
20
 
21
+ extend Riffer::Registrable
22
+
21
23
  # Executes the tool with the given arguments.
22
24
  # --
23
25
  # : (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
@@ -41,11 +43,10 @@ class Riffer::Tool
41
43
  # : (String, ?type: Symbol) -> Riffer::Tools::Response
42
44
  def error: (String, ?type: Symbol) -> Riffer::Tools::Response
43
45
 
44
- # Executes the tool with validation and timeout (used by Agent).
45
- #
46
- # Raises Riffer::ValidationError if validation fails.
47
- # Raises Riffer::TimeoutError if execution exceeds the configured timeout.
48
- # Raises Riffer::Error if the tool does not return a Response object.
46
+ # Executes the tool with validation and timeout, folding every +StandardError+
47
+ # into an error Response. Anything outside +StandardError+ — an unimplemented
48
+ # +#call+ above all — still propagates, because a broken tool is a broken
49
+ # deploy rather than a bad request.
49
50
  #
50
51
  # --
51
52
  # : (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
@@ -24,6 +24,10 @@ class Riffer::Tools::Response
24
24
  # The error type, or +nil+ on success.
25
25
  attr_reader error_type: Symbol?
26
26
 
27
+ # The exception an unhandled failure was folded from, or +nil+. Kept out of
28
+ # every serialized form so it never reaches an LLM or a message payload.
29
+ attr_reader exception: Exception?
30
+
27
31
  # Creates a success response.
28
32
  #
29
33
  # Raises Riffer::ArgumentError if format is invalid.
@@ -47,8 +51,8 @@ class Riffer::Tools::Response
47
51
  # Creates an error response.
48
52
  #
49
53
  # --
50
- # : (String, ?type: Symbol) -> Riffer::Tools::Response
51
- def self.error: (String, ?type: Symbol) -> Riffer::Tools::Response
54
+ # : (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
55
+ def self.error: (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
52
56
 
53
57
  # Returns true if the tool execution succeeded.
54
58
  # --
@@ -69,6 +73,6 @@ class Riffer::Tools::Response
69
73
  private
70
74
 
71
75
  # --
72
- # : (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
73
- def initialize: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
76
+ # : (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
77
+ def initialize: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
74
78
  end
@@ -44,8 +44,8 @@ class Riffer::Tools::Runtime
44
44
  def dispatch_tool_call: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
45
45
 
46
46
  # --
47
- # : (String?) -> Hash[Symbol, untyped]
48
- def parse_arguments: (String?) -> Hash[Symbol, untyped]
47
+ # : (String?) -> untyped
48
+ def parse_arguments: (String?) -> untyped
49
49
 
50
50
  # Emitted outside +around_tool_call+ so host enrichment spans nest beneath it.
51
51
  # --
@@ -62,8 +62,9 @@ class Riffer::Tools::Runtime
62
62
  # : (Hash[String, String]) -> Hash[String, String]
63
63
  def tag_attributes: (Hash[String, String]) -> Hash[String, String]
64
64
 
65
- # A returned error Response is a handled outcome, so its status stays unset —
66
- # an error span status is reserved for a raised exception.
65
+ # A deliberate error Response is a handled outcome, so its status stays unset.
66
+ # An error status is reserved for a Response carrying the exception it was
67
+ # folded from — the tool failed for a reason nobody anticipated.
67
68
  # --
68
69
  # : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tools::Response) -> void
69
70
  def record_tool_outcome: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Riffer::Tools::Response) -> void
@@ -13,7 +13,9 @@
13
13
  # required :input, String
14
14
  # end
15
15
  # end
16
- module Riffer::Tools::Toolable
16
+ #
17
+ # @rbs module-self Module
18
+ module Riffer::Tools::Toolable : Module
17
19
  @kind: Symbol?
18
20
 
19
21
  @params_builder: Riffer::Params?
@@ -15,7 +15,9 @@ module Riffer
15
15
  class ValidationError < Error
16
16
  end
17
17
 
18
- # Raised when tool execution times out.
18
+ # Raised inside a tool's +call+ when execution exceeds the configured
19
+ # timeout. Rescue it in the tool to clean up; otherwise it becomes a
20
+ # +:timeout_error+ response.
19
21
  class TimeoutError < Error
20
22
  end
21
23
 
@@ -23,6 +25,10 @@ module Riffer
23
25
  class ToolExecutionError < Error
24
26
  end
25
27
 
28
+ # Raised when two registered subclasses share the same identifier.
29
+ class DuplicateIdentifierError < Error
30
+ end
31
+
26
32
  # Returns the Riffer configuration.
27
33
  #
28
34
  # --
@@ -0,0 +1,7 @@
1
+ # `Riffer::Agent` extends `Riffer::Registrable`, whose generic signatures return
2
+ # `Class`. Narrow them here so callers get the agent singleton type back.
3
+ class Riffer::Agent
4
+ def self.find: (String | Symbol) -> singleton(Riffer::Agent)?
5
+
6
+ def self.all: () -> Array[singleton(Riffer::Agent)]
7
+ end
@@ -0,0 +1,5 @@
1
+ # `Riffer::Helpers::Identifier` uses `extend self`; rbs-inline doesn't emit
2
+ # that, so re-extend here to expose its instance methods as singleton methods.
3
+ module Riffer::Helpers::Identifier
4
+ extend ::Riffer::Helpers::Identifier
5
+ end
@@ -0,0 +1,7 @@
1
+ # `Riffer::Tool` extends `Riffer::Registrable`, whose generic signatures return
2
+ # `Class`. Narrow them here so callers get the tool singleton type back.
3
+ class Riffer::Tool
4
+ def self.find: (String | Symbol) -> singleton(Riffer::Tool)?
5
+
6
+ def self.all: () -> Array[singleton(Riffer::Tool)]
7
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.41.0
4
+ version: 0.43.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall
@@ -41,19 +41,15 @@ extra_rdoc_files:
41
41
  - LICENSE.txt
42
42
  - README.md
43
43
  files:
44
- - ".agents/architecture.md"
45
- - ".agents/code-style.md"
46
- - ".agents/providers.md"
47
- - ".agents/rbs-inline.md"
48
- - ".agents/testing.md"
49
44
  - ".bundle/config"
45
+ - ".claude/CLAUDE.md"
46
+ - ".claude/rules/comments.md"
47
+ - ".claude/rules/rbs-inline.md"
50
48
  - ".release-please-config.json"
51
49
  - ".release-please-manifest.json"
52
50
  - ".rubocop.yml"
53
51
  - ".ruby-version"
54
- - AGENTS.md
55
52
  - CHANGELOG.md
56
- - CLAUDE.md
57
53
  - CODE_OF_CONDUCT.md
58
54
  - Guardfile
59
55
  - LICENSE.txt
@@ -123,8 +119,8 @@ files:
123
119
  - lib/riffer/helpers.rb
124
120
  - lib/riffer/helpers/boolean.rb
125
121
  - lib/riffer/helpers/call_or_value.rb
126
- - lib/riffer/helpers/class_name_converter.rb
127
122
  - lib/riffer/helpers/dependencies.rb
123
+ - lib/riffer/helpers/identifier.rb
128
124
  - lib/riffer/mcp.rb
129
125
  - lib/riffer/mcp/authenticated_tool.rb
130
126
  - lib/riffer/mcp/client.rb
@@ -157,6 +153,7 @@ files:
157
153
  - lib/riffer/providers/open_router.rb
158
154
  - lib/riffer/providers/repository.rb
159
155
  - lib/riffer/providers/token_usage.rb
156
+ - lib/riffer/registrable.rb
160
157
  - lib/riffer/runner.rb
161
158
  - lib/riffer/runner/fibers.rb
162
159
  - lib/riffer/runner/sequential.rb
@@ -242,8 +239,8 @@ files:
242
239
  - sig/generated/riffer/helpers.rbs
243
240
  - sig/generated/riffer/helpers/boolean.rbs
244
241
  - sig/generated/riffer/helpers/call_or_value.rbs
245
- - sig/generated/riffer/helpers/class_name_converter.rbs
246
242
  - sig/generated/riffer/helpers/dependencies.rbs
243
+ - sig/generated/riffer/helpers/identifier.rbs
247
244
  - sig/generated/riffer/mcp.rbs
248
245
  - sig/generated/riffer/mcp/authenticated_tool.rbs
249
246
  - sig/generated/riffer/mcp/client.rbs
@@ -276,6 +273,7 @@ files:
276
273
  - sig/generated/riffer/providers/open_router.rbs
277
274
  - sig/generated/riffer/providers/repository.rbs
278
275
  - sig/generated/riffer/providers/token_usage.rbs
276
+ - sig/generated/riffer/registrable.rbs
279
277
  - sig/generated/riffer/runner.rbs
280
278
  - sig/generated/riffer/runner/fibers.rbs
281
279
  - sig/generated/riffer/runner/sequential.rbs
@@ -322,20 +320,22 @@ files:
322
320
  - sig/generated/riffer/version.rbs
323
321
  - sig/manifest.yaml
324
322
  - sig/manual/riffer.rbs
323
+ - sig/manual/riffer/agent.rbs
325
324
  - sig/manual/riffer/agent/run.rbs
326
325
  - sig/manual/riffer/agent/serializer.rbs
327
326
  - sig/manual/riffer/agent/session/repair.rbs
328
327
  - sig/manual/riffer/evals/evaluator_runner.rbs
329
328
  - sig/manual/riffer/helpers/boolean.rbs
330
329
  - sig/manual/riffer/helpers/call_or_value.rbs
331
- - sig/manual/riffer/helpers/class_name_converter.rbs
332
330
  - sig/manual/riffer/helpers/dependencies.rbs
331
+ - sig/manual/riffer/helpers/identifier.rbs
333
332
  - sig/manual/riffer/mcp.rbs
334
333
  - sig/manual/riffer/mcp/authenticated_tool.rbs
335
334
  - sig/manual/riffer/mcp/registry.rbs
336
335
  - sig/manual/riffer/mcp/tool_factory.rbs
337
336
  - sig/manual/riffer/providers.rbs
338
337
  - sig/manual/riffer/providers/repository.rbs
338
+ - sig/manual/riffer/tool.rbs
339
339
  - sig/manual/riffer/tracing.rbs
340
340
  - sig/manual/riffer/tracing/capture.rbs
341
341
  - sig/manual/riffer/tracing/no_op.rbs