Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e5273fc
[wrangler] Add wrangler agent-memory namespace commands
petebacondarwin Mar 27, 2026
862b5d1
[wrangler] Use namespace name instead of ID for agent-memory get/dele…
petebacondarwin Mar 31, 2026
cb7f39b
[workers-utils] Add agent_memory binding config and typing
petebacondarwin Apr 1, 2026
09134fa
[wrangler] Wire remote-only agent_memory bindings through dev and deploy
petebacondarwin Apr 1, 2026
6ba6871
[wrangler] Add type generation, tests, and config-diffs fix for agent…
petebacondarwin Apr 1, 2026
6fa9251
fixes to use agent_memory_namespace type
oliy Apr 7, 2026
1b3b6e9
Improve changeset description to follow official guidelines
petebacondarwin Apr 10, 2026
4be1d27
Fix lint, format, and test type errors
petebacondarwin Apr 20, 2026
ddd56ed
Fix deploy/bindings test to use agent_memory_namespace
petebacondarwin Apr 20, 2026
92bdc15
Address review feedback for agent_memory binding
petebacondarwin Apr 21, 2026
2ae9fcc
Use fetchResult<void> for agent_memory delete
petebacondarwin Apr 21, 2026
ca9d124
Address more agent_memory review feedback
petebacondarwin Apr 21, 2026
35ffa67
[wrangler] Add agentmemory:write OAuth scope
petebacondarwin Apr 22, 2026
2769588
[wrangler] Wrap user-caused agent-memory errors as UserError
petebacondarwin Apr 22, 2026
7ff5bef
fix the OAuth scope
petebacondarwin May 5, 2026
4e82c66
`agent_memory_namespace` is actually just `agent_memory` now
petebacondarwin May 5, 2026
935136f
fix type for new UserError
petebacondarwin May 5, 2026
0225cce
fix API paths
petebacondarwin May 5, 2026
1de595f
keep e2e namespace names below 32 chars
petebacondarwin May 5, 2026
24c2b06
fixup creation test checks
petebacondarwin May 5, 2026
5bd4958
tidy up telemetry message
petebacondarwin May 5, 2026
a545481
fixup! keep e2e namespace names below 32 chars
petebacondarwin May 5, 2026
e9de19d
fix e2e test colon mismatch in agent-memory create output
petebacondarwin May 5, 2026
4b9703c
expect open-beta warning in agent-memory e2e test stderr
petebacondarwin May 5, 2026
5b7b554
fix up e2e test and cleanup logic
petebacondarwin May 6, 2026
2bf5b71
test: retry transient API 5xx in remote-binding e2e suite
petebacondarwin May 6, 2026
53879be
test: refactor remote-binding e2e to use updateBindings per test
petebacondarwin May 7, 2026
50cc148
test: fix agent-memory e2e to match runtime binding API
petebacondarwin May 7, 2026
91d2b28
[wrangler] Fix agent-memory tests after upstream changes
petebacondarwin May 20, 2026
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
25 changes: 25 additions & 0 deletions .changeset/agent-memory-binding-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
"miniflare": minor
"wrangler": minor
---

Add support for `agent_memory` bindings

Agent Memory bindings allow Workers to connect to Cloudflare's Agent Memory service for storing and retrieving agent conversation state. This binding is remote-only, meaning it always connects to the Cloudflare API during `wrangler dev` rather than using a local simulation.

To configure an `agent_memory` binding, add the following to your `wrangler.json`:

```jsonc
{
"agent_memory": [
{
"binding": "MY_MEMORY",
"namespace": "my-namespace",
},
],
}
```

Wrangler will automatically provision the namespace during deployment if it does not already exist. Type generation via `wrangler types` is also supported.

This change also adds the `agent-memory:write` OAuth scope to Wrangler's default login scopes, so `wrangler login` can request the permissions needed to provision and manage Agent Memory namespaces.
14 changes: 14 additions & 0 deletions .changeset/agent-memory-namespace-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"wrangler": minor
---

Add `wrangler agent-memory namespace` commands

The following commands have been added for managing Agent Memory namespaces:

```bash
wrangler agent-memory namespace create <namespace>
wrangler agent-memory namespace list [--json]
wrangler agent-memory namespace get <namespace_name> [--json]
wrangler agent-memory namespace delete <namespace_name> [--force]
```
71 changes: 71 additions & 0 deletions packages/miniflare/src/plugins/agent-memory/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { z } from "zod";
import {
getUserBindingServiceName,
ProxyNodeBinding,
remoteProxyClientWorker,
} from "../shared";
import type { Plugin, RemoteProxyConnectionString } from "../shared";

const AgentMemoryEntrySchema = z.object({
namespace: z.string(),
remoteProxyConnectionString: z
.custom<RemoteProxyConnectionString>()
.optional(),
});

export const AgentMemoryOptionsSchema = z.object({
agentMemory: z.record(AgentMemoryEntrySchema).optional(),
});

export const AGENT_MEMORY_PLUGIN_NAME = "agent-memory";

const AGENT_MEMORY_SCOPE = "agent-memory";

export const AGENT_MEMORY_PLUGIN: Plugin<typeof AgentMemoryOptionsSchema> = {
options: AgentMemoryOptionsSchema,
async getBindings(options) {
if (!options.agentMemory) {
return [];
}

return Object.entries(options.agentMemory).map(([bindingName, entry]) => ({
name: bindingName,
service: {
name: getUserBindingServiceName(
AGENT_MEMORY_SCOPE,
bindingName,
entry.remoteProxyConnectionString
),
},
}));
},
getNodeBindings(options) {
if (!options.agentMemory) {
return {};
}

return Object.fromEntries(
Object.keys(options.agentMemory).map((bindingName) => [
bindingName,
new ProxyNodeBinding(),
])
);
},
async getServices({ options }) {
if (!options.agentMemory) {
return [];
}

return Object.entries(options.agentMemory).map(([bindingName, entry]) => ({
name: getUserBindingServiceName(
AGENT_MEMORY_SCOPE,
bindingName,
entry.remoteProxyConnectionString
),
worker: remoteProxyClientWorker(
entry.remoteProxyConnectionString,
bindingName
),
}));
},
};
4 changes: 4 additions & 0 deletions packages/miniflare/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AGENT_MEMORY_PLUGIN, AGENT_MEMORY_PLUGIN_NAME } from "./agent-memory";
import { AI_PLUGIN, AI_PLUGIN_NAME } from "./ai";
import { AI_SEARCH_PLUGIN, AI_SEARCH_PLUGIN_NAME } from "./ai-search";
import {
Expand Down Expand Up @@ -66,6 +67,7 @@ export const PLUGINS = {
[EMAIL_PLUGIN_NAME]: EMAIL_PLUGIN,
[ANALYTICS_ENGINE_PLUGIN_NAME]: ANALYTICS_ENGINE_PLUGIN,
[AI_PLUGIN_NAME]: AI_PLUGIN,
[AGENT_MEMORY_PLUGIN_NAME]: AGENT_MEMORY_PLUGIN,
[AI_SEARCH_PLUGIN_NAME]: AI_SEARCH_PLUGIN,
[BROWSER_RENDERING_PLUGIN_NAME]: BROWSER_RENDERING_PLUGIN,
[DISPATCH_NAMESPACE_PLUGIN_NAME]: DISPATCH_NAMESPACE_PLUGIN,
Expand Down Expand Up @@ -135,6 +137,7 @@ export type WorkerOptions = z.input<typeof CORE_PLUGIN.options> &
z.input<typeof SECRET_STORE_PLUGIN.options> &
z.input<typeof ANALYTICS_ENGINE_PLUGIN.options> &
z.input<typeof AI_PLUGIN.options> &
z.input<typeof AGENT_MEMORY_PLUGIN.options> &
z.input<typeof AI_SEARCH_PLUGIN.options> &
z.input<typeof BROWSER_RENDERING_PLUGIN.options> &
z.input<typeof DISPATCH_NAMESPACE_PLUGIN.options> &
Expand Down Expand Up @@ -226,6 +229,7 @@ export * from "./secret-store";
export * from "./email";
export * from "./analytics-engine";
export * from "./ai";
export * from "./agent-memory";
export * from "./ai-search";
export * from "./browser-rendering";
export * from "./dispatch-namespace";
Expand Down
1 change: 1 addition & 0 deletions packages/workers-utils/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ export const defaultWranglerConfig: Config = {
vectorize: [],
ai_search_namespaces: [],
ai_search: [],
agent_memory: [],
hyperdrive: [],
workflows: [],
secrets_store_secrets: [],
Expand Down
18 changes: 18 additions & 0 deletions packages/workers-utils/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,24 @@ export interface EnvironmentNonInheritable {
remote?: boolean;
}[];

/**
* Specifies Agent Memory namespace bindings that are bound to this Worker environment.
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default []
* @nonInheritable
*/
agent_memory: {
/** The binding name used to refer to the Agent Memory namespace in the Worker. */
binding: string;
/** The user-chosen namespace name. Must exist in Cloudflare at deploy time. */
namespace: string;
Comment thread
oliy marked this conversation as resolved.
/** Whether the Agent Memory binding should be remote in local development */
remote?: boolean;
}[];

/**
* Specifies Hyperdrive configs that are bound to this Worker environment.
*
Expand Down
48 changes: 48 additions & 0 deletions packages/workers-utils/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export type ConfigBindingFieldName =
| "vectorize"
| "ai_search_namespaces"
| "ai_search"
| "agent_memory"
| "hyperdrive"
| "r2_buckets"
| "logfwdr"
Expand Down Expand Up @@ -124,6 +125,7 @@ export const friendlyBindingNames: Record<ConfigBindingFieldName, string> = {
vectorize: "Vectorize Index",
ai_search_namespaces: "AI Search Namespace",
ai_search: "AI Search Instance",
agent_memory: "Agent Memory",
hyperdrive: "Hyperdrive Config",
r2_buckets: "R2 Bucket",
logfwdr: "logfwdr",
Expand Down Expand Up @@ -181,6 +183,7 @@ const bindingTypeFriendlyNames: Record<Binding["type"], string> = {
vectorize: "Vectorize Index",
ai_search_namespace: "AI Search Namespace",
ai_search: "AI Search Instance",
agent_memory: "Agent Memory",
hyperdrive: "Hyperdrive Config",
service: "Worker",
fetcher: "Service Binding",
Expand Down Expand Up @@ -1756,6 +1759,16 @@ function normalizeAndValidateEnvironment(
validateBindingArray(envName, validateAISearchBinding),
[]
),
agent_memory: notInheritable(
diagnostics,
topLevelEnv,
rawConfig,
rawEnv,
envName,
"agent_memory",
validateBindingArray(envName, validateAgentMemoryBinding),
[]
),
hyperdrive: notInheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -3024,6 +3037,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
"ai",
"ai_search_namespace",
"ai_search",
"agent_memory",
"kv_namespace",
"durable_object_namespace",
"d1_database",
Expand Down Expand Up @@ -4173,6 +4187,40 @@ const validateAISearchBinding: ValidatorFn = (diagnostics, field, value) => {
return isValid;
};

const validateAgentMemoryBinding: ValidatorFn = (diagnostics, field, value) => {
if (typeof value !== "object" || value === null) {
diagnostics.errors.push(
`"agent_memory" bindings should be objects, but got ${JSON.stringify(value)}`
);
return false;
}
let isValid = true;
if (!isRequiredProperty(value, "binding", "string")) {
diagnostics.errors.push(
`"${field}" bindings should have a string "binding" field but got ${JSON.stringify(value)}.`
);
isValid = false;
}
if (!isRequiredProperty(value, "namespace", "string")) {
diagnostics.errors.push(
`"${field}" bindings must have a "namespace" field but got ${JSON.stringify(value)}.`
);
isValid = false;
}

if (!isRemoteValid(value, field, diagnostics)) {
isValid = false;
}

validateAdditionalProperties(diagnostics, field, Object.keys(value), [
"binding",
"namespace",
"remote",
]);

return isValid;
};

const validateHyperdriveBinding: ValidatorFn = (diagnostics, field, value) => {
if (typeof value !== "object" || value === null) {
diagnostics.errors.push(
Expand Down
10 changes: 10 additions & 0 deletions packages/workers-utils/src/map-worker-metadata-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@ export function mapWorkerMetadataBindings(
},
];
break;
case "agent_memory": {
configObj.agent_memory = [
...(configObj.agent_memory ?? []),
{
binding: binding.name,
namespace: binding.namespace,
},
];
break;
}
case "hyperdrive":
configObj.hyperdrive = [
...(configObj.hyperdrive ?? []),
Expand Down
3 changes: 3 additions & 0 deletions packages/workers-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
} from "./config/environment";
import type {
CfAIBinding,
CfAgentMemory,
CfAISearch,
CfAISearchNamespace,
CfAnalyticsEngineDataset,
Expand Down Expand Up @@ -73,6 +74,7 @@ export type WorkerMetadataBinding =
| { type: "data_blob"; name: string; part: string }
| { type: "ai_search_namespace"; name: string; namespace: string }
| { type: "ai_search"; name: string; instance_name: string }
| { type: "agent_memory"; name: string; namespace: string }
| { type: "kv_namespace"; name: string; namespace_id: string; raw?: boolean }
| { type: "media"; name: string }
| {
Expand Down Expand Up @@ -334,6 +336,7 @@ export type Binding =
| ({ type: "vectorize" } & BindingOmit<CfVectorize>)
| ({ type: "ai_search_namespace" } & BindingOmit<CfAISearchNamespace>)
| ({ type: "ai_search" } & BindingOmit<CfAISearch>)
| ({ type: "agent_memory" } & BindingOmit<CfAgentMemory>)
| ({ type: "hyperdrive" } & BindingOmit<CfHyperdrive>)
| ({ type: "service" } & BindingOmit<CfService>)
| { type: "fetcher"; fetcher: ServiceFetch }
Expand Down
6 changes: 6 additions & 0 deletions packages/workers-utils/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ export interface CfAISearch {
remote?: boolean;
}

export interface CfAgentMemory {
binding: string;
namespace: string | typeof INHERIT_SYMBOL;
remote?: boolean;
}

export interface CfSecretsStoreSecrets {
binding: string;
store_id: string;
Expand Down
Loading
Loading