Skip to content

Commit 343fedc

Browse files
authored
fix(invoke): auto-generate session ID for bearer-token invocations (#953)
Closes #840 When invoking an agent with a bearer token (OAuth/CUSTOM_JWT) and no session ID, `AgentCoreMemoryConfig` raised a Pydantic validation error because `session_id=None` is rejected. Unlike SigV4 callers, bearer-token callers do not get a server-side auto-generated runtime session ID. Two-layer fix: 1. CLI synthesizes a UUID in `invoke` action when `--bearer-token` is set and `--session-id` is missing, using the existing `generateSessionId` helper. Covers both explicit `--bearer-token` and the CUSTOM_JWT auto-fetch path. 2. Strands memory session templates (http, agui, a2a) synthesize a UUID when `session_id` is falsy before constructing AgentCoreMemoryConfig. Protects direct runtime callers (curl, custom apps) who forget the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header. Snapshot tests updated.
1 parent f8dc490 commit 343fedc

5 files changed

Lines changed: 45 additions & 6 deletions

File tree

src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,7 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2
18891889
18901890
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
18911891
"import os
1892+
import uuid
18921893
from typing import Optional
18931894
18941895
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -1897,10 +1898,14 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
18971898
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
18981899
REGION = os.getenv("AWS_REGION")
18991900
1900-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1901+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
19011902
if not MEMORY_ID:
19021903
return None
19031904
1905+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
1906+
# without a runtime session header, so synthesize one when absent.
1907+
session_id = session_id or uuid.uuid4().hex
1908+
19041909
{{#if memoryProviders.[0].strategies.length}}
19051910
retrieval_config = {
19061911
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
@@ -2724,6 +2729,7 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ag
27242729
27252730
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/session.py should match snapshot 1`] = `
27262731
"import os
2732+
import uuid
27272733
from typing import Optional
27282734
27292735
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -2732,10 +2738,14 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
27322738
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
27332739
REGION = os.getenv("AWS_REGION")
27342740
2735-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
2741+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
27362742
if not MEMORY_ID:
27372743
return None
27382744
2745+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
2746+
# without a runtime session header, so synthesize one when absent.
2747+
session_id = session_id or uuid.uuid4().hex
2748+
27392749
{{#if memoryProviders.[0].strategies.length}}
27402750
retrieval_config = {
27412751
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
@@ -4993,6 +5003,7 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ht
49935003
49945004
exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/capabilities/memory/session.py should match snapshot 1`] = `
49955005
"import os
5006+
import uuid
49965007
from typing import Optional
49975008
49985009
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -5001,10 +5012,14 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
50015012
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
50025013
REGION = os.getenv("AWS_REGION")
50035014
5004-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
5015+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
50055016
if not MEMORY_ID:
50065017
return None
50075018
5019+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
5020+
# without a runtime session header, so synthesize one when absent.
5021+
session_id = session_id or uuid.uuid4().hex
5022+
50085023
{{#if memoryProviders.[0].strategies.length}}
50095024
retrieval_config = {
50105025
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}

src/assets/python/a2a/strands/capabilities/memory/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import uuid
23
from typing import Optional
34

45
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -7,10 +8,14 @@
78
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
89
REGION = os.getenv("AWS_REGION")
910

10-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1112
if not MEMORY_ID:
1213
return None
1314

15+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
16+
# without a runtime session header, so synthesize one when absent.
17+
session_id = session_id or uuid.uuid4().hex
18+
1419
{{#if memoryProviders.[0].strategies.length}}
1520
retrieval_config = {
1621
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}

src/assets/python/agui/strands/capabilities/memory/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import uuid
23
from typing import Optional
34

45
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -7,10 +8,14 @@
78
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
89
REGION = os.getenv("AWS_REGION")
910

10-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1112
if not MEMORY_ID:
1213
return None
1314

15+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
16+
# without a runtime session header, so synthesize one when absent.
17+
session_id = session_id or uuid.uuid4().hex
18+
1419
{{#if memoryProviders.[0].strategies.length}}
1520
retrieval_config = {
1621
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}

src/assets/python/http/strands/capabilities/memory/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import uuid
23
from typing import Optional
34

45
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -7,10 +8,14 @@
78
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
89
REGION = os.getenv("AWS_REGION")
910

10-
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11+
def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1112
if not MEMORY_ID:
1213
return None
1314

15+
# AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
16+
# without a runtime session header, so synthesize one when absent.
17+
session_id = session_id or uuid.uuid4().hex
18+
1419
{{#if memoryProviders.[0].strategies.length}}
1520
retrieval_config = {
1621
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}

src/cli/commands/invoke/action.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { InvokeLogger } from '../../logging';
1515
import { formatMcpToolList } from '../../operations/dev/utils';
1616
import { canFetchRuntimeToken, fetchRuntimeToken } from '../../operations/fetch-access';
17+
import { generateSessionId } from '../../operations/session';
1718
import type { InvokeOptions, InvokeResult } from './types';
1819

1920
export interface InvokeContext {
@@ -114,6 +115,14 @@ export async function handleInvoke(context: InvokeContext, options: InvokeOption
114115
}
115116
}
116117

118+
// When invoking with a bearer token (OAuth/CUSTOM_JWT), AgentCore does not
119+
// auto-generate a runtime session ID the way it does for SigV4 callers. Templates
120+
// that wire up AgentCoreMemorySessionManager require a non-null session_id, so
121+
// generate one here if the caller didn't pass --session-id.
122+
if (options.bearerToken && !options.sessionId) {
123+
options = { ...options, sessionId: generateSessionId() };
124+
}
125+
117126
// Exec mode: run shell command in runtime container
118127
if (options.exec) {
119128
const logger = new InvokeLogger({

0 commit comments

Comments
 (0)