|
276 | 276 | :approval-override nil |
277 | 277 | :hook-rejected? false |
278 | 278 | :arguments-modified? true} |
279 | | - plan))))))) |
| 279 | + plan)))))) |
| 280 | + |
| 281 | + (testing "ask_user with allowed config sends :ask to preToolCall hook" |
| 282 | + (h/reset-components!) |
| 283 | + (let [tool-call {:id "call-1" |
| 284 | + :full-name "eca__ask_user" |
| 285 | + :arguments {"question" "Why?"}} |
| 286 | + all-tools [{:name "ask_user" |
| 287 | + :full-name "eca__ask_user" |
| 288 | + :origin :eca |
| 289 | + :server {:name "eca"}}] |
| 290 | + db (h/db) |
| 291 | + config (h/config) |
| 292 | + agent-name :default |
| 293 | + chat-id "test-chat" |
| 294 | + hook-data* (atom nil)] |
| 295 | + (with-redefs [f.tools/approval (constantly :allow) |
| 296 | + f.hooks/trigger-if-matches! (fn [event data _ _ _] |
| 297 | + (when (= event :preToolCall) |
| 298 | + (reset! hook-data* data)))] |
| 299 | + (let [plan (#'tc/decide-tool-call-action tool-call all-tools db config agent-name chat-id)] |
| 300 | + (is (= :ask (:approval @hook-data*)) |
| 301 | + "preToolCall hook receives :ask so notification hooks fire while waiting on user") |
| 302 | + (is (match? {:decision :allow |
| 303 | + :hook-rejected? false} |
| 304 | + plan) |
| 305 | + "tool actually executes (:allow) so the question still reaches the user"))))) |
| 306 | + |
| 307 | + (testing "ask_user with denied config keeps :deny in preToolCall hook" |
| 308 | + (h/reset-components!) |
| 309 | + (let [tool-call {:id "call-1" |
| 310 | + :full-name "eca__ask_user" |
| 311 | + :arguments {"question" "Why?"}} |
| 312 | + all-tools [{:name "ask_user" |
| 313 | + :full-name "eca__ask_user" |
| 314 | + :origin :eca |
| 315 | + :server {:name "eca"}}] |
| 316 | + db (h/db) |
| 317 | + config (h/config) |
| 318 | + agent-name :default |
| 319 | + chat-id "test-chat" |
| 320 | + hook-data* (atom nil)] |
| 321 | + (with-redefs [f.tools/approval (constantly :deny) |
| 322 | + f.hooks/trigger-if-matches! (fn [event data _ _ _] |
| 323 | + (when (= event :preToolCall) |
| 324 | + (reset! hook-data* data)))] |
| 325 | + (let [plan (#'tc/decide-tool-call-action tool-call all-tools db config agent-name chat-id)] |
| 326 | + (is (= :deny (:approval @hook-data*)) |
| 327 | + "explicit :deny is preserved, not overridden to :ask") |
| 328 | + (is (= :deny (:decision plan))))))) |
| 329 | + |
| 330 | + (testing "non ask_user tool keeps :allow in preToolCall hook (regression)" |
| 331 | + (h/reset-components!) |
| 332 | + (let [tool-call {:id "call-1" |
| 333 | + :full-name "eca__test_tool" |
| 334 | + :arguments {:foo "bar"}} |
| 335 | + all-tools [{:name "test_tool" |
| 336 | + :full-name "eca__test_tool" |
| 337 | + :origin :eca |
| 338 | + :server {:name "eca"}}] |
| 339 | + db (h/db) |
| 340 | + config (h/config) |
| 341 | + agent-name :default |
| 342 | + chat-id "test-chat" |
| 343 | + hook-data* (atom nil)] |
| 344 | + (with-redefs [f.tools/approval (constantly :allow) |
| 345 | + f.hooks/trigger-if-matches! (fn [event data _ _ _] |
| 346 | + (when (= event :preToolCall) |
| 347 | + (reset! hook-data* data)))] |
| 348 | + (#'tc/decide-tool-call-action tool-call all-tools db config agent-name chat-id) |
| 349 | + (is (= :allow (:approval @hook-data*)) |
| 350 | + "ask_user override does not leak to other tools"))))) |
280 | 351 |
|
281 | 352 | (deftest on-tools-called!-returns-provider-auth-test |
282 | 353 | (testing "returns refreshed provider auth in result after token is renewed during tool execution" |
|
0 commit comments