Duo should remember command/tool approvals
# Overview
Users want Duo to remember their approval decisions for specific tools or commands during a single session, rather than asking for approval every time the same tool is invoked. This is especially problematic during debugging or testing workflows where the same commands run repeatedly.
# Proposed solution
* As a first iteration, a **hybrid approval granularity model** is being implemented that distinguishes between tool types:
* **Safe tools** (grep, find, readFile): Approved at tool level, remembers for all uses
* **Dangerous tools** (runCommand, writeFile, editFile, deleteFile): Approved at call level, remembers only exact arguments
This approach stores approval decisions in `ChatWorkflowState` with a `remember_approval` boolean flag. When a user approves a dangerous tool like `runCommand("npm install")`, the system stores the exact command signature and only auto-approves that specific command in future calls, not arbitrary shell commands.
* Multiple proposals suggest using configuration files as the source of truth:
* **MCP configuration** - Leverage `mcp.json` with a `preApprovedTools` field (boolean for all tools or array for specific tools)
* **IDE settings** - Allow users to configure allowlists/denylists directly in editor settings, similar to how Cursor and Claude Code implement this
* Add telemetry around usage to ensure data-driven decisions are made surrounding what feature is built/ iterated on next.
---
:white_check_mark: - Done
:construction: - In Progress
:white_circle: - Not Started
<details>
<summary>
### :sparkles: Pre-existing work - Session Approval for MCP Tools
</summary>
<table>
<tr>
<th></th>
<th>Description</th>
<th>Sub Epic / Issues</th>
<th>Status</th>
<th>Technical Lead</th>
</tr>
<tr>
<td>Implement Scoped Tool Approvals for MCP</td>
<td>
* Introduce **per-session tool approvals** (keyed by workflowId) and integrate them into reload so each tool’s isApproved reflects:
* isApproved = approved-by-config
* or, approved-for-this-session
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/work_items/1418+
</td>
<td>
:white_check_mark:
</td>
<td>
@john-slaughter
</td>
</tr>
<tr>
<td>Duo UI updates</td>
<td>
* The component now supports both simple single-button approval and more advanced split-button approval with multiple permission levels.
* This gives users more control over AI tool usage while maintaining backward compatibility with existing implementations.
</td>
<td>
https://gitlab.com/gitlab-org/duo-ui/-/merge_requests/279+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Integrate and update new UI for Approve for Session</td>
<td>
* Check the tool name before displaying the approval options UI split button in chat to ensure that when it is not a MCP tool call - users only see the `Approve` button.
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/work_items/1514+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>SPIKE (Client-Side)</td>
<td>
* A client side approach was spiked end to end as a catalyst for this work.
* The approach was decided that it may result in security vulnerabilities and a sever side approach will be implemented.
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/merge_requests/2723+
https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/merge_requests/4235+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
</table>
</details>
<details>
<summary>
### :sparkles: Core Foundational Elements - Non-MCP Tools - Server-Side Approach
</summary>
<table>
<tr>
<th></th>
<th>Description</th>
<th>Sub Epic / Issues</th>
<th>Status</th>
<th>Technical Lead</th>
</tr>
<tr>
<td>Create Backend Graphql For UpdateToolApproval</td>
<td>
* Create a graphql endpoint to handle tool approval accumulation in the DB
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/556045+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
@mikolaj_wawrzyniak
</td>
</tr>
<tr>
<td>Handle serverCapabilities for backwards compatibility</td>
<td>
* To ensure all versions of DWS work with tool approvals, introduced passing capabilities to the client from Server/DWS
</td>
<td>
https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/work_items/2001+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Update AI Gateway to handle tool approval</td>
<td>
* Ensure gateway can handle the new approved_tool_calls DB argument
</td>
<td>
https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/issues/1874+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
@mikolaj_wawrzyniak
</td>
</tr>
<tr>
<td>Return capabilities from DWS on direct_access token fetch</td>
<td>
* Without coupling LSP against a version number for DWS, now returning capabilities in token fetch to be sent to LSP through rails
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/592817+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>
\[LSP\] Extend Approve for Session option to all tool calls in DAP
</td>
<td>
* Only MCP tool calls currently can be approved for the entire session
* This will enable all tool calls to be handled by the backend server
* Checks which capabilities are available from Rails/DWS before displaying Tool Approval options
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/issues/1944+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Telemetry for Approve for Session Dropdown</td>
<td>
* Add telemetry feedback for existing features with the intention of continuing to add telemetry as features are created
* This is the SSOT for what users are using and where the direction of the product should point next
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/work_items/2205+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
</table>
</details>
### :sparkles: Core Iterations
<table>
<tr>
<th></th>
<th>Description</th>
<th>Sub Epic / Issues</th>
<th>Status</th>
<th>Technical Lead</th>
</tr>
<tr>
<td>Tiered Setting for Approve Tool for Sessions (IDE + CLI)</td>
<td>
* Alongside having approve for session as a feature, it is consistent across customers for admin to be able to turn the feature off
</td>
<td>
Sub-epic: https://gitlab.com/groups/gitlab-org/-/work_items/21593+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Wildcard Tool Approvals</td>
<td>
* In the first iteration, tool approvals rely on SHA hashes. If the tool is slightly different, the hash will be as well and tool approval will be required
* This iteration allows for groups of tool calls to be approved, i.e all `npm` tools could be approved for a session
</td>
<td>
Sub-epic: https://gitlab.com/groups/gitlab-org/-/epics/21850+
</td>
<td>
:construction:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Persistent Approvals Across Sessions</td>
<td>
* Allow users to configure allowlists/denylists in GitLab
* This will likely have two interfaces, one that caters to IDEs and one that caters to GitLab
</td>
<td>
Sub-epic: https://gitlab.com/groups/gitlab-org/-/work_items/21877+
</td>
<td>
:construction:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Autonomous Tool Calling for Session</td>
<td>
* Enable tool calling to always approve for all tool calls.
* This effectively gives users the ability to leave DAP alone while a task is completed
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/issues/587061+
</td>
<td>
:white_circle:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Audit Events on the Backend</td>
<td>
* Currently, only the client is exposing audit telemetry events about tool approvals and what type of approvals.
* The two issues in this section address the audit gap that exists on the backend in both rails and DWS
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/603370+
https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/work_items/2407+
</td>
<td>
:white_circle:
</td>
<td>TBD</td>
</tr>
<tr>
<td>Grouping Tools by Type/Action</td>
<td>
* Tools are currently grouped according to a few main groupings. However these groupings do not follow READ/WRITE/DELETE/etc groupings
* Reconsider how groupings are created to allow for better tool governance
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/598343+
</td>
<td>
:white_circle:
</td>
<td>TBD</td>
</tr>
<tr>
<td>Approve for Session in DAP on Web</td>
<td>
* As DAP on the web continues to increase it's tool usage that exists outside of GitLab's tools, it will become necessary to extend Approve for Session to this surface.
* This will include the need for a UI change/review since the UI used in IDEs is unique to that surface.
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/596879+
</td>
<td>
:white_circle:
</td>
<td>TBD</td>
</tr>
</table>
### :sparkles: Documentation
<table>
<tr>
<th></th>
<th>
**Description**
</th>
<th>
**Sub Epic / Issues**
</th>
<th>
**Status**
</th>
<th>
**Technical Lead**
</th>
</tr>
<tr>
<td>Create Architectural Design Doc for Approve for Session</td>
<td>
* This tracks the architecture and decision making during creating Approve for Session in DAP.
* This document lives as the SSOT for future developers to come back and reference specific decisions.
</td>
<td>
https://gitlab.com/gitlab-com/content-sites/handbook/-/issues/591+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
</td>
</tr>
<tr>
<td>Create Architectural Design Doc for Tool Management/Autonomous Tool Use</td>
<td>
* This serves as a space for discussion between AI focused initiatives and editor extensions to safely and securely autonomy of GitLab Duo Agentic Platform
</td>
<td>
https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/work_items/2167+
</td>
<td>
:white_check_mark:
</td>
<td>
@dbernardi
@jeanvdw
</td>
</tr>
<tr>
<td>Create Architectural Design Doc for Security approach for tool approvals</td>
<td>
* This serves as the reference for developers to understand the security approach taken during development of pattern approvals/autonomous mode in the AI SDK/Raiils
</td>
<td>Need to create</td>
<td>
:white_circle:
</td>
<td>
@dbernardi
</td>
</tr>
</table>
### :sparkles: Cleanup
<table>
<tr>
<th></th>
<th>
**Description**
</th>
<th>
**Sub Epic / Issues**
</th>
<th>
**Status**
</th>
<th>
**Technical Lead**
</th>
</tr>
<tr>
<td>
\[LSP\] Remove MCP specific approval code
</td>
<td>
* To ensure backwards compatibility with new releases, the old MCP approval code still exists.
* If possible, we want to consider removing this
</td>
<td></td>
<td>
:white_circle:
</td>
<td></td>
</tr>
<tr>
<td>Rails: Ensure capability gate catches unknown capabilities</td>
<td>
* Each new capability that is added also adds a gate where if not all three surfaces have the capability the tool is not shown.
* However, if an AI Gateway MR and LSP MR are merged that both pass a new capability before a rails MR merges to control that capability, the feature will display but will break the DAP chat.
* This issue is to explore options for removing this concern when creating new features that are added as capabilities.
</td>
<td>
https://gitlab.com/gitlab-org/gitlab/-/work_items/602098+
</td>
<td>
:white_circle:
</td>
<td>TBD</td>
</tr>
</table>
### :sparkles: Customer Zero - Dogfooding & Feature Feedback
* Feedback issue: @dbernardi to create
## References
* Legal approval issue: [https://gitlab.com/gitlab-com/legal-and-compliance/-/work_items/3164+](https://gitlab.com/gitlab-com/legal-and-compliance/-/work_items/3164)
epic