@@ -71,6 +71,13 @@ func killTask(ctx context.Context, id string) error {
7171// WaitTasksTool waits for one or more background tasks.
7272type WaitTasksTool struct {}
7373
74+ // WaitTasksInput is the typed input for WaitTasksTool.
75+ type WaitTasksInput struct {
76+ TaskID string `json:"task_id"`
77+ TaskIDs []string `json:"task_ids"`
78+ TimeoutSec int `json:"timeout_sec"`
79+ }
80+
7481func (WaitTasksTool ) Name () string { return "WaitTasks" }
7582func (WaitTasksTool ) Aliases () []string { return []string {"wait_tasks" , "GetTaskOutput" } }
7683func (WaitTasksTool ) RiskLevel () string { return "low" }
@@ -79,34 +86,29 @@ func (WaitTasksTool) Description() string {
7986 "Works for shell (Bash run_in_background), agent spawns, and Monitor tasks."
8087}
8188
82- func (WaitTasksTool ) Parameters () map [string ]interface {} {
83- return map [string ]interface {}{
84- "type" : "object" ,
85- "properties" : map [string ]interface {}{
86- "task_id" : map [string ]interface {}{
87- "type" : "string" ,
88- "description" : "Single task ID to wait for" ,
89- },
90- "task_ids" : map [string ]interface {}{
91- "type" : "array" ,
92- "items" : map [string ]interface {}{"type" : "string" },
93- "description" : "Multiple task IDs to wait for" ,
94- },
95- "timeout_sec" : map [string ]interface {}{
96- "type" : "integer" ,
97- "description" : "Max seconds to wait (default 120, max 600)" ,
98- },
89+ // Schema returns the typed input schema. Parameters() delegates to it so the
90+ // two cannot diverge.
91+ func (WaitTasksTool ) Schema () ToolSchema {
92+ return ToolSchema {
93+ Type : "object" ,
94+ Properties : map [string ]SchemaProperty {
95+ "task_id" : {Type : "string" , Description : "Single task ID to wait for" },
96+ "task_ids" : {Type : "array" , Items : & SchemaProperty {Type : "string" }, Description : "Multiple task IDs to wait for" },
97+ "timeout_sec" : {Type : "integer" , Description : "Max seconds to wait (default 120, max 600)" },
9998 },
10099 }
101100}
102101
102+ func (WaitTasksTool ) Parameters () map [string ]interface {} {
103+ return waitTasksSchema .ToJSONSchema ()
104+ }
105+
106+ // waitTasksSchema is the single source of truth for WaitTasks' input schema.
107+ var waitTasksSchema = WaitTasksTool {}.Schema ()
108+
103109func (WaitTasksTool ) Execute (ctx context.Context , input json.RawMessage ) (string , error ) {
104- var p struct {
105- TaskID string `json:"task_id"`
106- TaskIDs []string `json:"task_ids"`
107- TimeoutSec int `json:"timeout_sec"`
108- }
109- if err := json .Unmarshal (input , & p ); err != nil {
110+ p , err := DecodeInput [WaitTasksInput ]("WaitTasks" , input )
111+ if err != nil {
110112 return "" , err
111113 }
112114 ids := append ([]string {}, p .TaskIDs ... )
@@ -165,28 +167,40 @@ func (WaitTasksTool) Execute(ctx context.Context, input json.RawMessage) (string
165167// KillTaskTool stops a running background task.
166168type KillTaskTool struct {}
167169
170+ // KillTaskInput is the typed input for KillTaskTool.
171+ type KillTaskInput struct {
172+ TaskID string `json:"task_id"`
173+ }
174+
168175func (KillTaskTool ) Name () string { return "KillTask" }
169176func (KillTaskTool ) Aliases () []string { return []string {"kill_task" } }
170177func (KillTaskTool ) RiskLevel () string { return "medium" }
171178func (KillTaskTool ) Description () string {
172179 return "Kill a running background shell, agent, or monitor task by task_id."
173180}
174181
175- func (KillTaskTool ) Parameters () map [string ]interface {} {
176- return map [string ]interface {}{
177- "type" : "object" ,
178- "properties" : map [string ]interface {}{
179- "task_id" : map [string ]interface {}{"type" : "string" , "description" : "Background task ID" },
182+ // Schema returns the typed input schema. Parameters() delegates to it so the
183+ // two cannot diverge.
184+ func (KillTaskTool ) Schema () ToolSchema {
185+ return ToolSchema {
186+ Type : "object" ,
187+ Properties : map [string ]SchemaProperty {
188+ "task_id" : {Type : "string" , Description : "Background task ID" },
180189 },
181- "required" : []string {"task_id" },
190+ Required : []string {"task_id" },
182191 }
183192}
184193
194+ func (KillTaskTool ) Parameters () map [string ]interface {} {
195+ return killTaskSchema .ToJSONSchema ()
196+ }
197+
198+ // killTaskSchema is the single source of truth for KillTask's input schema.
199+ var killTaskSchema = KillTaskTool {}.Schema ()
200+
185201func (KillTaskTool ) Execute (ctx context.Context , input json.RawMessage ) (string , error ) {
186- var p struct {
187- TaskID string `json:"task_id"`
188- }
189- if err := json .Unmarshal (input , & p ); err != nil {
202+ p , err := DecodeInput [KillTaskInput ]("KillTask" , input )
203+ if err != nil {
190204 return "" , err
191205 }
192206 if p .TaskID == "" {
@@ -201,6 +215,14 @@ func (KillTaskTool) Execute(ctx context.Context, input json.RawMessage) (string,
201215// MonitorTool runs a command, streams lines into a background task, with rate limits.
202216type MonitorTool struct {}
203217
218+ // MonitorInput is the typed input for MonitorTool.
219+ type MonitorInput struct {
220+ Command string `json:"command"`
221+ MaxRuntimeSec int `json:"max_runtime_sec"`
222+ MaxLinesPerSec int `json:"max_lines_per_sec"`
223+ Description string `json:"description"`
224+ }
225+
204226func (MonitorTool ) Name () string { return "Monitor" }
205227func (MonitorTool ) Aliases () []string { return []string {"monitor" } }
206228func (MonitorTool ) RiskLevel () string { return "medium" }
@@ -210,39 +232,31 @@ func (MonitorTool) Description() string {
210232 "Rate-limited (max lines/sec) and auto-killed after max_runtime_sec."
211233}
212234
213- func (MonitorTool ) Parameters () map [string ]interface {} {
214- return map [string ]interface {}{
215- "type" : "object" ,
216- "properties" : map [string ]interface {}{
217- "command" : map [string ]interface {}{
218- "type" : "string" ,
219- "description" : "Shell command to monitor (e.g. 'tail -f log' or 'npm run dev')" ,
220- },
221- "max_runtime_sec" : map [string ]interface {}{
222- "type" : "integer" ,
223- "description" : "Auto-kill after this many seconds (default 300, max 3600)" ,
224- },
225- "max_lines_per_sec" : map [string ]interface {}{
226- "type" : "integer" ,
227- "description" : "Drop excess lines above this rate (default 50)" ,
228- },
229- "description" : map [string ]interface {}{
230- "type" : "string" ,
231- "description" : "Short label for the monitor task" ,
232- },
235+ // Schema returns the typed input schema. Parameters() delegates to it so the
236+ // two cannot diverge.
237+ func (MonitorTool ) Schema () ToolSchema {
238+ return ToolSchema {
239+ Type : "object" ,
240+ Properties : map [string ]SchemaProperty {
241+ "command" : {Type : "string" , Description : "Shell command to monitor (e.g. 'tail -f log' or 'npm run dev')" },
242+ "max_runtime_sec" : {Type : "integer" , Description : "Auto-kill after this many seconds (default 300, max 3600)" },
243+ "max_lines_per_sec" : {Type : "integer" , Description : "Drop excess lines above this rate (default 50)" },
244+ "description" : {Type : "string" , Description : "Short label for the monitor task" },
233245 },
234- "required" : []string {"command" },
246+ Required : []string {"command" },
235247 }
236248}
237249
250+ func (MonitorTool ) Parameters () map [string ]interface {} {
251+ return monitorSchema .ToJSONSchema ()
252+ }
253+
254+ // monitorSchema is the single source of truth for Monitor's input schema.
255+ var monitorSchema = MonitorTool {}.Schema ()
256+
238257func (MonitorTool ) Execute (ctx context.Context , input json.RawMessage ) (string , error ) {
239- var p struct {
240- Command string `json:"command"`
241- MaxRuntimeSec int `json:"max_runtime_sec"`
242- MaxLinesPerSec int `json:"max_lines_per_sec"`
243- Description string `json:"description"`
244- }
245- if err := json .Unmarshal (input , & p ); err != nil {
258+ p , err := DecodeInput [MonitorInput ]("Monitor" , input )
259+ if err != nil {
246260 return "" , err
247261 }
248262 if strings .TrimSpace (p .Command ) == "" {
0 commit comments