Skip to content

Commit 6ddfa78

Browse files
committed
feat(tools): wire config.trusted_roots into WorkerCreate tool
Previously WorkerCreate passed trusted_roots directly to spawn_worker with no config-level default. Any batch script omitting the field stalled all workers at TrustRequired with no recovery path. Now run_worker_create loads RuntimeConfig from the worker CWD before spawning and merges config.trusted_roots() with per-call overrides. Per-call overrides still take effect; config provides the default. Add test: worker_create_merges_config_trusted_roots_without_per_call_override - writes .claw/settings.json with trustedRoots=[<os-temp-dir>] in a temp worktree - calls WorkerCreate with no trusted_roots field - asserts trust_auto_resolve=true (config roots matched the CWD) 81 tool tests passing, 0 failing.
1 parent bcdc52d commit 6ddfa78

1 file changed

Lines changed: 49 additions & 1 deletion

File tree

rust/crates/tools/src/lib.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,9 +1427,20 @@ fn run_task_output(input: TaskIdInput) -> Result<String, String> {
14271427

14281428
#[allow(clippy::needless_pass_by_value)]
14291429
fn run_worker_create(input: WorkerCreateInput) -> Result<String, String> {
1430+
// Merge config-level trusted_roots with per-call overrides.
1431+
// Config provides the default allowlist; per-call roots add on top.
1432+
let config_roots: Vec<String> = ConfigLoader::default_for(&input.cwd)
1433+
.load()
1434+
.ok()
1435+
.map(|c| c.trusted_roots().to_vec())
1436+
.unwrap_or_default();
1437+
let merged_roots: Vec<String> = config_roots
1438+
.into_iter()
1439+
.chain(input.trusted_roots.iter().cloned())
1440+
.collect();
14301441
let worker = global_worker_registry().create(
14311442
&input.cwd,
1432-
&input.trusted_roots,
1443+
&merged_roots,
14331444
input.auto_recover_prompt_misdelivery,
14341445
);
14351446
to_pretty_json(worker)
@@ -5506,6 +5517,43 @@ mod tests {
55065517
assert_eq!(accepted_output["prompt_in_flight"], true);
55075518
}
55085519

5520+
#[test]
5521+
fn worker_create_merges_config_trusted_roots_without_per_call_override() {
5522+
use std::fs;
5523+
// Write a .claw/settings.json in a temp dir with trustedRoots
5524+
let worktree = temp_path("config-trust-worktree");
5525+
let claw_dir = worktree.join(".claw");
5526+
fs::create_dir_all(&claw_dir).expect("create .claw dir");
5527+
// Use the actual OS temp dir so the worktree path matches the allowlist
5528+
let tmp_root = std::env::temp_dir().to_str().expect("utf-8").to_string();
5529+
let settings = format!("{{\"trustedRoots\": [\"{tmp_root}\"]}}");
5530+
fs::write(
5531+
claw_dir.join("settings.json"),
5532+
settings,
5533+
)
5534+
.expect("write settings");
5535+
5536+
// WorkerCreate with no per-call trusted_roots — config should supply them
5537+
let cwd = worktree.to_str().expect("valid utf-8").to_string();
5538+
let created = execute_tool(
5539+
"WorkerCreate",
5540+
&json!({
5541+
"cwd": cwd
5542+
// trusted_roots intentionally omitted
5543+
}),
5544+
)
5545+
.expect("WorkerCreate should succeed");
5546+
let output: serde_json::Value = serde_json::from_str(&created).expect("json");
5547+
5548+
// worktree is under /tmp, so config roots auto-resolve trust
5549+
assert_eq!(
5550+
output["trust_auto_resolve"], true,
5551+
"config-level trustedRoots should auto-resolve trust without per-call override"
5552+
);
5553+
5554+
fs::remove_dir_all(&worktree).ok();
5555+
}
5556+
55095557
#[test]
55105558
fn worker_tools_detect_misdelivery_and_arm_prompt_replay() {
55115559
let created = execute_tool(

0 commit comments

Comments
 (0)