2025-06-12 21:04:34 +00:00
import { Tool } from "./tool"
import DESCRIPTION from "./task.txt"
2025-10-26 19:50:41 +00:00
import z from "zod"
2025-06-12 21:04:34 +00:00
import { Session } from "../session"
2026-03-11 23:30:17 +00:00
import { SessionID , MessageID } from "../session/schema"
2025-07-07 19:53:43 +00:00
import { MessageV2 } from "../session/message-v2"
2025-07-25 01:20:43 +00:00
import { Agent } from "../agent/agent"
2026-04-10 23:36:13 +00:00
import type { SessionPrompt } from "../session/prompt"
2025-12-03 17:19:43 +00:00
import { Config } from "../config/config"
2026-04-07 23:48:12 +00:00
import { Effect } from "effect"
2026-04-09 15:56:19 +00:00
import { Log } from "@/util/log"
2026-01-07 04:29:17 +00:00
2026-04-10 23:36:13 +00:00
export interface TaskPromptOps {
cancel ( sessionID : SessionID ) : void
2026-04-11 02:57:47 +00:00
resolvePromptParts ( template : string ) : Effect . Effect < SessionPrompt.PromptInput [ " parts " ] >
prompt ( input : SessionPrompt.PromptInput ) : Effect . Effect < MessageV2.WithParts >
2026-04-10 23:36:13 +00:00
}
2026-04-08 23:02:19 +00:00
const id = "task"
const parameters = z . object ( {
description : z.string ( ) . describe ( "A short (3-5 words) description of the task" ) ,
prompt : z.string ( ) . describe ( "The task for the agent to perform" ) ,
subagent_type : z.string ( ) . describe ( "The type of specialized agent to use for this task" ) ,
task_id : z
. string ( )
. describe (
"This should only be set if you mean to resume a previous task (you can pass a prior task_id and the task will continue the same subagent session as before instead of creating a fresh one)" ,
)
. optional ( ) ,
command : z.string ( ) . describe ( "The command that triggered this task" ) . optional ( ) ,
} )
2026-04-11 02:36:02 +00:00
export const TaskTool = Tool . define (
2026-04-08 23:02:19 +00:00
id ,
Effect . gen ( function * ( ) {
const agent = yield * Agent . Service
const config = yield * Config . Service
const run = Effect . fn ( "TaskTool.execute" ) ( function * ( params : z.infer < typeof parameters > , ctx : Tool.Context ) {
const cfg = yield * config . get ( )
2026-01-07 04:29:17 +00:00
2026-01-07 19:28:13 +00:00
if ( ! ctx . extra ? . bypassAgentCheck ) {
2026-04-11 02:36:02 +00:00
yield * ctx . ask ( {
permission : id ,
patterns : [ params . subagent_type ] ,
always : [ "*" ] ,
metadata : {
description : params.description ,
subagent_type : params.subagent_type ,
} ,
} )
2026-01-06 23:05:09 +00:00
}
2026-01-01 22:54:11 +00:00
2026-04-08 23:02:19 +00:00
const next = yield * agent . get ( params . subagent_type )
if ( ! next ) {
return yield * Effect . fail ( new Error ( ` Unknown agent type: ${ params . subagent_type } is not a valid agent type ` ) )
}
2025-10-30 17:15:21 +00:00
2026-04-08 23:02:19 +00:00
const canTask = next . permission . some ( ( rule ) = > rule . permission === id )
const canTodo = next . permission . some ( ( rule ) = > rule . permission === "todowrite" )
const taskID = params . task_id
const session = taskID
? yield * Effect . promise ( ( ) = > {
const id = SessionID . make ( taskID )
return Session . get ( id ) . catch ( ( ) = > undefined )
} )
: undefined
const nextSession =
session ? ?
( yield * Effect . promise ( ( ) = >
Session . create ( {
parentID : ctx.sessionID ,
title : params.description + ` (@ ${ next . name } subagent) ` ,
permission : [
. . . ( canTodo
? [ ]
: [
{
permission : "todowrite" as const ,
pattern : "*" as const ,
action : "deny" as const ,
} ,
] ) ,
. . . ( canTask
? [ ]
: [
{
permission : id ,
pattern : "*" as const ,
action : "deny" as const ,
} ,
] ) ,
. . . ( cfg . experimental ? . primary_tools ? . map ( ( item ) = > ( {
pattern : "*" ,
action : "allow" as const ,
permission : item ,
} ) ) ? ? [ ] ) ,
] ,
} ) ,
) )
const msg = yield * Effect . sync ( ( ) = > MessageV2 . get ( { sessionID : ctx.sessionID , messageID : ctx.messageID } ) )
if ( msg . info . role !== "assistant" ) return yield * Effect . fail ( new Error ( "Not an assistant message" ) )
const model = next . model ? ? {
2026-01-24 05:10:40 +00:00
modelID : msg.info.modelID ,
providerID : msg.info.providerID ,
}
2025-10-30 17:15:21 +00:00
ctx . metadata ( {
title : params.description ,
metadata : {
2026-04-08 23:02:19 +00:00
sessionId : nextSession.id ,
2026-01-24 05:10:40 +00:00
model ,
2025-10-30 17:15:21 +00:00
} ,
} )
2026-04-10 23:36:13 +00:00
const ops = ctx . extra ? . promptOps as TaskPromptOps
if ( ! ops ) return yield * Effect . fail ( new Error ( "TaskTool requires promptOps in ctx.extra" ) )
2026-03-11 23:30:17 +00:00
const messageID = MessageID . ascending ( )
2025-07-25 01:20:43 +00:00
2025-11-17 15:57:18 +00:00
function cancel() {
2026-04-10 23:36:13 +00:00
ops . cancel ( nextSession . id )
2025-11-17 15:57:18 +00:00
}
2025-11-19 06:17:26 +00:00
2026-04-08 23:02:19 +00:00
return yield * Effect . acquireUseRelease (
Effect . sync ( ( ) = > {
ctx . abort . addEventListener ( "abort" , cancel )
} ) ,
( ) = >
Effect . gen ( function * ( ) {
2026-04-11 02:57:47 +00:00
const parts = yield * ops . resolvePromptParts ( params . prompt )
const result = yield * ops . prompt ( {
messageID ,
sessionID : nextSession.id ,
model : {
modelID : model.modelID ,
providerID : model.providerID ,
} ,
agent : next.name ,
tools : {
. . . ( canTodo ? { } : { todowrite : false } ) ,
. . . ( canTask ? { } : { task : false } ) ,
. . . Object . fromEntries ( ( cfg . experimental ? . primary_tools ? ? [ ] ) . map ( ( item ) = > [ item , false ] ) ) ,
} ,
parts ,
} )
2026-04-08 23:02:19 +00:00
return {
title : params.description ,
metadata : {
sessionId : nextSession.id ,
model ,
} ,
output : [
` task_id: ${ nextSession . id } (for resuming to continue this task if needed) ` ,
"" ,
"<task_result>" ,
result . parts . findLast ( ( item ) = > item . type === "text" ) ? . text ? ? "" ,
"</task_result>" ,
] . join ( "\n" ) ,
}
} ) ,
( ) = >
Effect . sync ( ( ) = > {
ctx . abort . removeEventListener ( "abort" , cancel )
} ) ,
)
} )
return {
description : DESCRIPTION ,
parameters ,
2026-04-11 02:36:02 +00:00
execute : ( params : z.infer < typeof parameters > , ctx : Tool.Context ) = > run ( params , ctx ) . pipe ( Effect . orDie ) ,
2026-04-08 23:02:19 +00:00
}
} ) ,
)