feat: add explicit Worker WIP mode

This commit is contained in:
2026-10-04 00:26:15 +09:00
parent 85a8944a6c
commit 4a05aad508
15 changed files with 2546 additions and 18 deletions
+6
View File
@@ -3,6 +3,7 @@ name = "worker"
version = "0.1.0"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
autobins = false
[features]
@@ -34,6 +35,7 @@ chrono = "0.4"
config-source = { path = "../config-source" }
include_dir = "0.7.4"
fs4 = { workspace = true, features = ["sync"] }
http = { workspace = true }
flow = { path = "../flow" }
fs-operation = { workspace = true }
libc = { workspace = true }
@@ -46,6 +48,10 @@ arc-swap = "1.9.1"
tungstenite = { version = "0.28.0", default-features = false, features = ["handshake", "native-tls", "url"] }
tokio-tungstenite = { version = "0.28.0", default-features = false, features = ["native-tls", "connect"] }
futures-util = { version = "0.3", features = ["sink"] }
jsonschema = { version = "0.33.0", default-features = false }
wip-client = { workspace = true }
wip-http = { workspace = true }
wip-protocol = { workspace = true }
[dev-dependencies]
dotenv = "0.15.0"
+17
View File
@@ -1631,6 +1631,23 @@ where
if let Some((resident_summary, system_prompt_override)) = feature_prompt_contribution {
worker.install_system_prompt_contribution(resident_summary, system_prompt_override);
}
if worker.manifest().worker.mode == manifest::WorkerMode::Wip {
let permissions = worker.manifest().permissions.clone();
let security_context = format!(
"workspace={};worker={};session={}",
worker.workspace_id().map_or("standalone", |id| id.as_str()),
worker.manifest().worker.name,
worker.session_id()
);
crate::wip::install_wip_mode(worker.engine_mut(), permissions, security_context).map_err(
|error| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("install WIP Worker surface: {error}"),
)
},
)?;
}
worker.attach_tracker(tracker);
Ok(workdir_for_view)
}
+6
View File
@@ -123,6 +123,12 @@ where
authority,
} = spec;
// Feature-owned private Internal Workers use their explicitly supplied tool
// composition. They are not model-facing direct SubWorkers and must not
// inherit a parent's WIP surface (which is finalized only by the normal
// controller path); keeping Tool mode also preserves manifest permission
// hooks for these private jobs.
manifest.worker.mode = manifest::WorkerMode::Tools;
// Internal identities are run-scoped and never enter the public Runtime Worker catalog.
manifest.worker.name = format!("internal-{}-{}", identity.kind, identity.run_id);
// Internal jobs only receive the explicitly supplied Feature set below. A
+1
View File
@@ -20,6 +20,7 @@ pub mod shared_state;
mod shutdown_after_idle;
pub mod skill;
pub mod spawn;
pub mod wip;
mod internal_worker;
mod interrupt_prep;
+20 -11
View File
@@ -21,16 +21,7 @@ impl PermissionHook {
}
fn action_for(&self, input: &ToolCallSummary) -> ToolPermissionAction {
let target = permission_target(&input.arguments);
self.config
.rules
.iter()
.find(|rule| {
rule.tool.eq_ignore_ascii_case(&input.tool_name)
&& wildcard_match(&rule.pattern, &target)
})
.map(|rule| rule.action)
.unwrap_or(self.config.default_action)
permission_action_for(&self.config, &input.tool_name, &input.arguments)
}
}
@@ -39,7 +30,9 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
let Some(permissions) = self.manifest().permissions.clone() else {
return;
};
self.add_pre_tool_call_hook(PermissionHook::new(permissions));
if self.manifest().worker.mode == manifest::WorkerMode::Tools {
self.add_pre_tool_call_hook(PermissionHook::new(permissions));
}
}
}
@@ -59,6 +52,22 @@ impl Hook<PreToolCall> for PermissionHook {
}
}
pub(crate) fn permission_action_for(
config: &ToolPermissionConfig,
tool_name: &str,
arguments: &Value,
) -> ToolPermissionAction {
let target = permission_target(arguments);
config
.rules
.iter()
.find(|rule| {
rule.tool.eq_ignore_ascii_case(tool_name) && wildcard_match(&rule.pattern, &target)
})
.map(|rule| rule.action)
.unwrap_or(config.default_action)
}
fn permission_denied_message(input: &ToolCallSummary) -> String {
format!(
"permission denied: tool `{}` arguments matched the manifest permission policy",
+6 -1
View File
@@ -883,6 +883,7 @@ fn build_spawn_config_json(
let config = WorkerManifestConfig {
worker: WorkerMetaConfig {
name: Some(name.to_string()),
mode: None,
},
model: model.clone(),
engine: EngineManifestConfig {
@@ -957,6 +958,7 @@ fn manifest_to_reusable_config(manifest: &WorkerManifest) -> WorkerManifestConfi
WorkerManifestConfig {
worker: WorkerMetaConfig {
name: Some(manifest.worker.name.clone()),
mode: Some(manifest.worker.mode),
},
model: manifest.model.clone(),
engine: EngineManifestConfig {
@@ -1871,6 +1873,7 @@ enabled = false
WorkerManifestConfig {
worker: WorkerMetaConfig {
name: Some("parent".into()),
mode: None,
},
model: ModelManifest {
scheme: Some(SchemeKind::Anthropic),
@@ -2188,7 +2191,8 @@ max_tokens = 3333
let delegated = tmp.path().join("delegated");
std::fs::create_dir_all(&parent_deny).unwrap();
std::fs::create_dir_all(&delegated).unwrap();
let parent = parent_manifest(&parent_root, Some(&parent_deny));
let mut parent = parent_manifest(&parent_root, Some(&parent_deny));
parent.worker.mode = manifest::WorkerMode::Wip;
let scope = vec![abs_rule(&delegated, Permission::Read)];
let available = AvailableProfiles {
registry: None,
@@ -2206,6 +2210,7 @@ max_tokens = 3333
);
assert_eq!(config.worker.name.as_deref(), Some("inherited-child"));
assert_eq!(config.worker.mode, Some(manifest::WorkerMode::Wip));
assert_eq!(config.model.model_id.as_deref(), Some("parent-model"));
assert_eq!(config.engine.instruction.as_deref(), Some("default"));
assert_eq!(config.engine.language.as_deref(), Some("Parentish"));
File diff suppressed because it is too large Load Diff