Class: Insika::CapabilityRegistry
- Inherits:
-
Object
- Object
- Insika::CapabilityRegistry
- Defined in:
- lib/insika/capability_registry.rb
Overview
Intent→implementation resolution. Pure INDIRECTION: does NOT inherit
from Registry (which holds executables) — it holds Providers (resolution
metadata) and returns the impl_name that ANOTHER registry instantiates.
Immutable post-boot by construction (only boot/loader registers), like Registry.
Defined Under Namespace
Classes: Provider
Instance Method Summary collapse
- #capabilities ⇒ Object
-
#deregister_plugin(plugin_id) ⇒ Object
Loader rollback, symmetric to
Registry#deregister_plugin. -
#initialize ⇒ CapabilityRegistry
constructor
kind: :tool | :workflow priority: Integer | nil (nil = lowest; inherits plugin precedence) available: callable -> bool (default -> { true }; never nil in a registered Provider).
- #providers(capability) ⇒ Object
-
#register(capability, impl_name:, kind:, plugin: nil, priority: nil, available: nil) ⇒ Object
Unlike
Registry#register, there is NO "first wins": registering the same capability more than once is the normal case (providers competing) — dedup/tie-breaking happens inresolve, not here. -
#resolve(capability, profile:, context: {}, event_stream: nil) ⇒ Object
-> chosen Provider | raise CapabilityUnavailable | raise CapabilityAmbiguous.
Constructor Details
#initialize ⇒ CapabilityRegistry
kind: :tool | :workflow priority: Integer | nil (nil = lowest; inherits plugin precedence) available: callable -> bool (default -> { true }; never nil in a registered Provider)
14 15 16 17 |
# File 'lib/insika/capability_registry.rb', line 14 def initialize # capability(Symbol) -> [Provider], na ordem de registro (proxy de announce) @providers = Hash.new { |h, k| h[k] = [] } end |
Instance Method Details
#capabilities ⇒ Object
41 |
# File 'lib/insika/capability_registry.rb', line 41 def capabilities = @providers.keys |
#deregister_plugin(plugin_id) ⇒ Object
Loader rollback, symmetric to Registry#deregister_plugin. Removes
only the plugin's Providers; capabilities with no remaining provider drop
from capabilities (clears the key so the Hash.new-with-block won't recreate it empty).
46 47 48 49 50 |
# File 'lib/insika/capability_registry.rb', line 46 def deregister_plugin(plugin_id) @providers.each_value { |list| list.delete_if { |p| p.plugin == plugin_id.to_s } } @providers.delete_if { |_cap, list| list.empty? } nil end |
#providers(capability) ⇒ Object
39 |
# File 'lib/insika/capability_registry.rb', line 39 def providers(capability) = @providers[capability.to_sym].dup |
#register(capability, impl_name:, kind:, plugin: nil, priority: nil, available: nil) ⇒ Object
Unlike Registry#register, there is NO "first wins": registering the
same capability more than once is the normal case (providers competing) —
dedup/tie-breaking happens in resolve, not here.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/insika/capability_registry.rb', line 22 def register(capability, impl_name:, kind:, plugin: nil, priority: nil, available: nil) unless %i[tool workflow].include?(kind) raise ArgumentError, "invalid kind: #{kind.inspect} (use :tool or :workflow)" end if kind == :workflow warn "[capability_registry] '#{capability}' registered with kind: :workflow — " \ "exposure to the agent deferred (L5)" end @providers[capability.to_sym] << Provider.new( capability: capability.to_sym, impl_name: impl_name.to_s, kind: kind, plugin: plugin&.to_s, priority: priority, available: available || -> { true } ) self end |
#resolve(capability, profile:, context: {}, event_stream: nil) ⇒ Object
-> chosen Provider | raise CapabilityUnavailable | raise CapabilityAmbiguous.
PURE and deterministic: same input → same choice or same error. No
IO beyond the Provider's own available.call. Emits :capability_resolved
when event_stream: is present (audit).
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/insika/capability_registry.rb', line 56 def resolve(capability, profile:, context: {}, event_stream: nil) candidates = providers(capability) candidates = candidates.select { |p| p.available.call } candidates = apply_deny(candidates, profile) raise CapabilityUnavailable.new(capability: capability) if candidates.empty? chosen = pick_top(candidates, capability) event_stream&.emit(Insika::Event.new( type: :capability_resolved, data: { capability: capability.to_sym, chosen: chosen.impl_name, candidates: candidates.map do |p| { impl_name: p.impl_name, plugin: p.plugin, priority: p.priority } end } )) chosen end |