worker: preserve prompt overrides in internal children
This commit is contained in:
@@ -251,6 +251,7 @@ struct PackFile {
|
|||||||
/// `$yoi` / `$user` / `$workspace`.
|
/// `$yoi` / `$user` / `$workspace`.
|
||||||
pub struct PromptCatalog {
|
pub struct PromptCatalog {
|
||||||
env: Environment<'static>,
|
env: Environment<'static>,
|
||||||
|
loader: PromptLoader,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for PromptCatalog {
|
impl std::fmt::Debug for PromptCatalog {
|
||||||
@@ -260,6 +261,10 @@ impl std::fmt::Debug for PromptCatalog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PromptCatalog {
|
impl PromptCatalog {
|
||||||
|
pub(crate) fn loader(&self) -> PromptLoader {
|
||||||
|
self.loader.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Builtin-only catalog. All `{% include %}` references must resolve
|
/// Builtin-only catalog. All `{% include %}` references must resolve
|
||||||
/// through `$yoi` (user/workspace prefixes are unavailable).
|
/// through `$yoi` (user/workspace prefixes are unavailable).
|
||||||
pub fn builtins_only() -> Result<Arc<Self>, CatalogError> {
|
pub fn builtins_only() -> Result<Arc<Self>, CatalogError> {
|
||||||
@@ -483,7 +488,7 @@ fn build_catalog(
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PromptCatalog { env })
|
Ok(PromptCatalog { env, loader })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ pub struct SubWorkerSpawnTool {
|
|||||||
/// child config from reusable fields here, and selected profiles are
|
/// child config from reusable fields here, and selected profiles are
|
||||||
/// merged into the same internal handoff shape before launch.
|
/// merged into the same internal handoff shape before launch.
|
||||||
spawner_manifest: WorkerManifest,
|
spawner_manifest: WorkerManifest,
|
||||||
|
prompt_loader: PromptLoader,
|
||||||
/// Compact selector list shared by tool description and diagnostics.
|
/// Compact selector list shared by tool description and diagnostics.
|
||||||
available_profiles: AvailableProfiles,
|
available_profiles: AvailableProfiles,
|
||||||
/// Spawner's runtime scope. After a successful spawn, the
|
/// Spawner's runtime scope. After a successful spawn, the
|
||||||
@@ -259,6 +260,7 @@ impl SubWorkerSpawnTool {
|
|||||||
spawner_cwd: PathBuf,
|
spawner_cwd: PathBuf,
|
||||||
registry: Arc<SpawnedWorkerRegistry>,
|
registry: Arc<SpawnedWorkerRegistry>,
|
||||||
spawner_manifest: WorkerManifest,
|
spawner_manifest: WorkerManifest,
|
||||||
|
prompt_loader: PromptLoader,
|
||||||
available_profiles: AvailableProfiles,
|
available_profiles: AvailableProfiles,
|
||||||
spawner_scope: SharedScope,
|
spawner_scope: SharedScope,
|
||||||
delegation_scope: DelegationScope,
|
delegation_scope: DelegationScope,
|
||||||
@@ -272,6 +274,7 @@ impl SubWorkerSpawnTool {
|
|||||||
spawner_cwd,
|
spawner_cwd,
|
||||||
registry,
|
registry,
|
||||||
spawner_manifest,
|
spawner_manifest,
|
||||||
|
prompt_loader,
|
||||||
available_profiles,
|
available_profiles,
|
||||||
spawner_scope,
|
spawner_scope,
|
||||||
delegation_scope,
|
delegation_scope,
|
||||||
@@ -340,7 +343,7 @@ impl Tool for SubWorkerSpawnTool {
|
|||||||
let mut child = Worker::<Box<dyn llm_engine::llm_client::LlmClient>, EphemeralSessionStore>::from_internal_manifest_with_context(
|
let mut child = Worker::<Box<dyn llm_engine::llm_client::LlmClient>, EphemeralSessionStore>::from_internal_manifest_with_context(
|
||||||
child_manifest,
|
child_manifest,
|
||||||
store.clone(),
|
store.clone(),
|
||||||
PromptLoader::builtins_only(),
|
self.prompt_loader.clone(),
|
||||||
self.workspace_context.clone(),
|
self.workspace_context.clone(),
|
||||||
filesystem_authority,
|
filesystem_authority,
|
||||||
self.internal_client_override
|
self.internal_client_override
|
||||||
@@ -803,6 +806,7 @@ fn sub_worker_spawn_tool_impl(
|
|||||||
spawner_cwd.clone(),
|
spawner_cwd.clone(),
|
||||||
registry.clone(),
|
registry.clone(),
|
||||||
spawner_manifest.clone(),
|
spawner_manifest.clone(),
|
||||||
|
prompts.loader(),
|
||||||
available_profiles,
|
available_profiles,
|
||||||
spawner_scope.clone(),
|
spawner_scope.clone(),
|
||||||
DelegationScope::from_config(&spawner_manifest.delegation_scope)
|
DelegationScope::from_config(&spawner_manifest.delegation_scope)
|
||||||
@@ -858,6 +862,15 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let calls = Arc::new(AtomicUsize::new(0));
|
let calls = Arc::new(AtomicUsize::new(0));
|
||||||
let observed_parent_write_revoked = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
let observed_parent_write_revoked = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||||
|
let observed_instruction_override = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||||
|
let workspace_prompts = runtime.path().join("workspace-prompts");
|
||||||
|
std::fs::create_dir_all(&workspace_prompts).unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
workspace_prompts.join("custom-reviewer.md"),
|
||||||
|
"WORKSPACE REVIEWER OVERRIDE",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let prompt_loader = PromptLoader::new(None, Some(workspace_prompts));
|
||||||
let parent_notifies = crate::ipc::notify_buffer::NotifyBuffer::new();
|
let parent_notifies = crate::ipc::notify_buffer::NotifyBuffer::new();
|
||||||
let tool = SubWorkerSpawnTool::new(
|
let tool = SubWorkerSpawnTool::new(
|
||||||
"parent".into(),
|
"parent".into(),
|
||||||
@@ -868,6 +881,7 @@ mod tests {
|
|||||||
workspace_root.clone(),
|
workspace_root.clone(),
|
||||||
registry.clone(),
|
registry.clone(),
|
||||||
manifest.clone(),
|
manifest.clone(),
|
||||||
|
prompt_loader,
|
||||||
AvailableProfiles::discover(&workspace_root),
|
AvailableProfiles::discover(&workspace_root),
|
||||||
spawner_scope.clone(),
|
spawner_scope.clone(),
|
||||||
DelegationScope::from_config(&manifest.delegation_scope).unwrap(),
|
DelegationScope::from_config(&manifest.delegation_scope).unwrap(),
|
||||||
@@ -877,10 +891,12 @@ mod tests {
|
|||||||
parent_scope: spawner_scope.clone(),
|
parent_scope: spawner_scope.clone(),
|
||||||
delegated_path: workspace_root.clone(),
|
delegated_path: workspace_root.clone(),
|
||||||
observed_parent_write_revoked: observed_parent_write_revoked.clone(),
|
observed_parent_write_revoked: observed_parent_write_revoked.clone(),
|
||||||
|
observed_instruction_override: observed_instruction_override.clone(),
|
||||||
}));
|
}));
|
||||||
let input = serde_json::json!({
|
let input = serde_json::json!({
|
||||||
"name": "reviewer-child",
|
"name": "reviewer-child",
|
||||||
"profile": "builtin:reviewer",
|
"profile": "builtin:reviewer",
|
||||||
|
"instruction": "$workspace/custom-reviewer",
|
||||||
"task": "review immutable commit",
|
"task": "review immutable commit",
|
||||||
"scope": [{
|
"scope": [{
|
||||||
"target": workspace_root.clone(),
|
"target": workspace_root.clone(),
|
||||||
@@ -909,6 +925,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
||||||
assert!(observed_parent_write_revoked.load(Ordering::SeqCst));
|
assert!(observed_parent_write_revoked.load(Ordering::SeqCst));
|
||||||
|
assert!(observed_instruction_override.load(Ordering::SeqCst));
|
||||||
assert_eq!(parent_notifies.len(), 1);
|
assert_eq!(parent_notifies.len(), 1);
|
||||||
assert!(!runtime.path().join("reviewer-child/sock").exists());
|
assert!(!runtime.path().join("reviewer-child/sock").exists());
|
||||||
|
|
||||||
@@ -1080,6 +1097,7 @@ mod tests {
|
|||||||
parent_scope: SharedScope,
|
parent_scope: SharedScope,
|
||||||
delegated_path: PathBuf,
|
delegated_path: PathBuf,
|
||||||
observed_parent_write_revoked: Arc<std::sync::atomic::AtomicBool>,
|
observed_parent_write_revoked: Arc<std::sync::atomic::AtomicBool>,
|
||||||
|
observed_instruction_override: Arc<std::sync::atomic::AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -1090,7 +1108,7 @@ mod tests {
|
|||||||
|
|
||||||
async fn stream(
|
async fn stream(
|
||||||
&self,
|
&self,
|
||||||
_request: Request,
|
request: Request,
|
||||||
) -> Result<Pin<Box<dyn Stream<Item = Result<LlmEvent, ClientError>> + Send>>, ClientError>
|
) -> Result<Pin<Box<dyn Stream<Item = Result<LlmEvent, ClientError>> + Send>>, ClientError>
|
||||||
{
|
{
|
||||||
self.calls.fetch_add(1, Ordering::SeqCst);
|
self.calls.fetch_add(1, Ordering::SeqCst);
|
||||||
@@ -1101,6 +1119,13 @@ mod tests {
|
|||||||
.is_writable(&self.delegated_path),
|
.is_writable(&self.delegated_path),
|
||||||
Ordering::SeqCst,
|
Ordering::SeqCst,
|
||||||
);
|
);
|
||||||
|
self.observed_instruction_override.store(
|
||||||
|
request
|
||||||
|
.system_prompt
|
||||||
|
.as_deref()
|
||||||
|
.is_some_and(|prompt| prompt.contains("WORKSPACE REVIEWER OVERRIDE")),
|
||||||
|
Ordering::SeqCst,
|
||||||
|
);
|
||||||
Ok(Box::pin(futures::stream::iter(vec![
|
Ok(Box::pin(futures::stream::iter(vec![
|
||||||
Ok(LlmEvent::text_block_start(0)),
|
Ok(LlmEvent::text_block_start(0)),
|
||||||
Ok(LlmEvent::text_delta(0, "reviewed")),
|
Ok(LlmEvent::text_delta(0, "reviewed")),
|
||||||
|
|||||||
Reference in New Issue
Block a user