agentfootprint 7.13.0 → 7.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/hosting/agentcore.js +350 -0
- package/dist/adapters/hosting/agentcore.js.map +1 -0
- package/dist/adapters/memory/agentcore.js +125 -1
- package/dist/adapters/memory/agentcore.js.map +1 -1
- package/dist/adapters/security/agentcore.js +0 -0
- package/dist/adapters/security/agentcore.js.map +1 -0
- package/dist/core/Agent.js +79 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/esm/adapters/hosting/agentcore.d.ts +199 -0
- package/dist/esm/adapters/hosting/agentcore.js +321 -0
- package/dist/esm/adapters/hosting/agentcore.js.map +1 -0
- package/dist/esm/adapters/memory/agentcore.d.ts +88 -2
- package/dist/esm/adapters/memory/agentcore.js +125 -1
- package/dist/esm/adapters/memory/agentcore.js.map +1 -1
- package/dist/esm/adapters/security/agentcore.d.ts +157 -0
- package/dist/esm/adapters/security/agentcore.js +0 -0
- package/dist/esm/adapters/security/agentcore.js.map +1 -0
- package/dist/esm/core/Agent.d.ts +47 -0
- package/dist/esm/core/Agent.js +79 -2
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/hosting/envelope.d.ts +33 -0
- package/dist/esm/hosting/envelope.js +49 -0
- package/dist/esm/hosting/envelope.js.map +1 -0
- package/dist/esm/hosting/errors.d.ts +78 -0
- package/dist/esm/hosting/errors.js +119 -0
- package/dist/esm/hosting/errors.js.map +1 -0
- package/dist/esm/hosting/httpHost.d.ts +121 -0
- package/dist/esm/hosting/httpHost.js +248 -0
- package/dist/esm/hosting/httpHost.js.map +1 -0
- package/dist/esm/hosting/index.d.ts +60 -0
- package/dist/esm/hosting/index.js +58 -0
- package/dist/esm/hosting/index.js.map +1 -0
- package/dist/esm/hosting/memorySessions.d.ts +22 -0
- package/dist/esm/hosting/memorySessions.js +31 -0
- package/dist/esm/hosting/memorySessions.js.map +1 -0
- package/dist/esm/hosting/nodeHost.d.ts +71 -0
- package/dist/esm/hosting/nodeHost.js +78 -0
- package/dist/esm/hosting/nodeHost.js.map +1 -0
- package/dist/esm/hosting/standingAgent.d.ts +62 -0
- package/dist/esm/hosting/standingAgent.js +192 -0
- package/dist/esm/hosting/standingAgent.js.map +1 -0
- package/dist/esm/hosting/types.d.ts +206 -0
- package/dist/esm/hosting/types.js +21 -0
- package/dist/esm/hosting/types.js.map +1 -0
- package/dist/esm/hosting-providers.d.ts +50 -0
- package/dist/esm/hosting-providers.js +50 -0
- package/dist/esm/hosting-providers.js.map +1 -0
- package/dist/esm/lib/mcp/gatewayTransport.d.ts +103 -0
- package/dist/esm/lib/mcp/gatewayTransport.js +123 -0
- package/dist/esm/lib/mcp/gatewayTransport.js.map +1 -0
- package/dist/esm/lib/mcp/index.d.ts +2 -1
- package/dist/esm/lib/mcp/index.js +1 -0
- package/dist/esm/lib/mcp/index.js.map +1 -1
- package/dist/esm/lib/mcp/mcpClient.js +10 -2
- package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
- package/dist/esm/lib/mcp/types.d.ts +45 -1
- package/dist/esm/memory/store/types.d.ts +22 -0
- package/dist/esm/memory-providers.d.ts +1 -1
- package/dist/esm/memory-providers.js.map +1 -1
- package/dist/esm/security/index.d.ts +10 -1
- package/dist/esm/security/index.js +14 -1
- package/dist/esm/security/index.js.map +1 -1
- package/dist/esm/tool-providers/index.d.ts +6 -2
- package/dist/esm/tool-providers/index.js +5 -1
- package/dist/esm/tool-providers/index.js.map +1 -1
- package/dist/hosting/envelope.js +54 -0
- package/dist/hosting/envelope.js.map +1 -0
- package/dist/hosting/errors.js +126 -0
- package/dist/hosting/errors.js.map +1 -0
- package/dist/hosting/httpHost.js +276 -0
- package/dist/hosting/httpHost.js.map +1 -0
- package/dist/hosting/index.js +73 -0
- package/dist/hosting/index.js.map +1 -0
- package/dist/hosting/memorySessions.js +35 -0
- package/dist/hosting/memorySessions.js.map +1 -0
- package/dist/hosting/nodeHost.js +82 -0
- package/dist/hosting/nodeHost.js.map +1 -0
- package/dist/hosting/standingAgent.js +196 -0
- package/dist/hosting/standingAgent.js.map +1 -0
- package/dist/hosting/types.js +22 -0
- package/dist/hosting/types.js.map +1 -0
- package/dist/hosting-providers.js +57 -0
- package/dist/hosting-providers.js.map +1 -0
- package/dist/lib/mcp/gatewayTransport.js +129 -0
- package/dist/lib/mcp/gatewayTransport.js.map +1 -0
- package/dist/lib/mcp/index.js +4 -1
- package/dist/lib/mcp/index.js.map +1 -1
- package/dist/lib/mcp/mcpClient.js +10 -2
- package/dist/lib/mcp/mcpClient.js.map +1 -1
- package/dist/memory-providers.js.map +1 -1
- package/dist/security/index.js +16 -2
- package/dist/security/index.js.map +1 -1
- package/dist/tool-providers/index.js +7 -1
- package/dist/tool-providers/index.js.map +1 -1
- package/dist/types/adapters/hosting/agentcore.d.ts +200 -0
- package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -0
- package/dist/types/adapters/memory/agentcore.d.ts +88 -2
- package/dist/types/adapters/memory/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/security/agentcore.d.ts +158 -0
- package/dist/types/adapters/security/agentcore.d.ts.map +1 -0
- package/dist/types/core/Agent.d.ts +47 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/hosting/envelope.d.ts +34 -0
- package/dist/types/hosting/envelope.d.ts.map +1 -0
- package/dist/types/hosting/errors.d.ts +79 -0
- package/dist/types/hosting/errors.d.ts.map +1 -0
- package/dist/types/hosting/httpHost.d.ts +122 -0
- package/dist/types/hosting/httpHost.d.ts.map +1 -0
- package/dist/types/hosting/index.d.ts +61 -0
- package/dist/types/hosting/index.d.ts.map +1 -0
- package/dist/types/hosting/memorySessions.d.ts +23 -0
- package/dist/types/hosting/memorySessions.d.ts.map +1 -0
- package/dist/types/hosting/nodeHost.d.ts +72 -0
- package/dist/types/hosting/nodeHost.d.ts.map +1 -0
- package/dist/types/hosting/standingAgent.d.ts +63 -0
- package/dist/types/hosting/standingAgent.d.ts.map +1 -0
- package/dist/types/hosting/types.d.ts +207 -0
- package/dist/types/hosting/types.d.ts.map +1 -0
- package/dist/types/hosting-providers.d.ts +51 -0
- package/dist/types/hosting-providers.d.ts.map +1 -0
- package/dist/types/lib/mcp/gatewayTransport.d.ts +104 -0
- package/dist/types/lib/mcp/gatewayTransport.d.ts.map +1 -0
- package/dist/types/lib/mcp/index.d.ts +2 -1
- package/dist/types/lib/mcp/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/lib/mcp/types.d.ts +45 -1
- package/dist/types/lib/mcp/types.d.ts.map +1 -1
- package/dist/types/memory/store/types.d.ts +22 -0
- package/dist/types/memory/store/types.d.ts.map +1 -1
- package/dist/types/memory-providers.d.ts +1 -1
- package/dist/types/memory-providers.d.ts.map +1 -1
- package/dist/types/security/index.d.ts +10 -1
- package/dist/types/security/index.d.ts.map +1 -1
- package/dist/types/tool-providers/index.d.ts +6 -2
- package/dist/types/tool-providers/index.d.ts.map +1 -1
- package/package.json +27 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/hosting/agentcore — AWS Bedrock **AgentCore Runtime** adapters for
|
|
3
|
+
* the two hosting ports.
|
|
4
|
+
*
|
|
5
|
+
* import { agentCoreRuntimeHost, agentCoreSessions } from 'agentfootprint/hosting-providers';
|
|
6
|
+
* import { standingAgent } from 'agentfootprint/hosting';
|
|
7
|
+
*
|
|
8
|
+
* const handle = await standingAgent({
|
|
9
|
+
* agent,
|
|
10
|
+
* host: agentCoreRuntimeHost(),
|
|
11
|
+
* sessions: agentCoreSessions({ store: 'session-storage' }),
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* ── What this file actually is ───────────────────────────────────────────────
|
|
15
|
+
* Vendor paths, a header name, and two JSON body shapes. That is the whole
|
|
16
|
+
* adapter, and it is the claim the hosting ports were designed to make: a
|
|
17
|
+
* container runtime's contract is a CONFIGURATION of HTTP work that already
|
|
18
|
+
* exists, not a second implementation of it. Nothing here reaches into the
|
|
19
|
+
* ports, and nothing here needed the ports to change.
|
|
20
|
+
*
|
|
21
|
+
* AgentCore Runtime is a **container contract**: an ARM64 image serving HTTP on
|
|
22
|
+
* `0.0.0.0:8080` —
|
|
23
|
+
*
|
|
24
|
+
* POST /invocations JSON `{ "prompt": "..." }` → JSON `{ "response", "status" }`
|
|
25
|
+
* GET /ping → `{ "status": "Healthy", "time_of_last_update": <unix seconds> }`
|
|
26
|
+
*
|
|
27
|
+
* and the caller's conversation arrives in the
|
|
28
|
+
* `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header rather than in the body,
|
|
29
|
+
* which is the one thing paths-and-bodies configuration alone could not
|
|
30
|
+
* express before this release.
|
|
31
|
+
*
|
|
32
|
+
* ── Verification status, stated plainly ──────────────────────────────────────
|
|
33
|
+
* `agentCoreRuntimeHost` is **plain HTTP and is really verified**: it runs the
|
|
34
|
+
* same host conformance suite as `nodeHost`, over a real socket, in
|
|
35
|
+
* `test/hosting/host-contract.test.ts`. There is no AWS SDK on its path.
|
|
36
|
+
*
|
|
37
|
+
* `agentCoreSessions({ store: 'memory' })` is **contract-mapped and
|
|
38
|
+
* injection-tested**: its AgentCore Memory calls are exercised through the
|
|
39
|
+
* `_client` seam, never against AWS. Confirm the command and field names
|
|
40
|
+
* against your installed `@aws-sdk/client-bedrock-agentcore` before you rely
|
|
41
|
+
* on it; real-cloud verification lands with a field deployment.
|
|
42
|
+
*
|
|
43
|
+
* Pattern: Adapter (GoF). Role: outer ring. The file-backed session store uses
|
|
44
|
+
* `node:fs` and nothing else; the event-backed one lazy-loads the AWS SDK, so
|
|
45
|
+
* importing this module costs zero peer-dep load.
|
|
46
|
+
*/
|
|
47
|
+
import { readEnvelope } from '../../hosting/envelope.js';
|
|
48
|
+
import { headerValue, httpHost } from '../../hosting/httpHost.js';
|
|
49
|
+
import { lazyRequire } from '../../lib/lazyRequire.js';
|
|
50
|
+
// ─── The runtime host ────────────────────────────────────────────────
|
|
51
|
+
const HOST_NAME = 'agentCoreRuntimeHost';
|
|
52
|
+
/** The runtime's container contract, as constants rather than as scattered literals. */
|
|
53
|
+
const INVOKE_PATH = '/invocations';
|
|
54
|
+
const HEALTH_PATH = '/ping';
|
|
55
|
+
const RUNTIME_PORT = 8080;
|
|
56
|
+
/**
|
|
57
|
+
* The header the runtime puts the caller's conversation in. Matched
|
|
58
|
+
* case-insensitively — HTTP header names are case-insensitive and a proxy in
|
|
59
|
+
* front of the container is free to re-case them.
|
|
60
|
+
*/
|
|
61
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
62
|
+
/**
|
|
63
|
+
* The AgentCore Runtime contract as an {@link HttpWire}.
|
|
64
|
+
*
|
|
65
|
+
* Exported so the body shapes are inspectable and testable without binding a
|
|
66
|
+
* socket, and so a deployment that must serve the same bodies from somewhere
|
|
67
|
+
* else can reuse them by name.
|
|
68
|
+
*/
|
|
69
|
+
export function agentCoreRuntimeWire(busy) {
|
|
70
|
+
return {
|
|
71
|
+
readRequest(facts) {
|
|
72
|
+
// `prompt` is the field the runtime's own quickstart and this repo's
|
|
73
|
+
// deploy template use. `input` is accepted too because the runtime passes
|
|
74
|
+
// the payload through verbatim — it is the CALLER who picks the field —
|
|
75
|
+
// and refusing a caller who used the port's own word would be a rule this
|
|
76
|
+
// adapter invented rather than one the contract imposes.
|
|
77
|
+
const prompt = facts.body.prompt ?? facts.body.input;
|
|
78
|
+
const input = typeof prompt === 'string' ? prompt : '';
|
|
79
|
+
// The conversation id arrives in a header, never in the body. It is
|
|
80
|
+
// caller-adjacent data, not identity — the port says so and it is just as
|
|
81
|
+
// true here.
|
|
82
|
+
const sessionId = headerValue(facts, SESSION_HEADER);
|
|
83
|
+
return sessionId !== undefined ? { input, sessionId } : { input };
|
|
84
|
+
},
|
|
85
|
+
// The runtime polls this to decide whether the container is ready and
|
|
86
|
+
// whether to send it more work. `time_of_last_update` is unix SECONDS.
|
|
87
|
+
health: () => ({
|
|
88
|
+
status: busy?.() === true ? 'HealthyBusy' : 'Healthy',
|
|
89
|
+
time_of_last_update: Math.floor(Date.now() / 1000),
|
|
90
|
+
}),
|
|
91
|
+
output: (response) => ({ response, status: 'success' }),
|
|
92
|
+
// The message only — never a stack. The runtime surfaces the status code;
|
|
93
|
+
// the body is read by whoever called the agent.
|
|
94
|
+
failure: (error, code) => ({ error, status: 'error', ...(code !== undefined && { code }) }),
|
|
95
|
+
// A distinct field from `response` on purpose: a caller concatenating
|
|
96
|
+
// stream frames must not be able to double-count the final answer by
|
|
97
|
+
// reading the same key twice.
|
|
98
|
+
chunk: (chunk) => ({ chunk }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* An `AgentHost` that speaks AgentCore Runtime's container contract.
|
|
103
|
+
*
|
|
104
|
+
* Passes the same conformance suite as `nodeHost` — it is the same HTTP host
|
|
105
|
+
* with this runtime's two paths, its header, and its two body shapes.
|
|
106
|
+
*
|
|
107
|
+
* @example The container's entry point
|
|
108
|
+
* const handle = await standingAgent({
|
|
109
|
+
* agent,
|
|
110
|
+
* host: agentCoreRuntimeHost(),
|
|
111
|
+
* sessions: agentCoreSessions({ store: 'session-storage' }),
|
|
112
|
+
* });
|
|
113
|
+
* process.on('SIGTERM', () => void handle.close());
|
|
114
|
+
*/
|
|
115
|
+
export function agentCoreRuntimeHost(options = {}) {
|
|
116
|
+
return httpHost({
|
|
117
|
+
name: HOST_NAME,
|
|
118
|
+
wire: agentCoreRuntimeWire(options.busy),
|
|
119
|
+
invokePath: INVOKE_PATH,
|
|
120
|
+
healthPath: HEALTH_PATH,
|
|
121
|
+
port: options.port ?? RUNTIME_PORT,
|
|
122
|
+
hostname: options.hostname ?? '0.0.0.0',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** The default file the `'session-storage'` mode writes to. */
|
|
126
|
+
export const DEFAULT_SESSION_STORAGE_PATH = '/tmp/agentcore-session';
|
|
127
|
+
/**
|
|
128
|
+
* A `SessionLifecycle` backed by AgentCore, with the checkpoint's home chosen
|
|
129
|
+
* at construction.
|
|
130
|
+
*
|
|
131
|
+
* Both modes store the SAME `CheckpointEnvelope` the port defines, and both
|
|
132
|
+
* refuse an unknown `format` by name through the shared `readEnvelope` — a
|
|
133
|
+
* conversation written by a newer runtime is refused, never half-restored.
|
|
134
|
+
* That law is inherited, not re-implemented.
|
|
135
|
+
*
|
|
136
|
+
* @example Survive a stop/resume, no AWS SDK required
|
|
137
|
+
* agentCoreSessions({ store: 'session-storage' });
|
|
138
|
+
*
|
|
139
|
+
* @example Outlive the session entirely
|
|
140
|
+
* agentCoreSessions({ store: 'memory', memoryId: process.env.MEMORY_ID!, region: 'us-west-2' });
|
|
141
|
+
*/
|
|
142
|
+
export function agentCoreSessions(options) {
|
|
143
|
+
return options.store === 'memory' ? memoryEventSessions(options) : fileSessions(options);
|
|
144
|
+
}
|
|
145
|
+
function fileSessions(options) {
|
|
146
|
+
const path = options.path ?? DEFAULT_SESSION_STORAGE_PATH;
|
|
147
|
+
async function readFile() {
|
|
148
|
+
const { readFile: read } = await import('node:fs/promises');
|
|
149
|
+
let raw;
|
|
150
|
+
try {
|
|
151
|
+
raw = await read(path, 'utf8');
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// No file yet is the ordinary first-request state, not an error.
|
|
155
|
+
return { version: 1, sessions: {} };
|
|
156
|
+
}
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = JSON.parse(raw);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
throw new TypeError(`[hosting] the session file at '${path}' is not JSON (${err.message}). ` +
|
|
163
|
+
`Refusing rather than starting every conversation over silently — delete the file ` +
|
|
164
|
+
`to start fresh, or point 'path' somewhere else.`);
|
|
165
|
+
}
|
|
166
|
+
const sessions = parsed?.sessions;
|
|
167
|
+
return sessions && typeof sessions === 'object'
|
|
168
|
+
? { version: 1, sessions: sessions }
|
|
169
|
+
: { version: 1, sessions: {} };
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
async hydrate(sessionId) {
|
|
173
|
+
const file = await readFile();
|
|
174
|
+
const stored = file.sessions[sessionId];
|
|
175
|
+
if (stored === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
// Validate HERE as well as in the composer, so a refusal points at the
|
|
178
|
+
// store that produced the bytes rather than at whoever read them next.
|
|
179
|
+
readEnvelope(stored);
|
|
180
|
+
return stored;
|
|
181
|
+
},
|
|
182
|
+
async persist(sessionId, envelope) {
|
|
183
|
+
const { writeFile, rename, mkdir } = await import('node:fs/promises');
|
|
184
|
+
const { dirname } = await import('node:path');
|
|
185
|
+
const file = await readFile();
|
|
186
|
+
const next = {
|
|
187
|
+
version: 1,
|
|
188
|
+
sessions: { ...file.sessions, [sessionId]: envelope },
|
|
189
|
+
};
|
|
190
|
+
await mkdir(dirname(path), { recursive: true }).catch(() => undefined);
|
|
191
|
+
// Write-then-rename: a container killed mid-write leaves the previous
|
|
192
|
+
// conversation intact rather than a truncated file that refuses to parse.
|
|
193
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
194
|
+
await writeFile(temporary, JSON.stringify(next), 'utf8');
|
|
195
|
+
await rename(temporary, path);
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// ─── 'memory': one AgentCore Memory event per persist ────────────────
|
|
200
|
+
const DEFAULT_ACTOR_ID = 'afp-standing-agent';
|
|
201
|
+
function memoryEventSessions(options) {
|
|
202
|
+
if (!options.memoryId) {
|
|
203
|
+
throw new Error(`agentCoreSessions({ store: 'memory' }) requires 'memoryId'.`);
|
|
204
|
+
}
|
|
205
|
+
const memoryId = options.memoryId;
|
|
206
|
+
const actorId = options.actorId ?? DEFAULT_ACTOR_ID;
|
|
207
|
+
const client = options._client ?? options.client ?? createSessionClient(options.region, options._sdk);
|
|
208
|
+
return {
|
|
209
|
+
async hydrate(sessionId) {
|
|
210
|
+
// AgentCore Memory is an append-only log and lists newest-first, so the
|
|
211
|
+
// newest readable event IS the conversation. Older ones are the earlier
|
|
212
|
+
// turns and deliberately left where they are — they are the audit trail.
|
|
213
|
+
const page = await client.listEvents({
|
|
214
|
+
memoryId,
|
|
215
|
+
actorId,
|
|
216
|
+
sessionId: safeSessionId(sessionId),
|
|
217
|
+
maxResults: 1,
|
|
218
|
+
});
|
|
219
|
+
const newest = page.events[0];
|
|
220
|
+
if (!newest || newest.envelope === null || newest.envelope === undefined)
|
|
221
|
+
return undefined;
|
|
222
|
+
readEnvelope(newest.envelope);
|
|
223
|
+
return newest.envelope;
|
|
224
|
+
},
|
|
225
|
+
async persist(sessionId, envelope) {
|
|
226
|
+
await client.createEvent({
|
|
227
|
+
memoryId,
|
|
228
|
+
actorId,
|
|
229
|
+
sessionId: safeSessionId(sessionId),
|
|
230
|
+
envelope,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const SESSION_ID_MAX = 99;
|
|
236
|
+
/** AgentCore ids accept `[A-Za-z0-9_-]`; a session id is caller data and need not. */
|
|
237
|
+
function safeSessionId(raw) {
|
|
238
|
+
const slug = raw.replace(/[^A-Za-z0-9_-]/g, '-');
|
|
239
|
+
if (slug.length <= SESSION_ID_MAX)
|
|
240
|
+
return slug;
|
|
241
|
+
// Keep a readable head plus a stable tail so two long ids stay distinct.
|
|
242
|
+
return `${slug.slice(0, SESSION_ID_MAX - 9)}-${fnv1a(raw)}`;
|
|
243
|
+
}
|
|
244
|
+
function fnv1a(s) {
|
|
245
|
+
let h = 0x811c9dc5;
|
|
246
|
+
for (let i = 0; i < s.length; i++) {
|
|
247
|
+
h ^= s.charCodeAt(i);
|
|
248
|
+
h = Math.imul(h, 0x01000193);
|
|
249
|
+
}
|
|
250
|
+
return (h >>> 0).toString(36);
|
|
251
|
+
}
|
|
252
|
+
/** Pull the envelope out of an event's `payload` (a single `blob` document). */
|
|
253
|
+
function envelopeFromPayload(payload) {
|
|
254
|
+
if (!Array.isArray(payload))
|
|
255
|
+
return null;
|
|
256
|
+
for (const part of payload) {
|
|
257
|
+
const blob = part?.blob;
|
|
258
|
+
if (blob && typeof blob === 'object')
|
|
259
|
+
return blob;
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Map {@link AgentCoreSessionClientLike} onto the real SDK commands. If AWS
|
|
265
|
+
* renames a command, only this function changes — which is also why every test
|
|
266
|
+
* injects past it.
|
|
267
|
+
*/
|
|
268
|
+
function createSessionClient(region, injected) {
|
|
269
|
+
let mod;
|
|
270
|
+
if (injected) {
|
|
271
|
+
mod = injected;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
try {
|
|
275
|
+
mod = lazyRequire('@aws-sdk/client-bedrock-agentcore');
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
throw new Error(`agentCoreSessions({ store: 'memory' }) requires the ` +
|
|
279
|
+
'`@aws-sdk/client-bedrock-agentcore` peer dependency.\n' +
|
|
280
|
+
' Install: npm install @aws-sdk/client-bedrock-agentcore\n' +
|
|
281
|
+
" Or use { store: 'session-storage' }, which needs no SDK at all.");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!mod.BedrockAgentCoreClient) {
|
|
285
|
+
throw new Error('agentCoreSessions: `@aws-sdk/client-bedrock-agentcore` is installed but ' +
|
|
286
|
+
'`BedrockAgentCoreClient` was not found. Update the SDK.');
|
|
287
|
+
}
|
|
288
|
+
const sdk = new mod.BedrockAgentCoreClient({ ...(region && { region }) });
|
|
289
|
+
const send = async (Ctor, name, input) => {
|
|
290
|
+
if (!Ctor) {
|
|
291
|
+
throw new Error(`agentCoreSessions: \`@aws-sdk/client-bedrock-agentcore\` is missing ${name}. Upgrade the SDK.`);
|
|
292
|
+
}
|
|
293
|
+
return sdk.send(new Ctor(input));
|
|
294
|
+
};
|
|
295
|
+
return {
|
|
296
|
+
async createEvent({ memoryId, actorId, sessionId, envelope }) {
|
|
297
|
+
await send(mod.CreateEventCommand, 'CreateEventCommand', {
|
|
298
|
+
memoryId,
|
|
299
|
+
actorId,
|
|
300
|
+
sessionId,
|
|
301
|
+
eventTimestamp: new Date(),
|
|
302
|
+
payload: [{ blob: envelope }],
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
async listEvents({ memoryId, actorId, sessionId, maxResults }) {
|
|
306
|
+
const result = (await send(mod.ListEventsCommand, 'ListEventsCommand', {
|
|
307
|
+
memoryId,
|
|
308
|
+
actorId,
|
|
309
|
+
sessionId,
|
|
310
|
+
includePayloads: true,
|
|
311
|
+
...(maxResults !== undefined && { maxResults }),
|
|
312
|
+
}));
|
|
313
|
+
const events = (result?.events ?? []).map((event) => ({
|
|
314
|
+
eventId: event.eventId ?? '',
|
|
315
|
+
envelope: envelopeFromPayload(event.payload),
|
|
316
|
+
}));
|
|
317
|
+
return { events };
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=agentcore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentcore.js","sourceRoot":"","sources":["../../../../src/adapters/hosting/agentcore.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,14 +27,20 @@
|
|
|
27
27
|
* AgentCore's ids are server-assigned. **O(events in session)** — fine for typical
|
|
28
28
|
* window sizes; if you need O(1) keyed access at scale, use RedisStore.
|
|
29
29
|
* • `forget` → `ListEvents` + `DeleteEvent` per event (no `DeleteSession` on AgentCore).
|
|
30
|
-
* • `search` →
|
|
30
|
+
* • `search` → `RetrieveMemoryRecords`, **text-in**. AgentCore embeds and ranks on its
|
|
31
|
+
* own side, so it takes a natural-language query; the port's `search()` takes a
|
|
32
|
+
* vector. Pass `options.text` and this store serves the query; omit it and it refuses
|
|
33
|
+
* by name rather than ranking an empty set. See `search()` below for the whole story.
|
|
31
34
|
* • `putIfVersion` / `seen` / `feedback` → in-process emulation (AgentCore has no native
|
|
32
35
|
* CAS / dedup / feedback primitive; these don't survive process restart).
|
|
36
|
+
* • no `stream()` — AgentCore Memory has no streaming data-plane operation, and the
|
|
37
|
+
* `MemoryStore` port has no streaming method to implement. Inventing one for a single
|
|
38
|
+
* backend is how a port stops being a port.
|
|
33
39
|
*
|
|
34
40
|
* Role: Outer ring. Lazy-requires the AWS SDK; zero runtime cost when another adapter is
|
|
35
41
|
* in use. Emits: N/A (storage adapters don't emit).
|
|
36
42
|
*/
|
|
37
|
-
import type { ListOptions, ListResult, MemoryStore, PutIfVersionResult } from '../../memory/store/types.js';
|
|
43
|
+
import type { ListOptions, ListResult, MemoryStore, PutIfVersionResult, ScoredEntry, SearchOptions } from '../../memory/store/types.js';
|
|
38
44
|
import type { MemoryEntry } from '../../memory/entry/index.js';
|
|
39
45
|
import type { MemoryIdentity } from '../../memory/identity/index.js';
|
|
40
46
|
/** One event as the adapter cares about it: AgentCore's id + the decoded entry. */
|
|
@@ -75,6 +81,35 @@ export interface AgentCoreLikeClient {
|
|
|
75
81
|
sessionId: string;
|
|
76
82
|
eventId: string;
|
|
77
83
|
}): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Server-side semantic retrieval (`RetrieveMemoryRecords`). Optional: a client
|
|
86
|
+
* built before this existed still satisfies the interface, and `search()`
|
|
87
|
+
* feature-detects it rather than assuming.
|
|
88
|
+
*/
|
|
89
|
+
retrieveRecords?(input: {
|
|
90
|
+
memoryId: string;
|
|
91
|
+
namespace: string;
|
|
92
|
+
searchQuery: string;
|
|
93
|
+
maxResults?: number;
|
|
94
|
+
memoryStrategyId?: string;
|
|
95
|
+
}): Promise<{
|
|
96
|
+
records: readonly AgentCoreMemoryRecord[];
|
|
97
|
+
}>;
|
|
98
|
+
}
|
|
99
|
+
/** One record as `RetrieveMemoryRecords` returns it. */
|
|
100
|
+
export interface AgentCoreMemoryRecord {
|
|
101
|
+
/** AgentCore's own record id. */
|
|
102
|
+
readonly memoryRecordId: string;
|
|
103
|
+
/** The record's text content. */
|
|
104
|
+
readonly content: string;
|
|
105
|
+
/** Relevance as AgentCore scored it, when it reports one. */
|
|
106
|
+
readonly score?: number;
|
|
107
|
+
/** Which strategy produced the record (semantic, summary, user-preference, …). */
|
|
108
|
+
readonly memoryStrategyId?: string;
|
|
109
|
+
/** The namespace it was found in. */
|
|
110
|
+
readonly namespace?: string;
|
|
111
|
+
/** When AgentCore created it (unix ms), when reported. */
|
|
112
|
+
readonly createdAt?: number;
|
|
78
113
|
}
|
|
79
114
|
export interface AgentCoreStoreOptions {
|
|
80
115
|
/** AgentCore Memory ARN or id. Required. */
|
|
@@ -85,6 +120,27 @@ export interface AgentCoreStoreOptions {
|
|
|
85
120
|
readonly client?: AgentCoreLikeClient;
|
|
86
121
|
/** Page size for `listEvents`. Default 100. */
|
|
87
122
|
readonly pageSize?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Where `search()` looks. AgentCore organises extracted memory records into
|
|
125
|
+
* namespaces configured on the Memory resource's strategies (commonly
|
|
126
|
+
* something like `/strategies/{strategyId}/actors/{actorId}`).
|
|
127
|
+
*
|
|
128
|
+
* A function, because the namespace usually contains the actor: it is handed
|
|
129
|
+
* the resolved AgentCore ids for the identity being searched. Default:
|
|
130
|
+
* `/actors/{actorId}/sessions/{sessionId}` — the session's own records.
|
|
131
|
+
*/
|
|
132
|
+
readonly searchNamespace?: (scope: {
|
|
133
|
+
readonly actorId: string;
|
|
134
|
+
readonly sessionId: string;
|
|
135
|
+
}) => string;
|
|
136
|
+
/**
|
|
137
|
+
* Restrict `search()` to one extraction strategy (semantic, summary,
|
|
138
|
+
* user-preference…). Omit to search across all of them.
|
|
139
|
+
*
|
|
140
|
+
* This is the metadata filter that reaches AgentCore's own side; `tiers` /
|
|
141
|
+
* `minScore` / `k` from {@link SearchOptions} are applied to what comes back.
|
|
142
|
+
*/
|
|
143
|
+
readonly searchStrategyId?: string;
|
|
88
144
|
/** @internal Test injection — skips the SDK require entirely. */
|
|
89
145
|
readonly _client?: AgentCoreLikeClient;
|
|
90
146
|
/** @internal Test injection — the AWS SDK module (to exercise the real shim with a mock SDK). */
|
|
@@ -100,6 +156,8 @@ export declare class AgentCoreStore implements MemoryStore {
|
|
|
100
156
|
private readonly client;
|
|
101
157
|
private readonly memoryId;
|
|
102
158
|
private readonly pageSize;
|
|
159
|
+
private readonly searchNamespace;
|
|
160
|
+
private readonly searchStrategyId;
|
|
103
161
|
private closed;
|
|
104
162
|
private readonly signatures;
|
|
105
163
|
private readonly feedbackBag;
|
|
@@ -130,6 +188,33 @@ export declare class AgentCoreStore implements MemoryStore {
|
|
|
130
188
|
} | null>;
|
|
131
189
|
/** GDPR "everything for this identity, gone." No DeleteSession on AgentCore → delete every event. */
|
|
132
190
|
forget(identity: MemoryIdentity): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Server-side semantic retrieval over AgentCore's extracted memory records
|
|
193
|
+
* (`RetrieveMemoryRecords`).
|
|
194
|
+
*
|
|
195
|
+
* ── Read this before you call it ─────────────────────────────────────────
|
|
196
|
+
* **It takes TEXT, not the vector.** AgentCore embeds and ranks on its own
|
|
197
|
+
* side, so `query` — the port's vector — is unusable here, and the query it
|
|
198
|
+
* actually needs travels in `options.text`. Omit that and this method throws
|
|
199
|
+
* a corrective error naming what is missing, because the alternative is
|
|
200
|
+
* ranking nothing and handing back `[]`, which reads as "no matches" when it
|
|
201
|
+
* really means "wrong query form". Pass both and every store can serve you:
|
|
202
|
+
* local-ranking backends use the vector and ignore the text.
|
|
203
|
+
*
|
|
204
|
+
* **It searches a DIFFERENT population than `list()`.** `list` returns the
|
|
205
|
+
* events this store wrote. This returns the records AgentCore's extraction
|
|
206
|
+
* strategies derived FROM those events — summaries, semantic facts, user
|
|
207
|
+
* preferences. The ids therefore belong to AgentCore, not to entries you
|
|
208
|
+
* `put()`, and `store.get(entry.id)` will not find them. They arrive as
|
|
209
|
+
* entries so ranking code needs no special case, with `metadata.source`
|
|
210
|
+
* saying plainly where they came from.
|
|
211
|
+
*
|
|
212
|
+
* Filters: `searchStrategyId` and the namespace reach AgentCore's own side;
|
|
213
|
+
* `k`, `minScore` and `tiers` are applied to what comes back.
|
|
214
|
+
*
|
|
215
|
+
* @throws when `options.text` is absent, or when the client cannot retrieve.
|
|
216
|
+
*/
|
|
217
|
+
search<T = unknown>(identity: MemoryIdentity, query: readonly number[], options?: SearchOptions): Promise<readonly ScoredEntry<T>[]>;
|
|
133
218
|
close(): Promise<void>;
|
|
134
219
|
private ensureOpen;
|
|
135
220
|
}
|
|
@@ -143,4 +228,5 @@ export interface BedrockAgentCoreSdkModule {
|
|
|
143
228
|
readonly CreateEventCommand?: new (input: unknown) => unknown;
|
|
144
229
|
readonly ListEventsCommand?: new (input: unknown) => unknown;
|
|
145
230
|
readonly DeleteEventCommand?: new (input: unknown) => unknown;
|
|
231
|
+
readonly RetrieveMemoryRecordsCommand?: new (input: unknown) => unknown;
|
|
146
232
|
}
|
|
@@ -27,9 +27,15 @@
|
|
|
27
27
|
* AgentCore's ids are server-assigned. **O(events in session)** — fine for typical
|
|
28
28
|
* window sizes; if you need O(1) keyed access at scale, use RedisStore.
|
|
29
29
|
* • `forget` → `ListEvents` + `DeleteEvent` per event (no `DeleteSession` on AgentCore).
|
|
30
|
-
* • `search` →
|
|
30
|
+
* • `search` → `RetrieveMemoryRecords`, **text-in**. AgentCore embeds and ranks on its
|
|
31
|
+
* own side, so it takes a natural-language query; the port's `search()` takes a
|
|
32
|
+
* vector. Pass `options.text` and this store serves the query; omit it and it refuses
|
|
33
|
+
* by name rather than ranking an empty set. See `search()` below for the whole story.
|
|
31
34
|
* • `putIfVersion` / `seen` / `feedback` → in-process emulation (AgentCore has no native
|
|
32
35
|
* CAS / dedup / feedback primitive; these don't survive process restart).
|
|
36
|
+
* • no `stream()` — AgentCore Memory has no streaming data-plane operation, and the
|
|
37
|
+
* `MemoryStore` port has no streaming method to implement. Inventing one for a single
|
|
38
|
+
* backend is how a port stops being a port.
|
|
33
39
|
*
|
|
34
40
|
* Role: Outer ring. Lazy-requires the AWS SDK; zero runtime cost when another adapter is
|
|
35
41
|
* in use. Emits: N/A (storage adapters don't emit).
|
|
@@ -55,6 +61,8 @@ export class AgentCoreStore {
|
|
|
55
61
|
client;
|
|
56
62
|
memoryId;
|
|
57
63
|
pageSize;
|
|
64
|
+
searchNamespace;
|
|
65
|
+
searchStrategyId;
|
|
58
66
|
closed = false;
|
|
59
67
|
// In-process shadow state for things AgentCore doesn't surface natively.
|
|
60
68
|
signatures = new Map();
|
|
@@ -64,6 +72,10 @@ export class AgentCoreStore {
|
|
|
64
72
|
throw new Error('AgentCoreStore requires `memoryId`.');
|
|
65
73
|
this.memoryId = options.memoryId;
|
|
66
74
|
this.pageSize = options.pageSize ?? 100;
|
|
75
|
+
this.searchNamespace =
|
|
76
|
+
options.searchNamespace ??
|
|
77
|
+
(({ actorId, sessionId }) => `/actors/${actorId}/sessions/${sessionId}`);
|
|
78
|
+
this.searchStrategyId = options.searchStrategyId;
|
|
67
79
|
if (options._client)
|
|
68
80
|
this.client = options._client;
|
|
69
81
|
else if (options.client)
|
|
@@ -224,6 +236,90 @@ export class AgentCoreStore {
|
|
|
224
236
|
this.feedbackBag.delete(key);
|
|
225
237
|
}
|
|
226
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Server-side semantic retrieval over AgentCore's extracted memory records
|
|
241
|
+
* (`RetrieveMemoryRecords`).
|
|
242
|
+
*
|
|
243
|
+
* ── Read this before you call it ─────────────────────────────────────────
|
|
244
|
+
* **It takes TEXT, not the vector.** AgentCore embeds and ranks on its own
|
|
245
|
+
* side, so `query` — the port's vector — is unusable here, and the query it
|
|
246
|
+
* actually needs travels in `options.text`. Omit that and this method throws
|
|
247
|
+
* a corrective error naming what is missing, because the alternative is
|
|
248
|
+
* ranking nothing and handing back `[]`, which reads as "no matches" when it
|
|
249
|
+
* really means "wrong query form". Pass both and every store can serve you:
|
|
250
|
+
* local-ranking backends use the vector and ignore the text.
|
|
251
|
+
*
|
|
252
|
+
* **It searches a DIFFERENT population than `list()`.** `list` returns the
|
|
253
|
+
* events this store wrote. This returns the records AgentCore's extraction
|
|
254
|
+
* strategies derived FROM those events — summaries, semantic facts, user
|
|
255
|
+
* preferences. The ids therefore belong to AgentCore, not to entries you
|
|
256
|
+
* `put()`, and `store.get(entry.id)` will not find them. They arrive as
|
|
257
|
+
* entries so ranking code needs no special case, with `metadata.source`
|
|
258
|
+
* saying plainly where they came from.
|
|
259
|
+
*
|
|
260
|
+
* Filters: `searchStrategyId` and the namespace reach AgentCore's own side;
|
|
261
|
+
* `k`, `minScore` and `tiers` are applied to what comes back.
|
|
262
|
+
*
|
|
263
|
+
* @throws when `options.text` is absent, or when the client cannot retrieve.
|
|
264
|
+
*/
|
|
265
|
+
async search(identity, query, options = {}) {
|
|
266
|
+
this.ensureOpen('search');
|
|
267
|
+
const text = options.text?.trim();
|
|
268
|
+
if (!text) {
|
|
269
|
+
throw new Error('AgentCoreStore.search() needs the query as TEXT, in `options.text`.\n' +
|
|
270
|
+
` AgentCore embeds and ranks server-side (RetrieveMemoryRecords), so the ${query.length}-dimension\n` +
|
|
271
|
+
' vector this method was handed cannot be sent anywhere — and returning [] would look\n' +
|
|
272
|
+
' like "no matches" rather than "wrong query form".\n' +
|
|
273
|
+
' Fix: store.search(identity, vector, { text: theUserQuestion })\n' +
|
|
274
|
+
' Backends that rank locally ignore `text`, so passing both is always safe.');
|
|
275
|
+
}
|
|
276
|
+
if (!this.client.retrieveRecords) {
|
|
277
|
+
throw new Error('AgentCoreStore.search() requires a client that implements `retrieveRecords`.\n' +
|
|
278
|
+
' The built-in client does; a custom or older injected `client` / `_client` may not.');
|
|
279
|
+
}
|
|
280
|
+
const scope = this.scope(identity);
|
|
281
|
+
const k = options.k ?? 10;
|
|
282
|
+
const page = await this.client.retrieveRecords({
|
|
283
|
+
memoryId: this.memoryId,
|
|
284
|
+
namespace: this.searchNamespace({ actorId: scope.actorId, sessionId: scope.sessionId }),
|
|
285
|
+
searchQuery: text,
|
|
286
|
+
maxResults: k,
|
|
287
|
+
...(this.searchStrategyId !== undefined && { memoryStrategyId: this.searchStrategyId }),
|
|
288
|
+
});
|
|
289
|
+
const scored = [];
|
|
290
|
+
for (const record of page.records) {
|
|
291
|
+
const score = record.score ?? 0;
|
|
292
|
+
if (options.minScore !== undefined && score < options.minScore)
|
|
293
|
+
continue;
|
|
294
|
+
// AgentCore records carry no tier. A tier filter therefore excludes them
|
|
295
|
+
// all rather than silently ignoring the filter the caller asked for.
|
|
296
|
+
if (options.tiers && options.tiers.length > 0)
|
|
297
|
+
continue;
|
|
298
|
+
const createdAt = record.createdAt ?? Date.now();
|
|
299
|
+
const entry = {
|
|
300
|
+
id: record.memoryRecordId,
|
|
301
|
+
value: record.content,
|
|
302
|
+
version: 1,
|
|
303
|
+
createdAt,
|
|
304
|
+
updatedAt: createdAt,
|
|
305
|
+
lastAccessedAt: Date.now(),
|
|
306
|
+
accessCount: 0,
|
|
307
|
+
metadata: {
|
|
308
|
+
// Says plainly that this did not come from an event this store wrote.
|
|
309
|
+
source: 'agentcore-memory-record',
|
|
310
|
+
...(record.memoryStrategyId !== undefined && {
|
|
311
|
+
memoryStrategyId: record.memoryStrategyId,
|
|
312
|
+
}),
|
|
313
|
+
...(record.namespace !== undefined && { namespace: record.namespace }),
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
scored.push({ entry, score });
|
|
317
|
+
}
|
|
318
|
+
// AgentCore returns its own ranking; re-sort anyway so the port's
|
|
319
|
+
// "descending by score" holds even if a future API version reorders.
|
|
320
|
+
scored.sort((a, b) => b.score !== a.score ? b.score - a.score : a.entry.id < b.entry.id ? -1 : 1);
|
|
321
|
+
return scored.slice(0, k);
|
|
322
|
+
}
|
|
227
323
|
async close() {
|
|
228
324
|
if (this.closed)
|
|
229
325
|
return;
|
|
@@ -318,6 +414,34 @@ function createAgentCoreClient(region, injected) {
|
|
|
318
414
|
eventId,
|
|
319
415
|
});
|
|
320
416
|
},
|
|
417
|
+
async retrieveRecords({ memoryId, namespace, searchQuery, maxResults, memoryStrategyId }) {
|
|
418
|
+
// AgentCore nests the query under `searchCriteria`; the strategy id is the
|
|
419
|
+
// metadata filter that reaches its side rather than being applied after.
|
|
420
|
+
const r = (await send(mod.RetrieveMemoryRecordsCommand, 'RetrieveMemoryRecordsCommand', {
|
|
421
|
+
memoryId,
|
|
422
|
+
namespace,
|
|
423
|
+
searchCriteria: {
|
|
424
|
+
searchQuery,
|
|
425
|
+
...(maxResults !== undefined && { topK: maxResults }),
|
|
426
|
+
...(memoryStrategyId !== undefined && { memoryStrategyId }),
|
|
427
|
+
},
|
|
428
|
+
...(maxResults !== undefined && { maxResults }),
|
|
429
|
+
}));
|
|
430
|
+
const records = (r?.memoryRecordSummaries ?? []).map((s) => {
|
|
431
|
+
const content = typeof s.content === 'string' ? s.content : s.content?.text ?? '';
|
|
432
|
+
const namespaceValue = Array.isArray(s.namespace) ? s.namespace[0] : s.namespace;
|
|
433
|
+
const createdAt = s.createdAt === undefined ? undefined : new Date(s.createdAt).getTime();
|
|
434
|
+
return {
|
|
435
|
+
memoryRecordId: s.memoryRecordId ?? '',
|
|
436
|
+
content,
|
|
437
|
+
...(typeof s.score === 'number' && { score: s.score }),
|
|
438
|
+
...(s.memoryStrategyId !== undefined && { memoryStrategyId: s.memoryStrategyId }),
|
|
439
|
+
...(typeof namespaceValue === 'string' && { namespace: namespaceValue }),
|
|
440
|
+
...(createdAt !== undefined && Number.isFinite(createdAt) && { createdAt }),
|
|
441
|
+
};
|
|
442
|
+
});
|
|
443
|
+
return { records };
|
|
444
|
+
},
|
|
321
445
|
};
|
|
322
446
|
}
|
|
323
447
|
//# sourceMappingURL=agentcore.js.map
|