mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-27 08:02:39 +00:00
* feat: let flow builder operators build AI agents Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check linked agents' bound tools and type, validate on the write's transaction Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: bound the linked-agent walk by sorting bound tool ids Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: let builders bind only the agent tool inputs left to the model Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check every agent tool sharing a bound id Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: show builders the agent's own tool inputs read-only and name agents in the setting Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: show the agent's own tool inputs with the read-only step viewer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style: give the agent-set inputs heading the input label style Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style: label the agent's fixed tool inputs static inputs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: address cubic review on agent builder rights Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check agent writes against the role's flows capability Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: name the flows capability as the agent builder right Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: gate agent evals on the role's code capability Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: move the composed-flow check into windmill-api-auth Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
1.9 KiB
Rust
56 lines
1.9 KiB
Rust
use serde_json::json;
|
|
use sqlx::{Pool, Postgres};
|
|
use std::time::Duration;
|
|
use windmill_common::{flows::FlowValue, jobs::JobPayload};
|
|
use windmill_test_utils::{
|
|
completed_job, in_test_worker, listen_for_completed_jobs, ApiServer, RunJob, StreamFind,
|
|
};
|
|
|
|
/// Only writes of an agent are checked for operators, so a linked step must refuse a path whose
|
|
/// resource is no longer an agent rather than run the `tools` it now holds.
|
|
#[sqlx::test(fixtures("base"))]
|
|
#[serial_test::serial]
|
|
async fn linked_agent_step_refuses_a_resource_of_another_type(
|
|
db: Pool<Postgres>,
|
|
) -> anyhow::Result<()> {
|
|
sqlx::query(
|
|
"INSERT INTO resource (workspace_id, path, value, resource_type)
|
|
VALUES ('test-workspace', 'u/test-user/agent', $1, 'object')",
|
|
)
|
|
.bind(
|
|
json!({"tools": [{"id": "t", "value": {"tool_type": "flowmodule",
|
|
"type": "rawscript", "language": "bun", "input_transforms": {},
|
|
"content": "export async function main() { return 1 }"}}]}),
|
|
)
|
|
.execute(&db)
|
|
.await?;
|
|
|
|
let server = ApiServer::start(db.clone()).await?;
|
|
let flow: FlowValue = serde_json::from_value(json!({"modules": [{"id": "agent", "value": {
|
|
"type": "aiagent", "tools": [], "agent": "$res:u/test-user/agent",
|
|
"input_transforms": {"user_message": {"type": "static", "value": "hi"}}
|
|
}}]}))?;
|
|
let id = RunJob::from(JobPayload::RawFlow {
|
|
value: flow,
|
|
path: Some("u/test-user/linked".into()),
|
|
restarted_from: None,
|
|
})
|
|
.push(&db)
|
|
.await;
|
|
let notifications = listen_for_completed_jobs(&db).await;
|
|
tokio::time::timeout(
|
|
Duration::from_secs(45),
|
|
in_test_worker(&db, notifications.find(&id), server.addr.port()),
|
|
)
|
|
.await?;
|
|
let job = completed_job(id, &db).await;
|
|
server.close().await?;
|
|
assert!(!job.success);
|
|
assert!(
|
|
format!("{:?}", job.result).contains("not an AI agent"),
|
|
"{:?}",
|
|
job.result
|
|
);
|
|
Ok(())
|
|
}
|