Skip to content
Merged
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
12 changes: 6 additions & 6 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,12 @@ agentcore add memory \
--expiry 30
```

| Flag | Description |
| ---------------------- | --------------------------------------------------------------------------- |
| `--name <name>` | Memory name |
| `--strategies <types>` | Comma-separated: `SEMANTIC`, `SUMMARIZATION`, `USER_PREFERENCE`, `EPISODIC` |
| `--expiry <days>` | Event expiry duration in days (default: 30, min: 7, max: 365) |
| `--json` | JSON output |
| Flag | Description |
| ---------------------- | ------------------------------------------------------------------------------------- |
| `--name <name>` | Memory name |
| `--strategies <types>` | Comma-separated: `SEMANTIC`, `SUMMARIZATION`, `USER_PREFERENCE`, `EPISODIC`, `CUSTOM` |
| `--expiry <days>` | Event expiry duration in days (default: 30, min: 7, max: 365) |
| `--json` | JSON output |

### add gateway

Expand Down
12 changes: 7 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,13 @@ on the next deployment.

### Memory Strategies

| Strategy | Description |
| ----------------- | --------------------------------------------------- |
| `SEMANTIC` | Vector-based similarity search for relevant context |
| `SUMMARIZATION` | Compressed conversation history |
| `USER_PREFERENCE` | Store user-specific preferences and settings |
| Strategy | Description |
| ----------------- | ----------------------------------------------------------- |
| `SEMANTIC` | Vector-based similarity search for relevant context |
| `SUMMARIZATION` | Compressed conversation history |
| `USER_PREFERENCE` | Store user-specific preferences and settings |
| `EPISODIC` | Capture and reflect on meaningful interaction episodes |
| `CUSTOM` | Self-managed strategy with user-controlled extraction logic |

Strategy configuration:

Expand Down
43 changes: 37 additions & 6 deletions docs/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,13 @@ async def invoke(payload, context):

## Memory Strategies

| Strategy | Description |
| ----------------- | ------------------------------------------------------ |
| `SEMANTIC` | Vector-based similarity search for relevant context |
| `SUMMARIZATION` | Compressed conversation history |
| `USER_PREFERENCE` | Store user-specific preferences and settings |
| `EPISODIC` | Capture and reflect on meaningful interaction episodes |
| Strategy | Description |
| ----------------- | ----------------------------------------------------------- |
| `SEMANTIC` | Vector-based similarity search for relevant context |
| `SUMMARIZATION` | Compressed conversation history |
| `USER_PREFERENCE` | Store user-specific preferences and settings |
| `EPISODIC` | Capture and reflect on meaningful interaction episodes |
| `CUSTOM` | Self-managed strategy with user-controlled extraction logic |

You can combine multiple strategies:

Expand All @@ -171,6 +172,36 @@ You can combine multiple strategies:
}
```

### Self-Managed (Custom) Strategy

The `CUSTOM` strategy lets you control memory extraction logic externally rather than relying on built-in
implementations. This is useful when you need specialized extraction pipelines or want to integrate with your own
processing infrastructure.

**Prerequisites:** CUSTOM strategies require user-managed extraction logic and are not functional without it. You must
implement your own extraction mechanism (e.g., via AWS Lambda).

**Key characteristics:**

- No default namespaces are assigned — you provide your own or omit them
- Each memory supports at most one CUSTOM strategy
- You are responsible for implementing the extraction logic that processes memory events

```json
{
"type": "AgentCoreMemory",
"name": "MyMemory",
"eventExpiryDuration": 30,
"strategies": [
{
"type": "CUSTOM",
"name": "my_custom_strategy",
"description": "Custom extraction logic"
}
]
}
```

### Strategy Options

Each strategy can have optional configuration:
Expand Down
12 changes: 11 additions & 1 deletion src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,11 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
{{/if}}
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
{{#if (includes memoryProviders.[0].strategies "CUSTOM")}}
# TODO: Add your custom namespace and retrieval config.
# Custom strategies use user-controlled extraction logic.
# Example: f"/custom/{actor_id}/data": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
}
{{/if}}
Expand Down Expand Up @@ -3986,6 +3991,11 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
{{/if}}
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
{{#if (includes memoryProviders.[0].strategies "CUSTOM")}}
# TODO: Add your custom namespace and retrieval config.
# Custom strategies use user-controlled extraction logic.
# Example: f"/custom/{actor_id}/data": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
}
{{/if}}
Expand Down Expand Up @@ -4330,7 +4340,7 @@ file maps to a JSON config file and includes validation constraints as comments.
- **BuildType**: \`'CodeZip'\` | \`'Container'\`
- **NetworkMode**: \`'PUBLIC'\`
- **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\`
- **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\`
- **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\` | \`'CUSTOM'\`

### Build Types

Expand Down
2 changes: 1 addition & 1 deletion src/assets/agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ file maps to a JSON config file and includes validation constraints as comments.
- **BuildType**: `'CodeZip'` | `'Container'`
- **NetworkMode**: `'PUBLIC'`
- **RuntimeVersion**: `'PYTHON_3_10'` | `'PYTHON_3_11'` | `'PYTHON_3_12'` | `'PYTHON_3_13'`
- **MemoryStrategyType**: `'SEMANTIC'` | `'SUMMARIZATION'` | `'USER_PREFERENCE'` | `'EPISODIC'`
- **MemoryStrategyType**: `'SEMANTIC'` | `'SUMMARIZATION'` | `'USER_PREFERENCE'` | `'EPISODIC'` | `'CUSTOM'`

### Build Types

Expand Down
5 changes: 5 additions & 0 deletions src/assets/python/a2a/strands/capabilities/memory/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
{{/if}}
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
{{#if (includes memoryProviders.[0].strategies "CUSTOM")}}
# TODO: Add your custom namespace and retrieval config.
# Custom strategies use user-controlled extraction logic.
# Example: f"/custom/{actor_id}/data": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
}
{{/if}}
Expand Down
5 changes: 5 additions & 0 deletions src/assets/python/http/strands/capabilities/memory/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
{{/if}}
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
{{#if (includes memoryProviders.[0].strategies "CUSTOM")}}
# TODO: Add your custom namespace and retrieval config.
# Custom strategies use user-controlled extraction logic.
# Example: f"/custom/{actor_id}/data": RetrievalConfig(top_k=3, relevance_score=0.5),
{{/if}}
}
{{/if}}
Expand Down
13 changes: 7 additions & 6 deletions src/cli/commands/add/__tests__/add-memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,17 @@ describe('add memory command', () => {
expect(json.error.includes('INVALID'), `Error: ${json.error}`).toBeTruthy();
});

// Issue #235: CUSTOM strategy has been removed
it('rejects CUSTOM strategy', async () => {
// Issue #677: CUSTOM strategy is now supported
it('creates memory with CUSTOM strategy', async () => {
const memoryName = `memCustom${Date.now()}`;
const result = await runCLI(
['add', 'memory', '--name', 'testCustom', '--strategies', 'CUSTOM', '--json'],
['add', 'memory', '--name', memoryName, '--strategies', 'CUSTOM', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('CUSTOM'), `Error: ${json.error}`).toBeTruthy();
expect(json.success).toBe(true);
expect(json.memoryName).toBe(memoryName);
});
});

Expand Down
12 changes: 5 additions & 7 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -953,17 +953,15 @@ describe('validate', () => {
).toEqual({ valid: true });
});

// AC23: CUSTOM strategy is not supported (Issue #235)
it('rejects CUSTOM strategy', () => {
// Issue #677: CUSTOM strategy is now supported
it('accepts CUSTOM strategy', () => {
const result = validateAddMemoryOptions({ ...validMemoryOptions, strategies: 'CUSTOM' });
expect(result.valid).toBe(false);
expect(result.error).toContain('Invalid strategy: CUSTOM');
expect(result.valid).toBe(true);
});

it('rejects CUSTOM even when mixed with valid strategies', () => {
it('accepts CUSTOM mixed with other strategies', () => {
const result = validateAddMemoryOptions({ ...validMemoryOptions, strategies: 'SEMANTIC,CUSTOM' });
expect(result.valid).toBe(false);
expect(result.error).toContain('Invalid strategy: CUSTOM');
expect(result.valid).toBe(true);
});

// AC24: Each individual valid strategy should pass
Expand Down
9 changes: 5 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
BuildTypeSchema,
GatewayExceptionLevelSchema,
GatewayNameSchema,
MemoryStrategyTypeSchema,
ModelProviderSchema,
ProtocolModeSchema,
RuntimeAuthorizerTypeSchema,
Expand Down Expand Up @@ -33,8 +34,8 @@ export interface ValidationResult {
}

// Constants
const MEMORY_OPTIONS = ['none', 'shortTerm', 'longAndShortTerm'] as const;
const VALID_STRATEGIES = ['SEMANTIC', 'SUMMARIZATION', 'USER_PREFERENCE', 'EPISODIC'];
const MEMORY_OPTIONS = ['none', 'shortTerm', 'longAndShortTerm', 'custom'] as const;
const VALID_STRATEGIES: readonly string[] = MemoryStrategyTypeSchema.options;

/**
* Validate that a credential name exists in the project spec.
Expand Down Expand Up @@ -134,7 +135,7 @@ export function validateAddAgentOptions(options: AddAgentOptions): ValidationRes
if (!MEMORY_OPTIONS.includes(options.memory as (typeof MEMORY_OPTIONS)[number])) {
return {
valid: false,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, or longAndShortTerm`,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, longAndShortTerm, or custom`,
};
}
// Parse and validate lifecycle configuration for import path
Expand Down Expand Up @@ -242,7 +243,7 @@ export function validateAddAgentOptions(options: AddAgentOptions): ValidationRes
if (!MEMORY_OPTIONS.includes(options.memory as (typeof MEMORY_OPTIONS)[number])) {
return {
valid: false,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, or longAndShortTerm`,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, longAndShortTerm, or custom`,
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/create/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export async function createProject(options: CreateProjectOptions): Promise<Crea
}
}

type MemoryOption = 'none' | 'shortTerm' | 'longAndShortTerm';
type MemoryOption = 'none' | 'shortTerm' | 'longAndShortTerm' | 'custom';

export interface CreateWithAgentOptions {
name: string;
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/create/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async function handleCreateCLI(options: CreateOptions): Promise<void> {
framework: options.framework as SDKFramework | undefined,
modelProvider: options.modelProvider as ModelProvider | undefined,
apiKey: options.apiKey,
memory: (options.memory as 'none' | 'shortTerm' | 'longAndShortTerm') ?? 'none',
memory: (options.memory as 'none' | 'shortTerm' | 'longAndShortTerm' | 'custom') ?? 'none',
protocol: options.protocol as ProtocolMode | undefined,
agentId: options.agentId,
agentAliasId: options.agentAliasId,
Expand Down Expand Up @@ -170,7 +170,7 @@ export const registerCreate = (program: Command) => {
)
.option('--model-provider <provider>', 'Model provider (Bedrock, Anthropic, OpenAI, Gemini) [non-interactive]')
.option('--api-key <key>', 'API key for non-Bedrock providers [non-interactive]')
.option('--memory <option>', 'Memory option (none, shortTerm, longAndShortTerm) [non-interactive]')
.option('--memory <option>', 'Memory option (none, shortTerm, longAndShortTerm, custom) [non-interactive]')
.option('--protocol <protocol>', 'Protocol: HTTP, MCP, A2A (default: HTTP) [non-interactive]')
.option('--type <type>', 'Agent type: create or import (default: create) [non-interactive]')
.option('--agent-id <id>', 'Bedrock Agent ID (required for --type import) [non-interactive]')
Expand Down
6 changes: 3 additions & 3 deletions src/cli/commands/create/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface ValidationResult {
error?: string;
}

const MEMORY_OPTIONS = ['none', 'shortTerm', 'longAndShortTerm'] as const;
const MEMORY_OPTIONS = ['none', 'shortTerm', 'longAndShortTerm', 'custom'] as const;

/** Check if a folder with the given name already exists in the directory */
export function validateFolderNotExists(name: string, cwd: string): true | string {
Expand Down Expand Up @@ -72,7 +72,7 @@ export function validateCreateOptions(options: CreateOptions, cwd?: string): Val
if (!MEMORY_OPTIONS.includes(options.memory as (typeof MEMORY_OPTIONS)[number])) {
return {
valid: false,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, or longAndShortTerm`,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, longAndShortTerm, or custom`,
};
}
return { valid: true };
Expand Down Expand Up @@ -190,7 +190,7 @@ export function validateCreateOptions(options: CreateOptions, cwd?: string): Val
if (!MEMORY_OPTIONS.includes(options.memory as (typeof MEMORY_OPTIONS)[number])) {
return {
valid: false,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, or longAndShortTerm`,
error: `Invalid memory option: ${options.memory}. Use none, shortTerm, longAndShortTerm, or custom`,
};
}
}
Expand Down
Loading
Loading