Record the parent workflow for agent-started workflows
What does this MR do and why?
A running agent can call POST /api/v4/ai/duo_workflows/agent_workflows to start another workflow. Today nothing limits how long that chain of workflows can get.
This MR passes the calling workflow to the services as the parent workflow, so the chain limits added in !258724 (merged) apply to this endpoint. The change is behind the ai_flow_trigger_chaining feature flag, which is off by default.
Changes:
- When
ai_flow_trigger_chainingis enabled for the container's top-level namespace, the endpoint inee/lib/api/ai/duo_workflows/agent_workflows.rbreadsduo_workflow_idfromGitlab::ApplicationContext.Gitlab::Middleware::DuoWorkflowIdsets this value from theX-Gitlab-Duo-Workflow-Idheader. - The endpoint loads that workflow and checks
:update_duo_workflowfor the current user. The client sets the header, so the workflow is always authorized before it is used. - If the workflow is found and authorized, it is passed as
parent_workflowto both paths:Ai::Catalog::Flows::ExecuteService, for catalog flows (whenai_catalog_item_consumer_idis given).Ai::DuoWorkflows::CreateWorkflowService, for all other requests.
- The depth, breadth, root human, and namespace checks from !258724 (merged) then apply.
- If the header is missing, the workflow does not exist, or the user cannot update it, the request is rejected with 403. Every caller of this endpoint is a running agent, so a request without a parent is treated as an attempt to get around the limits.
- When the flag is disabled, nothing changes. The header is ignored.
- Chain rejections from
CreateWorkflowServicereturn 403 on the non-catalog path. On the catalog path they return 400, because that path already maps every service error to 400. This MR does not change that mapping. - Request specs in
ee/spec/requests/api/ai/duo_workflows/agent_workflows_spec.rbcover:- A child workflow is created with the parent and
trigger_source: flow. - Missing header.
- Unknown workflow.
- Workflow the user cannot update.
- Depth limit.
- Breadth limit.
- Autonomous root.
- Catalog path passes the parent.
- Catalog path rejects a missing header.
- Flag disabled ignores the header.
- A child workflow is created with the parent and
- The rest of the spec file stubs the flag off, because those requests do not come from a running workflow.
- No changelog, because the change is behind a default-off flag.
Known limitations
- Before we enable the flag, we must confirm that every caller sends
X-Gitlab-Duo-Workflow-Idon this request. Workhorse adds it to tool API calls, andglabsets it fromDUO_WORKFLOW_WORKFLOW_ID. If a caller does not send it, every agent call from that caller returns 403 once the flag is on. - The authorization check is the same one used when linking notes and merge requests to a workflow. It proves the caller may update the workflow, not that the caller is that workflow. An agent could name another workflow of the same human, for example a shallower one, and get a smaller depth. The breadth limit still caps each parent, but we should discuss this before the flag is enabled.
- The specs use a regular user token. In production the caller uses a composite identity token, and
current_useris the human the flow acts for.
References
- Plan: https://gitlab.com/gitlab-org/gitlab/-/work_items/630476
- MR 0, cloud events caused by a flow skip flow triggers: !258734 (merged)
- MR 1,
parent_workflow_idcolumn andflowtrigger source: !258704 (merged) - MR 2,
Ai::FlowTriggers::CallerContextand chain limits inCreateWorkflowService: !258724 (merged)
Screenshots or screen recordings
Not applicable. This is a backend-only change.
How to set up and validate locally
-
In the Rails console, enable the flag:
Feature.enable(:ai_flow_trigger_chaining) -
Pick a workflow you own:
parent = Ai::DuoWorkflows::Workflow.where(user: User.find_by_username('root')).last -
Call the endpoint with the parent workflow ID in the header:
curl --request POST \ --header "PRIVATE-TOKEN: <token>" \ --header "X-Gitlab-Duo-Workflow-Id: <parent id>" \ --data "project_id=<id>&workflow_definition=software_development&goal=test" \ "https://gitlab.example.com/api/v4/ai/duo_workflows/agent_workflows"Check that the new workflow has
parent_workflow_idset. -
Repeat the request without the
X-Gitlab-Duo-Workflow-Idheader. You get a 403 response. -
Run the specs:
bin/rspec ee/spec/requests/api/ai/duo_workflows/agent_workflows_spec.rb
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.