| 1 | package boot |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | |
| 7 | "reasonix/internal/ablation" |
| 8 | "reasonix/internal/skill" |
| 9 | "reasonix/internal/tool" |
| 10 | ) |
| 11 | |
| 12 | // gateSubagentArm refuses skills that would spawn a child while the subagent |
| 13 | // ablation arm is off. Delegation is delegation whichever tool reached it, so |
| 14 | // the arm has to cover profile skills too or it is not a single-agent control. |
| 15 | // Inline playbooks are unaffected: they never spawn a child. |
| 16 | func gateSubagentArm(set ablation.Set, run skill.SubagentRunner) skill.SubagentRunner { |
| 17 | if !set.Off(ablation.Subagent) { |
| 18 | return run |
| 19 | } |
| 20 | return func(_ context.Context, sk skill.Skill, _ string, _ skill.SubagentRunOptions) (string, error) { |
| 21 | return "", fmt.Errorf("subagent skill %q is disabled for this run (subagent ablation arm)", sk.Name) |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | // builtinSubagentTools returns the dedicated explore/research/review wrappers, |
| 26 | // or nothing under the arm so the control run does not even carry their schemas. |
| 27 | func builtinSubagentTools(set ablation.Set, store *skill.Store, run skill.SubagentRunner, profile ...skill.ProfileResolver) []tool.Tool { |
| 28 | if set.Off(ablation.Subagent) { |
| 29 | return nil |
| 30 | } |
| 31 | return skill.BuiltinSubagentTools(store, run, profile...) |
| 32 | } |
| 33 |