Problem
Squadron's unit of work assignment is the predefined agent: tools are declared on agent blocks, and tasks/missions list which agents may be used. The commander coordinates; agents do all the work.
In practice this paradigm is breaking down:
- Agents aren't reusable. An agent is a fixed bundle of model + personality + tools + skills. That exact bundle is rarely right for more than one or two tasks, so configs accumulate near-duplicate agents that exist only to carry a particular tool list to a particular task.
- The indirection adds friction. To give a task a capability, you must invent an agent for it, name it, give it a personality, and wire it into the mission's
agents list — even when there's nothing agent-like about the need ("this task needs the playwright tools").
- Tool intent lives in the wrong place. Which tools a task needs is a property of the task, not of some persona. Today that information is smeared across agent definitions.
We're simplifying the relationship between tasks and tools: tools get assigned at the task level (directly or via named groups), and an implicit dynamic agent per task carries those tools and does most of the work. Predefined agents remain fully supported for specialized cases — the commander decides when to delegate to one instead.
Proposal
1. tools on tasks
Tasks accept a tools = [...] attribute using the same reference syntax agents use today (builtins.*, plugins.*.all, mcp.*.*, tools.*):
mission "research" {
commander { model = models.anthropic.claude_sonnet_4 }
task "gather" {
objective = "Collect pricing data from the target sites"
tools = [plugins.playwright.all, builtins.http.get]
}
task "summarize" {
depends_on = [tasks.gather]
objective = "Summarize findings into a report"
tools = [mcp.filesystem.write_file]
}
}
No agent block required anywhere in this mission.
2. toolset blocks — named tool groups
A top-level block that names a reusable group of tool refs:
toolset "browsing" {
description = "Navigate and scrape the web"
tools = [plugins.playwright.all, builtins.http.get]
}
toolset "data_io" {
tools = [mcp.filesystem.read_file, mcp.filesystem.write_file]
}
task "gather" {
objective = "Collect pricing data"
tools = [toolsets.browsing, toolsets.data_io, builtins.utils.current_time]
}
Toolsets expand at config-load time (like .all does today). They are pure grouping — no model, no personality, no behavior. toolsets.* refs are also valid in agent tools lists, so predefined agents benefit too.
3. Dynamic agents
When a task declares tools, the runner materializes one implicit dynamic agent for that task, holding all of the task's tools. The commander sees it alongside any explicitly assigned agents and delegates to it via the existing call_agent/ask_agent flow.
- Naming: a reserved, deterministic name (e.g.
worker), consistent across events, debug logs (agent_<task>_worker.md), and persistence.
- System prompt: derived from the task objective and mission context rather than a configured personality.
- Commander awareness: the commander's system prompt lists the dynamic agent with its tool list, plus guidance: prefer the dynamic agent for general work; use specialized agents when their personality/skills fit the subtask.
- Lifecycle: identical to today's agents — instantiated on first
call_agent, session persisted, resumable, queryable via ask_agent.
4. Model selection
A mission-level default with per-task override:
mission "research" {
commander { model = models.anthropic.claude_sonnet_4 }
agent_defaults {
model = models.anthropic.claude_sonnet_4 # dynamic agents use this...
reasoning = "low" # (optional)
}
task "gather" {
objective = "Collect pricing data"
tools = [toolsets.browsing]
model = models.anthropic.claude_haiku # ...unless the task overrides
}
}
If no agent_defaults block exists, dynamic agents inherit the commander's model.
Coexistence with predefined agents
Nothing about agent blocks changes. A task may have tools, agents, or both:
task "gather" {
objective = "Collect pricing data; consult the domain expert for ambiguous SKUs"
tools = [toolsets.browsing]
agents = [agents.pricing_expert]
}
The commander decides at runtime which to use for each piece of work.
Validation rules
- Task
tools refs validated the same way agent tools are (reuse ValidateToolReferences).
- A task must have at least one work source:
tools, task-level agents, or mission-level agents (relaxes today's "agents required" rule).
- The dynamic agent's reserved name must not collide with any global or mission-scoped agent name.
toolset labels live in their own namespace; refs to unknown toolsets are config errors; toolsets cannot reference other toolsets (v1 — keeps expansion non-recursive).
agent_defaults is optional; at most one per mission; task-level model is only meaningful when the task has tools.
Implementation sketch
Config (config/)
- New
toolset block, parsed in Stage 3 alongside custom tool blocks (needs plugins + mcp context); expose a toolsets.* HCL namespace; expansion happens in BuildToolsMap or just before it.
Task gains Tools []string and Model string (config/mission.go); Mission gains an AgentDefaults block.
- Validation per the rules above.
Runtime (agent/, mission/)
BuildToolsMap (config/tools_builder.go) is already parametric — dynamic agents reuse it unchanged.
AgentManager (agent/agent_manager.go) learns to construct the dynamic agent: synthesize an agent config (reserved name, resolved model, task tools, objective-derived prompt) and feed it through the existing agent.New() path.
- Commander prompt (
agent/commander.go, agent/internal/prompts/prompts.go): include the dynamic agent with its tool list and selection guidance.
Persistence/resume (store/)
- Dynamic agent sessions persist under the deterministic name; resume works through the existing session-restore path. Likely no schema change; if one is needed, it goes through a numbered migration.
Docs
- README + extracted docs (
squadron docs) + CLAUDE.md sections on agents, missions, and the HCL reference.
Out of scope (follow-ups)
- Commander-chosen tool subsets per spawn (multiple dynamic agents per task with different tool slices).
- Nested/composed toolsets.
- Per-toolset settings or instructions (that's what skills are; relationship revisited later).
- Any deprecation of agent-level
tools.
Open questions
- Reserved name for the dynamic agent —
worker? agents.dynamic? Shows up in events, debug filenames, and the command center UI.
agent_defaults block name and placement — top of mission vs. inside commander?
- Should the dynamic agent get the mission's memory/scratchpad file tools automatically (matching current agent behavior)? Presumably yes.
- Should task-level
tools also be grantable to predefined agents assigned to that task (tool merging), or do predefined agents keep exactly their own tools? Proposal assumes the latter for v1.
- Iterator interaction: parallel iterations each get their own dynamic agent instance (mirrors current per-iteration agent instancing) — confirm.
Problem
Squadron's unit of work assignment is the predefined agent: tools are declared on
agentblocks, and tasks/missions list which agents may be used. The commander coordinates; agents do all the work.In practice this paradigm is breaking down:
agentslist — even when there's nothing agent-like about the need ("this task needs the playwright tools").We're simplifying the relationship between tasks and tools: tools get assigned at the task level (directly or via named groups), and an implicit dynamic agent per task carries those tools and does most of the work. Predefined agents remain fully supported for specialized cases — the commander decides when to delegate to one instead.
Proposal
1.
toolson tasksTasks accept a
tools = [...]attribute using the same reference syntax agents use today (builtins.*,plugins.*.all,mcp.*.*,tools.*):No
agentblock required anywhere in this mission.2.
toolsetblocks — named tool groupsA top-level block that names a reusable group of tool refs:
Toolsets expand at config-load time (like
.alldoes today). They are pure grouping — no model, no personality, no behavior.toolsets.*refs are also valid in agenttoolslists, so predefined agents benefit too.3. Dynamic agents
When a task declares
tools, the runner materializes one implicit dynamic agent for that task, holding all of the task's tools. The commander sees it alongside any explicitly assigned agents and delegates to it via the existingcall_agent/ask_agentflow.worker), consistent across events, debug logs (agent_<task>_worker.md), and persistence.call_agent, session persisted, resumable, queryable viaask_agent.4. Model selection
A mission-level default with per-task override:
If no
agent_defaultsblock exists, dynamic agents inherit the commander's model.Coexistence with predefined agents
Nothing about
agentblocks changes. A task may have tools, agents, or both:The commander decides at runtime which to use for each piece of work.
Validation rules
toolsrefs validated the same way agent tools are (reuseValidateToolReferences).tools, task-levelagents, or mission-levelagents(relaxes today's "agents required" rule).toolsetlabels live in their own namespace; refs to unknown toolsets are config errors; toolsets cannot reference other toolsets (v1 — keeps expansion non-recursive).agent_defaultsis optional; at most one per mission; task-levelmodelis only meaningful when the task hastools.Implementation sketch
Config (
config/)toolsetblock, parsed in Stage 3 alongside customtoolblocks (needsplugins+mcpcontext); expose atoolsets.*HCL namespace; expansion happens inBuildToolsMapor just before it.TaskgainsTools []stringandModel string(config/mission.go);Missiongains anAgentDefaultsblock.Runtime (
agent/,mission/)BuildToolsMap(config/tools_builder.go) is already parametric — dynamic agents reuse it unchanged.AgentManager(agent/agent_manager.go) learns to construct the dynamic agent: synthesize an agent config (reserved name, resolved model, task tools, objective-derived prompt) and feed it through the existingagent.New()path.agent/commander.go,agent/internal/prompts/prompts.go): include the dynamic agent with its tool list and selection guidance.Persistence/resume (
store/)Docs
squadron docs) + CLAUDE.md sections on agents, missions, and the HCL reference.Out of scope (follow-ups)
tools.Open questions
worker?agents.dynamic? Shows up in events, debug filenames, and the command center UI.agent_defaultsblock name and placement — top of mission vs. insidecommander?toolsalso be grantable to predefined agents assigned to that task (tool merging), or do predefined agents keep exactly their own tools? Proposal assumes the latter for v1.