ask-agent 0.25.2 → 0.25.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/CHANGELOG.md +30 -0
- data/lib/ask/agent/context_source.rb +1 -0
- data/lib/ask/agent/context_sources.rb +1 -0
- data/lib/ask/agent/definition.rb +12 -0
- data/lib/ask/agent/extensions/audit_log/active_record_writer.rb +1 -0
- data/lib/ask/agent/extensions/audit_log.rb +1 -0
- data/lib/ask/agent/loop.rb +11 -6
- data/lib/ask/agent/meta_agent.rb +1 -0
- data/lib/ask/agent/session.rb +12 -3
- data/lib/ask/agent/telemetry.rb +1 -0
- data/lib/ask/agent/tool_executor.rb +14 -4
- data/lib/ask/agent/version.rb +1 -1
- data/lib/ask/agent.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3703e22ceaf07962231c1cc08751e635d202a3dcb2bf7a0134ee3c1acbeeb975
|
|
4
|
+
data.tar.gz: d013b9b6a4d188dc71dd6c267388c1b36d83be5b43c0967d73b8cb10843d2123
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7bf19be96f3344b84b0c8ee7c2079e3eb1ad0a2655c26e5075652a863f66c1c47816d6b4418ba3f29b79b3722e8cf924bedac9aa9965da4099009b4993877b7b
|
|
7
|
+
data.tar.gz: f214e8356c0e55be0aba092d81c0a0ac79d57b79abf41ce1f2e59db45a73a8ac51fcd0566d6335931a4d68292cba894b3240d4dbda4175c9238ba6f7fc7d4639
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
## [0.25.4] - 2026-08-05
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **`skills_disclosure` opt-out for progressive skill disclosure.** Sessions
|
|
6
|
+
auto-inject the `load_skill` tool by default; agents with a fixed tool
|
|
7
|
+
surface (e.g. voice receptionists) can declare `skills_disclosure false`
|
|
8
|
+
in their definition (or pass `skills_disclosure: false` to
|
|
9
|
+
`Session.new`) to keep the tool payload minimal and deterministic.
|
|
10
|
+
|
|
11
|
+
## [0.25.3] - 2026-08-04
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Tools now respect the session's `parallel_tools` setting.** The agent
|
|
16
|
+
loop dispatched tool calls straight to `execute_parallel`, so tools always
|
|
17
|
+
ran in worker threads — even with `parallel_tools: false`. Sequential
|
|
18
|
+
sessions now run tools in the caller thread, which is what frameworks
|
|
19
|
+
like Rails rely on for per-request context (`CurrentAttributes` are
|
|
20
|
+
thread-local). The loop calls `ToolExecutor#execute`, which honors the
|
|
21
|
+
executor's `parallel` flag.
|
|
22
|
+
- **Parallel tool threads inherit the caller's thread-local state.** When
|
|
23
|
+
tools do run in threads (parallel mode), `execute_parallel` copies the
|
|
24
|
+
caller's `Thread.current` locals into each worker thread first, so
|
|
25
|
+
per-request context (Rails `CurrentAttributes`, log tags, etc.) reaches
|
|
26
|
+
the tools instead of being nil.
|
|
27
|
+
- `ToolExecutor#execute` accepts a `result_callback:` kwarg (invoked per
|
|
28
|
+
completed tool in both sequential and parallel modes); sequential
|
|
29
|
+
execution reports results through it too, matching parallel behavior.
|
|
30
|
+
|
|
1
31
|
## [0.25.2] - 2026-08-03
|
|
2
32
|
|
|
3
33
|
### Fixed
|
data/lib/ask/agent/definition.rb
CHANGED
|
@@ -92,6 +92,18 @@ module Ask
|
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
# Set or get the progressive skill disclosure flag. When enabled
|
|
96
|
+
# (default), sessions include the load_skill tool so the model can
|
|
97
|
+
# pull in skills on demand. Voice/UI agents with a fixed tool
|
|
98
|
+
# surface can disable it to keep the payload minimal.
|
|
99
|
+
def skills_disclosure(value = :__no_value__)
|
|
100
|
+
if value == :__no_value__
|
|
101
|
+
_config.key?(:skills_disclosure) ? _config[:skills_disclosure] : true
|
|
102
|
+
else
|
|
103
|
+
_config[:skills_disclosure] = value
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
95
107
|
# Set an arbitrary Session option. Accepts any key that
|
|
96
108
|
# Ask::Agent::Session.new understands.
|
|
97
109
|
#
|
data/lib/ask/agent/loop.rb
CHANGED
|
@@ -79,12 +79,17 @@ module Ask
|
|
|
79
79
|
|
|
80
80
|
if user_tool_calls.any?
|
|
81
81
|
# Execute user tool calls locally
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
data/lib/ask/agent/meta_agent.rb
CHANGED
data/lib/ask/agent/session.rb
CHANGED
|
@@ -21,12 +21,14 @@ module Ask
|
|
|
21
21
|
compactor: nil, hooks: {}, state: nil, persistence: nil,
|
|
22
22
|
id: nil, system_prompt: nil, parallel_tools: true,
|
|
23
23
|
reflector: nil, telemetry: true, meta_agent: nil,
|
|
24
|
-
agent_dir: nil, evaluator: nil, audit_log: nil,
|
|
24
|
+
agent_dir: nil, evaluator: nil, audit_log: nil,
|
|
25
|
+
skills_disclosure: true, **chat_options)
|
|
25
26
|
@id = id || SecureRandom.uuid
|
|
26
27
|
@agent_dir = agent_dir
|
|
27
28
|
@max_turns = max_turns
|
|
28
29
|
@max_tool_retries = max_tool_retries
|
|
29
30
|
@parallel_tools = parallel_tools
|
|
31
|
+
@skills_disclosure = skills_disclosure
|
|
30
32
|
@event_handlers = { all: [] }
|
|
31
33
|
@running = false
|
|
32
34
|
@deleted = false
|
|
@@ -356,13 +358,20 @@ module Ask
|
|
|
356
358
|
tool.is_a?(Class) ? tool.new : tool
|
|
357
359
|
end
|
|
358
360
|
# Always include the load_skill tool for progressive skill disclosure,
|
|
359
|
-
# unless
|
|
360
|
-
|
|
361
|
+
# unless disabled by the agent (voice agents keep a minimal tool
|
|
362
|
+
# surface) or the test framework is loaded (test mode keeps tools
|
|
363
|
+
# deterministic)
|
|
364
|
+
if skills_disclosure_enabled?
|
|
361
365
|
resolved << Skills::LoadSkillTool.new(registry: @skills_registry) unless resolved.any? { |t| t.name == "load_skill" }
|
|
362
366
|
end
|
|
363
367
|
resolved
|
|
364
368
|
end
|
|
365
369
|
|
|
370
|
+
# Whether progressive skill disclosure is active for this session.
|
|
371
|
+
def skills_disclosure_enabled?
|
|
372
|
+
@skills_disclosure && !(defined?(Ask::Agent::Test) && Ask::Agent::Test)
|
|
373
|
+
end
|
|
374
|
+
|
|
366
375
|
def build_compactor(config)
|
|
367
376
|
global = Ask::Agent.configuration
|
|
368
377
|
compactor = Compactor.new(
|
data/lib/ask/agent/telemetry.rb
CHANGED
|
@@ -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
|
data/lib/ask/agent/version.rb
CHANGED
data/lib/ask/agent.rb
CHANGED
|
@@ -170,6 +170,7 @@ module Ask
|
|
|
170
170
|
session_opts[:provider] = config[:provider] if config[:provider]
|
|
171
171
|
session_opts[:max_turns] = config[:max_turns] if config[:max_turns]
|
|
172
172
|
session_opts[:parallel_tools] = config[:parallel_tools] if config.key?(:parallel_tools)
|
|
173
|
+
session_opts[:skills_disclosure] = config[:skills_disclosure] if config.key?(:skills_disclosure)
|
|
173
174
|
|
|
174
175
|
# Pass arbitrary session options
|
|
175
176
|
if config[:options]
|