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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ agentcore # interactive TUI
│ └── endpoint
│ ├── get # get a Runtime endpoint by qualifier
│ └── list # list a Runtime's endpoints
├── memory # inspect AgentCore Memories
│ ├── get # fetch a Memory by id
│ └── list # list Memories (server-side paginated)
└── config # read/write global config values
```

Expand Down Expand Up @@ -108,6 +111,11 @@ agentcore runtime version list --id <runtimeId> --max-results 20
agentcore runtime endpoint get --id <runtimeId> --qualifier DEFAULT
agentcore runtime endpoint list --id <runtimeId> --max-results 20

# Inspect AgentCore Memories without project configuration or deployment
agentcore memory get --id <memoryId>
agentcore memory get --id <memoryId> --view without_decryption
agentcore memory list --max-results 20

# Manage API key credential providers
agentcore identity api-key-credential-provider create --name my-provider --api-key <key>
agentcore identity api-key-credential-provider get --name my-provider
Expand Down Expand Up @@ -537,8 +545,8 @@ A Husky pre-commit hook runs Prettier (via lint-staged) on staged files automati

- **Cover more AgentCore resources.** The harness surface (CRUD, versions,
endpoints, invoke, exec) is fully implemented in both the CLI and the TUI;
the same patterns extend naturally to gateways, memory, browser profiles,
and the other control-plane resources.
the same patterns extend naturally to gateways, Memory mutations and
data-plane operations, browser profiles, and the other AgentCore resources.
- **Implement `config`.** The `config` command is currently a stub — it should
read/write real global settings (telemetry, log level, ...) through an
injected config accessor.
2 changes: 2 additions & 0 deletions src/components/RouterScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ describe("menu rendering", () => {
expect(frame).toContain("manage agentcore harnesses");
expect(frame).toContain("runtime");
expect(frame).toContain("inspect AgentCore Runtimes");
expect(frame).toContain("memory");
expect(frame).toContain("manage AgentCore Memories");
expect(frame).toContain("config");
expect(frame).toContain("read/write global config values");
r.unmount();
Expand Down
3 changes: 2 additions & 1 deletion src/core/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,15 @@ test("data() caches independently of control()", () => {
expect(dataBuilt).toBe(1);
});

test("exposes a harness sub-client", () => {
test("exposes feature sub-clients", () => {
const core = new CoreClient({
createControlClient: fakeControl,
createDataClient: fakeData,
createIamClient: fakeIam,
logger: createSilentLogger(),
});
expect(core.harness).toBeDefined();
expect(core.memory).toBeDefined();
});

test("invokeHarness sends an InvokeHarnessCommand on the data client with the abort signal", async () => {
Expand Down
2 changes: 2 additions & 0 deletions src/core/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BedrockAgentCoreClient } from "@aws-sdk/client-bedrock-agentcore";
import { IAMClient } from "@aws-sdk/client-iam";
import { HarnessClient } from "./harness";
import { IdentityClient } from "./identity";
import { MemoryClient } from "./memory";
import { RuntimeClient } from "./runtime";
import type {
AwsClients,
Expand Down Expand Up @@ -47,6 +48,7 @@ export class CoreClient implements AwsClients {
// Feature-scoped sub-clients. Access as e.g. `coreClient.harness.getHarness(...)`.
readonly harness: HarnessClient = new HarnessClient(this);
readonly identity: IdentityClient = new IdentityClient(this);
readonly memory: MemoryClient = new MemoryClient(this);
readonly runtime: RuntimeClient = new RuntimeClient(this);

readonly projectManager: ProjectManager;
Expand Down
30 changes: 30 additions & 0 deletions src/core/memory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
GetMemoryCommand,
ListMemoriesCommand,
type GetMemoryOutput,
type ListMemoriesOutput,
type MemoryView,
} from "@aws-sdk/client-bedrock-agentcore-control";
import type { CoreMemoryClient } from "../handlers/memory/types";
import type { AwsClients, CoreOptions } from "./types";
import { toClientConfig } from "./utils";

export class MemoryClient implements CoreMemoryClient {
constructor(private readonly clients: AwsClients) {}

async getMemory(id: string, view: MemoryView, options: CoreOptions): Promise<GetMemoryOutput> {
return this.clients
.control(toClientConfig(options))
.send(new GetMemoryCommand({ memoryId: id, view }));
}

async listMemories(
nextToken: string | undefined,
maxResults: number | undefined,
options: CoreOptions,
): Promise<ListMemoriesOutput> {
return this.clients
.control(toClientConfig(options))
.send(new ListMemoriesCommand({ nextToken, maxResults }));
}
}
2 changes: 2 additions & 0 deletions src/handlers/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Router } from "../router";
import { createHarnessHandler } from "./harness/index.tsx";
import { createIdentityHandler } from "./identity/index.tsx";
import { createMemoryHandler } from "./memory/index.tsx";
import { createRuntimeHandler } from "./runtime/index.tsx";
import { DebugKey, EndpointKey, JsonKey, RegionKey } from "./keys.tsx";
import { createConfigHandler } from "./config/";
Expand Down Expand Up @@ -43,6 +44,7 @@ export function createRootHandler(core: Core, config: RootHandlerConfig): Router
root.handler(createHarnessHandler(core, io));
root.handler(createIdentityHandler(core, io));
root.handler(createRuntimeHandler(core, io));
root.handler(createMemoryHandler(core, io));
root.handler(createConfigHandler());
root.handler(createProjectHandler({ projectManager: core.projectManager }));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"memory": {
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_read_only_fixture-AbCd123456",
"id": "agentcore_cli_memory_read_only_fixture-AbCd123456",
"name": "agentcore_cli_memory_read_only_fixture",
"description": "Stable synthetic fixture for AgentCore CLI Memory read-only tests",
"eventExpiryDuration": 30,
"status": "ACTIVE",
"createdAt": {
"$date": "2026-07-20T12:00:00.000Z"
},
"updatedAt": {
"$date": "2026-07-21T12:00:00.000Z"
},
"strategies": [],
"indexedKeys": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"memory": {
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_read_only_fixture-AbCd123456",
"id": "agentcore_cli_memory_read_only_fixture-AbCd123456",
"name": "agentcore_cli_memory_read_only_fixture",
"description": "Stable synthetic fixture for AgentCore CLI Memory read-only tests",
"eventExpiryDuration": 30,
"status": "ACTIVE",
"createdAt": {
"$date": "2026-07-20T12:00:00.000Z"
},
"updatedAt": {
"$date": "2026-07-21T12:00:00.000Z"
},
"strategies": [],
"indexedKeys": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$error": {
"name": "ResourceNotFoundException",
"message": "Memory missing_memory-0000000000 was not found"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"memories": [
{
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_second-EfGh789012",
"id": "agentcore_cli_memory_second-EfGh789012",
"status": "ACTIVE",
"createdAt": {
"$date": "2026-07-22T12:00:00.000Z"
},
"updatedAt": {
"$date": "2026-07-22T12:30:00.000Z"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"memories": [
{
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_read_only_fixture-AbCd123456",
"id": "agentcore_cli_memory_read_only_fixture-AbCd123456",
"status": "ACTIVE",
"createdAt": {
"$date": "2026-07-20T12:00:00.000Z"
},
"updatedAt": {
"$date": "2026-07-21T12:00:00.000Z"
}
}
],
"nextToken": "memory-page-2"
}
14 changes: 14 additions & 0 deletions src/handlers/memory/__fixtures__/get.golden.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"memory": {
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_read_only_fixture-AbCd123456",
"id": "agentcore_cli_memory_read_only_fixture-AbCd123456",
"name": "agentcore_cli_memory_read_only_fixture",
"description": "Stable synthetic fixture for AgentCore CLI Memory read-only tests",
"eventExpiryDuration": 30,
"status": "ACTIVE",
"createdAt": "2026-07-20T12:00:00.000Z",
"updatedAt": "2026-07-21T12:00:00.000Z",
"strategies": [],
"indexedKeys": []
}
}
12 changes: 12 additions & 0 deletions src/handlers/memory/__fixtures__/list-page-1.golden.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"memories": [
{
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_read_only_fixture-AbCd123456",
"id": "agentcore_cli_memory_read_only_fixture-AbCd123456",
"status": "ACTIVE",
"createdAt": "2026-07-20T12:00:00.000Z",
"updatedAt": "2026-07-21T12:00:00.000Z"
}
],
"nextToken": "memory-page-2"
}
11 changes: 11 additions & 0 deletions src/handlers/memory/__fixtures__/list-page-2.golden.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"memories": [
{
"arn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/agentcore_cli_memory_second-EfGh789012",
"id": "agentcore_cli_memory_second-EfGh789012",
"status": "ACTIVE",
"createdAt": "2026-07-22T12:00:00.000Z",
"updatedAt": "2026-07-22T12:30:00.000Z"
}
]
}
28 changes: 28 additions & 0 deletions src/handlers/memory/get/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import z from "zod";
import { createHandler, flag } from "../../../router";
import { JsonRendererKey } from "../../../tui";
import type { Core } from "../../types";
import { coreOptsFromCtx } from "../../utils";

const MEMORY_VIEWS = ["full", "without_decryption"] as const;

export const createGetMemoryHandler = (core: Core) =>
createHandler({
name: "get",
description: "get an AgentCore Memory",
flags: [
flag("id", "the ID of the Memory", z.string().optional()),
flag("view", "response view", z.enum(MEMORY_VIEWS).optional()),
],
handle: async (ctx, flags) => {
if (!flags.id) {
throw new TypeError("required option '--id <id>' not specified");
}

ctx
.require(JsonRendererKey)
.renderJson(
await core.memory.getMemory(flags.id, flags.view ?? "full", coreOptsFromCtx(ctx)),
);
},
});
13 changes: 13 additions & 0 deletions src/handlers/memory/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Router } from "../../router";
import type { AppIO } from "../../io";
import type { Core } from "../types";
import { createHelpDefault } from "../help";
import { createGetMemoryHandler } from "./get";
import { createListMemoriesHandler } from "./list";

export function createMemoryHandler(core: Core, io: AppIO): Router {
return new Router("memory", "manage AgentCore Memories")
.default(createHelpDefault(io))
.handler(createGetMemoryHandler(core))
.handler(createListMemoriesHandler(core));
}
26 changes: 26 additions & 0 deletions src/handlers/memory/list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import z from "zod";
import { createHandler, flag } from "../../../router";
import { JsonRendererKey } from "../../../tui";
import type { Core } from "../../types";
import { coreOptsFromCtx } from "../../utils";

export const createListMemoriesHandler = (core: Core) =>
createHandler({
name: "list",
description: "list AgentCore Memories",
flags: [
flag("next-token", "pagination token returned by a previous request", z.string().optional()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOS here, but curious to others thoughts. Do we think its worth defining a shared flag for stuff like this? We already have the same definitions for runtime, harness, and now memory.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I’d prefer to keep that refactor out of this PR and follow up separately so it will be a follow-up once this is merged

flag("max-results", "maximum number of items to return", z.number().optional()),
],
handle: async (ctx, flags) => {
ctx
.require(JsonRendererKey)
.renderJson(
await core.memory.listMemories(
flags["next-token"],
flags["max-results"],
coreOptsFromCtx(ctx),
),
);
},
});
Loading
Loading