Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions docs/tools-custom/confirmation.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Get action confirmation for ADK Tools

<div class="language-support-tag">
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v1.14.0</span><span class="lst-typescript">TypeScript v0.2.0</span><span class="lst-go">Go v0.3.0</span><span class="lst-preview">Experimental</span>
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v1.14.0</span><span class="lst-typescript">TypeScript v0.2.0</span><span class="lst-go">Go v0.3.0</span><span class="lst-kotlin">Kotlin v0.1.0</span><span class="lst-preview">Experimental</span>
</div>

Some agent workflows require confirmation for decision making, verification,
Expand Down Expand Up @@ -49,9 +49,10 @@ agent pattern.
When your tool only requires a simple `yes` or `no` from the user, you can
append a confirmation step. In Python, Go, and Java, you can enable this by
wrapping the tool with the `FunctionTool` class and setting the
`require_confirmation` parameter (or equivalent) to `True`. In TypeScript, you
implement this logic manually within the `execute` function using the
`ToolContext`.
`require_confirmation` parameter (or equivalent) to `True`. In Kotlin, you set
`requireConfirmation = true` on the tool function's `@Tool` annotation. In
TypeScript, you implement this logic manually within the `execute` function
using the `ToolContext`.

The following examples show how to enable boolean confirmation:

Expand Down Expand Up @@ -118,9 +119,15 @@ The following examples show how to enable boolean confirmation:
.build();
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt:boolean_confirmation"
```

### Require confirmation function

You can modify the behavior of the confirmation requirement by using a function that returns a boolean response based on the tool's input. In TypeScript, this is handled by adding conditional logic to your `execute` function.
You can modify the behavior of the confirmation requirement by using a function that returns a boolean response based on the tool's input. In TypeScript, this is handled by adding conditional logic to your `execute` function. In Kotlin, the `@Tool` annotation's flag is a compile-time constant, so the conditional logic goes inside the tool function.

=== "Python"

Expand Down Expand Up @@ -198,6 +205,17 @@ You can modify the behavior of the confirmation requirement by using a function
.build();
```

=== "Kotlin"

!!! note
The `@Tool` annotation's `requireConfirmation` flag is a compile-time
constant, so a threshold is evaluated inside the tool using the
`ToolContext`, as in ADK Java.

```kotlin
--8<-- "examples/kotlin/snippets/tools/confirmation/dynamic/ReimbursementTools.kt:dynamic_confirmation"
```

## Advanced confirmation {#advanced-confirmation}

When a tool confirmation requires more details for the user or a more complex
Expand Down Expand Up @@ -333,6 +351,12 @@ time off requests for an employee:
}
```

=== "Kotlin"

```kotlin
--8<-- "examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt:advanced_confirmation"
```

## Remote confirmation with REST API {#remote-response}

If there is no active user interface for a human confirmation of an agent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.adk.kt.examples.tools.confirmation

import com.google.adk.kt.agents.LlmAgent
import com.google.adk.kt.annotations.Param
import com.google.adk.kt.annotations.Tool
import com.google.adk.kt.models.Gemini
import com.google.adk.kt.tools.ToolContext

// --8<-- [start:boolean_confirmation]
class ReimbursementTools {
/** Reimburse an amount. */
@Tool(requireConfirmation = true) // Pause for user confirmation before every call.
fun reimburse(
@Param("The amount to reimburse.") amount: Int,
): Map<String, Any?> = mapOf("status" to "ok", "reimbursedAmount" to amount)
}

val reimbursementAgent =
LlmAgent(
name = "reimbursement_agent",
model = Gemini(name = "gemini-flash-latest"),
tools = ReimbursementTools().generatedTools(),
)
// --8<-- [end:boolean_confirmation]

// --8<-- [start:advanced_confirmation]
class TimeOffTools {
/** Request day off for the employee. */
@Tool
fun requestTimeOff(
context: ToolContext,
@Param("The number of days requested.") days: Int,
): Map<String, Any?> {
val confirmation = context.toolConfirmation
if (confirmation == null) {
context.requestConfirmation(
hint =
"Please approve or reject the tool call requestTimeOff() by responding " +
"with a FunctionResponse with an expected ToolConfirmation payload.",
payload = mapOf("approved_days" to 0),
)
// Return an intermediate status indicating that the tool is waiting for
// a confirmation response:
return mapOf("status" to "Manager approval is required.")
}

// The payload comes back decoded from JSON, so the number may arrive as any
// Number subtype. Read it through Number rather than casting straight to Int.
val payload = confirmation.payload as? Map<*, *>
val approvedDays =
minOf((payload?.get("approved_days") as? Number)?.toInt() ?: 0, days)
if (approvedDays == 0) {
return mapOf("status" to "The time off request is rejected.", "approved_days" to 0)
}
return mapOf("status" to "ok", "approved_days" to approvedDays)
}
}
// --8<-- [end:advanced_confirmation]
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.adk.kt.examples.tools.confirmation.dynamic

import com.google.adk.kt.annotations.Param
import com.google.adk.kt.annotations.Tool
import com.google.adk.kt.tools.ToolContext

// --8<-- [start:dynamic_confirmation]
class ReimbursementTools {
/** Reimburse an amount, requiring manager approval above a threshold. */
@Tool
fun reimburse(
context: ToolContext,
@Param("The amount to reimburse.") amount: Int,
): Map<String, Any?> {
// The @Tool annotation's requireConfirmation flag is a compile-time constant,
// so the threshold is evaluated here using the ToolContext instead.
if (amount > 1000) {
val confirmation = context.toolConfirmation
if (confirmation == null) {
context.requestConfirmation(hint = "Amount > 1000 requires approval.")
// Return an intermediate status while the confirmation is pending.
return mapOf("status" to "Pending manager approval.")
}
if (!confirmation.confirmed) {
return mapOf("status" to "Reimbursement rejected.")
}
}
return mapOf("status" to "ok", "reimbursedAmount" to amount)
}
}
// --8<-- [end:dynamic_confirmation]
2 changes: 2 additions & 0 deletions tools/kotlin-snippets/files_to_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ snippets/tools/overview/UserPreferenceTools.kt
snippets/tools/overview/CustomerSupport.kt
snippets/tools/overview/DocAnalysisTools.kt
snippets/tools/overview/OrderTools.kt
snippets/tools/confirmation/ToolConfirmationExample.kt
snippets/tools/confirmation/dynamic/ReimbursementTools.kt
Loading