diff --git a/src/content/docs/user-guide/concepts/agents/hooks.mdx b/src/content/docs/user-guide/concepts/agents/hooks.mdx
index 064ab1eca..e871e4b84 100644
--- a/src/content/docs/user-guide/concepts/agents/hooks.mdx
+++ b/src/content/docs/user-guide/concepts/agents/hooks.mdx
@@ -876,8 +876,8 @@ class RetryOnServiceUnavailable(HookProvider):
-```ts
-// This feature is not yet available in TypeScript SDK
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:retry_on_service_unavailable_class"
```
@@ -898,8 +898,8 @@ result = agent("What is the capital of France?")
-```ts
-// This feature is not yet available in TypeScript SDK
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:retry_on_service_unavailable_usage"
```
@@ -941,8 +941,12 @@ agent = Agent(
-```ts
-// This feature is not yet available in TypeScript SDK
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:propagate_unexpected_exceptions_class"
+```
+
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:propagate_unexpected_exceptions_usage"
```
@@ -992,8 +996,8 @@ class RetryOnToolError(HookProvider):
-```ts
-// This feature is not yet available in TypeScript SDK
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:retry_on_tool_error_class"
```
@@ -1026,8 +1030,8 @@ result = agent("Look up the weather")
-```ts
-// This feature is not yet available in TypeScript SDK
+```typescript
+--8<-- "user-guide/concepts/agents/hooks.ts:retry_on_tool_error_usage"
```
diff --git a/src/content/docs/user-guide/concepts/agents/hooks.ts b/src/content/docs/user-guide/concepts/agents/hooks.ts
index 9bb28dba3..e5f04468b 100644
--- a/src/content/docs/user-guide/concepts/agents/hooks.ts
+++ b/src/content/docs/user-guide/concepts/agents/hooks.ts
@@ -425,3 +425,135 @@ void orchestratorCallbackExample
void conditionalNodeExecutionExample
void orchestratorAgnosticDesignExample
void layeredHooksExample
+
+// =====================
+// Cookbook: Retry Examples
+// =====================
+
+async function retryOnServiceUnavailableExample() {
+ // --8<-- [start:retry_on_service_unavailable_class]
+ class RetryOnServiceUnavailable implements Plugin {
+ readonly name = 'retry-on-service-unavailable'
+ private readonly maxRetries: number
+ private retryCount = 0
+
+ constructor(maxRetries = 3) {
+ this.maxRetries = maxRetries
+ }
+
+ initAgent(agent: LocalAgent): void {
+ agent.addHook(BeforeInvocationEvent, () => {
+ this.retryCount = 0
+ })
+ agent.addHook(AfterModelCallEvent, (event) => this.handleRetry(event))
+ }
+
+ private async handleRetry(event: AfterModelCallEvent): Promise {
+ if (event.error) {
+ if (
+ String(event.error).includes('ServiceUnavailable') &&
+ this.retryCount < this.maxRetries
+ ) {
+ this.retryCount++
+ await new Promise((resolve) =>
+ setTimeout(resolve, 2 ** this.retryCount * 1000),
+ )
+ event.retry = true
+ }
+ } else {
+ this.retryCount = 0
+ }
+ }
+ }
+ // --8<-- [end:retry_on_service_unavailable_class]
+
+ // --8<-- [start:retry_on_service_unavailable_usage]
+ const retryPlugin = new RetryOnServiceUnavailable(3)
+ const agent = new Agent({ plugins: [retryPlugin] })
+
+ const result = await agent.invoke('What is the capital of France?')
+ // --8<-- [end:retry_on_service_unavailable_usage]
+ void result
+}
+
+async function propagateUnexpectedExceptionsExample() {
+ // --8<-- [start:propagate_unexpected_exceptions_class]
+ class PropagateUnexpectedExceptions implements Plugin {
+ readonly name = 'propagate-unexpected-exceptions'
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ private readonly allowedExceptions: Array Error>
+
+ constructor(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ allowedExceptions: Array Error> = [],
+ ) {
+ this.allowedExceptions = allowedExceptions
+ }
+
+ initAgent(agent: LocalAgent): void {
+ agent.addHook(AfterToolCallEvent, (event) => this.checkException(event))
+ }
+
+ private checkException(event: AfterToolCallEvent): void {
+ if (!event.error) return // Tool succeeded
+ // Let model retry allowed exception types
+ if (this.allowedExceptions.some((Exc) => event.error instanceof Exc)) return
+ throw event.error // Propagate unexpected errors immediately
+ }
+ }
+ // --8<-- [end:propagate_unexpected_exceptions_class]
+
+ // --8<-- [start:propagate_unexpected_exceptions_usage]
+ const agent = new Agent({
+ tools: [myTool],
+ plugins: [new PropagateUnexpectedExceptions([TypeError])],
+ })
+ // --8<-- [end:propagate_unexpected_exceptions_usage]
+ void agent
+}
+
+async function retryOnToolErrorExample() {
+ // --8<-- [start:retry_on_tool_error_class]
+ class RetryOnToolError implements Plugin {
+ readonly name = 'retry-on-tool-error'
+ private readonly maxRetries: number
+ private readonly attemptCounts = new Map()
+
+ constructor(maxRetries = 1) {
+ this.maxRetries = maxRetries
+ }
+
+ initAgent(agent: LocalAgent): void {
+ agent.addHook(AfterToolCallEvent, (event) => this.handleRetry(event))
+ }
+
+ private handleRetry(event: AfterToolCallEvent): void {
+ const toolUseId = event.toolUse.toolUseId
+ const toolName = event.toolUse.name
+ const attempt = (this.attemptCounts.get(toolUseId) ?? 0) + 1
+ this.attemptCounts.set(toolUseId, attempt)
+
+ if (event.result.status === 'error' && attempt <= this.maxRetries) {
+ console.log(
+ `Retrying tool '${toolName}' (attempt ${attempt}/${this.maxRetries})`,
+ )
+ event.retry = true
+ } else if (event.result.status !== 'error') {
+ this.attemptCounts.delete(toolUseId)
+ }
+ }
+ }
+ // --8<-- [end:retry_on_tool_error_class]
+
+ // --8<-- [start:retry_on_tool_error_usage]
+ const retryPlugin = new RetryOnToolError(1)
+ const agent = new Agent({ tools: [calculator], plugins: [retryPlugin] })
+
+ const result = await agent.invoke('Look up the weather')
+ // --8<-- [end:retry_on_tool_error_usage]
+ void result
+}
+
+void retryOnServiceUnavailableExample
+void propagateUnexpectedExceptionsExample
+void retryOnToolErrorExample
diff --git a/src/content/docs/user-guide/concepts/agents/retry-strategies.mdx b/src/content/docs/user-guide/concepts/agents/retry-strategies.mdx
index f585a8c8d..0694b64ef 100644
--- a/src/content/docs/user-guide/concepts/agents/retry-strategies.mdx
+++ b/src/content/docs/user-guide/concepts/agents/retry-strategies.mdx
@@ -38,8 +38,8 @@ agent = Agent(
-```ts
-// Not supported in TypeScript
+```typescript
+--8<-- "user-guide/concepts/agents/retry-strategies.ts:customizing_retry"
```
@@ -69,8 +69,8 @@ agent = Agent(
-```ts
-// Not supported in TypeScript
+```typescript
+--8<-- "user-guide/concepts/agents/retry-strategies.ts:disabling_retry"
```
@@ -111,8 +111,8 @@ agent = Agent(hooks=[CustomRetry()])
-```ts
-// Not supported in TypeScript
+```typescript
+--8<-- "user-guide/concepts/agents/retry-strategies.ts:custom_retry_logic"
```
diff --git a/src/content/docs/user-guide/concepts/agents/retry-strategies.ts b/src/content/docs/user-guide/concepts/agents/retry-strategies.ts
new file mode 100644
index 000000000..77b6d9e5e
--- /dev/null
+++ b/src/content/docs/user-guide/concepts/agents/retry-strategies.ts
@@ -0,0 +1,64 @@
+import { Agent, AfterModelCallEvent, ModelThrottledError } from '@strands-agents/sdk'
+
+// ===========================
+// Customizing Retry Behavior
+// ===========================
+
+async function customizingRetryExample() {
+ // --8<-- [start:customizing_retry]
+ const agent = new Agent()
+
+ let attempts = 0
+ const maxAttempts = 3 // Total attempts (including first try)
+ const initialDelay = 2 // Seconds before first retry
+ const maxDelay = 60 // Cap on backoff delay
+
+ agent.addHook(AfterModelCallEvent, async (event) => {
+ if (event.error instanceof ModelThrottledError && attempts < maxAttempts - 1) {
+ const delay = Math.min(initialDelay * 2 ** attempts, maxDelay)
+ attempts++
+ await new Promise((resolve) => setTimeout(resolve, delay * 1000))
+ event.retry = true
+ } else {
+ attempts = 0
+ }
+ })
+ // --8<-- [end:customizing_retry]
+}
+
+// =====================
+// Disabling Retries
+// =====================
+
+async function disablingRetryExample() {
+ // --8<-- [start:disabling_retry]
+ // The TypeScript SDK does not perform built-in automatic retries for
+ // throttle errors. Retry only occurs when a hook explicitly sets
+ // event.retry = true. No additional configuration is needed to disable it.
+ const agent = new Agent()
+ // --8<-- [end:disabling_retry]
+}
+
+// =====================
+// Custom Retry Logic
+// =====================
+
+async function customRetryLogicExample() {
+ // --8<-- [start:custom_retry_logic]
+ const agent = new Agent()
+
+ let attempts = 0
+ const maxRetries = 3
+ const delay = 2.0 // seconds
+
+ agent.addHook(AfterModelCallEvent, async (event) => {
+ if (event.error && attempts < maxRetries) {
+ attempts++
+ await new Promise((resolve) => setTimeout(resolve, delay * 1000))
+ event.retry = true
+ } else {
+ attempts = 0
+ }
+ })
+ // --8<-- [end:custom_retry_logic]
+}