Class: Antigravity::Agent

Inherits:
Base
  • Object
show all
Defined in:
lib/antigravity/agent.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

inherited

Methods included from Emojifiable

#emoji, included

Constructor Details

#initialize(model: nil, system_instruction: nil, tools: [], skills: [], policies: [], policy: nil, workspace: nil, auto_logger: true, log_file: nil) {|_self| ... } ⇒ Agent

Returns a new instance of Agent.

Yields:

  • (_self)

Yield Parameters:



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/antigravity/agent.rb', line 10

def initialize(model: nil, system_instruction: nil, tools: [],
               skills: [], policies: [], policy: nil, workspace: nil, auto_logger: true, log_file: nil, &block)
  @model = model || Antigravity.config.default_model
  @api_key = Antigravity.config.api_key
  @system_instruction = system_instruction
  @workspace = resolve_workspace(workspace)
  @tools = tools.dup
  @skills = []
  @policies = []
  @sidecars = []
  @hooks = Hooks.new
  @client = Client.new
  @logger_guard = nil
  @connection = nil
  @conversation = nil
  @connected = false
  @born_at = Time.now

  # Register pre-provided tools into the tool runner
  @tool_runner = ToolRunner.new
  @tools.each { |t| @tool_runner.register(t) }

  # Load skills provided at construction (local paths or GitHub URLs)
  add_skills(skills) unless Array(skills).empty?

  # Resolve policy: sugar (symbol → preset, Policy object → use directly)
  if policy
    resolved = policy.is_a?(Symbol) ? Policy.preset(policy) : policy
    enforce(resolved)
  end

  # Load policies
  policies.each { |p| enforce(p) }

  # Automagic Logger attachment unless disabled via ENV["ANTIGRAVITY_LOGGER"]=false or auto_logger: false
  if auto_logger && logger_enabled?
    attach_logger(log_file)
  end

  yield(self) if block_given?
end

Instance Attribute Details

#api_keyObject

Returns the value of attribute api_key.



6
7
8
# File 'lib/antigravity/agent.rb', line 6

def api_key
  @api_key
end

#born_atObject (readonly)

Returns the value of attribute born_at.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def born_at
  @born_at
end

#clientObject (readonly)

Returns the value of attribute client.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def client
  @client
end

#connectionObject (readonly)

Returns the value of attribute connection.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def connection
  @connection
end

#conversationObject (readonly)

Returns the value of attribute conversation.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def conversation
  @conversation
end

#hooksObject (readonly)

Returns the value of attribute hooks.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def hooks
  @hooks
end

#logger_guardObject (readonly)

Returns the value of attribute logger_guard.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def logger_guard
  @logger_guard
end

#modelObject

Returns the value of attribute model.



6
7
8
# File 'lib/antigravity/agent.rb', line 6

def model
  @model
end

#policiesObject (readonly)

Returns the value of attribute policies.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def policies
  @policies
end

#policyObject (readonly)

Returns the value of attribute policy.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def policy
  @policy
end

#sidecarsObject (readonly)

Returns the value of attribute sidecars.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def sidecars
  @sidecars
end

#skillsObject (readonly)

Returns the value of attribute skills.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def skills
  @skills
end

#system_instructionObject

Returns the value of attribute system_instruction.



6
7
8
# File 'lib/antigravity/agent.rb', line 6

def system_instruction
  @system_instruction
end

#toolsObject (readonly)

Returns the value of attribute tools.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def tools
  @tools
end

#workspaceObject

Returns the value of attribute workspace.



7
8
9
# File 'lib/antigravity/agent.rb', line 7

def workspace
  @workspace
end

Class Method Details

.list_skills(path_or_url) ⇒ Array<String>

List discovered skills in a container path (without loading them).

Parameters:

  • path_or_url (String)

    local path or GitHub URL

Returns:

  • (Array<String>)

    skill directory paths



261
262
263
# File 'lib/antigravity/agent.rb', line 261

def self.list_skills(path_or_url)
  SkillResolver.resolve(path_or_url)
end

.open(**kwargs, &block) ⇒ Object

Block form: opens connection, yields agent, auto-closes.



62
63
64
65
66
67
68
69
70
# File 'lib/antigravity/agent.rb', line 62

def self.open(**kwargs, &block)
  agent = new(**kwargs)
  agent.connect!
  begin
    block.call(agent)
  ensure
    agent.close!
  end
end

Instance Method Details

#add_inline_skill(name:, description:, instructions:) ⇒ Skill

Create and add an inline skill (no file needed).

Parameters:

  • name (String)

    skill name

  • description (String)

    what the skill does

  • instructions (String)

    the skill body (markdown)

Returns:

  • (Skill)

    the inline skill



252
253
254
255
256
# File 'lib/antigravity/agent.rb', line 252

def add_inline_skill(name:, description:, instructions:)
  skill = Skill.inline(name: name, description: description, instructions: instructions)
  @skills << skill unless @skills.any? { |s| s.name == skill.name }
  skill
end

#add_skill(path_or_url, skill_name: nil) ⇒ Skill Also known as: load_skill

Add a single skill by path or GitHub URL. Raises if the path resolves to multiple skills (use add_skills instead).

Parameters:

  • path_or_url (String)

    local path or GitHub URL

  • skill_name (String, nil) (defaults to: nil)

    optional specific skill name within a repo

Returns:

  • (Skill)

    the loaded skill

Raises:

  • (ArgumentError)


224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/antigravity/agent.rb', line 224

def add_skill(path_or_url, skill_name: nil)
  target = skill_name ? "#{path_or_url.to_s.chomp('/')}/#{skill_name}" : path_or_url.to_s
  paths = SkillResolver.resolve(target)
  if paths.size > 1
    raise ArgumentError,
          "add_skill resolved to #{paths.size} skills. Use add_skills instead, " \
          "or specify skill_name: to pick one."
  end
  raise ArgumentError, "No skill found at #{target}" if paths.empty?

  load_single_skill(paths.first)
end

#add_skills(paths_or_urls) ⇒ Array<Skill>

Add one or more skills by path or GitHub URL. Accepts a single string or an array. Each entry is resolved (may expand to multiple).

Parameters:

  • paths_or_urls (String, Array<String>)

    local paths or GitHub URLs

Returns:

  • (Array<Skill>)

    all loaded skills



241
242
243
244
245
# File 'lib/antigravity/agent.rb', line 241

def add_skills(paths_or_urls)
  Array(paths_or_urls).flat_map do |p|
    SkillResolver.resolve(p).map { |skill_path| load_single_skill(skill_path) }
  end
end

#after_response(&block) ⇒ Object



272
273
274
# File 'lib/antigravity/agent.rb', line 272

def after_response(&block)
  hooks.after_response(&block)
end

#after_tool_call(&block) ⇒ Object Also known as: on_tool_call



280
281
282
# File 'lib/antigravity/agent.rb', line 280

def after_tool_call(&block)
  hooks.after_tool_call(&block)
end

#attach_logger(log_target = nil, level: :info, silent_notice: false) ⇒ Object



213
214
215
216
217
# File 'lib/antigravity/agent.rb', line 213

def attach_logger(log_target = nil, level: :info, silent_notice: false)
  @logger_guard = Guards::AgentLogger.new(log_target, level: level, silent_notice: silent_notice)
  @logger_guard.attach_to(self)
  @logger_guard
end

#attach_sidecar(sidecar) ⇒ Object



208
209
210
211
# File 'lib/antigravity/agent.rb', line 208

def attach_sidecar(sidecar)
  @sidecars << sidecar
  sidecar
end

#before_prompt(&block) ⇒ Object



268
269
270
# File 'lib/antigravity/agent.rb', line 268

def before_prompt(&block)
  hooks.before_prompt(&block)
end

#before_tool_call(&block) ⇒ Object



276
277
278
# File 'lib/antigravity/agent.rb', line 276

def before_tool_call(&block)
  hooks.before_tool_call(&block)
end

#close!Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/antigravity/agent.rb', line 126

def close!
  # Emit session_end hook before teardown
  if @connected
    hooks.emit(:session_end, {
      turn_count: turn_count,
      conversation_id: conversation_id,
    })
  end

  @connected = false
  @connection&.disconnect!
  @connection = nil
  @conversation = nil
end

#connect!Object

--- Connection Lifecycle ---



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/antigravity/agent.rb', line 78

def connect!
  return self if @connected

  # Auto-attach lifecycle logger if enabled via env
  if lifecycle_logger_enabled? && !@lifecycle_attached
    LifecycleLogger.attach!(self, verbose: ENV['ANTIGRAVITY_LIFECYCLE_VERBOSE'] == '1')
    @lifecycle_attached = true
  end

  @connection = Connection::LocalConnection.new
  @connection.connect!

  @conversation = Conversation.new(
    ws_client: @connection.ws_client,
    tool_runner: @tool_runner,
    hooks: @hooks
  )

  harness_config = build_harness_config

  # Emit indexing hooks — workspace indexing happens during session init
  index_t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @workspace
  hooks.emit(:indexing_start, { workspace: @workspace }) if @workspace

  @conversation.initialize_session!(harness_config: harness_config)
  @connected = true

  if @workspace
    index_elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - index_t0
    hooks.emit(:indexing_done, { workspace: @workspace, elapsed: index_elapsed.round(2) })
  end

  # Emit session_start hook
  hooks.emit(:session_start, {
    model: @model,
    conversation_id: conversation_id,
    workspace: @workspace,
    skills_count: @skills.length,
    tools_count: @tools.length,
  })

  self
end

#connected?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/antigravity/agent.rb', line 122

def connected?
  @connected && @connection&.connected?
end

#conversation_idObject

--- Metadata Accessors (mirrors Python SDK) ---



164
165
166
# File 'lib/antigravity/agent.rb', line 164

def conversation_id
  @conversation&.conversation_id
end

#emit_sidecar_event(event_type, payload = {}) ⇒ Object



285
286
287
# File 'lib/antigravity/agent.rb', line 285

def emit_sidecar_event(event_type, payload = {})
  @sidecars.each { |sidecar| sidecar.emit(event_type, payload) }
end

#enforce(policy) ⇒ Object



52
53
54
55
56
57
# File 'lib/antigravity/agent.rb', line 52

def enforce(policy)
  @policies << policy
  before_tool_call do |tool_name, args|
    policy.evaluate(tool_name, args)
  end
end

#prompt(message, timeout: Antigravity.config.timeout_llm, &block) ⇒ Object Also known as: ask

--- Chat ---



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/antigravity/agent.rb', line 143

def prompt(message, timeout: Antigravity.config.timeout_llm, &block)
  connect! unless @connected
  emit_sidecar_event(:prompt_started, prompt: message)
  hooks.run_pre_prompt(message)

  if @connected && @conversation
    response = @conversation.chat(message, timeout: timeout, &block)
  else
    # Legacy mock-client path (unit tests, pre-connection)
    response = client.send_turn(self, message, &block)
  end

  hooks.run_post_response(response)
  emit_sidecar_event(:turn_completed, response: response.content, model: model)

  response
end

#register_tool(tool_or_name = nil, description: "", &block) ⇒ Object

--- Tool Registration ---



195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/antigravity/agent.rb', line 195

def register_tool(tool_or_name = nil, description: "", &block)
  if block_given? && tool_or_name
    tool = Tool::Dynamic.new(tool_or_name, description: description, &block)
  elsif tool_or_name.respond_to?(:to_json_schema)
    tool = tool_or_name
  else
    raise ArgumentError, "Invalid tool definition"
  end
  @tools << tool
  @tool_runner.register(tool) if @tool_runner
  tool
end

#session_summaryObject



172
173
174
175
176
# File 'lib/antigravity/agent.rb', line 172

def session_summary
  return {} unless @conversation

  @conversation.session_summary(model: @model)
end

#turn_countObject



168
169
170
# File 'lib/antigravity/agent.rb', line 168

def turn_count
  @conversation&.turn_count || 0
end

#uptimeObject

Seconds since agent was created



179
180
181
# File 'lib/antigravity/agent.rb', line 179

def uptime
  Time.now - @born_at
end

#uptime_humanObject

Human-readable uptime: "1m 23.4s", "7.3s"



184
185
186
187
188
189
190
191
# File 'lib/antigravity/agent.rb', line 184

def uptime_human
  secs = uptime
  if secs >= 60
    "#{(secs / 60).to_i}m #{(secs % 60).round(1)}s"
  else
    "#{secs.round(1)}s"
  end
end