|
|
|
@@ -944,7 +944,7 @@ fn bind_workspace_memory_settings(
|
|
|
|
|
request: &CreateWorkerRequest,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
let Some(snapshot) = request.memory_settings.as_ref() else {
|
|
|
|
|
if request.workspace_api.is_some() {
|
|
|
|
|
if request.workspace_api.is_some() || request.subjektiv_attached {
|
|
|
|
|
return Err(
|
|
|
|
|
"Workspace Worker request is missing its bound Memory settings snapshot"
|
|
|
|
|
.to_string(),
|
|
|
|
@@ -967,7 +967,12 @@ fn bind_workspace_memory_settings(
|
|
|
|
|
.bind_workspace_settings(snapshot.clone())
|
|
|
|
|
.map_err(str::to_string)?;
|
|
|
|
|
}
|
|
|
|
|
if manifest.feature.subjektiv.profile.enabled {
|
|
|
|
|
if request.subjektiv_attached {
|
|
|
|
|
if !manifest.feature.subjektiv.profile.enabled {
|
|
|
|
|
return Err(
|
|
|
|
|
"subject-attached Worker profile does not enable subjektiv policy".to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
manifest
|
|
|
|
|
.feature
|
|
|
|
|
.subjektiv
|
|
|
|
@@ -992,6 +997,12 @@ fn validate_worker_memory_settings(
|
|
|
|
|
request: &CreateWorkerRequest,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
let Some(expected) = request.memory_settings.as_ref() else {
|
|
|
|
|
if request.subjektiv_attached {
|
|
|
|
|
return Err(
|
|
|
|
|
"subject-attached Worker restore is missing its trusted settings snapshot"
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return Ok(());
|
|
|
|
|
};
|
|
|
|
|
manifest
|
|
|
|
@@ -1004,30 +1015,50 @@ fn validate_worker_memory_settings(
|
|
|
|
|
.subjektiv
|
|
|
|
|
.validate_execution()
|
|
|
|
|
.map_err(str::to_string)?;
|
|
|
|
|
for (feature, enabled, actual) in [
|
|
|
|
|
(
|
|
|
|
|
"Memory",
|
|
|
|
|
manifest.feature.memory.profile.enabled,
|
|
|
|
|
manifest.feature.memory.workspace_settings(),
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"subjektiv",
|
|
|
|
|
manifest.feature.subjektiv.profile.enabled,
|
|
|
|
|
manifest.feature.subjektiv.workspace_settings(),
|
|
|
|
|
),
|
|
|
|
|
] {
|
|
|
|
|
if enabled && actual.as_ref() != Some(expected) {
|
|
|
|
|
let actual = actual.ok_or_else(|| {
|
|
|
|
|
format!("Workspace Worker restored without its bound {feature} settings snapshot")
|
|
|
|
|
if manifest.feature.memory.profile.enabled
|
|
|
|
|
&& manifest.feature.memory.workspace_settings().as_ref() != Some(expected)
|
|
|
|
|
{
|
|
|
|
|
let actual = manifest
|
|
|
|
|
.feature
|
|
|
|
|
.memory
|
|
|
|
|
.workspace_settings()
|
|
|
|
|
.ok_or_else(|| {
|
|
|
|
|
"Workspace Worker restored without its bound Memory settings snapshot".to_string()
|
|
|
|
|
})?;
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"Workspace Worker Memory settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
|
|
|
|
|
expected.workspace_id,
|
|
|
|
|
expected.settings_revision,
|
|
|
|
|
actual.workspace_id,
|
|
|
|
|
actual.settings_revision
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
let subjektiv_settings = manifest.feature.subjektiv.workspace_settings();
|
|
|
|
|
if request.subjektiv_attached {
|
|
|
|
|
if !manifest.feature.subjektiv.profile.enabled {
|
|
|
|
|
return Err(
|
|
|
|
|
"subject-attached Worker restored with a Profile that disables subjektiv policy"
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if subjektiv_settings.as_ref() != Some(expected) {
|
|
|
|
|
let actual = subjektiv_settings.ok_or_else(|| {
|
|
|
|
|
"subject-attached Worker restored without its bound subjektiv settings snapshot"
|
|
|
|
|
.to_string()
|
|
|
|
|
})?;
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"Workspace Worker {feature} settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
|
|
|
|
|
"Workspace Worker subjektiv settings snapshot mismatch: expected {} revision {}, restored {} revision {}",
|
|
|
|
|
expected.workspace_id,
|
|
|
|
|
expected.settings_revision,
|
|
|
|
|
actual.workspace_id,
|
|
|
|
|
actual.settings_revision
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
} else if subjektiv_settings.is_some() {
|
|
|
|
|
return Err(
|
|
|
|
|
"ordinary Workspace Worker restored with an unauthorized subjektiv attachment"
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
@@ -4412,9 +4443,70 @@ mod tests {
|
|
|
|
|
worker_observation_grants: Vec::new(),
|
|
|
|
|
workspace_api: None,
|
|
|
|
|
memory_settings: None,
|
|
|
|
|
subjektiv_attached: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn host_attachment_alone_activates_subjektiv_profile_policy() {
|
|
|
|
|
let manifest = || {
|
|
|
|
|
let mut manifest = WorkerManifest::from_toml(
|
|
|
|
|
r#"
|
|
|
|
|
[worker]
|
|
|
|
|
name = "subjektiv-attachment-test"
|
|
|
|
|
pwd = "./"
|
|
|
|
|
|
|
|
|
|
[model]
|
|
|
|
|
scheme = "anthropic"
|
|
|
|
|
model_id = "test-model"
|
|
|
|
|
auth = { kind = "none" }
|
|
|
|
|
|
|
|
|
|
[engine]
|
|
|
|
|
max_tokens = 100
|
|
|
|
|
|
|
|
|
|
[[scope.allow]]
|
|
|
|
|
target = "./"
|
|
|
|
|
permission = "read"
|
|
|
|
|
"#,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
manifest.feature.subjektiv.profile.enabled = true;
|
|
|
|
|
manifest.feature.subjektiv.profile.extraction.enabled = true;
|
|
|
|
|
manifest
|
|
|
|
|
};
|
|
|
|
|
let settings = manifest::WorkspaceMemorySettingsSnapshot {
|
|
|
|
|
workspace_id: "workspace-subject".to_string(),
|
|
|
|
|
settings_revision: 3,
|
|
|
|
|
language: "English".to_string(),
|
|
|
|
|
};
|
|
|
|
|
let mut ordinary_request = create_request("ordinary");
|
|
|
|
|
ordinary_request.workspace_api = Some(WorkspaceApiRef {
|
|
|
|
|
workspace_id: settings.workspace_id.clone(),
|
|
|
|
|
base_url: "http://workspace.invalid".to_string(),
|
|
|
|
|
});
|
|
|
|
|
ordinary_request.memory_settings = Some(settings.clone());
|
|
|
|
|
let mut ordinary = manifest();
|
|
|
|
|
bind_workspace_memory_settings(&mut ordinary, &ordinary_request).unwrap();
|
|
|
|
|
assert!(ordinary.feature.subjektiv.profile.enabled);
|
|
|
|
|
assert!(!ordinary.feature.subjektiv.execution_enabled());
|
|
|
|
|
validate_worker_memory_settings(&ordinary, &ordinary_request).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut attached_request = ordinary_request.clone();
|
|
|
|
|
attached_request.subjektiv_attached = true;
|
|
|
|
|
let mut attached = manifest();
|
|
|
|
|
bind_workspace_memory_settings(&mut attached, &attached_request).unwrap();
|
|
|
|
|
assert!(attached.feature.subjektiv.execution_enabled());
|
|
|
|
|
assert_eq!(
|
|
|
|
|
attached.feature.subjektiv.workspace_settings(),
|
|
|
|
|
Some(settings.clone())
|
|
|
|
|
);
|
|
|
|
|
validate_worker_memory_settings(&attached, &attached_request).unwrap();
|
|
|
|
|
assert!(validate_worker_memory_settings(&attached, &ordinary_request).is_err());
|
|
|
|
|
|
|
|
|
|
attached_request.memory_settings = None;
|
|
|
|
|
assert!(bind_workspace_memory_settings(&mut manifest(), &attached_request).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn git(path: &std::path::Path, args: &[&str]) {
|
|
|
|
|
let status = Command::new("git")
|
|
|
|
|
.arg("-C")
|
|
|
|
|