brainiac 0.4.5 → 0.4.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad4e2a76bc43943ef2dd556f67fa2d3b4181836dc5abf04c236944fe08c2bd74
4
- data.tar.gz: a26c2581219ed65257deef259809099e4c03b2ec08f7d95d4fb649f831ce6d44
3
+ metadata.gz: 31251e710a5798457b27ce451afac8416c61581a3c5e443766baac9523ecc2b6
4
+ data.tar.gz: 78519e9435feed9506750c3556f4ea9b2ea31a34bdc25bf1a1282cb9fb7f27a5
5
5
  SHA512:
6
- metadata.gz: 1bdfbf38d91e481e7b51b1f8701fa446bb67206989b651bcb1f7f0bd920b7c18e6f011d21f4e9e59cbab3bcb80a878808e299d4f82f054cb5fc18b1d6bc78f25
7
- data.tar.gz: 2c19663ee311472f732ae4c685b0708990deaa0d1101ac52191da5d5bc751444b8b572ae72c1fb002d4ea8158fcd4c8d61557dcd440e7ad0ce0454b10f9541a9
6
+ metadata.gz: e57de5ea8715a63a478bfba62af31f691f9f89948dfa82dd8d1a2cd1a4a81b9c743a6edd316c02a8ee00ede53893729dc1f955f43b1736fc339890a01cb6ed15
7
+ data.tar.gz: 7b3a815f89bbdad976f5db659d4e0f82ae2b0862a29814ed4394a713c94a25ebe3cf7f8b705197163ddc13f4166ce54c729435b8ddbeb63fea690a7a12b83390
@@ -142,7 +142,7 @@ def check_intent(message, agent_name:, channel: "conversation", context: nil)
142
142
  # Deterministic pre-check: if the message explicitly names another agent
143
143
  # and does NOT name this agent, skip without consulting the LLM.
144
144
  # This handles the common case of "Effie, tell me another" when checking Galen.
145
- if intent_names_other_agent?(message, agent_name)
145
+ if intent_names_other_agent?(message, agent_name, context)
146
146
  LOG.info "[Intent] Deterministic skip for #{agent_name} — message addresses another agent"
147
147
  return false
148
148
  end
@@ -327,11 +327,16 @@ end
327
327
  # - "Was Effie nice to you?" (asking about Effie)
328
328
  # - "I liked what Effie said" (referencing Effie)
329
329
  #
330
+ # Agents running on other machines aren't in AGENT_REGISTRY, so bot authors seen in
331
+ # the conversation context ("Name [bot]: ...") count as agents too.
332
+ #
330
333
  # @param message [String] The message text
331
334
  # @param agent_name [String] The agent being evaluated
335
+ # @param context [String, nil] Conversation history
332
336
  # @return [Boolean] true if another agent is directly addressed and this one isn't
333
- def intent_names_other_agent?(message, agent_name)
334
- return false unless defined?(AGENT_REGISTRY) && !AGENT_REGISTRY.empty?
337
+ def intent_names_other_agent?(message, agent_name, context = nil)
338
+ names = intent_agent_display_names.keys | context_bot_names(context).map(&:downcase)
339
+ return false if names.empty?
335
340
 
336
341
  msg_lower = message.downcase.strip
337
342
  agent_lower = agent_name.downcase
@@ -340,9 +345,7 @@ def intent_names_other_agent?(message, agent_name)
340
345
  return false if directly_addressed_to?(msg_lower, agent_lower)
341
346
 
342
347
  # Check if any OTHER agent is directly addressed (not merely mentioned)
343
- AGENT_REGISTRY.each do |key, entry|
344
- display = entry.is_a?(Hash) ? (entry["display_name"] || key.capitalize) : key.capitalize
345
- other_lower = display.downcase
348
+ names.each do |other_lower|
346
349
  next if other_lower == agent_lower
347
350
  next unless msg_lower.match?(/\b#{Regexp.escape(other_lower)}\b/)
348
351
 
@@ -403,64 +406,66 @@ def build_intent_agent_roster(agent_name)
403
406
  names.join(", ")
404
407
  end
405
408
 
409
+ # Map of lowercased agent display name => display name, from the local AGENT_REGISTRY.
410
+ def intent_agent_display_names
411
+ return {} unless defined?(AGENT_REGISTRY) && !AGENT_REGISTRY.empty?
412
+
413
+ AGENT_REGISTRY.each_with_object({}) do |(key, entry), names|
414
+ display = entry.is_a?(Hash) ? (entry["display_name"] || key.capitalize) : key.capitalize
415
+ names[display.downcase] = display
416
+ end
417
+ end
418
+
419
+ # Parse a context line ("username: message", or "username [bot]: message" when the
420
+ # plugin knows the author is a bot). Returns [username, bot?] or nil.
421
+ def parse_context_author(line)
422
+ match = line.match(/\A(\S+?)( \[bot\])?:\s/)
423
+ match && [match[1], !match[2].nil?]
424
+ end
425
+
426
+ # Usernames of every bot author in the context.
427
+ def context_bot_names(context)
428
+ return [] if context.nil?
429
+
430
+ context.lines.filter_map do |line|
431
+ username, bot = parse_context_author(line)
432
+ username if bot
433
+ end.uniq
434
+ end
435
+
406
436
  # Detect the last agent to respond in the conversation context.
407
437
  # Returns a prompt-friendly string telling the model who was last to respond.
408
438
  #
409
- # The context is formatted as "username: message\n..." lines.
410
- # We scan backwards to find the most recent line authored by a known agent.
411
- #
412
439
  # @param context [String, nil] Conversation history
413
440
  # @param agent_name [String] The current agent being evaluated
414
441
  # @return [String] Prompt block describing the last responder
415
442
  def detect_last_responder(context, _agent_name = nil)
416
- return "No agent has spoken yet." if context.nil? || context.strip.empty?
417
- return "No agent has spoken yet." unless defined?(AGENT_REGISTRY) && !AGENT_REGISTRY.empty?
418
-
419
- # Build a lookup of agent display names (lowercase) to their proper display name
420
- agent_names = {}
421
- AGENT_REGISTRY.each do |key, entry|
422
- display = entry.is_a?(Hash) ? (entry["display_name"] || key.capitalize) : key.capitalize
423
- agent_names[display.downcase] = display
424
- end
425
-
426
- # Scan context lines in reverse to find the last agent message
427
- lines = context.strip.lines.reverse
428
- lines.each do |line|
429
- # Lines are formatted as "username: message content"
430
- match = line.match(/\A(\S+?):\s/)
431
- next unless match
443
+ responder = detect_last_responder_name(context)
444
+ return "No agent has spoken yet." unless responder
432
445
 
433
- username = match[1].downcase
434
- next unless agent_names.key?(username)
435
-
436
- responder = agent_names[username]
437
- return "#{responder} was the last to speak. The human is continuing the conversation with #{responder}."
438
- end
439
-
440
- "No agent has spoken yet."
446
+ "#{responder} was the last to speak. The human is continuing the conversation with #{responder}."
441
447
  end
442
448
 
443
449
  # Detect the last agent to respond — returns just the display name (or nil).
444
450
  # Used for deterministic conversational-flow checks without LLM involvement.
445
451
  #
452
+ # The context is formatted as "username: message\n..." lines. We scan backwards for
453
+ # the most recent line from a known agent, or from any bot — an agent on another
454
+ # machine is still the one the human is talking to.
455
+ #
446
456
  # @param context [String, nil] Conversation history
447
457
  # @return [String, nil] Display name of last agent to speak, or nil
448
458
  def detect_last_responder_name(context)
449
459
  return nil if context.nil? || context.strip.empty?
450
- return nil unless defined?(AGENT_REGISTRY) && !AGENT_REGISTRY.empty?
451
460
 
452
- agent_names = {}
453
- AGENT_REGISTRY.each do |key, entry|
454
- display = entry.is_a?(Hash) ? (entry["display_name"] || key.capitalize) : key.capitalize
455
- agent_names[display.downcase] = display
456
- end
461
+ agent_names = intent_agent_display_names
457
462
 
458
463
  context.strip.lines.reverse_each do |line|
459
- match = line.match(/\A(\S+?):\s/)
460
- next unless match
464
+ username, bot = parse_context_author(line)
465
+ next unless username
461
466
 
462
- username = match[1].downcase
463
- return agent_names[username] if agent_names.key?(username)
467
+ return agent_names[username.downcase] if agent_names.key?(username.downcase)
468
+ return username if bot
464
469
  end
465
470
 
466
471
  nil
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.4.5"
5
+ VERSION = "0.4.6"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis