Class: Xeno::AgentDefinition

Inherits:
Object
  • Object
show all
Defined in:
lib/xeno/agent_definition.rb

Overview

The resolved agent: everything discovered under the app's agent/ directory, plus diagnostics for anything misplaced or broken. Discovery never raises — a broken agent directory yields a definition whose diagnostics say exactly what is wrong (surfaced by rake xeno:info).

Defined Under Namespace

Classes: Diagnostic, InstructionContext, Schedule

Constant Summary collapse

SLOT_DIRS =
%w[tools skills channels schedules hooks lib].freeze
TOOL_SLUG =
/\A[a-z][a-z0-9_]*\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:, name:, resolver: nil) ⇒ AgentDefinition

Returns a new instance of AgentDefinition.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/xeno/agent_definition.rb', line 29

def initialize(root:, name:, resolver: nil)
  @root = Pathname(root)
  @name = name
  @resolver = resolver || method(:resolve_tool_constant)
  @config = AgentConfig.new
  @instructions = nil
  @dynamic_instructions = nil
  @tools = {}
  @schedules = {}
  @channels = {}
  @hooks = {}
  @diagnostics = []
end

Instance Attribute Details

#channelsObject (readonly)

Returns the value of attribute channels.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def channels
  @channels
end

#configObject (readonly)

Returns the value of attribute config.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def config
  @config
end

#diagnosticsObject (readonly)

Returns the value of attribute diagnostics.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def diagnostics
  @diagnostics
end

#dynamic_instructionsObject (readonly)

Returns the value of attribute dynamic_instructions.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def dynamic_instructions
  @dynamic_instructions
end

#hooksObject (readonly)

Returns the value of attribute hooks.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def hooks
  @hooks
end

#instructionsObject (readonly)

Returns the value of attribute instructions.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def instructions
  @instructions
end

#nameObject (readonly)

Returns the value of attribute name.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def name
  @name
end

#rootObject (readonly)

Returns the value of attribute root.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def root
  @root
end

#schedulesObject (readonly)

Returns the value of attribute schedules.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def schedules
  @schedules
end

#toolsObject (readonly)

Returns the value of attribute tools.



19
20
21
# File 'lib/xeno/agent_definition.rb', line 19

def tools
  @tools
end

Class Method Details

.load(root, name:, resolver: nil) ⇒ Object

Builds the definition for the app's agent root. resolver maps a tool file's camelized basename to its constant; the default asks Zeitwerk (via const_get on Xeno::Tools) and is only overridden in tests.



25
26
27
# File 'lib/xeno/agent_definition.rb', line 25

def self.load(root, name:, resolver: nil)
  new(root: root, name: name, resolver: resolver).tap(&:discover)
end

Instance Method Details

#discoverObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/xeno/agent_definition.rb', line 43

def discover
  unless root.directory?
    error "no agent/ directory at #{root} — run `rails g xeno:install`"
    return self
  end

  load_agent_config
  load_instructions
  discover_tools
  discover_schedules
  discover_channels
  discover_hooks
  report_misplaced_files
  self
end

#dynamic_instructions?Boolean

Returns:

  • (Boolean)


63
# File 'lib/xeno/agent_definition.rb', line 63

def dynamic_instructions? = !@dynamic_instructions.nil?

#errors?Boolean

Returns:

  • (Boolean)


86
# File 'lib/xeno/agent_definition.rb', line 86

def errors? = diagnostics.any?(&:error?)

#instructions_for(session: nil) ⇒ Object

The system prompt for one turn: static markdown first, the dynamic block's return appended (resolved fresh at every turn stage with the session context — the current_user use case). A raising block never bricks the session: the turn proceeds on the static instructions and the failure is logged.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/xeno/agent_definition.rb', line 70

def instructions_for(session: nil)
  parts = [ instructions ]
  if @dynamic_instructions
    context = InstructionContext.new(session: session, principal: session&.principal)
    begin
      parts << @dynamic_instructions.call(context).to_s
    rescue StandardError => e
      Rails.logger.warn(
        "xeno: agent/instructions.rb raised #{e.class}: #{e.message} — using static instructions only"
      )
    end
  end
  combined = parts.compact.map(&:strip).reject(&:empty?).join("\n\n")
  combined.empty? ? nil : combined
end

#tool_classesObject



59
60
61
# File 'lib/xeno/agent_definition.rb', line 59

def tool_classes
  tools.values
end