ask-agent 0.25.2 → 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: d6d5dd31344228811bd4c7993547c9e0f9d7c39c779f58250d82ad95f271bd7d
4
- data.tar.gz: ba08c57e9fc7dfc410e6a835ffab1076d1f4b5704bee866f7a38e158da712d34
3
+ metadata.gz: ec5991dad0128d2383500176d9872d4fda5ac504f81826eeac343a1b8435616b
4
+ data.tar.gz: f62dd7104119cd637c2ca590bc8c3e7c7c6c32e8f85d2d0e7e2cc2cbd2e8b019
5
5
  SHA512:
6
- metadata.gz: 5d46b653b22b50dee9597238cc65ce5fe72c3840322e1ecd03efccfe6494a4494bff473f3bc3c4eff1ae56df849142a45f52bc77e23c9a93a9c240f465d7db68
7
- data.tar.gz: 9c158ab0aa0be906723681458ea54d90b15cd7289647e7acb8527faf367ead4490919a84b0a2e71186b4b93945ac065db063a33ead26b2532d0d50efd2dd8bf3
6
+ metadata.gz: 0ea8e0cf1e8750265f971b7379db7fddbc8c425d7f765835bc1d2b23e0fdf79001b2ceb93cc70e5647999e4327f6aa0ae3e56ed6708cdd8c66ea8e0006ddb039
7
+ data.tar.gz: bd758b33c296cd20fd90429d269fb079b5a1501b074c854d1dcfe27c007c2234b509693e7a136abf801738c775ed7c64894e2e2bc7c7414499f14ec2bbd6b416
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
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
+
1
21
  ## [0.25.2] - 2026-08-03
2
22
 
3
23
  ### Fixed
@@ -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
 
@@ -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
@@ -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.2"
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.2
4
+ version: 0.25.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto