Skip to content

Commit 7a79fe1

Browse files
authored
Merge pull request #378 from editor-code-assistant/improve-dynamically-specified-subagent-model
Fix issue with dynamically specifying the subagent model
2 parents 7febfb0 + c636a12 commit 7a79fe1

6 files changed

Lines changed: 47 additions & 43 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## Unreleased
4+
- Show the selected `variant` in `spawn_agent` subagent details and harden restrictions regarding the use of the optional model in sub-agent. #369
45

56
- Fix MCP OAuth browser not opening on Windows by using `cmd /c start` instead of `java.awt.Desktop`, which is unavailable in the native image.
67

@@ -32,7 +33,7 @@
3233
## 0.116.5
3334

3435
- Add `/remote` command to display connection URL, password and setup guide. Password is no longer shown in logs or welcome message.
35-
- Improve server port binding for remote.
36+
- Improve server port binding for remote.
3637

3738
## 0.116.4
3839

@@ -160,7 +161,7 @@
160161

161162
- Fix MCP OAuth client registration failing for servers that require Content-Type header
162163
- Prevent MCP servers from getting stuck at "starting" when OAuth or other initialization errors occur.
163-
- Fix GitHub Copilot premium request overconsumption:
164+
- Fix GitHub Copilot premium request overconsumption:
164165
- Chat titles LLM request don't spend premium requests.
165166
- Subagents now use same premium request of primary agent.
166167
- gpt-5.3-codex and gpt-5.4 now spend way less premium requests.

docs/protocol.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,11 @@ interface SubagentDetails {
10861086
*/
10871087
model: string;
10881088

1089+
/**
1090+
* The variant this subagent is using, when one is explicitly selected.
1091+
*/
1092+
variant?: string;
1093+
10891094
/**
10901095
* The name of the agent being spawned.
10911096
*/
Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
Spawn a new agent to handle multistep tasks in isolated context.
1+
Spawn an isolated sub-agent to handle complex, multi-step tasks without polluting your current context.
22

3-
The agent runs independently with its own conversation history and returns a summary of its findings/actions.
4-
Use this for:
5-
- Codebase exploration without polluting your context
6-
- Focused research on specific areas
7-
- Delegating specialized tasks (review, analysis, etc.)
3+
Use for: Codebase exploration, codebase editing and refactoring, focused research, or delegating specialized tasks.
4+
Proactive use: If the specific agent's description suggests proactive use, use it whenever the task complexity justifies delegation.
5+
Restrictions: Avoid sub-agents for simple tasks, file reading, or basic lookups. Delegate ONLY if the task is complex, requires multi-step processing, or benefits from summarization and token saving.
6+
Agent Limits: Sub-agents cannot spawn other agents (no nesting) and have access only to their configured tools.
87

9-
The spawned agent:
10-
- Has access only to its configured tools
11-
- Cannot spawn other agents (no nesting)
12-
- Must return a summary when complete
13-
14-
Usage notes:
15-
- Your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final message.
16-
- Clearly tell the agent whether you expect it to write code or just to do research and how to verify its work if possible.
17-
- If the agent description suggests proactive use, use it whenever the task complexity justifies delegation.
18-
- Avoid sub-agents for simple tasks, file reading, or basic lookups. Delegate only if the task is complex, requires multi-step processing, or benefits from summarization and token saving.
8+
Strict rules for arguments:
9+
- 'task': Provide a highly detailed prompt. Explicitly state whether it should write/edit code or just research, how to verify its work, and exactly what specific information it must return to you.
10+
- 'activity': Must be a concise 3-4 word label for the UI (e.g., "exploring codebase", "refactoring module").
11+
- 'model' & 'variant': - Only include these keys if the user explicitly requests a specific model or variant. Otherwise, the agent defaults to its default configuration.
12+
- If the user did not ask for a model, OMIT the `model` key entirely. Never send an empty string.
13+
- If the user did not ask for a variant, OMIT the `variant` key entirely. Never send an empty string.

src/eca/features/tools/agent.clj

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
(defn ^:private send-step-progress!
5757
"Send a toolCallRunning notification with current step progress to the parent chat."
58-
[messenger chat-id tool-call-id agent-name activity subagent-chat-id step max-steps model arguments]
58+
[messenger chat-id tool-call-id agent-name activity subagent-chat-id step max-steps model variant arguments]
5959
(messenger/chat-content-received
6060
messenger
6161
{:chat-id chat-id
@@ -67,12 +67,13 @@
6767
:origin "native"
6868
:summary (format "%s: %s" agent-name activity)
6969
:arguments arguments
70-
:details {:type :subagent
71-
:subagent-chat-id subagent-chat-id
72-
:model model
73-
:agent-name agent-name
74-
:step step
75-
:max-steps max-steps}}}))
70+
:details (cond-> {:type :subagent
71+
:subagent-chat-id subagent-chat-id
72+
:model model
73+
:agent-name agent-name
74+
:step step
75+
:max-steps max-steps}
76+
variant (assoc :variant variant))}}))
7677

7778
(defn ^:private stop-subagent-chat!
7879
"Stop a running subagent chat silently (parent already shows 'Prompt stopped')."
@@ -150,7 +151,7 @@
150151
;; Create subagent chat session using deterministic id based on tool-call-id
151152
subagent-chat-id (->subagent-chat-id tool-call-id)
152153

153-
user-model (get arguments "model")
154+
user-model (tools.util/normalize-optional-string (get arguments "model"))
154155
_ (when user-model
155156
(let [available-models (:models db)]
156157
(when (and (seq available-models)
@@ -167,7 +168,7 @@
167168
;; Variant validation: reject only when the resolved model has configured
168169
;; variants and the user-specified one isn't among them. Models with no
169170
;; configured variants accept any variant (the LLM API will reject if invalid).
170-
user-variant (get arguments "variant")
171+
user-variant (tools.util/normalize-optional-string (get arguments "variant"))
171172
_ (when user-variant
172173
(let [valid-variants (model-variant-names config subagent-model)]
173174
(when (and (seq valid-variants)
@@ -224,7 +225,7 @@
224225
;; Send step progress when step advances
225226
(when (> current-step last-step)
226227
(send-step-progress! messenger chat-id tool-call-id agent-name activity
227-
subagent-chat-id current-step max-steps-limit subagent-model arguments))
228+
subagent-chat-id current-step max-steps-limit subagent-model user-variant arguments))
228229
(cond
229230
;; Parent chat stopped — propagate stop to subagent
230231
(= :stopping (:status (call-state-fn)))
@@ -259,7 +260,7 @@
259260
(throw e))))))
260261

261262
(defn ^:private build-description
262-
"Build tool description with available agents listed."
263+
"Build tool description with available agents and models listed."
263264
[config]
264265
(let [base-description (tools.util/read-tool-description "spawn_agent")
265266
agents (all-agents config)
@@ -280,19 +281,17 @@
280281
:properties {"agent" {:type "string"
281282
:description "Name of the agent to spawn"}
282283
"task" {:type "string"
283-
:description "Clear description of what the agent should accomplish"}
284+
:description "The detailed instructions for the agent"}
284285
"activity" {:type "string"
285286
:description "Concise label (max 3-4 words) shown in the UI while the agent runs, e.g. \"exploring codebase\", \"reviewing changes\", \"analyzing tests\"."}
286287
"model" {:type "string"
287-
:description (multi-str
288-
"If user specified a model to use."
289-
"DO NOT pass this arg if user didn't request it."
290-
"Known models: "
291-
model-names)}
292-
"variant" (cond-> {:type "string"
293-
:description (str "Variant (Usually reasoning related) for the subagent, only pass if user specify."
294-
"Available variants are model-dependent; pick the closest match when the user asks informally (e.g. \"high reasoning\"\"high\").")}
295-
(seq variant-names) (assoc :enum variant-names))}
288+
:description (cond-> "Optional model override. Include this key ONLY when the user explicitly asked for a specific model. Otherwise omit the key entirely; never send an empty string."
289+
(and (seq model-names) (> (count model-names) 1))
290+
(multi-str "One of these models can be used if the user explicitely requests it: " model-names))}
291+
"variant" {:type "string"
292+
:description (cond-> "Optional variant override. Include this key ONLY when the user explicitly asked for a specific variant."
293+
(seq variant-names) (multi-str "One of these variants can be used if the user requests it: "
294+
variant-names))}}
296295
:required ["agent" "task" "activity"]}
297296
:handler #'spawn-agent
298297
:summary-fn (fn [{:keys [args]}]
@@ -304,8 +303,8 @@
304303
(defmethod tools.util/tool-call-details-before-invocation :spawn_agent
305304
[_name arguments _server {:keys [db config chat-id tool-call-id]}]
306305
(let [agent-name (get arguments "agent")
307-
user-model (get arguments "model")
308-
user-variant (get arguments "variant")
306+
user-model (tools.util/normalize-optional-string (get arguments "model"))
307+
user-variant (tools.util/normalize-optional-string (get arguments "variant"))
309308
subagent (when agent-name
310309
(get-agent agent-name config))
311310
parent-model (get-in db [:chats chat-id :model])

src/eca/features/tools/util.clj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[clojure.java.io :as io]
66
[clojure.java.shell :as shell]
77
[clojure.string :as string]
8+
[clojure.string :as str]
89
[eca.cache :as cache]
910
[eca.logger :as logger]
1011
[eca.shared :as shared]))
@@ -202,3 +203,7 @@
202203
(assoc result :contents [{:type :text
203204
:text (str truncated notice)}]))
204205
result)))))
206+
207+
(defn normalize-optional-string
208+
[value]
209+
(some-> value str/trim not-empty))

test/eca/features/tools/agent_test.clj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,7 @@
584584
"task" {:type "string"}
585585
"activity" {:type "string"}
586586
"model" {:type "string"}
587-
"variant" {:type "string"
588-
:enum ["high" "low" "max" "medium"]}}
587+
"variant" {:type "string"}}
589588
:required ["agent" "task" "activity"]}
590589
(:parameters tool)))))
591590

0 commit comments

Comments
 (0)