Compare commits
522 Commits
ddad7166ad
..
skills
| Author | SHA1 | Date | |
|---|---|---|---|
| 68a912ec38 | |||
| f405ec5e16 | |||
| b997e9493c | |||
| 8d6e9bef32 | |||
| e54a2e42c9 | |||
| b1696c3425 | |||
| feef3f67b5 | |||
| dc066bee0d | |||
| 6c4e042dad | |||
| 30f3b01358 | |||
| ebf3b5f776 | |||
| 84dcb3078b | |||
| 7b320e08c4 | |||
| 7078280b3d | |||
| 43607dbe8d | |||
| 8f7a57f8e6 | |||
| 40fdf3aaa7 | |||
| 46d4b78ccc | |||
| b0a3b0a9a5 | |||
| 53b3ce9ab1 | |||
| 44f533018e | |||
| bbb23f4884 | |||
| 8de0eef4f9 | |||
| 73a4499c68 | |||
| 97100bee29 | |||
| 9a25438643 | |||
| f6da937c5d | |||
| eeaeb42c9a | |||
| 1dde7f4442 | |||
| 9879980304 | |||
| 7ec81ae607 | |||
| dac2a16677 | |||
| 260bf4e5bc | |||
| ece66448e0 | |||
| a254d60876 | |||
| c36c4f4699 | |||
| 4a14d80d97 | |||
| c6a9268856 | |||
| 2914a1070b | |||
| 5ebf8649a6 | |||
| 0272412334 | |||
| 7a7824be6a | |||
| aa2d4f3265 | |||
| 28a283283f | |||
| 652ab0b180 | |||
| 8ad764527d | |||
| bba094086d | |||
| 658ca7fec3 | |||
| 156de15a33 | |||
| 695a684b8d | |||
| 307e2cfc50 | |||
| ed59f793fc | |||
| c17db05f39 | |||
| b1782b614f | |||
| 2acff31213 | |||
| a564085449 | |||
| 2d5cdb96d2 | |||
| 5a47a6637f | |||
| 625a251931 | |||
| d0ebe7408f | |||
| 976ba7066d | |||
| ff3789f869 | |||
| 744dd213f5 | |||
| f6b4bf05b6 | |||
| 94e3c3535c | |||
| 31b44fbeb7 | |||
| 07f4b134b6 | |||
| 5c374bb5bf | |||
| 0f90dd5f53 | |||
| d07caf2a4b | |||
| 81a2bd1d00 | |||
| 5fa6ffb81d | |||
| 1faab15377 | |||
| a4ddc3d65d | |||
| 588c69ea6c | |||
| bf8dad2a4f | |||
| 2e06c0e7d2 | |||
| de42cae87f | |||
| cdc4bd154a | |||
| aa2e627a5f | |||
| 3359c62429 | |||
| 75a6a5e145 | |||
| a9cad501ff | |||
| 26584c7500 | |||
| 62fdf4a2b5 | |||
| 296aa6f50f | |||
| 93cc498731 | |||
| b1cd8351fa | |||
| ccf5e73341 | |||
| be5d280c32 | |||
| 6633a8c0bf | |||
| 097d8936e3 | |||
| 8a53b7934b | |||
| 0facb15e32 | |||
| c172736362 | |||
| 4a2b9fa42a | |||
| 98db37866c | |||
| ad31fbd169 | |||
| d69e28fd39 | |||
| 279eaa5300 | |||
| e687d78931 | |||
| 0c2e4df647 | |||
| 6221875f64 | |||
| 895b9c27db | |||
| e661ca2eda | |||
| 7066edd904 | |||
| 61bdf29bea | |||
| ef39c7d9ff | |||
| e9e46158e7 | |||
| 34dc4b0dce | |||
| cd226577e7 | |||
| b5fc633454 | |||
| 484b18ef16 | |||
| 7333046cfe | |||
| 815f0e5c39 | |||
| dacccbfcf7 | |||
| 5370637274 | |||
| e6da252a5a | |||
| 4aaff21f45 | |||
| 2678afe02b | |||
| 558b764db8 | |||
| 0bb312a85c | |||
| d81d233527 | |||
| 597f823bdf | |||
| 81c037515e | |||
| 3c7d19da07 | |||
| 4536d00067 | |||
| 98d16d9a56 | |||
| 26de81e84e | |||
| 20c28b55d5 | |||
| 7d6f1dda26 | |||
| 9a061944ae | |||
| 1f50af0974 | |||
| bdacf9fc78 | |||
| a9f2a5edc2 | |||
| 2df8b1a541 | |||
| de055bf8a4 | |||
| 8fb0eece4b | |||
| ba03c3037d | |||
| afa0e4af67 | |||
| 5a9a00bc6f | |||
| e7bb668ac7 | |||
| 04498b96ec | |||
| eb2843d38a | |||
| 696ce03ee4 | |||
| a3d67bfbf7 | |||
| 5bd0766a60 | |||
| 35e1b14843 | |||
| 503c9b4699 | |||
| 7a8b09542d | |||
| da5cd21c1c | |||
| 27fcb1fc15 | |||
| e292c414c5 | |||
| 8a2f18204f | |||
| c70ac98223 | |||
| 249d1fc881 | |||
| 3f4fd91b3f | |||
| 48c52b5829 | |||
| f58f751c59 | |||
| fc7fdc98b4 | |||
| f4d7d0fb73 | |||
| 4b38f53488 | |||
| 186422ff58 | |||
| 9bc4f8b621 | |||
| 84497d3d65 | |||
| 3ea9116a23 | |||
| bfcd73c32a | |||
| 3cd3ba55ff | |||
| 3535edba79 | |||
| bf0343e245 | |||
| b001ae4c18 | |||
| 9ce088a530 | |||
| 16f3f71188 | |||
| 0af5fa02f9 | |||
| d6a0676264 | |||
| b582bab17c | |||
| a8732c63d6 | |||
| 389d0b768f | |||
| 70a251a7e2 | |||
| 462f136596 | |||
| bf9d7d750e | |||
| 540ec648c9 | |||
| e69352ee2d | |||
| ee4e3bc13f | |||
| a576961bd6 | |||
| 59c7fc1276 | |||
| bcf512fcfc | |||
| 195401c496 | |||
| 34d8d20ec6 | |||
| 08ba6f0446 | |||
| 26984892af | |||
| 526a426073 | |||
| c53e0546d4 | |||
| 349b3748bd | |||
| e23e5f9f7b | |||
| 8d02782de6 | |||
| 27ceefdb40 | |||
| 5168eb6781 | |||
| ddb73a9a33 | |||
| 53eff10d75 | |||
| 1df6114ff3 | |||
| 975484cc2b | |||
| 0421c9b643 | |||
| fb69c21252 | |||
| 0cb9122d16 | |||
| c164ad3cbb | |||
| 9b4171a468 | |||
| 5cae4e44fb | |||
| a145a42b2b | |||
| 715807645a | |||
| 1259c6865f | |||
| ff42460cb4 | |||
| 39a16f8d56 | |||
| 83de60f59c | |||
| cf60e090a5 | |||
| 0fb37c33ab | |||
| d81508c22a | |||
| 883ac659b2 | |||
| c6c10b5e24 | |||
| a4e5bef1b7 | |||
| f72c7b03f9 | |||
| bd6f709374 | |||
| 00f2201157 | |||
| b3f0d66071 | |||
| 8730d413bc | |||
| 79140fda3c | |||
| 67e749ea3a | |||
| 7bcfc133ae | |||
| e3e246607e | |||
| 16104cb2c5 | |||
| 224e51c386 | |||
| b022ca089c | |||
| 0ebb761c09 | |||
| c8067828d5 | |||
| 30eedd9b8c | |||
| d701b45057 | |||
| 722c9c101e | |||
| 86aa45f0c4 | |||
| cf45dc4820 | |||
| db77034431 | |||
| abdaec11b0 | |||
| 95fb349656 | |||
| d0b6b6c324 | |||
| d74c23ccf5 | |||
| ea1cfda0d6 | |||
| 5623f47f9a | |||
| e4df9ec193 | |||
| a6306d6b76 | |||
| 64529ba5cc | |||
| cc7f963b89 | |||
| 0ce86af116 | |||
| 2cb0ed3f64 | |||
| fb61854f11 | |||
| 53ba3344b1 | |||
| e20c8be8bb | |||
| 894dcb1d3c | |||
| 9a9e890f8a | |||
| 818ea634f0 | |||
| 780460f8d8 | |||
| e19483a920 | |||
| aca93f1cae | |||
| 1371a4aad2 | |||
| db4a45c0f6 | |||
| e95b1e5f82 | |||
| 15f4008f4b | |||
| f45f81fb45 | |||
| 2220fd2542 | |||
| 564480e165 | |||
| 297c63d91a | |||
| 26e2cd3f65 | |||
| 9f899466d4 | |||
| 38393ea4cf | |||
| a4f25826e3 | |||
| 93484fb33f | |||
| c90f003f92 | |||
| 24793b9b8d | |||
| 78e772f455 | |||
| 1e0d269aad | |||
| f6b1d408fc | |||
| 442b318b6c | |||
| a7c97aedb7 | |||
| 746f9e7b24 | |||
| 0d6c61af5c | |||
| 673f31c059 | |||
| 369a4f0a89 | |||
| 8d54eae4d0 | |||
| a805d5beab | |||
| dbb2aec8b6 | |||
| 1a98b76a1f | |||
| 51d10ab2b5 | |||
| 1aad750395 | |||
| e0aab6bd02 | |||
| 6cb93132b7 | |||
| 04126b99d6 | |||
| 0794eb960d | |||
| d619ad1d48 | |||
| 5b147e07b3 | |||
| 944ce441d8 | |||
| a7dcb8519b | |||
| d912d44fb3 | |||
| 4f7254a634 | |||
| bf923cb296 | |||
| d9f737e1bf | |||
| 59690d045e | |||
| 5d95acba53 | |||
| d46225d2a9 | |||
| 3af30a0e62 | |||
| 69eca4d96d | |||
| 7b2e4a83c9 | |||
| 344b80872a | |||
| ddf828ff5f | |||
| 4e170b069b | |||
| 22c75fb578 | |||
| 11ab9eb6b8 | |||
| 29b232f407 | |||
| 53e8c920e5 | |||
| 78d19bed4d | |||
| 10f4160635 | |||
| 7622836e8b | |||
| 4d4713a9fa | |||
| 25008599f9 | |||
| c00ab074f8 | |||
| aed1f1957f | |||
| c6a959e2e1 | |||
| 02b7ed37f6 | |||
| 0d84aaabb9 | |||
| 6efdcf9610 | |||
| 4266d317d8 | |||
| 4ce7aafcbd | |||
| 35d8b69f92 | |||
| 562057e608 | |||
| b7024e5340 | |||
| 088588231b | |||
| eff117d3d9 | |||
| 968c535709 | |||
| c8b6fa7b11 | |||
| 0aa334b54e | |||
| 78a49f841d | |||
| 43b2bd937e | |||
| a4326875ba | |||
| eb31a58346 | |||
| a6b0acc35d | |||
| cc7fcd0b5b | |||
| 02fe59b913 | |||
| 6fd5f47089 | |||
| 2a2922760e | |||
| a3793460fd | |||
| e0927a04d9 | |||
| 8665604bab | |||
| d4c3c135b3 | |||
| 60bd5e493c | |||
| 0753b2d841 | |||
| 17e6fbd692 | |||
| 0710441650 | |||
| 20a76cee3e | |||
| cb64785867 | |||
| e6e26103c4 | |||
| 15529a14f1 | |||
| 86839188e0 | |||
| 39701b378b | |||
| 45ff6da737 | |||
| a260dd1503 | |||
| 57859301df | |||
| 8c968d3f53 | |||
| 0034bfbe46 | |||
| a733b9247a | |||
| e0afa349b9 | |||
| 7d0ce94907 | |||
| 9045763c35 | |||
| 29898552d7 | |||
| 9d7c2f5c2f | |||
| 5c0fa42351 | |||
| ab045b0ef3 | |||
| 41e6843db1 | |||
| 911ec3c9b9 | |||
| fc6f0a1a7b | |||
| 21873da278 | |||
| d1cd6be2c9 | |||
| 0c0ae41bca | |||
| c9ed7a904a | |||
| d200a8f554 | |||
| 3d04c8fcf1 | |||
| f53f165d91 | |||
| e5645e4064 | |||
| 95e15ca8c4 | |||
| dbf7329e87 | |||
| ed6c3ae431 | |||
| 214d2ecc67 | |||
| 29c95671de | |||
| 238f93a096 | |||
| c76877e7b3 | |||
| 12e5a9c5aa | |||
| 7f4be2ca3f | |||
| 29ffe12d8c | |||
| d34bed4f15 | |||
| aec7ea7e80 | |||
| 5938e1af29 | |||
| 60902297c5 | |||
| 12a95aa6fa | |||
| 78fc459a97 | |||
| 281565804c | |||
| 33a32fd9c8 | |||
| b64aad55e9 | |||
| 2392958114 | |||
| ec04e8e24a | |||
| 4e14ee7f50 | |||
| 7ba4ab0608 | |||
| fd816112fb | |||
| d0ee85be40 | |||
| 9448704af3 | |||
| 9dad9d6ca8 | |||
| 3f41abed7c | |||
| debcbab445 | |||
| 7fcabf1de7 | |||
| e116a1841d | |||
| cd3103ca14 | |||
| 50d07a4b13 | |||
| ed1352936e | |||
| f4b4156a0c | |||
| 5cf2cce0e3 | |||
| 249453d829 | |||
| c14939cecc | |||
| 72f516abb1 | |||
| 66478ed264 | |||
| 6b10dff41d | |||
| f8cc736482 | |||
| a0794fecfc | |||
| c68059e5b3 | |||
| 832ca6b0de | |||
| 89ee43830e | |||
| f7cf13901e | |||
| ad41fa93fb | |||
| 617b7dcd49 | |||
| 417ea032c4 | |||
| b77bb6e200 | |||
| 1fa3b4a600 | |||
| 99bd502f62 | |||
| 25a271dc95 | |||
| 5002ac7716 | |||
| d92a559460 | |||
| 3d571e1a31 | |||
| d338daa4b6 | |||
| 6f802c2a58 | |||
| a3f0168817 | |||
| 677702655f | |||
| b0bbd0c083 | |||
| 5cbf23a1f4 | |||
| 39eb9b34ec | |||
| 5da8616518 | |||
| b267fe05cd | |||
| 29f7ebe559 | |||
| bbffaca511 | |||
| 80532836c3 | |||
| 9474f4f322 | |||
| 93a09d3a9f | |||
| e3935ce699 | |||
| 58c15e7833 | |||
| fd2b7f3aa0 | |||
| 5ccbc629d1 | |||
| e98ff5e8e5 | |||
| a6fffa7b57 | |||
| 3ac153dd06 | |||
| 8db3108c94 | |||
| e25ff4ad19 | |||
| 21e76c6461 | |||
| 103aa1a432 | |||
| d2f4fefcf3 | |||
| 629527988d | |||
| 7f520f1346 | |||
| e28619b55a | |||
| f474e6130e | |||
| 4b5bcb45ac | |||
| 50565a0f17 | |||
| cf37db4fa2 | |||
| ad9b4097ef | |||
| c22c01c6c3 | |||
| 31f7f50c4a | |||
| a7f6ed4b16 | |||
| 73ada5a221 | |||
| 2f96256893 | |||
| 23d9e0775f | |||
| 72ade39144 | |||
| ec64c68777 | |||
| 80932e069f | |||
| 2f9b154b07 | |||
| 20bf911732 | |||
| 65a3dbb228 | |||
| 5844cc93ca | |||
| 4d23ce58c4 | |||
| 2bb592d5f6 | |||
| 3146b20c15 | |||
| 455cf67750 | |||
| a6d6a877b0 | |||
| a7bd54471c | |||
| fe5f803163 | |||
| 66a9b5362a | |||
| f3569cf68b | |||
| 2573f14726 | |||
| f1fb2d6abf | |||
| 4934e0ff0a | |||
| f772a80501 | |||
| 8950843be2 | |||
| 9b89e68908 | |||
| ba134ca53f | |||
| 21dbd9c057 | |||
| 40a68f8e05 | |||
| 37d861a631 | |||
| 31f3e885ce | |||
| 7ffaab2012 | |||
| 35b7946b0d | |||
| 3a05a8e712 | |||
| 294a1149ef | |||
| 8d80370014 | |||
| 1cbdef36cf | |||
| 4c8accbfc1 | |||
| c4c2d9cb93 | |||
| 7aed112326 | |||
| 216a3d53cd | |||
| e0823b343b | |||
| cb0bc65ee4 | |||
| 5b9ab6636f | |||
| 9fd77feebb |
@@ -507,9 +507,7 @@ open_link() {
|
||||
|
||||
guard_operation() {
|
||||
if [[ -z "$AUTO_CONFIRM" && -z "$LLM_AGENT_VAR_AUTO_CONFIRM" ]]; then
|
||||
# 2>/dev/tty: keep the prompt off the host-captured stderr pipe so it
|
||||
# can't leak into tool_call_error JSON when the wrapped command fails.
|
||||
ans="$(confirm "${1:-Are you sure you want to continue?}" 2>/dev/tty)"
|
||||
ans="$(confirm "${1:-Are you sure you want to continue?}")"
|
||||
|
||||
if [[ "$ans" == 0 ]]; then
|
||||
error "Operation aborted!" 2>&1
|
||||
@@ -659,8 +657,7 @@ guard_path() {
|
||||
confirmation_prompt="$2"
|
||||
|
||||
if [[ ! "$path" == "$(pwd)"* && -z "$AUTO_CONFIRM" && -z "$LLM_AGENT_VAR_AUTO_CONFIRM" ]]; then
|
||||
# 2>/dev/tty: see guard_operation — prevents prompt text leaking via captured stderr.
|
||||
ans="$(confirm "$confirmation_prompt" 2>/dev/tty)"
|
||||
ans="$(confirm "$confirmation_prompt")"
|
||||
|
||||
if [[ "$ans" == 0 ]]; then
|
||||
error "Operation aborted!" >&2
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
---
|
||||
skills_enabled: false
|
||||
---
|
||||
As a professional Prompt Engineer, your role is to create effective and innovative prompts for interacting with AI models.
|
||||
|
||||
Your core skills include:
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
---
|
||||
skills_enabled: false
|
||||
---
|
||||
Create a concise, 3-6 word title.
|
||||
|
||||
**Notes**:
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
---
|
||||
skills_enabled: false
|
||||
---
|
||||
Provide a terse, single sentence description of the given shell command.
|
||||
Describe each argument and option of the command.
|
||||
Provide short responses in about 80 words.
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
---
|
||||
skills_enabled: false
|
||||
---
|
||||
Provide only {{__shell__}} commands for {{__os_distro__}} without any description.
|
||||
Ensure the output is a valid {{__shell__}} command.
|
||||
If there is a lack of details, provide most logical solution.
|
||||
|
||||
@@ -26,9 +26,6 @@ auto_continue: false # Enable automatic continuation when incomplete
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping
|
||||
inject_todo_instructions: true # Inject the default todo tool usage instructions into the agent's system prompt
|
||||
continuation_prompt: null # Custom prompt used when auto-continuing (optional; uses default if null)
|
||||
inject_skill_instructions: true # Inject a short hint pointing the model at `skill__list` when skills are enabled
|
||||
# (default: true). Suppressed automatically when no skills are available.
|
||||
skill_instructions: null # Custom text for the skill hint (optional; uses built-in default if null)
|
||||
# Sub-Agent Spawning System
|
||||
# Enable this agent to spawn and manage child agents in parallel.
|
||||
# See https://github.com/Dark-Alex-17/coyote/wiki/Agents for detailed documentation.
|
||||
|
||||
@@ -162,10 +162,6 @@ auto_continue: false # Enable automatic continuation when incomplet
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping (default: 10)
|
||||
inject_todo_instructions: true # Inject default todo usage instructions into the system prompt (default: true)
|
||||
continuation_prompt: null # Custom prompt used when auto-continuing. If null, uses built-in default
|
||||
inject_skill_instructions: true # Inject a short hint pointing the model at `skill__list` when skills are enabled in
|
||||
# this context. Only injected if `function_calling_support`, `skills_enabled`, and the
|
||||
# effective enabled skill set is non-empty (default: true).
|
||||
skill_instructions: null # Custom text used for the skill hint when injected. If null, uses built-in default.
|
||||
|
||||
# ---- Session ----
|
||||
# See the [Session documentation](https://github.com/Dark-Alex-17/coyote/wiki/Sessions) for more information
|
||||
|
||||
@@ -30,8 +30,5 @@ auto_continue: false # Enable automatic continuation when incom
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping (default: 10)
|
||||
inject_todo_instructions: true # Inject default todo tool usage instructions into the system prompt (default: true)
|
||||
continuation_prompt: null # Custom prompt used when auto-continuing. If null, uses built-in default
|
||||
inject_skill_instructions: true # Inject a short hint pointing the model at `skill__list` when skills are enabled
|
||||
# (default: true). Suppressed automatically when no skills are available.
|
||||
skill_instructions: null # Custom text for the skill hint (optional; uses built-in default if null)
|
||||
---
|
||||
You are an expert at doing things. This is where you write the instructions for the role.
|
||||
|
||||
@@ -63,9 +63,6 @@ enabled_skills:
|
||||
- code-review
|
||||
- git-master
|
||||
- ai-slop-remover
|
||||
inject_skill_instructions: true # Inject a hint pointing the model at `skill__list`. Defaults to true; suppressed
|
||||
# automatically when no skills are available.
|
||||
skill_instructions: null # Custom text for the skill hint (optional; uses the built-in default if omitted).
|
||||
|
||||
conversation_starters: # Suggested prompts surfaced in the UI
|
||||
- "Research the current state of WebAssembly outside the browser"
|
||||
@@ -176,12 +173,8 @@ nodes:
|
||||
# catches violations at load time). `skills_enabled: false` would
|
||||
# disable skills entirely for this node (no meta-tools exposed).
|
||||
# Nothing is auto-loaded: the model decides when to load a skill.
|
||||
skills_enabled: true # Whether skills are enabled on this llm node; defaults to 'true'
|
||||
enabled_skills:
|
||||
- ai-slop-remover
|
||||
inject_skill_instructions: true # Override skill-hint injection for just this node. Falls back to
|
||||
# agent/graph/global default when omitted.
|
||||
skill_instructions: null # Per-node skill-hint text override; uses the built-in default when omitted.
|
||||
output_schema: # Optional JSON Schema. The output is parsed to JSON
|
||||
type: object # and its top-level object keys auto-merge into state
|
||||
properties: # (so `topic` / `needs_deep_dive` become {{topic}} etc).
|
||||
|
||||
@@ -464,14 +464,6 @@ impl Agent {
|
||||
self.config.continuation_prompt.clone()
|
||||
}
|
||||
|
||||
pub fn inject_skill_instructions(&self) -> bool {
|
||||
self.config.inject_skill_instructions
|
||||
}
|
||||
|
||||
pub fn skill_instructions_value(&self) -> Option<String> {
|
||||
self.config.skill_instructions.clone()
|
||||
}
|
||||
|
||||
pub fn can_spawn_agents(&self) -> bool {
|
||||
self.config.can_spawn_agents
|
||||
}
|
||||
@@ -633,10 +625,6 @@ pub struct AgentConfig {
|
||||
pub inject_todo_instructions: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub inject_spawn_instructions: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub inject_skill_instructions: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skill_instructions: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub compression_threshold: Option<usize>,
|
||||
#[serde(default)]
|
||||
@@ -716,8 +704,6 @@ impl AgentConfig {
|
||||
mcp_servers: graph.mcp_servers.clone(),
|
||||
skills_enabled: graph.skills_enabled,
|
||||
enabled_skills: graph.enabled_skills.clone(),
|
||||
inject_skill_instructions: graph.inject_skill_instructions.unwrap_or(true),
|
||||
skill_instructions: graph.skill_instructions.clone(),
|
||||
conversation_starters: graph.conversation_starters.clone(),
|
||||
variables: graph.variables.clone(),
|
||||
can_spawn_agents: graph.has_agent_node(),
|
||||
|
||||
@@ -52,8 +52,6 @@ pub struct AppConfig {
|
||||
pub max_auto_continues: usize,
|
||||
pub inject_todo_instructions: bool,
|
||||
pub continuation_prompt: Option<String>,
|
||||
pub inject_skill_instructions: bool,
|
||||
pub skill_instructions: Option<String>,
|
||||
|
||||
pub repl_prelude: Option<String>,
|
||||
pub cmd_prelude: Option<String>,
|
||||
@@ -120,8 +118,6 @@ impl Default for AppConfig {
|
||||
max_auto_continues: 10,
|
||||
inject_todo_instructions: true,
|
||||
continuation_prompt: None,
|
||||
inject_skill_instructions: true,
|
||||
skill_instructions: None,
|
||||
|
||||
repl_prelude: None,
|
||||
cmd_prelude: None,
|
||||
@@ -189,8 +185,6 @@ impl AppConfig {
|
||||
max_auto_continues: config.max_auto_continues,
|
||||
inject_todo_instructions: config.inject_todo_instructions,
|
||||
continuation_prompt: config.continuation_prompt,
|
||||
inject_skill_instructions: config.inject_skill_instructions,
|
||||
skill_instructions: config.skill_instructions,
|
||||
|
||||
repl_prelude: config.repl_prelude,
|
||||
cmd_prelude: config.cmd_prelude,
|
||||
|
||||
+2
-6
@@ -6,7 +6,7 @@ mod install_remote;
|
||||
mod macros;
|
||||
mod mcp_factory;
|
||||
pub(crate) mod paths;
|
||||
pub(crate) mod prompts;
|
||||
mod prompts;
|
||||
mod rag_cache;
|
||||
mod request_context;
|
||||
mod role;
|
||||
@@ -28,7 +28,7 @@ pub use self::app_state::AppState;
|
||||
pub use self::input::Input;
|
||||
pub use self::install_remote::{install_remote, install_remote_from_repl_args};
|
||||
#[allow(unused_imports)]
|
||||
pub use self::request_context::{RenderMode, RequestContext, should_inject_skill_instructions};
|
||||
pub use self::request_context::{RenderMode, RequestContext};
|
||||
pub use self::role::{
|
||||
CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, Role, RoleLike, SHELL_ROLE,
|
||||
};
|
||||
@@ -214,8 +214,6 @@ pub struct Config {
|
||||
pub max_auto_continues: usize,
|
||||
pub inject_todo_instructions: bool,
|
||||
pub continuation_prompt: Option<String>,
|
||||
pub inject_skill_instructions: bool,
|
||||
pub skill_instructions: Option<String>,
|
||||
|
||||
pub repl_prelude: Option<String>,
|
||||
pub cmd_prelude: Option<String>,
|
||||
@@ -282,8 +280,6 @@ impl Default for Config {
|
||||
max_auto_continues: 10,
|
||||
inject_todo_instructions: true,
|
||||
continuation_prompt: None,
|
||||
inject_skill_instructions: true,
|
||||
skill_instructions: None,
|
||||
|
||||
repl_prelude: None,
|
||||
cmd_prelude: None,
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
use indoc::indoc;
|
||||
|
||||
pub(crate) const DEFAULT_SKILL_INSTRUCTIONS: &str = indoc! {"
|
||||
## Skills
|
||||
Specialized skills may be available in this context. Call `skill__list` early in a task to
|
||||
discover any that match the work, then `skill__load` the relevant ones. Their instructions and
|
||||
granted tools will become active for subsequent turns. Call `skill__unload` when their work is
|
||||
complete to keep the context lean."
|
||||
};
|
||||
|
||||
pub(in crate::config) const DEFAULT_TODO_INSTRUCTIONS: &str = indoc! {"
|
||||
## Task Tracking
|
||||
You have built-in task tracking tools. Use them to track your progress:
|
||||
|
||||
@@ -39,7 +39,6 @@ use indoc::formatdoc;
|
||||
use inquire::{Confirm, MultiSelect, Text, list_option::ListOption, validator::Validation};
|
||||
use log::warn;
|
||||
use parking_lot::RwLock;
|
||||
use prompts::DEFAULT_SKILL_INSTRUCTIONS;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::fs::{File, OpenOptions, read_dir, read_to_string, remove_dir_all, remove_file};
|
||||
use std::io::Write;
|
||||
@@ -54,20 +53,6 @@ pub struct AutoContinueConfig {
|
||||
pub continuation_prompt: Option<String>,
|
||||
}
|
||||
|
||||
pub struct SkillInstructionsConfig {
|
||||
pub inject: bool,
|
||||
pub instructions: Option<String>,
|
||||
}
|
||||
|
||||
/// Must stay in sync with the predicate that registers `skill__*` tools in `rebuild_tool_scope`
|
||||
/// (and in `graph::llm::run_llm_node`). Telling the model to call tools that are not exposed
|
||||
/// is a footgun. `compatible_enabled` is the post-filter universe that `skill__list` would
|
||||
/// actually return (cascade-allowed AND surviving `Skill::is_compatible` for current
|
||||
/// `mcp_server_support`), so an empty set means the hint has nothing to point at.
|
||||
pub fn should_inject_skill_instructions(app: &AppConfig, policy: &SkillPolicy) -> bool {
|
||||
app.function_calling_support && policy.skills_enabled && !policy.compatible_enabled.is_empty()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum RenderMode {
|
||||
#[default]
|
||||
@@ -649,62 +634,9 @@ impl RequestContext {
|
||||
self.agent.as_ref(),
|
||||
self.session.as_ref(),
|
||||
)?;
|
||||
|
||||
if should_inject_skill_instructions(app, &policy) {
|
||||
let config = self.skill_instructions_config();
|
||||
|
||||
if config.inject {
|
||||
let separator = if role.is_empty_prompt() { "" } else { "\n\n" };
|
||||
|
||||
role.append_to_prompt(separator);
|
||||
role.append_to_prompt(
|
||||
config
|
||||
.instructions
|
||||
.as_deref()
|
||||
.unwrap_or(DEFAULT_SKILL_INSTRUCTIONS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.skill_registry.effective_role(&role, &policy))
|
||||
}
|
||||
|
||||
pub fn skill_instructions_config(&self) -> SkillInstructionsConfig {
|
||||
if let Some(agent) = &self.agent {
|
||||
return SkillInstructionsConfig {
|
||||
inject: agent.inject_skill_instructions(),
|
||||
instructions: agent.skill_instructions_value(),
|
||||
};
|
||||
}
|
||||
|
||||
let app = &self.app.config;
|
||||
let inject = self
|
||||
.session
|
||||
.as_ref()
|
||||
.and_then(|s| s.inject_skill_instructions())
|
||||
.or_else(|| {
|
||||
self.role
|
||||
.as_ref()
|
||||
.and_then(|r| r.inject_skill_instructions())
|
||||
})
|
||||
.unwrap_or(app.inject_skill_instructions);
|
||||
let instructions = self
|
||||
.session
|
||||
.as_ref()
|
||||
.and_then(|s| s.skill_instructions().map(|v| v.to_string()))
|
||||
.or_else(|| {
|
||||
self.role
|
||||
.as_ref()
|
||||
.and_then(|r| r.skill_instructions().map(|v| v.to_string()))
|
||||
})
|
||||
.or_else(|| app.skill_instructions.clone());
|
||||
|
||||
SkillInstructionsConfig {
|
||||
inject,
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn auto_continue_config(&self) -> AutoContinueConfig {
|
||||
if let Some(agent) = &self.agent {
|
||||
return AutoContinueConfig {
|
||||
@@ -1275,8 +1207,7 @@ impl RequestContext {
|
||||
.iter()
|
||||
.filter(|v| {
|
||||
(v.name.starts_with(USER_FUNCTION_PREFIX)
|
||||
|| (!matches!(role.skills_enabled(), Some(false))
|
||||
&& v.name.starts_with(SKILL_FUNCTION_PREFIX)))
|
||||
|| v.name.starts_with(SKILL_FUNCTION_PREFIX))
|
||||
&& !existing.contains(&v.name)
|
||||
})
|
||||
.cloned()
|
||||
@@ -1776,7 +1707,7 @@ impl RequestContext {
|
||||
}
|
||||
|
||||
let value = match key {
|
||||
"continuation_prompt" | "skill_instructions" => raw_value,
|
||||
"continuation_prompt" => raw_value,
|
||||
_ => {
|
||||
if raw_value.contains(char::is_whitespace) {
|
||||
bail!("Usage: .set <key> <value>. If value is null, unset key.");
|
||||
@@ -1976,22 +1907,6 @@ impl RequestContext {
|
||||
self.update_app_config(|app| app.continuation_prompt = value);
|
||||
}
|
||||
}
|
||||
"inject_skill_instructions" => {
|
||||
let value: bool = value.parse().with_context(|| "Invalid value")?;
|
||||
if let Some(session) = self.session.as_mut() {
|
||||
session.set_inject_skill_instructions(Some(value));
|
||||
} else {
|
||||
self.update_app_config(|app| app.inject_skill_instructions = value);
|
||||
}
|
||||
}
|
||||
"skill_instructions" => {
|
||||
let value: Option<String> = super::parse_value(value)?;
|
||||
if let Some(session) = self.session.as_mut() {
|
||||
session.set_skill_instructions(value);
|
||||
} else {
|
||||
self.update_app_config(|app| app.skill_instructions = value);
|
||||
}
|
||||
}
|
||||
_ => bail!("Unknown key '{key}'"),
|
||||
}
|
||||
Ok(())
|
||||
@@ -2091,8 +2006,6 @@ impl RequestContext {
|
||||
"enabled_tools",
|
||||
"enabled_mcp_servers",
|
||||
"inject_todo_instructions",
|
||||
"inject_skill_instructions",
|
||||
"skill_instructions",
|
||||
"max_auto_continues",
|
||||
"save_session",
|
||||
"compression_threshold",
|
||||
@@ -2259,11 +2172,6 @@ impl RequestContext {
|
||||
super::complete_bool(config.inject_instructions)
|
||||
}
|
||||
"continuation_prompt" => vec!["null".to_string()],
|
||||
"inject_skill_instructions" => {
|
||||
let config = self.skill_instructions_config();
|
||||
super::complete_bool(config.inject)
|
||||
}
|
||||
"skill_instructions" => vec!["null".to_string()],
|
||||
_ => vec![],
|
||||
};
|
||||
values = candidates.into_iter().map(|v| (v, None)).collect();
|
||||
@@ -3073,12 +2981,11 @@ mod tests {
|
||||
use super::super::mcp_factory::McpFactory;
|
||||
use super::*;
|
||||
use crate::config::AppState;
|
||||
use crate::function::{ToolCall, skill};
|
||||
use crate::function::ToolCall;
|
||||
use crate::mcp::{McpServer, McpServersConfig, McpTransportType};
|
||||
use crate::utils;
|
||||
use crate::utils::get_env_name;
|
||||
use crate::vault::Vault;
|
||||
use serde_json::json;
|
||||
use serial_test::serial;
|
||||
use std::env;
|
||||
use std::fs::{create_dir_all, remove_dir_all, write};
|
||||
@@ -3216,108 +3123,6 @@ mod tests {
|
||||
assert_eq!(extracted.name(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_inject_skill_instructions_requires_function_calling() {
|
||||
let app = AppConfig {
|
||||
function_calling_support: false,
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
let policy = SkillPolicy {
|
||||
skills_enabled: true,
|
||||
enabled: ["a".to_string()].into_iter().collect(),
|
||||
compatible_enabled: ["a".to_string()].into_iter().collect(),
|
||||
};
|
||||
|
||||
assert!(!should_inject_skill_instructions(&app, &policy));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_inject_skill_instructions_requires_skills_enabled() {
|
||||
let app = AppConfig {
|
||||
function_calling_support: true,
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
let policy = SkillPolicy {
|
||||
skills_enabled: false,
|
||||
enabled: ["a".to_string()].into_iter().collect(),
|
||||
compatible_enabled: ["a".to_string()].into_iter().collect(),
|
||||
};
|
||||
|
||||
assert!(!should_inject_skill_instructions(&app, &policy));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_inject_skill_instructions_suppresses_when_no_compatible_skills() {
|
||||
let app = AppConfig {
|
||||
function_calling_support: true,
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
// `enabled` has names, but none survive the compatibility filter — hint must suppress.
|
||||
let policy = SkillPolicy {
|
||||
skills_enabled: true,
|
||||
enabled: ["a".to_string()].into_iter().collect(),
|
||||
compatible_enabled: Default::default(),
|
||||
};
|
||||
|
||||
assert!(!should_inject_skill_instructions(&app, &policy));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_inject_skill_instructions_when_all_conditions_met() {
|
||||
let app = AppConfig {
|
||||
function_calling_support: true,
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
let policy = SkillPolicy {
|
||||
skills_enabled: true,
|
||||
enabled: ["a".to_string()].into_iter().collect(),
|
||||
compatible_enabled: ["a".to_string()].into_iter().collect(),
|
||||
};
|
||||
|
||||
assert!(should_inject_skill_instructions(&app, &policy));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_instructions_config_falls_back_to_app_default() {
|
||||
let ctx = create_test_ctx();
|
||||
|
||||
let cfg = ctx.skill_instructions_config();
|
||||
|
||||
assert!(cfg.inject);
|
||||
assert!(cfg.instructions.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_instructions_config_respects_role_disable() {
|
||||
let mut ctx = create_test_ctx();
|
||||
let role = Role::new("r", "---\ninject_skill_instructions: false\n---\nhello");
|
||||
ctx.use_role_obj(role).unwrap();
|
||||
|
||||
let cfg = ctx.skill_instructions_config();
|
||||
|
||||
assert!(!cfg.inject);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_instructions_config_session_overrides_role() {
|
||||
let mut ctx = create_test_ctx();
|
||||
let role = Role::new("r", "---\ninject_skill_instructions: false\n---\nhello");
|
||||
ctx.use_role_obj(role).unwrap();
|
||||
let mut session = Session::default();
|
||||
session.set_inject_skill_instructions(Some(true));
|
||||
session.set_skill_instructions(Some("custom hint".into()));
|
||||
ctx.session = Some(session);
|
||||
|
||||
let cfg = ctx.skill_instructions_config();
|
||||
|
||||
assert!(cfg.inject);
|
||||
assert_eq!(cfg.instructions.as_deref(), Some("custom hint"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exit_session_clears_session() {
|
||||
let mut ctx = create_test_ctx();
|
||||
@@ -3643,182 +3448,6 @@ mod tests {
|
||||
assert!(!names.contains(&"todo__done"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_functions_re_adds_skill_tools_when_role_skills_enabled_unset() {
|
||||
let mut ctx = create_test_ctx();
|
||||
ctx.tool_scope.functions.append_skill_functions();
|
||||
|
||||
let mut role = Role::new("r", "p");
|
||||
role.set_enabled_tools(Some(vec!["foo".to_string()]));
|
||||
|
||||
let fns = ctx.select_functions(&role).unwrap();
|
||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||
assert!(names.contains(&"skill__list"));
|
||||
assert!(names.contains(&"skill__load"));
|
||||
assert!(names.contains(&"skill__unload"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_functions_suppresses_skill_tools_when_role_skills_enabled_false() {
|
||||
let mut ctx = create_test_ctx();
|
||||
ctx.tool_scope.functions.append_skill_functions();
|
||||
ctx.tool_scope.functions.append_todo_functions();
|
||||
|
||||
let mut role = Role::new("r", "---\nskills_enabled: false\n---\np");
|
||||
role.set_enabled_tools(Some(vec!["todo__init".to_string()]));
|
||||
|
||||
let fns = ctx.select_functions(&role).unwrap();
|
||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||
assert!(names.contains(&"todo__init"));
|
||||
assert!(!names.contains(&"skill__list"));
|
||||
assert!(!names.contains(&"skill__load"));
|
||||
assert!(!names.contains(&"skill__unload"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_functions_still_re_adds_user_tools_when_role_skills_enabled_false() {
|
||||
let mut ctx = create_test_ctx();
|
||||
ctx.tool_scope.functions.append_user_interaction_functions();
|
||||
ctx.tool_scope.functions.append_skill_functions();
|
||||
|
||||
let mut role = Role::new("r", "---\nskills_enabled: false\n---\np");
|
||||
role.set_enabled_tools(Some(vec!["foo".to_string()]));
|
||||
|
||||
let fns = ctx.select_functions(&role).unwrap();
|
||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||
assert!(names.contains(&"user__ask"));
|
||||
assert!(!names.contains(&"skill__list"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn select_functions_re_adds_skill_tools_when_agent_skills_enabled_not_false() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let mut ctx = create_test_ctx();
|
||||
let app = ctx.app.config.clone();
|
||||
let agent_name = format!(
|
||||
"test_skill_agent_{}",
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
);
|
||||
let agent_dir = paths::agent_data_dir(&agent_name);
|
||||
create_dir_all(&agent_dir).unwrap();
|
||||
write(
|
||||
agent_dir.join("graph.yaml"),
|
||||
format!(
|
||||
"name: {agent_name}\nversion: \"1.0\"\nstart: done\nnodes:\n done:\n type: end\n output: ok\n"
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let abort = utils::create_abort_signal();
|
||||
run_async(ctx.use_agent(&app, &agent_name, None, abort)).unwrap();
|
||||
ctx.tool_scope.functions.append_skill_functions();
|
||||
|
||||
let mut role = Role::new("r", "p");
|
||||
role.set_enabled_tools(Some(vec!["foo".to_string()]));
|
||||
|
||||
let fns = ctx.select_functions(&role).unwrap();
|
||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||
assert!(names.contains(&"skill__list"));
|
||||
assert!(names.contains(&"skill__load"));
|
||||
assert!(names.contains(&"skill__unload"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork_for_branch_clones_skill_registry() {
|
||||
let mut ctx = create_test_ctx();
|
||||
let skill = Skill::new("shared", "---\nauto_unload: false\n---\nbody");
|
||||
ctx.skill_registry.insert(skill).unwrap();
|
||||
|
||||
let fork = ctx.fork_for_branch();
|
||||
|
||||
assert!(
|
||||
fork.skill_registry.is_loaded("shared"),
|
||||
"Parallel branches must share loaded skills with parent"
|
||||
);
|
||||
assert!(ctx.skill_registry.is_loaded("shared"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_skill_tool_returns_error_when_skills_disabled() {
|
||||
let mut ctx = create_test_ctx();
|
||||
let role = Role::new("r", "---\nskills_enabled: false\n---\np");
|
||||
ctx.use_role_obj(role).unwrap();
|
||||
|
||||
let result = run_async(skill::handle_skill_tool(
|
||||
&mut ctx,
|
||||
"skill__list",
|
||||
&json!({}),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
result.get("error").is_some(),
|
||||
"Expected error when skills are disabled, got: {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_unload_returns_error_when_skill_not_loaded() {
|
||||
let mut ctx = create_test_ctx();
|
||||
|
||||
let result = run_async(skill::handle_skill_tool(
|
||||
&mut ctx,
|
||||
"skill__unload",
|
||||
&json!({"name": "ghost"}),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
result.get("error").is_some(),
|
||||
"Expected error when unloading unloaded skill, got: {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn select_functions_suppresses_skill_tools_when_agent_skills_enabled_false() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let mut ctx = create_test_ctx();
|
||||
let app = ctx.app.config.clone();
|
||||
let agent_name = format!(
|
||||
"test_skill_agent_off_{}",
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
);
|
||||
let agent_dir = paths::agent_data_dir(&agent_name);
|
||||
create_dir_all(&agent_dir).unwrap();
|
||||
write(
|
||||
agent_dir.join("graph.yaml"),
|
||||
format!(
|
||||
"name: {agent_name}\nversion: \"1.0\"\nstart: done\nnodes:\n done:\n type: end\n output: ok\n"
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let abort = utils::create_abort_signal();
|
||||
run_async(ctx.use_agent(&app, &agent_name, None, abort)).unwrap();
|
||||
ctx.agent
|
||||
.as_mut()
|
||||
.expect("agent loaded")
|
||||
.set_skills_enabled(Some(false));
|
||||
ctx.tool_scope.functions.append_skill_functions();
|
||||
|
||||
let mut role = Role::new("r", "p");
|
||||
role.set_enabled_tools(Some(vec!["foo".to_string()]));
|
||||
|
||||
let fns = ctx.select_functions(&role).unwrap();
|
||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||
assert!(!names.contains(&"skill__list"));
|
||||
assert!(!names.contains(&"skill__load"));
|
||||
assert!(!names.contains(&"skill__unload"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_enabled_mcp_servers_returns_empty_when_mcp_disabled() {
|
||||
let app_state = {
|
||||
@@ -4048,7 +3677,8 @@ mod tests {
|
||||
|
||||
let input = Input::from_str(&ctx, "hello", None).unwrap();
|
||||
let app = Arc::clone(&ctx.app.config);
|
||||
let tool_result = ToolResult::new(crate::function::ToolCall::default(), json!({}));
|
||||
let tool_result =
|
||||
ToolResult::new(crate::function::ToolCall::default(), serde_json::json!({}));
|
||||
ctx.after_chat_completion(app.as_ref(), &input, "", &[tool_result])
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -79,10 +79,6 @@ pub struct Role {
|
||||
inject_todo_instructions: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
continuation_prompt: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
inject_skill_instructions: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
skill_instructions: Option<String>,
|
||||
|
||||
#[serde(skip)]
|
||||
model: Model,
|
||||
@@ -128,10 +124,6 @@ impl Role {
|
||||
"continuation_prompt" => {
|
||||
role.continuation_prompt = value.as_str().map(|v| v.to_string())
|
||||
}
|
||||
"inject_skill_instructions" => role.inject_skill_instructions = value.as_bool(),
|
||||
"skill_instructions" => {
|
||||
role.skill_instructions = value.as_str().map(|v| v.to_string())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -197,14 +189,6 @@ impl Role {
|
||||
if let Some(continuation_prompt) = &self.continuation_prompt {
|
||||
metadata.push(format!("continuation_prompt: {continuation_prompt}"));
|
||||
}
|
||||
if let Some(inject_skill_instructions) = self.inject_skill_instructions {
|
||||
metadata.push(format!(
|
||||
"inject_skill_instructions: {inject_skill_instructions}"
|
||||
));
|
||||
}
|
||||
if let Some(skill_instructions) = &self.skill_instructions {
|
||||
metadata.push(format!("skill_instructions: {skill_instructions}"));
|
||||
}
|
||||
if metadata.is_empty() {
|
||||
format!("{}\n", self.prompt)
|
||||
} else if self.prompt.is_empty() {
|
||||
@@ -315,14 +299,6 @@ impl Role {
|
||||
self.continuation_prompt.as_deref()
|
||||
}
|
||||
|
||||
pub fn inject_skill_instructions(&self) -> Option<bool> {
|
||||
self.inject_skill_instructions
|
||||
}
|
||||
|
||||
pub fn skill_instructions(&self) -> Option<&str> {
|
||||
self.skill_instructions.as_deref()
|
||||
}
|
||||
|
||||
pub fn skills_enabled(&self) -> Option<bool> {
|
||||
self.skills_enabled
|
||||
}
|
||||
|
||||
@@ -56,10 +56,6 @@ pub struct Session {
|
||||
inject_todo_instructions: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
continuation_prompt: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
inject_skill_instructions: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
skill_instructions: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
role_name: Option<String>,
|
||||
@@ -231,12 +227,6 @@ impl Session {
|
||||
if let Some(continuation_prompt) = self.continuation_prompt() {
|
||||
data["continuation_prompt"] = continuation_prompt.into();
|
||||
}
|
||||
if let Some(inject_skill_instructions) = self.inject_skill_instructions() {
|
||||
data["inject_skill_instructions"] = inject_skill_instructions.into();
|
||||
}
|
||||
if let Some(skill_instructions) = self.skill_instructions() {
|
||||
data["skill_instructions"] = skill_instructions.into();
|
||||
}
|
||||
let (tokens, percent) = self.tokens_usage();
|
||||
data["total_tokens"] = tokens.into();
|
||||
if let Some(max_input_tokens) = self.model().max_input_tokens() {
|
||||
@@ -315,15 +305,6 @@ impl Session {
|
||||
if let Some(continuation_prompt) = self.continuation_prompt() {
|
||||
items.push(("continuation_prompt", continuation_prompt.to_string()));
|
||||
}
|
||||
if let Some(inject_skill_instructions) = self.inject_skill_instructions() {
|
||||
items.push((
|
||||
"inject_skill_instructions",
|
||||
inject_skill_instructions.to_string(),
|
||||
));
|
||||
}
|
||||
if let Some(skill_instructions) = self.skill_instructions() {
|
||||
items.push(("skill_instructions", skill_instructions.to_string()));
|
||||
}
|
||||
|
||||
if let Some(max_input_tokens) = self.model().max_input_tokens() {
|
||||
items.push(("max_input_tokens", max_input_tokens.to_string()));
|
||||
@@ -465,14 +446,6 @@ impl Session {
|
||||
self.continuation_prompt.as_deref()
|
||||
}
|
||||
|
||||
pub fn inject_skill_instructions(&self) -> Option<bool> {
|
||||
self.inject_skill_instructions
|
||||
}
|
||||
|
||||
pub fn skill_instructions(&self) -> Option<&str> {
|
||||
self.skill_instructions.as_deref()
|
||||
}
|
||||
|
||||
pub fn set_inject_todo_instructions(&mut self, value: Option<bool>) {
|
||||
if self.inject_todo_instructions != value {
|
||||
self.inject_todo_instructions = value;
|
||||
@@ -487,20 +460,6 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_inject_skill_instructions(&mut self, value: Option<bool>) {
|
||||
if self.inject_skill_instructions != value {
|
||||
self.inject_skill_instructions = value;
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_skill_instructions(&mut self, value: Option<String>) {
|
||||
if self.skill_instructions != value {
|
||||
self.skill_instructions = value;
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn needs_compression(&self, global_compression_threshold: usize) -> bool {
|
||||
if self.compressing {
|
||||
return false;
|
||||
|
||||
+29
-251
@@ -3,16 +3,14 @@ use super::app_config::AppConfig;
|
||||
use super::paths;
|
||||
use super::role::Role;
|
||||
use super::session::Session;
|
||||
use super::skill::Skill;
|
||||
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SkillPolicy {
|
||||
pub skills_enabled: bool,
|
||||
pub enabled: HashSet<String>,
|
||||
pub compatible_enabled: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl SkillPolicy {
|
||||
@@ -29,27 +27,20 @@ impl SkillPolicy {
|
||||
session,
|
||||
&paths::has_skill,
|
||||
&paths::list_skills,
|
||||
&|name, mcp_on| {
|
||||
Skill::load(name)
|
||||
.map(|s| s.is_compatible(mcp_on))
|
||||
.unwrap_or(false)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn effective_with<F, G, H>(
|
||||
fn effective_with<F, G>(
|
||||
global: &AppConfig,
|
||||
role: Option<&Role>,
|
||||
agent: Option<&Agent>,
|
||||
session: Option<&Session>,
|
||||
skill_exists: &F,
|
||||
list_installed: &G,
|
||||
skill_is_compatible: &H,
|
||||
) -> Result<Self>
|
||||
where
|
||||
F: Fn(&str) -> bool,
|
||||
G: Fn() -> Vec<String>,
|
||||
H: Fn(&str, bool) -> bool,
|
||||
{
|
||||
let mut skills_enabled = global.skills_enabled;
|
||||
if let Some(r) = role
|
||||
@@ -113,21 +104,9 @@ impl SkillPolicy {
|
||||
},
|
||||
};
|
||||
|
||||
let compatible_enabled: BTreeSet<String> = if skills_enabled {
|
||||
let mcp_on = global.mcp_server_support;
|
||||
enabled
|
||||
.iter()
|
||||
.filter(|name| skill_is_compatible(name, mcp_on))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
skills_enabled,
|
||||
enabled,
|
||||
compatible_enabled,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,10 +128,6 @@ mod tests {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn all_compatible(_: &str, _: bool) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn make_app_config(
|
||||
skills_enabled: bool,
|
||||
enabled: Option<&str>,
|
||||
@@ -170,16 +145,9 @@ mod tests {
|
||||
fn defaults_yield_skills_enabled_with_empty_universe() {
|
||||
let global = AppConfig::default();
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &empty_installed)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.skills_enabled);
|
||||
assert!(policy.enabled.is_empty());
|
||||
@@ -190,16 +158,9 @@ mod tests {
|
||||
let global = AppConfig::default();
|
||||
let installed = || vec!["alpha".to_string(), "beta".to_string()];
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &installed)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(policy.enabled.len(), 2);
|
||||
assert!(policy.enabled.contains("alpha"));
|
||||
@@ -210,16 +171,9 @@ mod tests {
|
||||
fn falls_back_to_visible_when_visible_set_but_no_enabled() {
|
||||
let global = make_app_config(true, None, Some(&["alpha", "beta"]));
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &empty_installed)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(policy.enabled.len(), 2);
|
||||
assert!(policy.enabled.contains("alpha"));
|
||||
@@ -230,16 +184,9 @@ mod tests {
|
||||
fn global_enabled_skills_is_effective_when_no_other_levels() {
|
||||
let global = make_app_config(true, Some("alpha,beta"), Some(&["alpha", "beta", "gamma"]));
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &empty_installed)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.enabled.contains("alpha"));
|
||||
assert!(policy.enabled.contains("beta"));
|
||||
@@ -258,7 +205,6 @@ mod tests {
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -278,7 +224,6 @@ mod tests {
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -292,15 +237,9 @@ mod tests {
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&|| vec!["alpha".to_string()],
|
||||
&all_compatible,
|
||||
)
|
||||
let policy = SkillPolicy::effective_with(&global, None, None, None, &always_true, &|| {
|
||||
vec!["alpha".to_string()]
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(!policy.allows("alpha"));
|
||||
@@ -310,16 +249,9 @@ mod tests {
|
||||
fn allows_returns_true_when_skill_in_enabled_set() {
|
||||
let global = make_app_config(true, Some("alpha"), None);
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &empty_installed)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.allows("alpha"));
|
||||
assert!(!policy.allows("beta"));
|
||||
@@ -329,16 +261,9 @@ mod tests {
|
||||
fn validation_rejects_uninstalled_skill_reference() {
|
||||
let global = make_app_config(true, Some("ghost"), None);
|
||||
|
||||
let err = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&|_| false,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap_err();
|
||||
let err =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &|_| false, &empty_installed)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(err.to_string().contains("not installed"));
|
||||
assert!(err.to_string().contains("ghost"));
|
||||
@@ -348,16 +273,9 @@ mod tests {
|
||||
fn validation_rejects_skill_not_in_visible_set() {
|
||||
let global = make_app_config(true, Some("beta"), Some(&["alpha"]));
|
||||
|
||||
let err = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap_err();
|
||||
let err =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &always_true, &empty_installed)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
@@ -370,16 +288,9 @@ mod tests {
|
||||
fn validation_skipped_when_no_explicit_enabled_skills() {
|
||||
let global = make_app_config(true, None, None);
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&|_| false,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
let policy =
|
||||
SkillPolicy::effective_with(&global, None, None, None, &|_| false, &empty_installed)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.enabled.is_empty());
|
||||
}
|
||||
@@ -396,142 +307,9 @@ mod tests {
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.enabled.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_enabled_is_empty_when_skills_disabled() {
|
||||
let global = AppConfig {
|
||||
skills_enabled: false,
|
||||
enabled_skills: Some(vec!["alpha".into()]),
|
||||
visible_skills: Some(vec!["alpha".into()]),
|
||||
..AppConfig::default()
|
||||
};
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(!policy.skills_enabled);
|
||||
assert!(policy.compatible_enabled.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_enabled_short_circuits_callback_when_skills_disabled() {
|
||||
use std::cell::Cell;
|
||||
let global = AppConfig {
|
||||
skills_enabled: false,
|
||||
enabled_skills: Some(vec!["alpha".into()]),
|
||||
visible_skills: Some(vec!["alpha".into()]),
|
||||
..AppConfig::default()
|
||||
};
|
||||
let invoked = Cell::new(0u32);
|
||||
let counting = |_: &str, _: bool| {
|
||||
invoked.set(invoked.get() + 1);
|
||||
true
|
||||
};
|
||||
|
||||
SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&counting,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
invoked.get(),
|
||||
0,
|
||||
"skill_is_compatible callback must not run when skills are disabled"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_enabled_includes_all_when_callback_passes() {
|
||||
let global = make_app_config(true, Some("alpha,beta"), Some(&["alpha", "beta"]));
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&all_compatible,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(policy.compatible_enabled.len(), 2);
|
||||
assert!(policy.compatible_enabled.contains("alpha"));
|
||||
assert!(policy.compatible_enabled.contains("beta"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_enabled_excludes_incompatible_skills() {
|
||||
let global = make_app_config(true, Some("alpha,beta"), Some(&["alpha", "beta"]));
|
||||
let only_alpha_compat = |name: &str, _: bool| name == "alpha";
|
||||
|
||||
let policy = SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&only_alpha_compat,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(policy.compatible_enabled.contains("alpha"));
|
||||
assert!(!policy.compatible_enabled.contains("beta"));
|
||||
assert_eq!(policy.compatible_enabled.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_enabled_passes_mcp_flag_to_callback() {
|
||||
use std::cell::Cell;
|
||||
let global = AppConfig {
|
||||
skills_enabled: true,
|
||||
mcp_server_support: false,
|
||||
enabled_skills: Some(vec!["alpha".into()]),
|
||||
visible_skills: Some(vec!["alpha".into()]),
|
||||
..AppConfig::default()
|
||||
};
|
||||
let observed_mcp = Cell::new(None::<bool>);
|
||||
let capture = |_: &str, mcp_on: bool| {
|
||||
observed_mcp.set(Some(mcp_on));
|
||||
true
|
||||
};
|
||||
|
||||
SkillPolicy::effective_with(
|
||||
&global,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&always_true,
|
||||
&empty_installed,
|
||||
&capture,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
observed_mcp.get(),
|
||||
Some(false),
|
||||
"callback must receive mcp_server_support flag from AppConfig"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ impl SkillRegistry {
|
||||
let policy = SkillPolicy {
|
||||
skills_enabled: true,
|
||||
enabled: self.loaded.keys().cloned().collect(),
|
||||
compatible_enabled: self.loaded.keys().cloned().collect(),
|
||||
};
|
||||
self.effective_role(base, &policy)
|
||||
}
|
||||
|
||||
+15
-10
@@ -14,11 +14,9 @@ pub fn skill_function_declarations() -> Vec<FunctionDeclaration> {
|
||||
FunctionDeclaration {
|
||||
name: format!("{SKILL_FUNCTION_PREFIX}list"),
|
||||
description:
|
||||
"List skills available in this context. Call this early in any non-trivial task to \
|
||||
discover specialized skills that may apply to the work before deciding on an \
|
||||
approach. Returns each skill's name, description, what tools and MCP servers it \
|
||||
grants on load, and whether it is currently loaded. Pair with `skill__load` to \
|
||||
activate the skills you choose."
|
||||
"List skills available in this context. Returns each skill's name, description, \
|
||||
what tools and MCP servers it grants on load, and whether it is currently loaded. \
|
||||
Call this to discover skills before using skill__load."
|
||||
.to_string(),
|
||||
parameters: JsonSchema {
|
||||
type_value: Some("object".to_string()),
|
||||
@@ -30,10 +28,9 @@ pub fn skill_function_declarations() -> Vec<FunctionDeclaration> {
|
||||
FunctionDeclaration {
|
||||
name: format!("{SKILL_FUNCTION_PREFIX}load"),
|
||||
description:
|
||||
"Load a skill module into the current context after confirming via `skill__list` \
|
||||
that it applies to the task at hand. The skill's instructions and any tools or \
|
||||
MCP servers it grants become active for subsequent turns. Call `skill__unload` \
|
||||
when the skill's work is complete to keep the context lean."
|
||||
"Load a skill module into the current context. The skill's instructions and any \
|
||||
tools or MCP servers it grants become active for subsequent turns. Call \
|
||||
skill__unload when the skill's work is complete to keep the context lean."
|
||||
.to_string(),
|
||||
parameters: JsonSchema {
|
||||
type_value: Some("object".to_string()),
|
||||
@@ -105,6 +102,8 @@ pub async fn handle_skill_tool(
|
||||
}
|
||||
|
||||
fn handle_list(ctx: &RequestContext, policy: &SkillPolicy) -> Result<Value> {
|
||||
let mcp_on = ctx.app.config.mcp_server_support;
|
||||
|
||||
let visible_names: Vec<String> = match ctx.app.config.visible_skills.as_deref() {
|
||||
Some(list) => list.to_vec(),
|
||||
None => paths::list_skills(),
|
||||
@@ -112,7 +111,7 @@ fn handle_list(ctx: &RequestContext, policy: &SkillPolicy) -> Result<Value> {
|
||||
|
||||
let mut entries = Vec::new();
|
||||
for name in visible_names {
|
||||
if !policy.compatible_enabled.contains(&name) {
|
||||
if !policy.allows(&name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -123,6 +122,12 @@ fn handle_list(ctx: &RequestContext, policy: &SkillPolicy) -> Result<Value> {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !skill.is_compatible(mcp_on) {
|
||||
warn!(
|
||||
"Skill '{name}' filtered from list: declares MCP servers but MCP support is disabled"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push(json!({
|
||||
"name": skill.name(),
|
||||
|
||||
+1
-31
@@ -2,10 +2,7 @@ use super::state::StateManager;
|
||||
use super::structured;
|
||||
use super::types::LlmNode;
|
||||
use crate::client::{Model, ModelType, call_chat_completions};
|
||||
use crate::config::prompts::DEFAULT_SKILL_INSTRUCTIONS;
|
||||
use crate::config::{
|
||||
Input, RequestContext, Role, RoleLike, SkillPolicy, should_inject_skill_instructions,
|
||||
};
|
||||
use crate::config::{Input, RequestContext, Role, RoleLike, SkillPolicy};
|
||||
use crate::function::skill::skill_function_declarations;
|
||||
use crate::utils::create_abort_signal;
|
||||
use anyhow::{Context, Error, Result, anyhow, bail};
|
||||
@@ -142,31 +139,6 @@ async fn run(
|
||||
role.set_enabled_tools(Some(tools));
|
||||
}
|
||||
|
||||
if should_inject_skill_instructions(&parent_ctx.app.config, &policy) {
|
||||
let app = &parent_ctx.app.config;
|
||||
let agent = parent_ctx.agent.as_ref();
|
||||
let inject = node
|
||||
.inject_skill_instructions
|
||||
.or_else(|| agent.map(|a| a.inject_skill_instructions()))
|
||||
.unwrap_or(app.inject_skill_instructions);
|
||||
|
||||
if inject {
|
||||
let instructions = node
|
||||
.skill_instructions
|
||||
.clone()
|
||||
.or_else(|| agent.and_then(|a| a.skill_instructions_value()))
|
||||
.or_else(|| app.skill_instructions.clone());
|
||||
let separator = if role.is_empty_prompt() { "" } else { "\n\n" };
|
||||
|
||||
role.append_to_prompt(separator);
|
||||
role.append_to_prompt(
|
||||
instructions
|
||||
.as_deref()
|
||||
.unwrap_or(DEFAULT_SKILL_INSTRUCTIONS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let composed_role = parent_ctx.skill_registry.effective_role(&role, &policy);
|
||||
|
||||
let saved_role = parent_ctx.role.clone();
|
||||
@@ -484,8 +456,6 @@ mod tests {
|
||||
timeout: None,
|
||||
skills_enabled: None,
|
||||
enabled_skills: None,
|
||||
inject_skill_instructions: None,
|
||||
skill_instructions: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,6 @@ pub struct Graph {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled_skills: Option<Vec<String>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub inject_skill_instructions: Option<bool>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub skill_instructions: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub conversation_starters: Vec<String>,
|
||||
|
||||
@@ -311,12 +305,6 @@ pub struct LlmNode {
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled_skills: Option<Vec<String>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub inject_skill_instructions: Option<bool>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub skill_instructions: Option<String>,
|
||||
}
|
||||
|
||||
fn default_llm_max_attempts() -> u32 {
|
||||
|
||||
@@ -950,8 +950,6 @@ mod tests {
|
||||
mcp_servers: Vec::new(),
|
||||
skills_enabled: None,
|
||||
enabled_skills: None,
|
||||
inject_skill_instructions: None,
|
||||
skill_instructions: None,
|
||||
conversation_starters: Vec::new(),
|
||||
variables: Vec::new(),
|
||||
settings: GraphSettings::default(),
|
||||
@@ -1053,8 +1051,6 @@ mod tests {
|
||||
timeout: None,
|
||||
skills_enabled: None,
|
||||
enabled_skills: None,
|
||||
inject_skill_instructions: None,
|
||||
skill_instructions: None,
|
||||
}),
|
||||
next: next.map(NextTargets::from),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user