RFC: Skill-Like Subagents for Keen Code¶
Summary¶
Implement Keen subagents using a skill-like authoring and discovery mechanism:
Markdown files with YAML frontmatter, discovered from project and user
directories, surfaced to the main agent through a catalog, and invoked through a
dedicated delegate_task tool.
The important distinction is runtime behavior:
- A skill is loaded into the main agent's context and changes how the main agent works.
- A subagent profile configures a separate child agent that runs in its own context and returns a compact result to the parent.
This keeps the authoring model familiar while preserving Keen's existing context discipline.
Goals¶
- Let users define reusable subagents with a lightweight Markdown + YAML format.
- Reuse the mental model and parser/discovery patterns of Keen skills where it makes sense.
- Give the main agent a tool for explicit bounded delegation.
- Keep noisy exploration out of the parent agent's live context.
- Start with read-only subagents to avoid file conflicts and permission ambiguity.
- Preserve Keen's existing tool permission model.
- Keep implementation provider-neutral across Keen's LLM clients.
- Support the profile fields needed for model routing:
provider,model, andthinking_effort. - Keep subagents independent from Keen skills and MCP support.
Non-Goals¶
- Do not implement autonomous agent teams.
- Do not let subagents recursively spawn subagents in the first version.
- Do not implement worktree or filesystem isolation in this RFC.
- Do not delegate gratuitously or for ordinary prompts that the main agent can handle directly.
- Do not replace skills, turn memory, compaction, or the
/adversarycommand. - Do not add write-capable parallel workers until read-only delegation is stable.
Motivation¶
Keen already has strong cross-turn context management through TurnMemory.
Completed tool-heavy turns are compressed into small durable summaries instead
of replaying every file read, grep result, and command output in later turns.
Subagents still add value in a different place: within a single active turn. During a long turn, the parent agent can still accumulate noisy intermediate state while exploring broad areas of the codebase. A child agent can do that work in a separate context and return only the distilled findings.
The most valuable first use cases are:
- broad codebase exploration
- review passes
- test failure analysis
- log summarization
- dependency or API research
- adversarial critique of a proposed plan
These are usually read-heavy and bounded, which makes them a good fit for a safe MVP.
Design Principles¶
-
Deliberate delegation. The main agent may choose to use a subagent on its own when the work is bounded, independent, and benefits from a narrower role or reduced parent context. Users can also explicitly request a named subagent. The main agent should not delegate gratuitously or for every task.
-
Skill-like authoring, separate execution. Users should define subagents in simple Markdown files, but invocation should start a separate child context.
-
Read-only first. Start with agents that can inspect and summarize. Writing can come later with stronger isolation.
-
Permissions are an intersection. A subagent should never gain tools or access that the parent session does not have.
-
Summaries, not transcripts. The parent should receive a concise structured result. Raw child tool output should be inspectable separately, not injected wholesale into the parent context.
-
Provider-neutral orchestration. Subagent orchestration should live above provider clients, not inside a specific Anthropic/OpenAI/Genkit implementation.
User Model¶
Users create subagent profiles in files such as:
.keen/agents/reviewer.md
.agents/agents/reviewer.md
~/.keen/agents/reviewer.md
~/.agents/agents/reviewer.md
Example:
---
name: reviewer
description: Reviews code for correctness, security, regressions, and missing tests.
tools: [read_file, glob, grep]
provider: inherit
model: inherit
thinking_effort: inherit
---
Review like a maintainer.
Prioritize correctness, security, behavior regressions, and missing tests.
Return only actionable findings with file references.
Do not edit files.
The main agent sees a subagent catalog in the system prompt and can call:
delegate_task(agent: "reviewer", task: "Review this branch for regressions")
The subagent runs separately, then returns a compact result to the parent agent.
Profile Format¶
Subagent profiles are Markdown files with YAML frontmatter followed by instructions.
Required fields:
| Field | Type | Purpose |
|---|---|---|
name |
string | Stable subagent identifier used by delegate_task |
description |
string | When this subagent should be used |
Supported optional fields:
| Field | Type | Default | Purpose |
|---|---|---|---|
tools |
string[] | read-only default | Tool allowlist. MVP supports only read_file, glob, and grep; requested tools are intersected with parent availability. |
provider |
string | inherit |
Provider override or inherited parent provider |
model |
string | inherit |
Model override or inherited parent model |
thinking_effort |
string | inherit |
Reasoning/thinking effort override when the selected model supports it |
timeout_seconds |
number | 600 |
Child runtime cap in seconds |
hidden |
bool | false |
Hide from catalog/autocomplete but allow direct internal use |
Future fields:
| Field | Purpose |
|---|---|
mode |
subagent, primary, or all if Keen later supports primary agents |
disallowed_tools |
Remove tools from inherited/default tool sets |
permission_mode |
inherit, read-only, ask, or auto |
color |
Optional UI color hint |
Unknown fields should produce warnings, not hard failures, unless they make the profile unsafe or ambiguous. This allows partial compatibility with other agent-file ecosystems.
The minimal useful profile is therefore:
---
name: reviewer
description: Reviews code for correctness, regressions, security, and missing tests.
---
Review like a maintainer.
Return actionable findings with file references.
All omitted optional fields inherit from the main agent's resolved runtime environment or global agent defaults. This keeps profile files small while still allowing explicit overrides when a subagent needs a different model, tool surface, timeout, or thinking effort.
Subagents do not have Keen skills or MCP support. Profile frontmatter must not
include skills or mcp_skills; if those fields appear, they are treated as
unknown fields and reported as warnings.
Discovery¶
Use a discovery flow similar to skills:
- Find candidate Markdown files under configured roots.
- Parse YAML frontmatter.
- Validate required fields.
- Resolve duplicates by priority.
- Return a catalog plus warnings.
Suggested root priority:
<working-dir>/.agents/agents/<working-dir>/.keen/agents/<working-dir>/.claude/agents/for compatible subset import~/.agents/agents/~/.keen/agents/~/.claude/agents/for compatible subset import- bundled Keen agents
This mirrors the current skill discovery philosophy while keeping subagents in their own namespace.
Built-In Subagents¶
Keen should ship the first MVP with one bundled read-only profile:
| Name | Tools | Purpose |
|---|---|---|
explorer |
read_file, glob, grep |
Investigate code, docs, or configuration and return concise findings with references |
Additional bundled read-only profiles such as reviewers or analyzers can be added
later after the execution model is stable. Do not include a write-capable
worker in the default MVP.
Invocation Tool¶
Add a built-in tool:
delegate_task
Suggested input schema:
{
"type": "object",
"required": ["agent", "task"],
"properties": {
"agent": {
"type": "string",
"description": "Name of the subagent profile to run."
},
"task": {
"type": "string",
"description": "Bounded task for the subagent."
},
"timeout_seconds": {
"type": "integer",
"description": "Optional lower timeout than the profile/global timeout."
}
}
}
Tool output should be structured:
{
"agent": "review",
"status": "completed",
"summary": "...",
"findings": [
{
"severity": "high",
"file": "internal/foo.go",
"line": 42,
"message": "..."
}
],
"commands_run": [],
"errors": [],
"session_id": "child-session-id"
}
The exact Go output type can be simpler at first, but the model-facing result should be predictable and concise.
Parent Prompt Guidance¶
The main system prompt should gain a small section explaining subagents:
- Use
delegate_taskfor bounded, independent work that would otherwise create noisy context or benefit from a narrower role. - Prefer subagents when the task is separable, has a clear objective, and matches a listed subagent's description.
- Good uses vary by subagent and can include investigation, review, comparison, summarization, tracing, or domain-specific analysis.
- Do not use subagents for quick file reads, direct edits, ambiguous requests that need clarification, or tightly coupled implementation work.
- Ask subagents for concise results with relevant references.
- Synthesize results yourself after child agents return.
This keeps the feature useful without encouraging gratuitous fan-out.
Child Prompt Contract¶
Each child agent gets a system prompt from the selected subagent markdown file plus a delegated user task. The markdown body after the YAML frontmatter is the subagent's system prompt.
The child system prompt is composed from:
- The selected agent profile body, from the markdown after frontmatter.
- Runtime context such as the working directory.
- No skill or MCP catalog; subagents use only the tools provided by their filtered child registry.
The delegated task is sent as the child user message, not embedded in the system prompt.
Any global subagent constraints should live in the bundled/profile markdown itself, not in a separate Keen base child-agent prompt.
Runtime Architecture¶
Add a new package:
internal/subagents
Suggested files:
internal/subagents/profile.go
internal/subagents/parse.go
internal/subagents/discover.go
internal/subagents/catalog.go
internal/subagents/runner.go
internal/tools/delegate_tool.go
Core types:
type Profile struct {
Name string
Description string
Tools []string
Provider string
Model string
ThinkingEffort string
TimeoutSeconds int
Hidden bool
Instructions string
}
type Runner struct {
WorkingDir string
Config *config.ResolvedConfig
Profiles Catalog
NewClient ClientFactory
Registry *tools.Registry
}
The runner should:
- Resolve the profile.
- Build child messages.
- Build an effective tool registry.
- Create a fresh child LLM client.
- Resolve profile
provider,model, andthinking_effortagainst parent config. - Do not resolve or load skills for child agents.
- Apply timeout and cancellation.
- Capture child stream events.
- Return a compact result.
- Persist child transcript metadata.
Client Lifecycle¶
Do not reuse the parent LLMClient instance for child agents unless it is
explicitly safe.
Reason:
- Some provider clients hold pending state for incomplete turns.
- Some clients may maintain provider-specific session state.
- Parallel subagents would create race risk if they share one mutable client.
Add a client factory that can instantiate a new client from a resolved config. For MVP, subagents can inherit the parent provider/model but still use a fresh client instance.
Tool and Permission Model¶
Effective tools are an intersection:
effective_tools =
parent_available_tools
intersect read_only_subagent_tools
intersect profile.tools_or_default
minus delegate_task
Rules:
- Child agents never receive
delegate_taskin the MVP. - MVP child agents receive only read-only tools:
read_file,glob, andgrep. - The
toolsfrontmatter field can further restrict that read-only set, but it cannot grant write tools,bash, MCP tools, or recursive delegation. call_mcp_toolis never exposed to child agents because subagents do not have MCP support.- Permission prompts must show the child agent label when a child triggers them.
This gives users predictable control and avoids privilege escalation.
Subagent profile prompts should make their own constraints clear. The child prompt should not include the parent skill catalog or MCP catalog.
Configuration¶
MVP runtime defaults:
max_concurrency = 1
max_depth = 1
default_timeout_seconds = 600
For the first implementation, these are code-level defaults. A later iteration
can add a global Agents config section after the runner and transcript model
are proven.
Session and Transcript Persistence¶
Child work should not disappear, but it also should not pollute parent context.
MVP approach:
- Create a normal session for each child run.
- Store metadata linking it to the parent session:
- parent session ID
- child session ID
- agent name
- task
- status
- started/ended timestamps
- Return the child session ID in
delegate_taskoutput.
Potential event additions:
KindSubagentStarted
KindSubagentFinished
These can be added later if the UI needs richer replay. The MVP can keep the parent transcript clean by representing subagent invocation as a normal tool call result.
TUI Behavior¶
MVP:
- Render
delegate_tasklike any other tool call. - Show the agent name and short task summary.
- On completion, show the compact result.
Later:
/agentslists available profiles./agents reloadreloads profile files./agents statuslists child runs for the current session./agent <session-id>opens or prints the child transcript.- Permission overlays show the child label.
Avoid building a complex agent dashboard until the basic execution model is stable.
Headless Behavior¶
In text mode, include only the parent agent's final answer.
In JSON mode, eventually include child metadata:
{
"session_id": "parent",
"text": "...",
"subagents": [
{
"session_id": "child",
"agent": "review",
"status": "completed",
"summary": "..."
}
]
}
The first implementation can omit this and rely on the tool result in the parent transcript, but the data model should not block JSON support.
Relationship to Skills¶
Subagents should reuse skill-like machinery where practical:
- YAML frontmatter parsing
- Markdown body instructions
- discovery roots
- catalog generation
- enable/disable config pattern
- bundled profiles
Subagents should not reuse skill activation semantics directly:
- A skill is read or injected into the main context.
- A subagent starts a separate child context.
- A skill has no independent tool registry.
- A subagent has its own filtered tool registry.
- A skill does not produce a child transcript.
- A subagent should produce a child transcript or linked metadata.
- Subagents do not have skills or MCP support.
This distinction should be explicit in docs to avoid user confusion.
Relationship to TurnMemory¶
TurnMemory remains Keen's cross-turn memory compression mechanism.
Subagents add within-turn isolation:
- Parent context receives a compact result.
- Child raw file reads and searches stay in the child transcript.
- Later parent turns still retain only the parent assistant response plus normal turn memory.
Do not use subagents as a replacement for compaction or turn memory.
Rollout Plan¶
Phase 1: Read-Only MVP¶
- Add
internal/subagentsparser/discovery/catalog. - Add bundled
explorerprofile. - Add serial
Runner. - Add
delegate_tasktool. - Use fresh child clients.
- Use read-only child registries.
- Support
toolsas a read-only allowlist. - Support
provider,model, andthinking_effortin profile resolution. - Do not support skills or MCP in subagents.
- Disable recursive delegation.
- Add unit tests.
Phase 2: Commands and Config¶
- Add
/agents list. - Add
/agents reload. - Add
AgentsConfig. - Add enable/disable support if needed.
- Document profile format.
Phase 3: Better Persistence and Headless Metadata¶
- Link child sessions to parent sessions.
- Add child metadata to headless JSON.
- Add child transcript lookup.
Phase 4: Parallel Subagents¶
- Raise
max_concurrency. - Add cancellation for multiple active children.
- Add clear TUI status.
- Run race tests heavily.
Phase 5: Write-Capable Workers¶
- Add experimental write-capable profiles.
- Enforce a single-writer rule.
- Add conflict detection.
- Consider filesystem or worktree isolation only in a separate future RFC.
Implementation Status¶
Completed in the first iteration:
- Added
internal/subagentsfor profile parsing, bundled discovery/catalog, and the serial runner. - Added bundled
exploreras the first read-only subagent, using onlyread_file,glob, andgrep. - Added
delegate_taskininternal/tools, withagent,task, and optionaltimeout_seconds. - Wired the parent prompt to show available subagents and registered
delegate_taskfor the main agent only. - Child agents use the selected markdown body as their system prompt, inherit runtime model/provider settings, run through a fresh client, and do not receive skills, MCP, bash, write tools, or recursive delegation.
- The delegate tool buffers the child stream and returns a concise final result to the parent.
Testing Plan¶
Parser/discovery:
- Valid profile parses.
- Missing
namefails. - Missing
descriptionfails. - Invalid YAML returns clear error.
- Unknown fields produce warnings.
- Duplicate names resolve by root priority.
- Hidden profiles are omitted from catalog.
Tool registry:
- Child registry contains only allowed read-only tools.
toolscan restrict the read-only set.delegate_taskis never present in child registry.- Unknown profile returns a tool error.
- Profile cannot grant tools unavailable to parent.
call_mcp_toolis never present in child registry.
Runner:
- Child messages include the markdown body system prompt and delegated task.
- Timeout cancels the child run.
- Child result is summarized.
- Child errors are returned to parent as structured errors.
- Fresh client factory is called for each child.
- Provider/model/thinking overrides are applied to the child config.
- Child prompt does not include the parent skill catalog or MCP catalog.
Permissions:
- Read-only profile cannot write.
- Permission prompts include agent identity once child prompts are wired through the requester.
Integration:
- Main agent can call
delegate_taskand use the result. - Headless run can complete with a delegated child task.
- Session transcript stores the parent tool result.
Concurrency, later:
- Parallel children respect max concurrency.
- Race test passes with multiple child runs.
- Canceling parent cancels children.
Open Questions¶
-
Should
.agents/agents/or.keen/agents/have higher priority? The RFC proposes.agents/agents/first for ecosystem compatibility, but.keen/agents/first would make Keen-specific behavior more predictable. -
Should
bashbe allowed for specialized analyzer subagents later? The safer answer is no for the MVP. A later phase can add read-only command classes or command allowlists. -
Should hidden profiles be callable by explicit user prompt? The MVP should treat
hiddenas "not in catalog" rather than "not callable," unless a lateruser_invocablefield is added. -
Should child sessions be visible in
/sessions? They should probably be hidden by default and reachable from parent metadata.
Decision¶
Proceed with skill-like subagent profiles plus a delegate_task tool.
The first implementation should be deliberately conservative:
- read-only
- serial
- deliberate delegation
- max depth 1
- fresh child client
- provider/model/thinking override support
- no skills or MCP in child agents
- no recursive delegation
- no bash in child agents
- compact result returned to parent
This gives Keen the useful part of subagents while preserving the core design values already present in the codebase: small context, explicit memory, provider-neutral execution, and strict tool permissions.