Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kit"
version = "0.1.111"
version = "0.1.112"
edition = "2024"
rust-version = "1.94.0"
publish = false
Expand Down
12 changes: 7 additions & 5 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,11 @@ impl Runtime {
}

fn system_prompt(&self, depth: usize) -> String {
let delegation_context = if depth == 1 {
"This task was delegated to you by the primary agent. Investigate it and carry out the work.\n\n"
} else {
""
};
format!(
concat!(
"You are a coding agent using Kit version {} as your harness, working in {}. This is your cwd and project context, not a filesystem boundary. ",
Expand All @@ -1001,14 +1006,11 @@ impl Runtime {
"Set the outer `background` argument to `true` to detach immediately or to a positive integer to wait that many seconds before detaching. ",
"After detaching, continue any independent work, including launching more detached work. When the remaining work depends on background results, yield; yielding continues the task with those results, so the user's answer need not be completed first. ",
"Keep work foregrounded when the next step needs its result in the current turn, and do not treat backgrounding as durable job execution.\n\n",
"When subagent tools are available and work changes phase or objective, start fresh subagents from concise summaries of prior results instead of carrying unrelated history. ",
"Keep outputs focused, pass only necessary context, reuse sessions only when continuity helps, and close subagents when no longer needed.\n\n",
"Current subagent depth: {depth}/{}."
"{}"
),
env!("CARGO_PKG_VERSION"),
self.root.display(),
self.max_subagent_depth,
depth = depth
delegation_context
)
}
}
Expand Down
26 changes: 24 additions & 2 deletions src/runtime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,23 @@ fn maximum_depth_compose_omits_depth_increasing_tools() {
);
}

let subagent_description = ToolSource::get(&below_maximum.compose, &ToolName::new("subagent"))
.unwrap()
.current_spec()
.unwrap()
.description;
assert!(subagent_description.contains(
"Use this only if you uncover independent workstreams whose parallel execution would yield quicker or better results."
));
let fork_description = ToolSource::get(&below_maximum.compose, &ToolName::new("fork"))
.unwrap()
.current_spec()
.unwrap()
.description;
assert!(fork_description.contains(
"Use this only for an independent workstream whose parallel execution would yield quicker or better results"
));

let at_maximum = runtime.compose(max_depth);
for name in ["subagent", "fork"] {
assert!(
Expand Down Expand Up @@ -967,6 +984,11 @@ fn system_prompt_guides_compose_and_subagent_hygiene() {
);
assert!(prompt.contains("keep intermediate results inside it"));
assert!(prompt.contains("return only the bare minimum information necessary"));
assert!(prompt.contains("start fresh subagents from concise summaries"));
assert!(prompt.contains("close subagents when no longer needed"));
let delegated_prompt = runtime.system_prompt(1);
assert!(delegated_prompt.contains(
"This task was delegated to you by the primary agent. Investigate it and carry out the work."
));
assert!(!prompt.contains("This task was delegated to you by the primary agent."));
let max_depth_prompt = runtime.system_prompt(runtime.max_subagent_depth());
assert!(!max_depth_prompt.contains("This task was delegated to you by the primary agent."));
}
26 changes: 24 additions & 2 deletions src/tools/subagent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,21 @@ fn call_id_schema() -> serde_json::Value {
impl SubagentTool {
pub fn new(manager: Subagents, depth: usize) -> Self {
let harnesses = manager.harness_references();
Self { manager, depth, spec: ToolSpec::new(ToolName::new("subagent"), "Start a parent-owned configured ACP harness, preferably assign a concise role-oriented display name, prompt it, and return its reusable session value. Omit `harness` and `model` unless the user or active workflow explicitly supplies the exact override or a configured alias. Never choose an override based on your own model, provider, publisher, familiarity, cost, or perceived quality; advertised choices indicate availability, not preference.", json!({"type":"object","properties":{"prompt":{"type":"string"},"name":display_name_schema(),"harness":{"type":"string","enum":harnesses,"description":"Override the user's configured harness preference with this value. Default to omitting it."},"model":{"type":"string","minLength":1,"description":"Exact ACP model selection ID or configured alias explicitly requested by the user or active workflow. Applies only to this new session; default to omitting it."},"cwd":{"type":"string","minLength":1,"description":"Working directory for the new subagent. Relative paths resolve from Kit's working directory."},"output_schema":{"oneOf":[{"type":"object"},{"type":"boolean"}]}},"required":["prompt"],"additionalProperties":false})).with_output_schema(value_schema()).with_annotations(ToolAnnotations::new()) }
let usage = if depth == 1 {
concat!(
"Use this only if you uncover independent workstreams whose parallel execution would yield quicker or better results. ",
"Give each subagent a focused assignment based on what you discovered, and synthesize its findings into your response. "
)
} else {
concat!(
"Use a fresh subagent for work that changes phase or objective instead of carrying unrelated history. ",
"Keep outputs focused, pass only necessary context, reuse sessions only when continuity helps, and close subagents when no longer needed. "
)
};
let description = format!(
"Start a parent-owned configured ACP harness, preferably assign a concise role-oriented display name, prompt it, and return its reusable session value. {usage}Omit `harness` and `model` unless the user or active workflow explicitly supplies the exact override or a configured alias. Never choose an override based on your own model, provider, publisher, familiarity, cost, or perceived quality; advertised choices indicate availability, not preference."
);
Self { manager, depth, spec: ToolSpec::new(ToolName::new("subagent"), description, json!({"type":"object","properties":{"prompt":{"type":"string"},"name":display_name_schema(),"harness":{"type":"string","enum":harnesses,"description":"Override the user's configured harness preference with this value. Default to omitting it."},"model":{"type":"string","minLength":1,"description":"Exact ACP model selection ID or configured alias explicitly requested by the user or active workflow. Applies only to this new session; default to omitting it."},"cwd":{"type":"string","minLength":1,"description":"Working directory for the new subagent. Relative paths resolve from Kit's working directory."},"output_schema":{"oneOf":[{"type":"object"},{"type":"boolean"}]}},"required":["prompt"],"additionalProperties":false})).with_output_schema(value_schema()).with_annotations(ToolAnnotations::new()) }
}
}
impl PromptTool {
Expand All @@ -1324,7 +1338,15 @@ impl PromptTool {
}
impl ForkTool {
pub fn new(manager: Subagents, depth: usize) -> Self {
Self { manager, depth, spec: ToolSpec::new(ToolName::new("fork"), "Fork a completed ACP subagent session using native capability support or the isolated Kit fallback, preferably assign the fork a concise role-oriented display name, prompt it, and return the new session value.", json!({"type":"object","properties":{"subagent":value_schema(),"prompt":{"type":"string"},"name":display_name_schema(),"output_schema":{"oneOf":[{"type":"object"},{"type":"boolean"}]}},"required":["subagent","prompt"],"additionalProperties":false})).with_output_schema(value_schema()).with_annotations(ToolAnnotations::new()) }
let usage = if depth == 1 {
" Use this only for an independent workstream whose parallel execution would yield quicker or better results, and synthesize its findings into your response."
} else {
""
};
let description = format!(
"Fork a completed ACP subagent session using native capability support or the isolated Kit fallback, preferably assign the fork a concise role-oriented display name, prompt it, and return the new session value.{usage}"
);
Self { manager, depth, spec: ToolSpec::new(ToolName::new("fork"), description, json!({"type":"object","properties":{"subagent":value_schema(),"prompt":{"type":"string"},"name":display_name_schema(),"output_schema":{"oneOf":[{"type":"object"},{"type":"boolean"}]}},"required":["subagent","prompt"],"additionalProperties":false})).with_output_schema(value_schema()).with_annotations(ToolAnnotations::new()) }
}
}
impl SubagentsTool {
Expand Down