ask-agent 0.25.0 → 0.25.3

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: bbb7c5376d7ba7cf03099123c5aab9f12dd9a10efca9906fd07ea2782b28d296
4
- data.tar.gz: 8c82d88e763cdf7e765fec8837b22ab5dc8a5a7cbaca01d9ffff59c8328a1e99
3
+ metadata.gz: ec5991dad0128d2383500176d9872d4fda5ac504f81826eeac343a1b8435616b
4
+ data.tar.gz: f62dd7104119cd637c2ca590bc8c3e7c7c6c32e8f85d2d0e7e2cc2cbd2e8b019
5
5
  SHA512:
6
- metadata.gz: f886aedfbed678ce466c545e9f20255320a5ac77930c60d164613e833345455c52de0256f9c1a9357cda6040a79c2f17ed6aa687c5b8f135c6d355a50ecea5b6
7
- data.tar.gz: be66be79539f4f284c8741ec634f17c4e5a12d18e4cc182209be5ac5f8988101589b10dfe331234cc1bb6880aa1a2c0af0ffbe4817b9ab382dd3fbcb2d6fd6b1
6
+ metadata.gz: 0ea8e0cf1e8750265f971b7379db7fddbc8c425d7f765835bc1d2b23e0fdf79001b2ceb93cc70e5647999e4327f6aa0ae3e56ed6708cdd8c66ea8e0006ddb039
7
+ data.tar.gz: bd758b33c296cd20fd90429d269fb079b5a1501b074c854d1dcfe27c007c2234b509693e7a136abf801738c775ed7c64894e2e2bc7c7414499f14ec2bbd6b416
data/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## [0.25.3] - 2026-08-04
2
+
3
+ ### Fixed
4
+
5
+ - **Tools now respect the session's `parallel_tools` setting.** The agent
6
+ loop dispatched tool calls straight to `execute_parallel`, so tools always
7
+ ran in worker threads — even with `parallel_tools: false`. Sequential
8
+ sessions now run tools in the caller thread, which is what frameworks
9
+ like Rails rely on for per-request context (`CurrentAttributes` are
10
+ thread-local). The loop calls `ToolExecutor#execute`, which honors the
11
+ executor's `parallel` flag.
12
+ - **Parallel tool threads inherit the caller's thread-local state.** When
13
+ tools do run in threads (parallel mode), `execute_parallel` copies the
14
+ caller's `Thread.current` locals into each worker thread first, so
15
+ per-request context (Rails `CurrentAttributes`, log tags, etc.) reaches
16
+ the tools instead of being nil.
17
+ - `ToolExecutor#execute` accepts a `result_callback:` kwarg (invoked per
18
+ completed tool in both sequential and parallel modes); sequential
19
+ execution reports results through it too, matching parallel behavior.
20
+
21
+ ## [0.25.2] - 2026-08-03
22
+
23
+ ### Fixed
24
+
25
+ - **Streaming no longer depends on ActiveSupport's `String#truncate`.** The
26
+ SSE event serialization (streaming.rb) and the max-consecutive-tool-turns
27
+ summary (loop.rb) called `String#truncate`, which only exists when
28
+ ActiveSupport's core extensions are loaded — so a bare `require
29
+ "ask-agent"` raised `NoMethodError` as soon as a tool emitted a partial
30
+ result. Both call sites now use a plain-Ruby truncation helper.
31
+
32
+ ## [0.25.1] - 2026-08-02
33
+
34
+ ### Fixed
35
+
36
+ - **Session passes resolved tool instances to Chat.** Tool classes passed
37
+ as `tools: [MyTool]` were resolved for the session but handed to the
38
+ underlying Chat unresolved, so `ToolDef.from_tool` used `Class#name`
39
+ and raised `Ask::InvalidToolDefinition` on the first run. Sessions now
40
+ resolve tools before building the Chat; classes and instances both work.
41
+
1
42
  ## [0.25.0] — 2026-08-02
2
43
 
3
44
  ### Added
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # ask-agent
2
2
 
3
- Agent runtime for the ask-rb ecosystem. The core agent loop: think call tools → execute → feed back → repeat.
4
-
5
- Ported from `RubyLLM::Conductor` into the `Ask::Agent` namespace.
3
+ Agent runtime for the ask-rb ecosystem. Runs the core agent loop: think, call
4
+ tools, execute, feed results back, and repeat until the task is done. Built on
5
+ ask-core, ask-state-providers, ask-llm-providers, ask-tools, ask-skills, and
6
+ ask-instrumentation, and it powers the `askr` CLI.
6
7
 
7
8
  ## Installation
8
9
 
@@ -15,114 +16,12 @@ gem "ask-agent"
15
16
  ```ruby
16
17
  require "ask-agent"
17
18
 
18
- session = Ask::Agent::Session.new(
19
- model: "gpt-4o",
20
- tools: [Ask::Tools::Shell::Bash, Ask::Tools::Shell::Read]
21
- )
22
-
19
+ session = Ask::Agent::Session.new(model: "gpt-4o", max_turns: 25)
23
20
  response = session.run("What files are in the current directory?")
24
21
  puts response
25
22
  ```
26
23
 
27
- ## Components
28
-
29
- | Component | File | Purpose |
30
- |---|---|---|
31
- | `Ask::Agent::Session` | session.rb | Full agent loop — message → tool calls → results → follow-up |
32
- | `Ask::Agent::Loop` | loop.rb | Turn management, loop detection, max-turn guard |
33
- | `Ask::Agent::ToolExecutor` | tool_executor.rb | Parallel/sequential tool execution with retry and abort |
34
- | `Ask::Agent::Compactor` | compactor.rb | Context window management with proactive/overflow compaction |
35
- | `Ask::Agent::Hooks` | hooks.rb | Before/after tool lifecycle callbacks |
36
- | `Ask::Agent::Events` | events.rb | Data.define event types for streaming and monitoring |
37
- | `Ask::Agent::Telemetry` | telemetry.rb | File-backed telemetry for error tracking |
38
- | `Ask::Agent::Reflector` | reflector.rb | Assistant response self-evaluation |
39
- | `Ask::Agent::MetaAgent` | meta_agent.rb | LLM-powered self-improvement from telemetry |
40
- | `Ask::Agent::Evaluator` | evaluator.rb | Independent response evaluation with structured rubric — different model, isolated context |
41
- | `Ask::Agent::Configuration` | configuration.rb | Global config: model, turns, concurrency, evaluator |
42
-
43
- ## Evaluator
44
-
45
- Independent response evaluation with generator/evaluator separation. The
46
- evaluator uses a **separate model** (different from the session's model) and an
47
- **isolated context** to judge the agent's output — preventing the anti-pattern
48
- of a model grading its own work.
49
-
50
- ### Quick start
51
-
52
- ```ruby
53
- session = Ask::Agent::Session.new(
54
- model: "gpt-4o",
55
- evaluator: { model: "claude-sonnet-4", goal: "Write an email validator" }
56
- )
57
- session.run("Write email validation")
58
- ```
59
-
60
- ### Verdicts
61
-
62
- | Verdict | Behavior |
63
- |---------|----------|
64
- | `:accept` | Output passes — falls through to reflection |
65
- | `:revise` | Evaluator provides feedback; session runs another turn with it injected |
66
- | `:block` | Output is fundamentally wrong — returns blocked message, emits `EvaluationBlocked` |
67
-
68
- ### Configuration
69
-
70
- ```ruby
71
- # Set a global default evaluator model
72
- Ask::Agent.configure do |c|
73
- c.default_evaluator_model = "claude-sonnet-4"
74
- end
75
-
76
- # Then use evaluator: true to enable with the default
77
- session = Ask::Agent::Session.new(model: "gpt-4o", evaluator: true)
78
- ```
79
-
80
- ### Custom rubric
81
-
82
- ```ruby
83
- evaluator = Ask::Agent::Evaluator.new(
84
- model: "claude-sonnet-4",
85
- rubric: [
86
- Ask::Agent::Evaluator::Dimension.new(
87
- name: "performance",
88
- description: "Is the implementation efficient?",
89
- weight: 2
90
- )
91
- ]
92
- )
93
-
94
- result = evaluator.evaluate(
95
- goal: "Write an email validator",
96
- response: agent_output
97
- )
98
- result.accept? # => true/false
99
- result.scores # => { performance: 2 }
100
- result.feedback # => "Add edge case for unicode characters"
101
- ```
102
-
103
- ### Events
104
-
105
- The evaluator emits its own events during evaluation:
106
-
107
- ```ruby
108
- session.on_event do |event|
109
- case event
110
- when Ask::Agent::Events::EvaluationStart
111
- puts "Evaluating against: #{event.dimensions.join(', ')}"
112
- when Ask::Agent::Events::EvaluationDelta
113
- print event.content
114
- when Ask::Agent::Events::EvaluationEnd
115
- puts "Decision: #{event.decision}"
116
- puts "Scores: #{event.scores}"
117
- when Ask::Agent::Events::EvaluationBlocked
118
- puts "Blocked: #{event.feedback}"
119
- end
120
- end
121
- ```
122
-
123
- ## Events
124
-
125
- Stream session execution in real-time:
24
+ Stream execution in real time with events:
126
25
 
127
26
  ```ruby
128
27
  session.on_event do |event|
@@ -131,95 +30,17 @@ session.on_event do |event|
131
30
  print event.content
132
31
  when Ask::Agent::Events::ToolExecutionStart
133
32
  puts "\nRunning #{event.name}..."
134
- when Ask::Agent::Events::ToolExecutionEnd
135
- puts " → #{event.duration_ms}ms #{event.is_error ? 'error' : 'ok'}"
136
33
  end
137
34
  end
138
35
  ```
139
36
 
140
- ## Extensions
141
-
142
- Opt-in safety modules:
143
-
144
- - **Permissions** — Access control for tools. Supports named access modes (`:full_access`, `:read_only`, `:ask_before_changes`) or custom blocked-tool lists.
145
- - **RateLimiter** — Prevent runaway tool calls (configurable per-minute and per-turn limits)
146
- - **AuditLog** — Immutable, append-only log of every tool call
147
-
148
- ```ruby
149
- extensions = [
150
- Ask::Agent::Extensions::Permissions.new(mode: :read_only),
151
- Ask::Agent::Extensions::RateLimiter.new(max_calls_per_minute: 30),
152
- Ask::Agent::Extensions::AuditLog.new(path: "agent.log")
153
- ]
154
-
155
- session = Ask::Agent::Session.new(
156
- model: "gpt-4o",
157
- tools: [...],
158
- hooks: {
159
- before_tool: extensions.map(&:method(:before_tool_call)),
160
- after_tool: extensions.select { |e| e.respond_to?(:after_tool_call) }.map(&:method(:after_tool_call))
161
- }
162
- )
163
- ```
164
-
165
- ## Middleware
166
-
167
- Wrapping LLM provider calls with cross-cutting behavior:
168
-
169
- - **RetryOnFailure** — Retry on rate limits and server errors with exponential backoff
170
- - **ModelFallback** — Switch to a fallback model+provider on transient errors
171
- - **LogCalls** — Log every LLM provider call
172
- - **DefaultSettings** — Inject default generation parameters
173
-
174
- ```ruby
175
- Ask::Agent.configure do |c|
176
- c.middleware.use :retry_on_failure, max_retries: 3
177
- c.middleware.use :model_fallback, fallbacks: [
178
- { model: "claude-sonnet-4", provider: :anthropic },
179
- { model: "gemini-2.0-flash", provider: :google }
180
- ]
181
- c.middleware.use :log_calls, logger: Rails.logger
182
- c.middleware.use :default_settings, temperature: 0.7
183
- end
184
- ```
185
-
186
- ### ModelFallback
187
-
188
- When the primary LLM is overloaded or down, `ModelFallback` transparently switches to a backup model+provider. Credentials for each provider are resolved automatically.
189
-
190
- **Static fallbacks** — ordered list tried in sequence:
191
- ```ruby
192
- c.middleware.use :model_fallback, fallbacks: [
193
- { model: "claude-sonnet-4", provider: :anthropic },
194
- { model: "gemini-2.0-flash", provider: :google }
195
- ]
196
- ```
197
-
198
- **Dynamic fallbacks** — lambda that receives the error and request:
199
- ```ruby
200
- c.middleware.use :model_fallback, fallbacks: ->(error, request) {
201
- if request[:messages].sum { |m| m[:content].to_s.length } > 100_000
202
- [{ model: "claude-sonnet-4", provider: :anthropic }] # long-context
203
- else
204
- [{ model: "gpt-4o-mini", provider: :openai }] # cheaper
205
- end
206
- }
207
- ```
37
+ ## Declarative Agents
208
38
 
209
- **Custom eligible errors** by default rate limits, server errors, and service unavailable:
210
- ```ruby
211
- c.middleware.use :model_fallback,
212
- fallbacks: [{ model: "claude-sonnet-4", provider: :anthropic }],
213
- eligible_errors: [Ask::RateLimitError, Ask::ServerError]
214
- ```
215
-
216
- ## Agents
217
-
218
- Declarative agents follow a file convention. Each agent lives in a
219
- directory under `agents/` (or `app/agents/` in Rails); the directory
220
- name is the agent name, the file `agent.rb` defines the agent as a
221
- `<Name>::Agent < Ask::Agent::Definition` subclass, and a sibling
222
- `instructions.md` is auto-loaded as the system prompt.
39
+ Agents follow a file convention. Each agent lives in a directory under
40
+ `agents/` (or `app/agents/` in Rails); the directory name is the agent name,
41
+ the file `agent.rb` defines the agent as a `<Name>::Agent <
42
+ Ask::Agent::Definition` subclass, and a sibling `instructions.md` is
43
+ auto-loaded as the system prompt.
223
44
 
224
45
  ```
225
46
  agents/
@@ -246,10 +67,22 @@ agent = Ask::Agent.new("health_check")
246
67
  response = agent.run("Check server health")
247
68
  ```
248
69
 
249
- Shared tools for all agents go in `agents/shared/tools/`. Per-agent
250
- skills go in `agents/<name>/skills/`, shared skills in `agents/shared/skills/`.
70
+ Shared tools for all agents go in `agents/shared/tools/`. Per-agent skills go
71
+ in `agents/<name>/skills/`, shared skills in `agents/shared/skills/`.
72
+
73
+ ## Essential API
74
+
75
+ | Entry point | Purpose |
76
+ |---|---|
77
+ | `Ask::Agent::Session.new(model:, tools: [], max_turns: 25, ...)` | Full agent loop: message, tool calls, results, follow-up |
78
+ | `session.run(message)` | Run the loop for one message |
79
+ | `session.on_event { \|e\| }` | Stream `Ask::Agent::Events` (text deltas, tool execution, evaluation) |
80
+ | `Ask::Agent.new("name")` | Build a session from a declarative agent definition |
81
+ | `Ask.chat(message)` | One-shot chat without instantiating a Session |
82
+ | `Ask::Agent.configure { \|c\| ... }` | Global defaults: model, provider, turns, compactor, middleware |
83
+ | `askr` | CLI: `askr run <agent> [prompt]`, `askr list`, `askr schedule`, `askr new`, `askr skills` |
251
84
 
252
- ## Configuration
85
+ ### Configuration
253
86
 
254
87
  ```ruby
255
88
  Ask::Agent.configure do |c|
@@ -263,26 +96,23 @@ Ask::Agent.configure do |c|
263
96
  end
264
97
  ```
265
98
 
266
- `default_provider` pins which provider serves the default model when the
267
- model name doesn't uniquely identify one (for example, the same model id
268
- registered under multiple OpenAI-compatible providers). A `provider:`
269
- passed to `Session.new` or declared in an agent `Definition` always wins
270
- over the global default.
99
+ `default_provider` pins which provider serves the default model when the model
100
+ name doesn't uniquely identify one (for example, the same model id registered
101
+ under multiple OpenAI-compatible providers). A `provider:` passed to
102
+ `Session.new` or declared in an agent `Definition` always wins over the global
103
+ default.
271
104
 
272
- ## Persistence
105
+ ## Full documentation
273
106
 
274
- ```ruby
275
- store = Ask::Agent::Persistence::InMemory.new
276
- session = Ask::Agent::Session.new(model: "gpt-4o", persistence: store)
277
- session.run("Hello")
278
- session.save # persisted to store
279
- ```
107
+ The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs.
108
+ https://ask-rb.github.io/ask-docs/core/agent covers ask-agent in depth,
109
+ including the evaluator, middleware, extensions, cost tracking, and
110
+ persistence. API reference: https://ask-rb.github.io/ask-docs/reference/api.
280
111
 
281
112
  ## Development
282
113
 
283
- ```bash
114
+ bundle install
284
115
  bundle exec rake test
285
- ```
286
116
 
287
117
  ## License
288
118
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "time"
2
3
 
3
4
  module Ask
4
5
  module Agent
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "time"
2
3
 
3
4
  module Ask
4
5
  module Agent
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "time"
2
3
 
3
4
  module Ask
4
5
  module Agent
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "time"
4
5
 
5
6
  module Ask
6
7
  module Agent
@@ -79,12 +79,17 @@ module Ask
79
79
 
80
80
  if user_tool_calls.any?
81
81
  # Execute user tool calls locally
82
- user_results = tool_executor.execute_parallel(
83
- user_tool_calls, tools, hooks, event_emitter, ToolAbortController.new
84
- ) do |tool_call_id, result|
85
- tc = user_tool_calls[tool_call_id]
86
- chat.add_message(role: :tool, content: result[:message].to_s, tool_call_id: tool_call_id) if tc
87
- end
82
+ # Respect the session's parallel_tools setting: parallel tools run
83
+ # in threads (with the caller's thread-local context inherited);
84
+ # sequential tools run in the caller thread so per-request context
85
+ # (e.g. Rails CurrentAttributes) is visible without any copying.
86
+ user_results = tool_executor.execute(
87
+ user_tool_calls, tools, hooks: hooks, event_emitter: event_emitter,
88
+ result_callback: lambda do |tool_call_id, result|
89
+ tc = user_tool_calls[tool_call_id]
90
+ chat.add_message(role: :tool, content: result[:message].to_s, tool_call_id: tool_call_id) if tc
91
+ end
92
+ )
88
93
  all_tool_results.concat(user_results)
89
94
  end
90
95
 
@@ -94,7 +99,7 @@ module Ask
94
99
  end
95
100
 
96
101
  if @consecutive_tool_turns >= @max_consecutive_tool_turns
97
- summary = all_tool_results.map { |r| r[:message].to_s.truncate(80) }.first(2).join("; ")
102
+ summary = all_tool_results.map { |r| truncate(r[:message], 80) }.first(2).join("; ")
98
103
  return "Based on my investigation: #{summary}"
99
104
  end
100
105
 
@@ -137,6 +142,15 @@ module Ask
137
142
 
138
143
  private
139
144
 
145
+ # Truncate a string for summaries without depending on ActiveSupport's
146
+ # String#truncate (which is not loaded by a bare `require "ask-agent"`).
147
+ def truncate(text, length)
148
+ s = text.to_s
149
+ return s if s.length <= length
150
+
151
+ "#{s[0, length - 3]}..."
152
+ end
153
+
140
154
  def loop_detected?(results)
141
155
  return false if results.empty?
142
156
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "set"
4
4
  require "json"
5
+ require "time"
5
6
 
6
7
  module Ask
7
8
  module Agent
@@ -41,8 +41,8 @@ module Ask
41
41
 
42
42
  @telemetry = telemetry.is_a?(Telemetry) ? telemetry : Telemetry.new(enabled: !!telemetry)
43
43
 
44
- @chat = build_chat(model, system_prompt, tools, **chat_options)
45
44
  @tools = resolve_tools(tools)
45
+ @chat = build_chat(model, system_prompt, @tools, **chat_options)
46
46
  @loop = Loop.new(max_turns: max_turns)
47
47
  @tool_executor = ToolExecutor.new(max_retries: max_tool_retries, parallel: parallel_tools)
48
48
  @compactor = compactor ? build_compactor(compactor) : nil
@@ -82,6 +82,15 @@ module Ask
82
82
 
83
83
  private
84
84
 
85
+ # Truncate a string for telemetry without depending on ActiveSupport's
86
+ # String#truncate (which is not loaded by a bare `require "ask-agent"`).
87
+ def truncate(text, length)
88
+ s = text.to_s
89
+ return s if s.length <= length
90
+
91
+ "#{s[0, length - 3]}..."
92
+ end
93
+
85
94
  def run_with_block(session, prompt, mapping)
86
95
  errors = []
87
96
 
@@ -154,7 +163,7 @@ module Ask
154
163
  when Events::ToolExecutionStart
155
164
  { name: event.name, id: event.id, args: safe_args(event.arguments) }
156
165
  when Events::ToolExecutionUpdate
157
- { id: event.id, partial_result: event.partial_result.to_s.truncate(200) }
166
+ { id: event.id, partial_result: truncate(event.partial_result, 200) }
158
167
  when Events::ToolExecutionEnd
159
168
  { name: event.name, id: event.id, duration_ms: event.duration_ms, is_error: event.is_error }
160
169
  when Events::SessionEnd
@@ -3,6 +3,7 @@
3
3
  require "fileutils"
4
4
  require "json"
5
5
  require "securerandom"
6
+ require "time"
6
7
 
7
8
  module Ask
8
9
  module Agent
@@ -19,7 +19,7 @@ module Ask
19
19
 
20
20
  attr_writer :telemetry
21
21
 
22
- def execute(tool_calls, tools, hooks:, event_emitter:, session_id: nil)
22
+ def execute(tool_calls, tools, hooks:, event_emitter:, session_id: nil, result_callback: nil)
23
23
  return [] if tool_calls.empty?
24
24
 
25
25
  @total_executions = 0
@@ -27,9 +27,11 @@ module Ask
27
27
  sibling_abort = ToolAbortController.new
28
28
 
29
29
  if @parallel
30
- execute_parallel(tool_calls, tools, hooks, event_emitter, sibling_abort)
30
+ execute_parallel(tool_calls, tools, hooks, event_emitter, sibling_abort, &result_callback)
31
31
  else
32
- execute_sequential(tool_calls, tools, hooks, event_emitter, sibling_abort)
32
+ execute_sequential(tool_calls, tools, hooks, event_emitter, sibling_abort) do |id, result|
33
+ result_callback&.call(id, result)
34
+ end
33
35
  end
34
36
  end
35
37
 
@@ -38,8 +40,15 @@ module Ask
38
40
  mutex = Mutex.new
39
41
  results = {}
40
42
 
43
+ # Inherit the caller's thread-local state (Rails CurrentAttributes
44
+ # and similar frameworks store per-request context in Thread.current)
45
+ # so tools see the same context they would in a sequential run.
46
+ inherited_locals = {}
47
+ Thread.current.keys.each { |key| inherited_locals[key] = Thread.current[key] }
48
+
41
49
  tool_calls.each do |id, tool_call|
42
50
  threads << Thread.new do
51
+ inherited_locals.each { |key, value| Thread.current[key] = value }
43
52
  begin
44
53
  if sibling_abort.aborted?
45
54
  mutex.synchronize { results[id] = aborted_result(tool_call) }
@@ -75,13 +84,14 @@ module Ask
75
84
  tool_calls.keys.map { |id| results[id] }.compact
76
85
  end
77
86
 
78
- def execute_sequential(tool_calls, tools, hooks, event_emitter, sibling_abort)
87
+ def execute_sequential(tool_calls, tools, hooks, event_emitter, sibling_abort, &result_callback)
79
88
  results = []
80
89
  tool_calls.each do |id, tool_call|
81
90
  break if sibling_abort.aborted?
82
91
 
83
92
  result = execute_single_tool(tool_call, tools, hooks, event_emitter, sibling_abort)
84
93
  results << result
94
+ result_callback&.call(id, result)
85
95
  break if result[:critical_failure]
86
96
  break if result[:halted]
87
97
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module Agent
5
- VERSION = "0.25.0"
5
+ VERSION = "0.25.3"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.25.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto