| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | |
| 6 | "reasonix/internal/checkpoint" |
| 7 | "reasonix/internal/event" |
| 8 | "reasonix/internal/imageinput" |
| 9 | "reasonix/internal/provider" |
| 10 | "reasonix/internal/runtimepolicy" |
| 11 | "reasonix/internal/tool" |
| 12 | ) |
| 13 | |
| 14 | // NewTaskToolWithOptions is the internal standard constructor for TaskTool. |
| 15 | // An empty SysPrompt still resolves to DefaultTaskSystemPrompt. No extra |
| 16 | // validation or default overrides are applied beyond the historical NewTaskTool |
| 17 | // behavior. |
| 18 | func NewTaskToolWithOptions(opts TaskToolOptions) *TaskTool { |
| 19 | sysPrompt := opts.SysPrompt |
| 20 | if sysPrompt == "" { |
| 21 | sysPrompt = DefaultTaskSystemPrompt |
| 22 | } |
| 23 | return &TaskTool{ |
| 24 | imageInput: opts.ImageInput, |
| 25 | prov: opts.Provider, |
| 26 | pricing: opts.Pricing, |
| 27 | quoteContext: opts.QuoteContext, |
| 28 | parentReg: opts.ParentRegistry, |
| 29 | maxSteps: opts.MaxSteps, |
| 30 | contextWindow: opts.ContextWindow, |
| 31 | recentKeep: opts.RecentKeep, |
| 32 | compactRatio: opts.CompactRatio, |
| 33 | temperature: opts.Temperature, |
| 34 | archiveDir: opts.ArchiveDir, |
| 35 | keepPolicy: opts.KeepPolicy, |
| 36 | sysPrompt: sysPrompt, |
| 37 | gate: opts.Gate, |
| 38 | subagentModel: opts.SubagentModel, |
| 39 | subagentEffort: opts.SubagentEffort, |
| 40 | resolveProvider: opts.ResolveProvider, |
| 41 | maxSubagentDepth: DefaultMaxSubagentDepth, |
| 42 | imageResolver: opts.ImageRequestResolver, |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // subagentOptions is the single construction point for the run options every |
| 47 | // sub-agent spawned through this tool shares (task, read_only_task, and |
| 48 | // parallel_tasks children). Compaction, language preferences, and depth limits |
| 49 | // must stay uniform across those paths — add new fields here, not at call sites. |
| 50 | func (t *TaskTool) subagentOptions(ctx context.Context, maxSteps int, pricing *provider.Pricing, ctxWin, childDepth int, recoveryTaskID string, mutationObserver *checkpoint.MutationObserver) Options { |
| 51 | opts := Options{ |
| 52 | ImageInput: t.imageInput, |
| 53 | MaxSteps: maxSteps, |
| 54 | MaxOutputTokens: childOutputBudgetFrom(ctx), |
| 55 | Temperature: t.temperature, |
| 56 | Pricing: pricing, |
| 57 | QuoteContext: t.quoteContext, |
| 58 | UsageSource: event.UsageSourceSubagent, |
| 59 | Gate: t.gate, |
| 60 | ContextWindow: ctxWin, |
| 61 | RecentKeep: t.recentKeep, |
| 62 | CompactRatio: t.compactRatio, |
| 63 | ArchiveDir: t.archiveDir, |
| 64 | KeepPolicy: t.keepPolicy, |
| 65 | ResponseLanguage: ResponseLanguageFromContext(ctx), |
| 66 | ReasoningLanguage: ReasoningLanguageFromContext(ctx), |
| 67 | SubagentDepth: childDepth, |
| 68 | MaxSubagentDepth: t.maxDepth(), |
| 69 | Ablation: t.ablation, |
| 70 | WorkspaceLease: t.workspaceLease, |
| 71 | MutationObserver: mutationObserver, |
| 72 | WriteRoots: t.writeRoots, |
| 73 | DisableWriteAccessExpand: true, |
| 74 | WriteWorkspaceRoot: t.workspaceRoot, |
| 75 | ImageRequestResolver: t.imageResolver, |
| 76 | } |
| 77 | // Writer children inherit the parent turn's frozen risk and closure floors. |
| 78 | // The parent publishes its policy into the run context; a child that never |
| 79 | // received it (direct unit construction) keeps its own derived policy. |
| 80 | if inherited, ok := runtimepolicy.InheritedFromContext(ctx); ok { |
| 81 | copy := inherited |
| 82 | opts.InheritedExecution = © |
| 83 | } else if constraints, ok := runtimepolicy.FromContext(ctx); ok { |
| 84 | opts.InheritedExecution = &runtimepolicy.InheritedExecutionContext{Constraints: constraints} |
| 85 | } |
| 86 | return opts |
| 87 | } |
| 88 | |
| 89 | func (t *TaskTool) WithImageRequestResolver(resolver ImageRequestResolver) *TaskTool { |
| 90 | if t == nil { |
| 91 | return nil |
| 92 | } |
| 93 | t.imageResolver = resolver |
| 94 | return t |
| 95 | } |
| 96 | |
| 97 | // TaskToolOptions holds the construction parameters for a TaskTool. |
| 98 | // Prefer NewTaskToolWithOptions for new call sites; the positional NewTaskTool |
| 99 | // remains as a compatibility wrapper for one full iteration cycle. |
| 100 | type TaskToolOptions struct { |
| 101 | ImageInput *imageinput.Config |
| 102 | ImageRequestResolver ImageRequestResolver |
| 103 | Provider provider.Provider |
| 104 | Pricing *provider.Pricing |
| 105 | QuoteContext *event.QuoteContext |
| 106 | ParentRegistry *tool.Registry |
| 107 | MaxSteps int |
| 108 | ContextWindow int |
| 109 | RecentKeep int |
| 110 | SoftCompactRatio float64 |
| 111 | ToolResultSnipRatio float64 |
| 112 | CompactRatio float64 |
| 113 | CompactForceRatio float64 |
| 114 | Temperature float64 |
| 115 | ContextEditing, ArchiveDir, SysPrompt string |
| 116 | Gate Gate |
| 117 | KeepPolicy KeepPolicy |
| 118 | SubagentModel string |
| 119 | SubagentEffort string |
| 120 | ResolveProvider func(string, string) (provider.Provider, *provider.Pricing, int, error) |
| 121 | } |
| 122 |