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
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ Strategy configuration:
"type": "SEMANTIC",
"name": "custom_semantic",
"description": "Custom semantic memory",
"namespaces": ["/users/facts", "/users/preferences"]
"namespaceTemplates": ["/users/facts", "/users/preferences"]
}
```

Expand Down
18 changes: 10 additions & 8 deletions docs/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,19 @@ Each strategy can have optional configuration:
"type": "SEMANTIC",
"name": "custom_semantic",
"description": "Custom semantic memory",
"namespaces": ["/users/facts", "/users/preferences"]
"namespaceTemplates": ["/users/facts", "/users/preferences"]
}
```

| Field | Required | Description |
| ---------------------- | ------------- | --------------------------------------------------------------------------- |
| `type` | Yes | Strategy type |
| `name` | No | Custom name (defaults to `<memoryName>-<type>`) |
| `description` | No | Strategy description |
| `namespaces` | No | Array of namespace paths for scoping |
| `reflectionNamespaces` | EPISODIC only | Namespaces for cross-episode reflections (must be a prefix of `namespaces`) |
| Field | Required | Description |
| ------------------------------ | ------------- | --------------------------------------------------------------------------------------------- |
| `type` | Yes | Strategy type |
| `name` | No | Custom name (defaults to `<memoryName>-<type>`) |
| `description` | No | Strategy description |
| `namespaceTemplates` | No | Array of namespace templates for scoping |
| `reflectionNamespaceTemplates` | EPISODIC only | Templates for cross-episode reflections (must be a prefix of `namespaceTemplates`) |
| `namespaces` | No | **Deprecated alias for `namespaceTemplates`.** Accepted for backward compatibility. |
| `reflectionNamespaces` | EPISODIC only | **Deprecated alias for `reflectionNamespaceTemplates`.** Accepted for backward compatibility. |

## Event Expiry

Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/fixtures/import/setup_memory_full.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def main():
"semanticMemoryStrategy": {
"name": "bugbash_semantic",
"description": "Semantic strategy for bugbash testing",
"namespaces": ["default"],
"namespaceTemplates": ["default"],
}
},
{
Expand Down Expand Up @@ -68,7 +68,7 @@ def main():
print(f" eventExpiryDuration: 30")
print(f" executionRoleArn: {role_arn}")
print(" strategies:")
print(" - type: SEMANTIC, name: bugbash_semantic, namespaces: [default]")
print(" - type: SEMANTIC, name: bugbash_semantic, namespaceTemplates: [default]")
print(" - type: SUMMARIZATION, name: bugbash_summary")
print(" - type: USER_PREFERENCE, name: bugbash_userpref")
print(" tags: {env: bugbash, team: agentcore-cli}")
Expand Down
10 changes: 5 additions & 5 deletions integ-tests/add-remove-resources.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('integration: add and remove resources', () => {
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
it('adds a memory with EPISODIC strategy and verifies reflectionNamespaceTemplates', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
Expand All @@ -57,19 +57,19 @@ describe('integration: add and remove resources', () => {
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

// Verify EPISODIC in config with reflectionNamespaces
// Verify EPISODIC in config with reflectionNamespaceTemplates
const config = await readProjectConfig(project.projectPath);
const memories = config.memories as {
name: string;
strategies: { type: string; reflectionNamespaces?: string[] }[];
strategies: { type: string; reflectionNamespaceTemplates?: string[] }[];
}[];
const mem = memories.find(m => m.name === episodicMemName);
expect(mem, 'Memory should exist').toBeTruthy();

const episodic = mem!.strategies.find(s => s.type === 'EPISODIC');
expect(episodic, 'EPISODIC strategy should exist').toBeTruthy();
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);
expect(episodic!.reflectionNamespaceTemplates, 'Should have reflectionNamespaceTemplates').toBeDefined();
expect(episodic!.reflectionNamespaceTemplates!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
Expand Down
8 changes: 4 additions & 4 deletions integ-tests/create-memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe.skipIf(!prereqs.npm || !prereqs.git)('integration: create with memory o

// longAndShortTerm should have strategies defined
const memory = memories![0]!;
const strategies = memory.strategies as { type: string; reflectionNamespaces?: string[] }[] | undefined;
const strategies = memory.strategies as { type: string; reflectionNamespaceTemplates?: string[] }[] | undefined;
expect(strategies, 'memory should have strategies').toBeDefined();
expect(strategies!.length).toBe(4);

Expand All @@ -91,10 +91,10 @@ describe.skipIf(!prereqs.npm || !prereqs.git)('integration: create with memory o
expect(types).toContain('SUMMARIZATION');
expect(types).toContain('EPISODIC');

// Verify EPISODIC has reflectionNamespaces
// Verify EPISODIC has reflectionNamespaceTemplates
const episodic = strategies!.find(s => s.type === 'EPISODIC');
expect(episodic, 'EPISODIC strategy should exist').toBeTruthy();
expect(episodic!.reflectionNamespaces, 'EPISODIC should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);
expect(episodic!.reflectionNamespaceTemplates, 'EPISODIC should have reflectionNamespaceTemplates').toBeDefined();
expect(episodic!.reflectionNamespaceTemplates!.length).toBeGreaterThan(0);
});
});
18 changes: 9 additions & 9 deletions integ-tests/tui/add-memory-episodic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
*
* Drives the "Add Memory" wizard through the TUI to verify that when a user
* selects the EPISODIC strategy, it is correctly persisted in agentcore.json
* with both namespaces and reflectionNamespaces.
* with both namespaceTemplates and reflectionNamespaceTemplates.
*
* Exercises:
* - Navigation from HelpScreen -> Add Resource -> Memory
* - Memory name input
* - Expiry selection (default 30 days)
* - Strategy multi-select including EPISODIC
* - Confirm review screen
* - Verification that agentcore.json contains EPISODIC with reflectionNamespaces
* - Verification that agentcore.json contains EPISODIC with reflectionNamespaceTemplates
*/
import { TuiSession, WaitForTimeoutError } from '../../src/tui-harness/index.js';
import { createMinimalProjectDir } from './helpers.js';
Expand Down Expand Up @@ -177,14 +177,14 @@ describe('Add Memory with EPISODIC Strategy', () => {
expect(found).toBe(true);
});

it('Step 9: agentcore.json contains EPISODIC with reflectionNamespaces', async () => {
it('Step 9: agentcore.json contains EPISODIC with reflectionNamespaceTemplates', async () => {
const configPath = join(projectDir.dir, 'agentcore', 'agentcore.json');
const raw = await readFileAsync(configPath, 'utf-8');
const config = JSON.parse(raw);

const memories = config.memories as {
name: string;
strategies: { type: string; namespaces?: string[]; reflectionNamespaces?: string[] }[];
strategies: { type: string; namespaceTemplates?: string[]; reflectionNamespaceTemplates?: string[] }[];
}[];
expect(memories.length).toBeGreaterThan(0);

Expand All @@ -198,12 +198,12 @@ describe('Add Memory with EPISODIC Strategy', () => {
expect(types).toContain('USER_PREFERENCE');
expect(types).toContain('EPISODIC');

// Verify EPISODIC has namespaces AND reflectionNamespaces
// Verify EPISODIC has namespaceTemplates AND reflectionNamespaceTemplates
const episodic = memory!.strategies.find(s => s.type === 'EPISODIC');
expect(episodic, 'EPISODIC strategy should exist').toBeTruthy();
expect(episodic!.namespaces, 'EPISODIC should have namespaces').toBeDefined();
expect(episodic!.namespaces!.length).toBeGreaterThan(0);
expect(episodic!.reflectionNamespaces, 'EPISODIC should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);
expect(episodic!.namespaceTemplates, 'EPISODIC should have namespaceTemplates').toBeDefined();
expect(episodic!.namespaceTemplates!.length).toBeGreaterThan(0);
expect(episodic!.reflectionNamespaceTemplates, 'EPISODIC should have reflectionNamespaceTemplates').toBeDefined();
expect(episodic!.reflectionNamespaceTemplates!.length).toBeGreaterThan(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,7 @@ dependencies = [
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
{{/if}}"a2a-sdk[all] >= 0.2.0, < 1.0.0",
"aws-opentelemetry-distro",
"bedrock-agentcore[a2a] >= 1.0.3",
"bedrock-agentcore[a2a] >= 1.9.1",
"botocore[crt] >= 1.35.0",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
Expand Down Expand Up @@ -2786,7 +2786,7 @@ dependencies = [
{{/if}}"ag-ui-strands >= 0.1.7",
"ag-ui-protocol >= 0.1.10",
"aws-opentelemetry-distro",
"bedrock-agentcore >= 1.0.3",
"bedrock-agentcore >= 1.9.1",
"botocore[crt] >= 1.35.0",
"fastapi >= 0.115.12",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
Expand Down Expand Up @@ -5167,7 +5167,7 @@ requires-python = ">=3.10"
dependencies = [
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
{{/if}}"aws-opentelemetry-distro",
"bedrock-agentcore >= 1.0.3",
"bedrock-agentcore >= 1.9.1",
"botocore[crt] >= 1.35.0",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
{{/if}}"mcp >= 1.19.0",
Expand Down
2 changes: 1 addition & 1 deletion src/assets/python/a2a/strands/base/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies = [
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
{{/if}}"a2a-sdk[all] >= 0.2.0, < 1.0.0",
"aws-opentelemetry-distro",
"bedrock-agentcore[a2a] >= 1.0.3",
"bedrock-agentcore[a2a] >= 1.9.1",
"botocore[crt] >= 1.35.0",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/assets/python/agui/strands/base/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
{{/if}}"ag-ui-strands >= 0.1.7",
"ag-ui-protocol >= 0.1.10",
"aws-opentelemetry-distro",
"bedrock-agentcore >= 1.0.3",
"bedrock-agentcore >= 1.9.1",
"botocore[crt] >= 1.35.0",
"fastapi >= 0.115.12",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/assets/python/http/strands/base/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ requires-python = ">=3.10"
dependencies = [
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
{{/if}}"aws-opentelemetry-distro",
"bedrock-agentcore >= 1.0.3",
"bedrock-agentcore >= 1.9.1",
"botocore[crt] >= 1.35.0",
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
{{/if}}"mcp >= 1.19.0",
Expand Down
14 changes: 9 additions & 5 deletions src/cli/aws/agentcore-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ export interface MemoryDetail {
type: string;
name?: string;
description?: string;
namespaces?: string[];
reflectionNamespaces?: string[];
namespaceTemplates?: string[];
reflectionNamespaceTemplates?: string[];
}[];
tags?: Record<string, string>;
encryptionKeyArn?: string;
Expand Down Expand Up @@ -424,13 +424,17 @@ export async function getMemoryDetail(options: GetMemoryOptions): Promise<Memory
if (!s.type) {
throw new Error(`Memory ${options.memoryId} has a strategy with missing required field: type`);
}
const episodicNamespaces = s.configuration?.reflection?.episodicReflectionConfiguration?.namespaces;
// Prefer the new `namespaceTemplates` field; fall back to the deprecated `namespaces` field.
const namespaceTemplates = s.namespaceTemplates ?? s.namespaces;
const reflectionConfig = s.configuration?.reflection?.episodicReflectionConfiguration;
const reflectionTemplates = reflectionConfig?.namespaceTemplates ?? reflectionConfig?.namespaces;
return {
type: s.type,
name: s.name,
description: s.description,
namespaces: s.namespaces,
...(episodicNamespaces && episodicNamespaces.length > 0 && { reflectionNamespaces: episodicNamespaces }),
...(namespaceTemplates && namespaceTemplates.length > 0 && { namespaceTemplates }),
...(reflectionTemplates &&
reflectionTemplates.length > 0 && { reflectionNamespaceTemplates: reflectionTemplates }),
};
}),
};
Expand Down
16 changes: 8 additions & 8 deletions src/cli/commands/add/__tests__/add-memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,20 @@ describe('add memory command', () => {
const memory = projectSpec.memories.find((m: { name: string }) => m.name === memoryName);

const semantic = memory?.strategies?.find((s: { type: string }) => s.type === 'SEMANTIC');
expect(semantic?.namespaces).toEqual(['/users/{actorId}/facts']);
expect(semantic?.namespaceTemplates).toEqual(['/users/{actorId}/facts']);

const userPref = memory?.strategies?.find((s: { type: string }) => s.type === 'USER_PREFERENCE');
expect(userPref?.namespaces).toEqual(['/users/{actorId}/preferences']);
expect(userPref?.namespaceTemplates).toEqual(['/users/{actorId}/preferences']);

const summarization = memory?.strategies?.find((s: { type: string }) => s.type === 'SUMMARIZATION');
expect(summarization?.namespaces).toEqual(['/summaries/{actorId}/{sessionId}']);
expect(summarization?.namespaceTemplates).toEqual(['/summaries/{actorId}/{sessionId}']);

const episodic = memory?.strategies?.find((s: { type: string }) => s.type === 'EPISODIC');
expect(episodic?.namespaces).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaces).toEqual(['/episodes/{actorId}']);
expect(episodic?.namespaceTemplates).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaceTemplates).toEqual(['/episodes/{actorId}']);
});

it('creates memory with EPISODIC strategy including default namespaces and reflectionNamespaces', async () => {
it('creates memory with EPISODIC strategy including default namespaceTemplates and reflectionNamespaceTemplates', async () => {
const memoryName = `epi${Date.now()}`;
const result = await runCLI(
['add', 'memory', '--name', memoryName, '--strategies', 'EPISODIC', '--json'],
Expand All @@ -162,8 +162,8 @@ describe('add memory command', () => {
const memory = projectSpec.memories.find((m: { name: string }) => m.name === memoryName);
const episodic = memory?.strategies?.find((s: { type: string }) => s.type === 'EPISODIC');
expect(episodic).toBeTruthy();
expect(episodic?.namespaces).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaces).toEqual(['/episodes/{actorId}']);
expect(episodic?.namespaceTemplates).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaceTemplates).toEqual(['/episodes/{actorId}']);
});
});
});
10 changes: 5 additions & 5 deletions src/cli/commands/create/__tests__/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,18 @@ describe('create command', () => {
const memory = projectSpec.memories[0];

const semantic = memory?.strategies?.find((s: { type: string }) => s.type === 'SEMANTIC');
expect(semantic?.namespaces).toEqual(['/users/{actorId}/facts']);
expect(semantic?.namespaceTemplates).toEqual(['/users/{actorId}/facts']);

const userPref = memory?.strategies?.find((s: { type: string }) => s.type === 'USER_PREFERENCE');
expect(userPref?.namespaces).toEqual(['/users/{actorId}/preferences']);
expect(userPref?.namespaceTemplates).toEqual(['/users/{actorId}/preferences']);

const summarization = memory?.strategies?.find((s: { type: string }) => s.type === 'SUMMARIZATION');
expect(summarization?.namespaces).toEqual(['/summaries/{actorId}/{sessionId}']);
expect(summarization?.namespaceTemplates).toEqual(['/summaries/{actorId}/{sessionId}']);

const episodic = memory?.strategies?.find((s: { type: string }) => s.type === 'EPISODIC');
expect(episodic, 'EPISODIC strategy should exist in longAndShortTerm').toBeTruthy();
expect(episodic?.namespaces).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaces).toEqual(['/episodes/{actorId}']);
expect(episodic?.namespaceTemplates).toEqual(['/episodes/{actorId}/{sessionId}']);
expect(episodic?.reflectionNamespaceTemplates).toEqual(['/episodes/{actorId}']);
});

it('uses --project-name for project and --name for agent resource', async () => {
Expand Down
Loading
Loading