diff --git a/Cargo.lock b/Cargo.lock index 649153ef5..f64b67e06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6766,6 +6766,7 @@ dependencies = [ "uuid", "webauthn-authenticator-rs", "webauthn-rs", + "wip-protocol", "workdir", "worker", "worker-runtime", diff --git a/crates/workspace-server/Cargo.toml b/crates/workspace-server/Cargo.toml index 1f5f4f66a..7b50a34cf 100644 --- a/crates/workspace-server/Cargo.toml +++ b/crates/workspace-server/Cargo.toml @@ -56,6 +56,7 @@ zeroize.workspace = true webauthn-rs = { workspace = true } [dev-dependencies] +wip-protocol.workspace = true agen.workspace = true tempfile.workspace = true tools.workspace = true diff --git a/crates/workspace-server/src/server_workspace_config_wip_tests.rs b/crates/workspace-server/src/server_workspace_config_wip_tests.rs index c0df1d1a5..11f3842ba 100644 --- a/crates/workspace-server/src/server_workspace_config_wip_tests.rs +++ b/crates/workspace-server/src/server_workspace_config_wip_tests.rs @@ -139,26 +139,27 @@ impl worker::WorkspaceClient for ConfigRouterClient { Ok(response) } } -fn config_interface(path: &str) -> String { - // Use the published contextual interface identity, not a model-managed validator. - let hex = path - .bytes() - .map(|byte| format!("{byte:02x}")) - .collect::(); - format!("yoi.workspace-config/node/v1/@/{hex}") +fn config_interface(path: &str) -> wip_protocol::InterfaceReference { + // Use the published structured Interface identity, not a model-managed validator. + wip_protocol::InterfaceReference { + scope: path.into(), + name: "yoi.workspace-config/node/v1".into(), + } } -async fn config_discover(runtime: &worker::wip::WipRuntime, path: &str) { - let observed = runtime - .discover(path.into(), 0, true) - .await - .unwrap() - .content - .unwrap(); +async fn config_inspect(runtime: &worker::wip::WipRuntime, path: &str) { + let output = runtime.inspect(path.into(), true).await.unwrap(); + let observed: Value = serde_json::from_str(output.content.as_deref().unwrap()).unwrap(); assert!( - observed.contains(&config_interface(path)), - "interface must come from live discovery: {observed}" + observed["interfaces"] + .as_array() + .unwrap() + .iter() + .any(|interface| { + interface["reference"] + == json!({"scope": path, "name": config_interface(path).name}) + }), + "Interface must come from live Object inspection: {observed}" ); - runtime.inspect(config_interface(path), true).await.unwrap(); } async fn config_call( runtime: &worker::wip::WipRuntime, @@ -167,7 +168,7 @@ async fn config_call( args: Value, ) -> std::result::Result { runtime - .call( + .invoke( path.into(), config_interface(path), operation.into(), @@ -183,23 +184,24 @@ async fn catalog_call( base: &str, operation: &str, ) -> Value { - let hex = path - .bytes() - .map(|byte| format!("{byte:02x}")) - .collect::(); - let interface = format!("{base}/@/{hex}"); - let discovered = runtime - .discover(path.into(), 0, true) - .await - .unwrap() - .content - .unwrap(); - assert!(discovered.contains(&interface), "{discovered}"); - runtime.inspect(interface.clone(), true).await.unwrap(); + let interface = json!({"scope": path, "name": base}); + let inspected = runtime.inspect(path.into(), true).await.unwrap(); + let observed: Value = serde_json::from_str(inspected.content.as_deref().unwrap()).unwrap(); + assert!( + observed["interfaces"] + .as_array() + .unwrap() + .iter() + .any(|published| published["reference"] == interface), + "{observed}" + ); let output = runtime - .call( + .invoke( path.into(), - interface, + wip_protocol::InterfaceReference { + scope: path.into(), + name: base.into(), + }, operation.into(), json!({}), agen::tool::ToolExecutionContext::direct(), @@ -378,7 +380,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im .connection_id, attached.connection_id ); - config_discover(&wip, CONTENT_ROOT).await; + config_inspect(&wip, CONTENT_ROOT).await; config_call( &wip, CONTENT_ROOT, @@ -388,7 +390,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im .await .unwrap(); let path = "/workspace-config/notes/wip.md"; - config_discover(&wip, path).await; + config_inspect(&wip, path).await; assert!( config_call(&wip, path, "read", json!({})) .await @@ -422,7 +424,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im config_call(&wip, path, "write", json!({"content":"replacement note"})) .await .unwrap(); - config_discover(&wip, CONTENT_ROOT).await; + config_inspect(&wip, CONTENT_ROOT).await; config_call(&wip,CONTENT_ROOT,"apply_changes",json!({"changes":[ {"kind":"create","path":"fragments/prompts.dcdl","content_type":"decodal","content":"{ common = { language = \"CONFIG_WIP_E2E\"; }; }"}, {"kind":"update","path":"main.dcdl","content":"{ prompts = import \"./fragments/prompts.dcdl\"; } as WorkspaceConfigSchema"} @@ -436,7 +438,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im .unwrap(); let prompts = crate::prompt_settings::project_prompts_from_workspace_config(&state).unwrap(); assert_eq!(prompts.templates["common.language"], "CONFIG_WIP_E2E"); - config_discover(&wip, path).await; + config_inspect(&wip, path).await; config_call(&wip, path, "delete", json!({})).await.unwrap(); assert!( fixture @@ -449,7 +451,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im .get(&config_source::VirtualPath::parse("notes/wip.md").unwrap()) .is_none() ); - config_discover(&wip, CONTENT_ROOT).await; + config_inspect(&wip, CONTENT_ROOT).await; let before = fixture .api .config_store @@ -492,7 +494,7 @@ async fn workspace_config_real_selected_invoke_backend_wip_edit_create_delete_im dispatched + 1, "a lost response cannot trigger automatic retry" ); - config_discover(&wip, CONTENT_ROOT).await; + config_inspect(&wip, CONTENT_ROOT).await; let readonly = workspace_config::attach( &fixture.api, &fixture.worker, @@ -633,15 +635,15 @@ async fn assert_config_production_http_adapter( ) .unwrap(); assert_config_catalog(&wip, &attached).await; - config_discover(&wip, "/workspace-config/main.dcdl").await; + config_inspect(&wip, "/workspace-config/main.dcdl").await; let main_interface = wip - .inspect(config_interface("/workspace-config/main.dcdl"), true) + .inspect("/workspace-config/main.dcdl".into(), true) .await .unwrap() .content .unwrap(); assert!( - !main_interface.contains("\"delete\""), + !main_interface.contains("delete("), "required entrypoint cannot advertise delete: {main_interface}" ); assert!( @@ -652,7 +654,7 @@ async fn assert_config_production_http_adapter( .unwrap() .contains("WorkspaceConfigSchema") ); - config_discover(&wip, "/workspace-config").await; + config_inspect(&wip, "/workspace-config").await; config_call( &wip, "/workspace-config",