antigravity-sdk 0.5.0 ā 0.5.4
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 +4 -4
- data/VERSION +1 -0
- data/lib/antigravity/agent.rb +51 -8
- data/lib/antigravity/connection/binary_fetcher.rb +6 -2
- data/lib/antigravity/connection/local_connection.rb +1 -2
- data/lib/antigravity/conversation.rb +56 -20
- data/lib/antigravity/harness.rb +1 -2
- data/lib/antigravity/lifecycle_logger.rb +30 -14
- data/lib/antigravity/skill.rb +5 -1
- data/lib/antigravity/version.rb +7 -1
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25e15da1171b05a2bcc0be36f79e4f1f6e6e0e6135d520d3776282007071ee05
|
|
4
|
+
data.tar.gz: 3ddfcb833bb6d1339573b81e5910f33c37cd4113bfb28c753367386f0d67d88e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70112ba04d101d659b82372f4f49f03a301d9d1e8fb4c89de871ccf2abdee7bed7017b3fe8440eff5c935902f0c545238081d2439e7aff1e4ca5a63d76f87e83
|
|
7
|
+
data.tar.gz: feb64a14685fefe355772238556f05e27a08aad6f9ad44ae74db4fd0c16e1821eda88f78dbcc24cf990cb90d744139697b06eb883da4fe1f447d67a9d486de7c
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.4
|
data/lib/antigravity/agent.rb
CHANGED
|
@@ -5,14 +5,14 @@ module Antigravity
|
|
|
5
5
|
|
|
6
6
|
attr_accessor :model, :system_instruction, :api_key
|
|
7
7
|
attr_reader :tools, :skills, :hooks, :sidecars, :client, :logger_guard,
|
|
8
|
-
:workspace, :connection, :conversation
|
|
8
|
+
:workspace, :connection, :conversation, :policy, :policies, :born_at
|
|
9
9
|
|
|
10
10
|
def initialize(model: nil, system_instruction: nil, tools: [],
|
|
11
11
|
skills: [], policies: [], policy: nil, workspace: nil, auto_logger: true, log_file: nil, &block)
|
|
12
12
|
@model = model || Antigravity.config.default_model
|
|
13
13
|
@api_key = Antigravity.config.api_key
|
|
14
14
|
@system_instruction = system_instruction
|
|
15
|
-
@workspace =
|
|
15
|
+
@workspace = resolve_workspace(workspace)
|
|
16
16
|
@tools = tools.dup
|
|
17
17
|
@skills = []
|
|
18
18
|
@policies = []
|
|
@@ -23,6 +23,7 @@ module Antigravity
|
|
|
23
23
|
@connection = nil
|
|
24
24
|
@conversation = nil
|
|
25
25
|
@connected = false
|
|
26
|
+
@born_at = Time.now
|
|
26
27
|
|
|
27
28
|
# Register pre-provided tools into the tool runner
|
|
28
29
|
@tool_runner = ToolRunner.new
|
|
@@ -68,6 +69,10 @@ module Antigravity
|
|
|
68
69
|
end
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
def workspace=(path)
|
|
73
|
+
@workspace = resolve_workspace(path)
|
|
74
|
+
end
|
|
75
|
+
|
|
71
76
|
# --- Connection Lifecycle ---
|
|
72
77
|
|
|
73
78
|
def connect!
|
|
@@ -89,9 +94,19 @@ module Antigravity
|
|
|
89
94
|
)
|
|
90
95
|
|
|
91
96
|
harness_config = build_harness_config
|
|
97
|
+
|
|
98
|
+
# Emit indexing hooks ā workspace indexing happens during session init
|
|
99
|
+
index_t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @workspace
|
|
100
|
+
hooks.emit(:indexing_start, { workspace: @workspace }) if @workspace
|
|
101
|
+
|
|
92
102
|
@conversation.initialize_session!(harness_config: harness_config)
|
|
93
103
|
@connected = true
|
|
94
104
|
|
|
105
|
+
if @workspace
|
|
106
|
+
index_elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - index_t0
|
|
107
|
+
hooks.emit(:indexing_done, { workspace: @workspace, elapsed: index_elapsed.round(2) })
|
|
108
|
+
end
|
|
109
|
+
|
|
95
110
|
# Emit session_start hook
|
|
96
111
|
hooks.emit(:session_start, {
|
|
97
112
|
model: @model,
|
|
@@ -160,6 +175,21 @@ module Antigravity
|
|
|
160
175
|
@conversation.session_summary(model: @model)
|
|
161
176
|
end
|
|
162
177
|
|
|
178
|
+
# Seconds since agent was created
|
|
179
|
+
def uptime
|
|
180
|
+
Time.now - @born_at
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Human-readable uptime: "1m 23.4s", "7.3s"
|
|
184
|
+
def uptime_human
|
|
185
|
+
secs = uptime
|
|
186
|
+
if secs >= 60
|
|
187
|
+
"#{(secs / 60).to_i}m #{(secs % 60).round(1)}s"
|
|
188
|
+
else
|
|
189
|
+
"#{secs.round(1)}s"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
163
193
|
# --- Tool Registration ---
|
|
164
194
|
|
|
165
195
|
def register_tool(tool_or_name = nil, description: "", &block)
|
|
@@ -266,11 +296,11 @@ module Antigravity
|
|
|
266
296
|
end
|
|
267
297
|
|
|
268
298
|
def lifecycle_logger_enabled?
|
|
269
|
-
# Explicit opt-in
|
|
270
|
-
return true if ENV['ANTIGRAVITY_LIFECYCLE'] == '1'
|
|
299
|
+
# Explicit opt-in (strip handles trailing spaces in .env files)
|
|
300
|
+
return true if ENV['ANTIGRAVITY_LIFECYCLE']&.strip == '1'
|
|
271
301
|
# Rails/Rack test or development
|
|
272
|
-
return true if %w[test development].include?(ENV['RAILS_ENV']&.downcase)
|
|
273
|
-
return true if %w[test development].include?(ENV['RACK_ENV']&.downcase)
|
|
302
|
+
return true if %w[test development].include?(ENV['RAILS_ENV']&.strip&.downcase)
|
|
303
|
+
return true if %w[test development].include?(ENV['RACK_ENV']&.strip&.downcase)
|
|
274
304
|
# Explicit opt-out
|
|
275
305
|
false
|
|
276
306
|
end
|
|
@@ -361,8 +391,21 @@ module Antigravity
|
|
|
361
391
|
skill
|
|
362
392
|
end
|
|
363
393
|
|
|
364
|
-
def
|
|
365
|
-
|
|
394
|
+
def resolve_workspace(val)
|
|
395
|
+
return nil if val.nil? || val == false
|
|
396
|
+
|
|
397
|
+
raw_path = case val
|
|
398
|
+
when :here, :current, true, '.'
|
|
399
|
+
'.'
|
|
400
|
+
else
|
|
401
|
+
val.to_s
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
expanded = File.expand_path(raw_path)
|
|
405
|
+
expanded += '/' unless expanded.end_with?('/')
|
|
406
|
+
|
|
407
|
+
$stderr.puts "š Setting workspace to \e[34m#{expanded}\e[0m" rescue nil
|
|
408
|
+
expanded
|
|
366
409
|
end
|
|
367
410
|
end
|
|
368
411
|
end
|
|
@@ -87,7 +87,7 @@ module Antigravity
|
|
|
87
87
|
|
|
88
88
|
unless wheel
|
|
89
89
|
# Try all versions for the platform
|
|
90
|
-
versions = data['releases']&.keys&.sort_by { |v| Gem::Version.
|
|
90
|
+
versions = data['releases']&.keys&.sort_by { |v| Gem::Version.correct?(v) ? Gem::Version.new(v) : Gem::Version.new('0') }&.reverse
|
|
91
91
|
versions&.each do |ver|
|
|
92
92
|
files = data.dig('releases', ver) || []
|
|
93
93
|
wheel = files.find { |u| u['filename']&.include?(platform) && u['filename']&.end_with?('.whl') }
|
|
@@ -134,7 +134,11 @@ module Antigravity
|
|
|
134
134
|
# Wheels are just ZIP files. Extract the binary.
|
|
135
135
|
# Look for: google/antigravity/bin/localharness (or language_server)
|
|
136
136
|
extract_dir = Dir.mktmpdir('agy-extract-')
|
|
137
|
-
system('unzip', '-q', '-o', wheel_path, '-d', extract_dir)
|
|
137
|
+
success = system('unzip', '-q', '-o', wheel_path, '-d', extract_dir)
|
|
138
|
+
unless success
|
|
139
|
+
FileUtils.rm_rf(extract_dir)
|
|
140
|
+
raise HarnessNotFoundError, "Failed to extract wheel using unzip command"
|
|
141
|
+
end
|
|
138
142
|
|
|
139
143
|
# Search for the binary inside (prefer localharness over language_server)
|
|
140
144
|
candidates = Dir.glob("#{extract_dir}/**/localharness") +
|
|
@@ -121,8 +121,7 @@ module Antigravity
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def perform_handshake!
|
|
124
|
-
storage_dir =
|
|
125
|
-
FileUtils.mkdir_p(storage_dir)
|
|
124
|
+
storage_dir = Dir.mktmpdir('antigravity-ruby-')
|
|
126
125
|
|
|
127
126
|
# Send InputConfig via stdin
|
|
128
127
|
input_config = Protocol.encode_input_config(
|
|
@@ -131,10 +131,9 @@ module Antigravity
|
|
|
131
131
|
|
|
132
132
|
if step[:textDelta] && !step[:textDelta].empty? && is_model_step && is_target_user && !is_error_step
|
|
133
133
|
text_parts << step[:textDelta]
|
|
134
|
-
chunk =
|
|
134
|
+
chunk = Chunk.new(
|
|
135
135
|
content: step[:textDelta],
|
|
136
|
-
role: :assistant
|
|
137
|
-
delta: true
|
|
136
|
+
role: :assistant
|
|
138
137
|
)
|
|
139
138
|
block&.call(chunk)
|
|
140
139
|
end
|
|
@@ -170,37 +169,44 @@ module Antigravity
|
|
|
170
169
|
handle_tool_call(tool_call)
|
|
171
170
|
end
|
|
172
171
|
|
|
173
|
-
# Usage update
|
|
172
|
+
# Usage update ā do NOT set seen_any_step here!
|
|
173
|
+
# usageUpdate can leak from the previous turn and trick the GHI #18
|
|
174
|
+
# stale-FULLY_IDLE guard into accepting a stale FULLY_IDLE as real.
|
|
174
175
|
if (usage = msg[:usageUpdate])
|
|
175
176
|
update_usage(usage)
|
|
176
|
-
seen_any_step = true
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
# Trajectory state: STATE_FULLY_IDLE / STATE_CANCELLED = turn complete (authoritative signal from harness)
|
|
180
|
-
# GHI #18 FIX: Only honor FULLY_IDLE if we've seen at least one stepUpdate
|
|
181
|
-
# from this turn, OR if enough time has elapsed
|
|
182
|
-
# A stale FULLY_IDLE from a previous turn sitting
|
|
183
|
-
# collect_response to return immediately with
|
|
180
|
+
# GHI #18 + #24 FIX: Only honor FULLY_IDLE if we've seen at least one stepUpdate or toolCall
|
|
181
|
+
# (NOT usageUpdate ā it leaks across turns) from this turn, OR if enough time has elapsed
|
|
182
|
+
# (2s) that this can't be a stale leftover. A stale FULLY_IDLE from a previous turn sitting
|
|
183
|
+
# in the WebSocket buffer was causing collect_response to return immediately with 0B text.
|
|
184
184
|
if (traj = msg[:trajectoryStateUpdate])
|
|
185
|
-
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - turn_started_at
|
|
186
185
|
if traj[:state].to_s =~ /FULLY_IDLE|CANCELLED/
|
|
187
|
-
if seen_any_step
|
|
186
|
+
if seen_any_step
|
|
188
187
|
finished = true
|
|
189
188
|
finished_at = Time.now
|
|
190
189
|
else
|
|
191
190
|
# Stale FULLY_IDLE ā skip it (likely leftover from previous turn or init)
|
|
192
|
-
@hooks&.emit(:ws_message, { _debug: 'skipped_stale_fully_idle',
|
|
191
|
+
@hooks&.emit(:ws_message, { _debug: 'skipped_stale_fully_idle', seen_any_step: seen_any_step })
|
|
193
192
|
end
|
|
194
193
|
end
|
|
195
194
|
end
|
|
196
195
|
|
|
197
196
|
# Stop conditions (in priority order):
|
|
198
|
-
# 1. Session end ā always stop
|
|
199
|
-
# 2.
|
|
200
|
-
|
|
197
|
+
# 1. Session end ā always hard-stop immediately
|
|
198
|
+
# 2. Finished (DONE or FULLY_IDLE) WITH text ā short drain for trailing usage
|
|
199
|
+
# 3. Finished WITHOUT text but with steps ā model was working (thinking),
|
|
200
|
+
# text is likely still in-flight. Drain longer (3s) to catch it. (GHI #24)
|
|
201
|
+
# 4. Not finished but text started ā idle timeout for more text
|
|
202
|
+
if msg.key?(:sessionEndResponse)
|
|
201
203
|
:stop
|
|
204
|
+
elsif finished && !text_parts.empty?
|
|
205
|
+
[:idle_timeout, 0.5]
|
|
206
|
+
elsif finished && text_parts.empty?
|
|
207
|
+
# FULLY_IDLE arrived but no text yet ā wait longer for trailing text
|
|
208
|
+
[:idle_timeout, 3.0]
|
|
202
209
|
elsif !text_parts.empty?
|
|
203
|
-
# If assistant has sent text response, allow 3s idle timeout for trailing metadata
|
|
204
210
|
[:idle_timeout, 3.0]
|
|
205
211
|
end
|
|
206
212
|
end
|
|
@@ -241,10 +247,40 @@ module Antigravity
|
|
|
241
247
|
# Symbolize keys for Ruby kwargs
|
|
242
248
|
kwargs = args.transform_keys(&:to_sym)
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
# Emit tool_call hook BEFORE execution
|
|
251
|
+
@hooks&.emit(:tool_call, { tool_name: tool_name, params: args, tool_id: tool_id })
|
|
252
|
+
|
|
253
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
254
|
+
policy_check = @hooks ? @hooks.run_pre_tool(tool_name, args) : { allowed: true }
|
|
255
|
+
|
|
256
|
+
if !policy_check[:allowed]
|
|
257
|
+
reason = policy_check[:reason]
|
|
258
|
+
# Same format as client.rb sidecar emission
|
|
259
|
+
@hooks&.emit(:tool_blocked, { tool: tool_name, reason: reason })
|
|
260
|
+
result = "ā TOOL BLOCKED: #{reason}"
|
|
261
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
262
|
+
|
|
263
|
+
# We run the post_tool hook here as well to satisfy AgentLogger's pairing
|
|
264
|
+
result = @hooks ? @hooks.run_post_tool(tool_name, args, result) : result
|
|
265
|
+
@hooks&.emit(:tool_result, { tool_name: tool_name, result: result.to_s, duration: duration, tool_id: tool_id })
|
|
266
|
+
else
|
|
267
|
+
begin
|
|
268
|
+
raw_result = @tool_runner.execute(tool_name, **kwargs)
|
|
269
|
+
|
|
270
|
+
# Run post_tool filters/maskers
|
|
271
|
+
result = @hooks ? @hooks.run_post_tool(tool_name, args, raw_result) : raw_result
|
|
272
|
+
|
|
273
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
274
|
+
|
|
275
|
+
# Emit tool_result hook AFTER execution
|
|
276
|
+
@hooks&.emit(:tool_result, { tool_name: tool_name, result: result.to_s, duration: duration, tool_id: tool_id })
|
|
277
|
+
rescue ToolNotFoundError => e
|
|
278
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
279
|
+
result = { error: e.message }
|
|
280
|
+
|
|
281
|
+
# Emit tool_error hook on failure
|
|
282
|
+
@hooks&.emit(:tool_error, { tool_name: tool_name, error: e.message, duration: duration, tool_id: tool_id })
|
|
283
|
+
end
|
|
248
284
|
end
|
|
249
285
|
|
|
250
286
|
# Send tool response back (protobuf InputEvent.tool_response format)
|
data/lib/antigravity/harness.rb
CHANGED
|
@@ -22,8 +22,7 @@ module Antigravity
|
|
|
22
22
|
return true
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
@stdin, @stdout, @stderr, wait_thr = Open3.popen3(cmd)
|
|
25
|
+
@stdin, @stdout, @stderr, wait_thr = Open3.popen3(@bin_path, "--port=#{@port}")
|
|
27
26
|
@pid = wait_thr.pid
|
|
28
27
|
|
|
29
28
|
at_exit { stop! }
|
|
@@ -20,8 +20,9 @@ module Antigravity
|
|
|
20
20
|
def self.status_line(agent)
|
|
21
21
|
turns = agent.turn_count rescue 0
|
|
22
22
|
summary = agent.session_summary rescue {}
|
|
23
|
-
tokens = summary
|
|
23
|
+
tokens = summary[:total_tokens] || 0
|
|
24
24
|
tok_str = tokens > 999 ? "#{(tokens / 1000.0).round(1)}k" : tokens.to_s
|
|
25
|
+
tok_str = "šŖ#{tok_str}"
|
|
25
26
|
model = summary[:model] || agent.model || '?'
|
|
26
27
|
conv_id = (summary[:conversation_id] || '?')[0..7]
|
|
27
28
|
|
|
@@ -54,7 +55,18 @@ module Antigravity
|
|
|
54
55
|
logger.instance_variable_set(:@session_start_time, Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
55
56
|
model = info[:model] || agent.model || '?'
|
|
56
57
|
conv_id = (info[:conversation_id] || '?')[0..11]
|
|
57
|
-
puts C.gray("
|
|
58
|
+
puts C.gray("\nšŖš¢ #{C.dim("session_start")} | model=#{C.cyan(model)} | conv=#{C.cyan(conv_id)}")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
agent.hooks.on(:indexing_start) do |info|
|
|
62
|
+
ws = info[:workspace] || '?'
|
|
63
|
+
puts C.gray("šŖ š #{C.dim("indexing")} | #{C.blue(ws)}")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
agent.hooks.on(:indexing_done) do |info|
|
|
67
|
+
ws = info[:workspace] || '?'
|
|
68
|
+
elapsed = info[:elapsed] ? "#{info[:elapsed]}s" : '?'
|
|
69
|
+
puts C.gray("šŖ ā
#{C.dim("indexed")} | #{C.blue(ws)} | #{C.green(elapsed)}")
|
|
58
70
|
end
|
|
59
71
|
|
|
60
72
|
agent.hooks.on(:session_end) do |info|
|
|
@@ -65,9 +77,9 @@ module Antigravity
|
|
|
65
77
|
end
|
|
66
78
|
turns = info[:turn_count] || agent.turn_count rescue 0
|
|
67
79
|
summary = agent.session_summary rescue {}
|
|
68
|
-
tokens = summary
|
|
80
|
+
tokens = summary[:total_tokens] || 0
|
|
69
81
|
tok_str = tokens > 999 ? "#{(tokens / 1000.0).round(1)}k" : tokens.to_s
|
|
70
|
-
puts C.gray("
|
|
82
|
+
puts C.gray("šŖš“ #{C.dim("session_end")} | #{C.bold("#{turns} turns")} | šŖ#{tok_str} | #{elapsed}s")
|
|
71
83
|
end
|
|
72
84
|
end
|
|
73
85
|
|
|
@@ -81,7 +93,7 @@ module Antigravity
|
|
|
81
93
|
preview = text.to_s[0..60].gsub("\n", ' ')
|
|
82
94
|
preview += '...' if text.to_s.length > 60
|
|
83
95
|
status = self.class.status_line(agent) rescue C.dim("T#{count}")
|
|
84
|
-
puts C.gray("
|
|
96
|
+
puts C.gray("šŖ ā”ļø #{C.dim("pre_turn")} T#{count} | #{C.yellow("\"#{preview}\"")} | #{status}")
|
|
85
97
|
end
|
|
86
98
|
|
|
87
99
|
agent.hooks.after_response do |response|
|
|
@@ -99,12 +111,12 @@ module Antigravity
|
|
|
99
111
|
tool_count = response.respond_to?(:tool_calls_count) ? (response.tool_calls_count || 0) : 0
|
|
100
112
|
status = self.class.status_line(agent) rescue ''
|
|
101
113
|
|
|
102
|
-
parts = ["#{C.green("#{chars}
|
|
103
|
-
parts << "#{C.magenta("#{thinking_len}
|
|
114
|
+
parts = ["#{C.green("#{chars}B")} #{C.dim("#{lines}L")}"]
|
|
115
|
+
parts << "#{C.magenta("#{thinking_len}B")} think" if thinking_len > 0
|
|
104
116
|
parts << "#{C.cyan("#{tool_count}")} tools" if tool_count > 0
|
|
105
117
|
parts << "#{C.blue("#{elapsed}s")}"
|
|
106
118
|
|
|
107
|
-
puts C.gray("
|
|
119
|
+
puts C.gray("\nšŖ ā¬
ļø #{C.dim("post_turn")} T#{logger.instance_variable_get(:@turn_count)} | #{parts.join(' | ')} | #{status}")
|
|
108
120
|
if logger.instance_variable_get(:@verbose)
|
|
109
121
|
puts C.dim(" \"#{preview}\"")
|
|
110
122
|
end
|
|
@@ -117,28 +129,32 @@ module Antigravity
|
|
|
117
129
|
agent.hooks.on(:tool_call) do |info|
|
|
118
130
|
name = info[:tool_name] || info[:name] || '?'
|
|
119
131
|
params_preview = (info[:params] || {}).keys.join(', ')
|
|
120
|
-
puts C.gray("
|
|
132
|
+
puts C.gray(" šŖ š§ #{C.dim("tool_call")} | #{C.cyan(name)}(#{C.dim(params_preview)})")
|
|
121
133
|
end
|
|
122
134
|
|
|
123
135
|
agent.hooks.on(:tool_result) do |info|
|
|
124
136
|
name = info[:tool_name] || info[:name] || '?'
|
|
125
|
-
|
|
137
|
+
result_str = info[:result].to_s
|
|
138
|
+
result_len = result_str.bytesize rescue 0
|
|
139
|
+
preview = result_str[0..120].gsub("\n", ' ')
|
|
140
|
+
preview += '...' if result_str.length > 120
|
|
126
141
|
duration = info[:duration] ? "#{info[:duration].round(2)}s" : nil
|
|
127
|
-
parts = [C.cyan(name), "#{result_len}
|
|
142
|
+
parts = [C.cyan(name), "#{result_len}B"]
|
|
128
143
|
parts << duration if duration
|
|
129
|
-
puts C.gray("
|
|
144
|
+
puts C.gray(" šŖ ā
#{C.dim("tool_done")} | #{parts.join(' | ')}")
|
|
145
|
+
puts C.dim(" ā #{C.yellow(preview)}") if result_len > 0
|
|
130
146
|
end
|
|
131
147
|
|
|
132
148
|
agent.hooks.on(:tool_blocked) do |info|
|
|
133
149
|
name = info[:tool] || '?'
|
|
134
150
|
reason = info[:reason] || 'policy'
|
|
135
|
-
puts C.red("
|
|
151
|
+
puts C.red(" šŖ š« #{C.dim("tool_deny")} | #{C.red(name)} ā #{reason}")
|
|
136
152
|
end
|
|
137
153
|
|
|
138
154
|
agent.hooks.on(:tool_error) do |info|
|
|
139
155
|
name = info[:tool_name] || info[:name] || '?'
|
|
140
156
|
error = info[:error] || info[:message] || '?'
|
|
141
|
-
puts C.red("
|
|
157
|
+
puts C.red(" šŖ š„ #{C.dim("tool_error")} | #{C.red(name)} ā #{error.to_s[0..80]}")
|
|
142
158
|
end
|
|
143
159
|
end
|
|
144
160
|
end
|
data/lib/antigravity/skill.rb
CHANGED
|
@@ -71,7 +71,11 @@ module Antigravity
|
|
|
71
71
|
|
|
72
72
|
content = File.read(skill_file, encoding: 'UTF-8')
|
|
73
73
|
if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
|
74
|
-
front_matter =
|
|
74
|
+
front_matter = begin
|
|
75
|
+
YAML.safe_load(Regexp.last_match(1)) || {}
|
|
76
|
+
rescue Psych::SyntaxError => e
|
|
77
|
+
raise ArgumentError, "Invalid YAML frontmatter in #{skill_file}: #{e.message}"
|
|
78
|
+
end
|
|
75
79
|
@name = front_matter["name"] || File.basename(@path)
|
|
76
80
|
@description = front_matter["description"] || ""
|
|
77
81
|
@metadata = front_matter.fetch("metadata", {})
|
data/lib/antigravity/version.rb
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Antigravity
|
|
4
|
-
VERSION =
|
|
4
|
+
VERSION = begin
|
|
5
|
+
version_file = [
|
|
6
|
+
File.expand_path("../../VERSION", __dir__),
|
|
7
|
+
File.expand_path("../VERSION", __dir__)
|
|
8
|
+
].find { |f| File.exist?(f) }
|
|
9
|
+
version_file ? File.read(version_file).strip : "0.5.0"
|
|
10
|
+
end
|
|
5
11
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: antigravity-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Riccardo Carlesso
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '1.5'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: dotenv
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.0'
|
|
54
68
|
description: An elegant, Ruby-like SDK for building autonomous AI agents with Google
|
|
55
69
|
Antigravity.
|
|
56
70
|
email:
|
|
@@ -59,6 +73,7 @@ executables: []
|
|
|
59
73
|
extensions: []
|
|
60
74
|
extra_rdoc_files: []
|
|
61
75
|
files:
|
|
76
|
+
- VERSION
|
|
62
77
|
- lib/antigravity.rb
|
|
63
78
|
- lib/antigravity/agent.rb
|
|
64
79
|
- lib/antigravity/base.rb
|