@polpo-ai/server 0.15.40 → 0.15.42
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/deps.d.ts +12 -0
- package/dist/deps.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/completions/agent-step-runner.d.ts +3 -2
- package/dist/routes/completions/agent-step-runner.d.ts.map +1 -1
- package/dist/routes/completions/agent-step-runner.js +29 -22
- package/dist/routes/completions/agent-step-runner.js.map +1 -1
- package/dist/routes/completions/chat-handler.d.ts +3 -1
- package/dist/routes/completions/chat-handler.d.ts.map +1 -1
- package/dist/routes/completions/chat-handler.js.map +1 -1
- package/dist/routes/completions/conversation-turn.d.ts +3 -1
- package/dist/routes/completions/conversation-turn.d.ts.map +1 -1
- package/dist/routes/completions/conversation-turn.js +42 -1
- package/dist/routes/completions/conversation-turn.js.map +1 -1
- package/dist/routes/completions/execution-router-runtime.test.js +74 -0
- package/dist/routes/completions/execution-router-runtime.test.js.map +1 -1
- package/dist/routes/completions/project-loop-runner.d.ts +11 -2
- package/dist/routes/completions/project-loop-runner.d.ts.map +1 -1
- package/dist/routes/completions/project-loop-runner.js +101 -3
- package/dist/routes/completions/project-loop-runner.js.map +1 -1
- package/dist/routes/completions/runtime-context-hook.test.d.ts +2 -0
- package/dist/routes/completions/runtime-context-hook.test.d.ts.map +1 -0
- package/dist/routes/completions/runtime-context-hook.test.js +250 -0
- package/dist/routes/completions/runtime-context-hook.test.js.map +1 -0
- package/dist/routes/completions.d.ts +9 -1
- package/dist/routes/completions.d.ts.map +1 -1
- package/dist/routes/completions.js +2 -0
- package/dist/routes/completions.js.map +1 -1
- package/dist/routes/memory-items.d.ts +9 -0
- package/dist/routes/memory-items.d.ts.map +1 -0
- package/dist/routes/memory-items.js +362 -0
- package/dist/routes/memory-items.js.map +1 -0
- package/dist/routes/memory-items.test.d.ts +2 -0
- package/dist/routes/memory-items.test.d.ts.map +1 -0
- package/dist/routes/memory-items.test.js +281 -0
- package/dist/routes/memory-items.test.js.map +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { MEMORY_KINDS, MEMORY_STATUSES, MemoryAuthorizationError, MemoryConflictError, MemoryContractError, MemoryPolicyError, createMemoryItem, normalizeMemoryScope, } from "@polpo-ai/core";
|
|
3
|
+
import { nanoid } from "nanoid";
|
|
4
|
+
class InvalidMemoryRequestError extends Error {
|
|
5
|
+
constructor(message = "Invalid Memory request") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "InvalidMemoryRequestError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const memoryKinds = new Set(MEMORY_KINDS);
|
|
11
|
+
const memoryStatuses = new Set(MEMORY_STATUSES);
|
|
12
|
+
const createKeys = new Set([
|
|
13
|
+
"id",
|
|
14
|
+
"scope",
|
|
15
|
+
"kind",
|
|
16
|
+
"content",
|
|
17
|
+
"summary",
|
|
18
|
+
"provenance",
|
|
19
|
+
"confidence",
|
|
20
|
+
"status",
|
|
21
|
+
"expiresAt",
|
|
22
|
+
]);
|
|
23
|
+
const patchKeys = new Set([
|
|
24
|
+
"content",
|
|
25
|
+
"summary",
|
|
26
|
+
"confidence",
|
|
27
|
+
"status",
|
|
28
|
+
"expiresAt",
|
|
29
|
+
]);
|
|
30
|
+
const listQueryKeys = new Set([
|
|
31
|
+
"kinds",
|
|
32
|
+
"statuses",
|
|
33
|
+
"scopeKind",
|
|
34
|
+
"scopeSubjectId",
|
|
35
|
+
"scopeAgentName",
|
|
36
|
+
"includeExpired",
|
|
37
|
+
"limit",
|
|
38
|
+
]);
|
|
39
|
+
const searchKeys = new Set([
|
|
40
|
+
"query",
|
|
41
|
+
"kinds",
|
|
42
|
+
"scope",
|
|
43
|
+
"tokenBudget",
|
|
44
|
+
"maxResults",
|
|
45
|
+
]);
|
|
46
|
+
function object(value, allowedKeys) {
|
|
47
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
48
|
+
throw new InvalidMemoryRequestError();
|
|
49
|
+
}
|
|
50
|
+
const record = value;
|
|
51
|
+
for (const key of Object.keys(record)) {
|
|
52
|
+
if (!allowedKeys.has(key)) {
|
|
53
|
+
throw new InvalidMemoryRequestError(`Unknown Memory field: ${key}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return record;
|
|
57
|
+
}
|
|
58
|
+
async function jsonObject(context, allowedKeys) {
|
|
59
|
+
const value = await context.req.json().catch(() => {
|
|
60
|
+
throw new InvalidMemoryRequestError();
|
|
61
|
+
});
|
|
62
|
+
return object(value, allowedKeys);
|
|
63
|
+
}
|
|
64
|
+
function agentName(context) {
|
|
65
|
+
const value = context.req.param("agentName").trim();
|
|
66
|
+
if (value.length === 0 || value.length > 128) {
|
|
67
|
+
throw new InvalidMemoryRequestError("Invalid agent name");
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function commaList(value, allowed, field) {
|
|
72
|
+
if (value === undefined)
|
|
73
|
+
return undefined;
|
|
74
|
+
const values = value.split(",").map((entry) => entry.trim());
|
|
75
|
+
if (values.length === 0
|
|
76
|
+
|| values.some((entry) => entry.length === 0 || !allowed.has(entry))) {
|
|
77
|
+
throw new InvalidMemoryRequestError(`Invalid ${field}`);
|
|
78
|
+
}
|
|
79
|
+
return [...new Set(values)];
|
|
80
|
+
}
|
|
81
|
+
function optionalBoolean(value) {
|
|
82
|
+
if (value === undefined)
|
|
83
|
+
return undefined;
|
|
84
|
+
if (value === "true")
|
|
85
|
+
return true;
|
|
86
|
+
if (value === "false")
|
|
87
|
+
return false;
|
|
88
|
+
throw new InvalidMemoryRequestError("Invalid boolean query value");
|
|
89
|
+
}
|
|
90
|
+
function optionalInteger(value, field) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (!/^(?:0|[1-9]\d*)$/u.test(value)) {
|
|
94
|
+
throw new InvalidMemoryRequestError(`Invalid ${field}`);
|
|
95
|
+
}
|
|
96
|
+
const parsed = Number(value);
|
|
97
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
98
|
+
throw new InvalidMemoryRequestError(`Invalid ${field}`);
|
|
99
|
+
}
|
|
100
|
+
return parsed;
|
|
101
|
+
}
|
|
102
|
+
function parseListQuery(context) {
|
|
103
|
+
const url = new URL(context.req.url);
|
|
104
|
+
for (const key of url.searchParams.keys()) {
|
|
105
|
+
if (!listQueryKeys.has(key)) {
|
|
106
|
+
throw new InvalidMemoryRequestError(`Unknown Memory query field: ${key}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const get = (name) => url.searchParams.get(name) ?? undefined;
|
|
110
|
+
const scopeKind = get("scopeKind");
|
|
111
|
+
const scopeSubjectId = get("scopeSubjectId");
|
|
112
|
+
const scopeAgentName = get("scopeAgentName");
|
|
113
|
+
if (!scopeKind && (scopeSubjectId || scopeAgentName)) {
|
|
114
|
+
throw new InvalidMemoryRequestError("scopeKind is required for scope filters");
|
|
115
|
+
}
|
|
116
|
+
const scope = scopeKind
|
|
117
|
+
? normalizeMemoryScope({
|
|
118
|
+
kind: scopeKind,
|
|
119
|
+
...(scopeSubjectId ? { subjectId: scopeSubjectId } : {}),
|
|
120
|
+
...(scopeAgentName ? { agentName: scopeAgentName } : {}),
|
|
121
|
+
})
|
|
122
|
+
: undefined;
|
|
123
|
+
return {
|
|
124
|
+
kinds: commaList(get("kinds"), memoryKinds, "kinds"),
|
|
125
|
+
statuses: commaList(get("statuses"), memoryStatuses, "statuses"),
|
|
126
|
+
...(scope ? { scope } : {}),
|
|
127
|
+
includeExpired: optionalBoolean(get("includeExpired")),
|
|
128
|
+
limit: optionalInteger(get("limit"), "limit"),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function parseSearchQuery(value) {
|
|
132
|
+
const input = object(value, searchKeys);
|
|
133
|
+
if (typeof input.query !== "string") {
|
|
134
|
+
throw new InvalidMemoryRequestError("Memory search query must be a string");
|
|
135
|
+
}
|
|
136
|
+
if (input.kinds !== undefined
|
|
137
|
+
&& (!Array.isArray(input.kinds)
|
|
138
|
+
|| input.kinds.some((kind) => typeof kind !== "string" || !memoryKinds.has(kind)))) {
|
|
139
|
+
throw new InvalidMemoryRequestError("Invalid Memory kinds");
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
query: input.query,
|
|
143
|
+
...(input.kinds === undefined
|
|
144
|
+
? {}
|
|
145
|
+
: { kinds: input.kinds }),
|
|
146
|
+
...(input.scope === undefined
|
|
147
|
+
? {}
|
|
148
|
+
: { scope: normalizeMemoryScope(input.scope) }),
|
|
149
|
+
...(input.tokenBudget === undefined
|
|
150
|
+
? {}
|
|
151
|
+
: { tokenBudget: input.tokenBudget }),
|
|
152
|
+
...(input.maxResults === undefined
|
|
153
|
+
? {}
|
|
154
|
+
: { maxResults: input.maxResults }),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parsePatch(value) {
|
|
158
|
+
const input = object(value, patchKeys);
|
|
159
|
+
if (Object.keys(input).length === 0) {
|
|
160
|
+
throw new InvalidMemoryRequestError("Memory patch cannot be empty");
|
|
161
|
+
}
|
|
162
|
+
return input;
|
|
163
|
+
}
|
|
164
|
+
function timestamp(value) {
|
|
165
|
+
const parsed = value instanceof Date ? value : new Date(value);
|
|
166
|
+
if (!Number.isFinite(parsed.getTime())) {
|
|
167
|
+
throw new MemoryContractError("Memory operation time must be valid");
|
|
168
|
+
}
|
|
169
|
+
return parsed.toISOString();
|
|
170
|
+
}
|
|
171
|
+
async function appendUsage(deps, store, context, memoryId, type) {
|
|
172
|
+
const event = {
|
|
173
|
+
id: deps.createUsageId?.() ?? `memory-usage-${nanoid(16)}`,
|
|
174
|
+
memoryId,
|
|
175
|
+
type,
|
|
176
|
+
at: timestamp(deps.now?.() ?? context.now ?? new Date()),
|
|
177
|
+
};
|
|
178
|
+
try {
|
|
179
|
+
await store.appendUsage(event, context);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
try {
|
|
183
|
+
await deps.onUsageError?.(error, event);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Usage telemetry cannot change the completed Memory operation.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function invalidResponse(context) {
|
|
191
|
+
return context.json({
|
|
192
|
+
ok: false,
|
|
193
|
+
error: "Invalid Memory request",
|
|
194
|
+
code: "INVALID_MEMORY_REQUEST",
|
|
195
|
+
}, 400);
|
|
196
|
+
}
|
|
197
|
+
function errorResponse(context, error) {
|
|
198
|
+
if (error instanceof MemoryAuthorizationError) {
|
|
199
|
+
return context.json({
|
|
200
|
+
ok: false,
|
|
201
|
+
error: "Memory access denied",
|
|
202
|
+
code: "MEMORY_FORBIDDEN",
|
|
203
|
+
}, 403);
|
|
204
|
+
}
|
|
205
|
+
if (error instanceof MemoryConflictError) {
|
|
206
|
+
const equivalent = error.message.startsWith("An equivalent Memory item");
|
|
207
|
+
return context.json({
|
|
208
|
+
ok: false,
|
|
209
|
+
error: equivalent
|
|
210
|
+
? "An equivalent Memory item already exists"
|
|
211
|
+
: "Memory conflict",
|
|
212
|
+
code: "MEMORY_CONFLICT",
|
|
213
|
+
}, 409);
|
|
214
|
+
}
|
|
215
|
+
if (error instanceof MemoryPolicyError) {
|
|
216
|
+
return context.json({
|
|
217
|
+
ok: false,
|
|
218
|
+
error: "Memory write denied by policy",
|
|
219
|
+
code: "MEMORY_POLICY_DENIED",
|
|
220
|
+
}, 422);
|
|
221
|
+
}
|
|
222
|
+
if (error instanceof MemoryContractError
|
|
223
|
+
|| error instanceof InvalidMemoryRequestError) {
|
|
224
|
+
return invalidResponse(context);
|
|
225
|
+
}
|
|
226
|
+
return context.json({
|
|
227
|
+
ok: false,
|
|
228
|
+
error: "Memory operation failed",
|
|
229
|
+
code: "MEMORY_OPERATION_FAILED",
|
|
230
|
+
}, 500);
|
|
231
|
+
}
|
|
232
|
+
async function resolve(context, getDeps) {
|
|
233
|
+
try {
|
|
234
|
+
const agent = agentName(context);
|
|
235
|
+
const deps = getDeps(context);
|
|
236
|
+
if (!deps.memoryItemStore || typeof deps.resolveMemoryContext !== "function") {
|
|
237
|
+
return context.json({
|
|
238
|
+
ok: false,
|
|
239
|
+
error: "Typed Memory is not available",
|
|
240
|
+
code: "MEMORY_UNAVAILABLE",
|
|
241
|
+
}, 503);
|
|
242
|
+
}
|
|
243
|
+
const memoryContext = await deps.resolveMemoryContext(agent, context);
|
|
244
|
+
if (memoryContext.access.agentName !== agent) {
|
|
245
|
+
throw new MemoryAuthorizationError();
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
deps,
|
|
249
|
+
store: deps.memoryItemStore,
|
|
250
|
+
memoryContext,
|
|
251
|
+
agent,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
return errorResponse(context, error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Host-neutral typed Memory API. Authentication and tenant/external-user
|
|
260
|
+
* scoping are supplied by the composition root through `resolveMemoryContext`.
|
|
261
|
+
*/
|
|
262
|
+
export function memoryItemRoutes(getDeps) {
|
|
263
|
+
const app = new Hono();
|
|
264
|
+
app.get("/agents/:agentName/memory/items", async (context) => {
|
|
265
|
+
const resolved = await resolve(context, getDeps);
|
|
266
|
+
if (resolved instanceof Response)
|
|
267
|
+
return resolved;
|
|
268
|
+
try {
|
|
269
|
+
const items = await resolved.store.list(parseListQuery(context), resolved.memoryContext);
|
|
270
|
+
return context.json({ ok: true, data: { items } }, 200);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
return errorResponse(context, error);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
app.post("/agents/:agentName/memory/items", async (context) => {
|
|
277
|
+
const resolved = await resolve(context, getDeps);
|
|
278
|
+
if (resolved instanceof Response)
|
|
279
|
+
return resolved;
|
|
280
|
+
try {
|
|
281
|
+
const input = await jsonObject(context, createKeys);
|
|
282
|
+
const item = createMemoryItem(input, {
|
|
283
|
+
createId: resolved.deps.createId,
|
|
284
|
+
now: resolved.deps.now,
|
|
285
|
+
});
|
|
286
|
+
const duplicate = await resolved.store.findDedupeCandidate(item, resolved.memoryContext);
|
|
287
|
+
if (duplicate) {
|
|
288
|
+
throw new MemoryConflictError("An equivalent Memory item already exists");
|
|
289
|
+
}
|
|
290
|
+
const created = await resolved.store.create(item, resolved.memoryContext);
|
|
291
|
+
await appendUsage(resolved.deps, resolved.store, resolved.memoryContext, created.id, "written");
|
|
292
|
+
return context.json({ ok: true, data: { item: created } }, 201);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
return errorResponse(context, error);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
app.post("/agents/:agentName/memory/search", async (context) => {
|
|
299
|
+
const resolved = await resolve(context, getDeps);
|
|
300
|
+
if (resolved instanceof Response)
|
|
301
|
+
return resolved;
|
|
302
|
+
try {
|
|
303
|
+
const input = await jsonObject(context, searchKeys);
|
|
304
|
+
const results = await resolved.store.search(parseSearchQuery(input), resolved.memoryContext);
|
|
305
|
+
for (const result of results) {
|
|
306
|
+
await appendUsage(resolved.deps, resolved.store, resolved.memoryContext, result.item.id, "retrieved");
|
|
307
|
+
}
|
|
308
|
+
return context.json({ ok: true, data: { results } }, 200);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
return errorResponse(context, error);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
app.patch("/agents/:agentName/memory/items/:itemId", async (context) => {
|
|
315
|
+
const resolved = await resolve(context, getDeps);
|
|
316
|
+
if (resolved instanceof Response)
|
|
317
|
+
return resolved;
|
|
318
|
+
try {
|
|
319
|
+
const itemId = context.req.param("itemId");
|
|
320
|
+
const patch = parsePatch(await jsonObject(context, patchKeys));
|
|
321
|
+
const item = await resolved.store.update(itemId, patch, resolved.memoryContext);
|
|
322
|
+
if (!item) {
|
|
323
|
+
return context.json({
|
|
324
|
+
ok: false,
|
|
325
|
+
error: "Memory item not found",
|
|
326
|
+
code: "MEMORY_NOT_FOUND",
|
|
327
|
+
}, 404);
|
|
328
|
+
}
|
|
329
|
+
await appendUsage(resolved.deps, resolved.store, resolved.memoryContext, item.id, "updated");
|
|
330
|
+
return context.json({ ok: true, data: { item } }, 200);
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
return errorResponse(context, error);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
app.delete("/agents/:agentName/memory/items/:itemId", async (context) => {
|
|
337
|
+
const resolved = await resolve(context, getDeps);
|
|
338
|
+
if (resolved instanceof Response)
|
|
339
|
+
return resolved;
|
|
340
|
+
try {
|
|
341
|
+
const itemId = context.req.param("itemId");
|
|
342
|
+
const forgotten = await resolved.store.forget(itemId, resolved.memoryContext);
|
|
343
|
+
if (!forgotten) {
|
|
344
|
+
return context.json({
|
|
345
|
+
ok: false,
|
|
346
|
+
error: "Memory item not found",
|
|
347
|
+
code: "MEMORY_NOT_FOUND",
|
|
348
|
+
}, 404);
|
|
349
|
+
}
|
|
350
|
+
await appendUsage(resolved.deps, resolved.store, resolved.memoryContext, itemId, "forgotten");
|
|
351
|
+
return context.json({
|
|
352
|
+
ok: true,
|
|
353
|
+
data: { forgotten: true, itemId },
|
|
354
|
+
}, 200);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
return errorResponse(context, error);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return app;
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=memory-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-items.js","sourceRoot":"","sources":["../../src/routes/memory-items.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-items.test.d.ts","sourceRoot":"","sources":["../../src/routes/memory-items.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { InMemoryMemoryItemStore, MemoryAuthorizationError, } from "@polpo-ai/core";
|
|
3
|
+
import { memoryItemRoutes, } from "./memory-items.js";
|
|
4
|
+
const now = "2026-07-28T10:00:00.000Z";
|
|
5
|
+
function context(namespace = "project-a", externalUserId = "user-a") {
|
|
6
|
+
return {
|
|
7
|
+
namespace,
|
|
8
|
+
access: {
|
|
9
|
+
projectId: namespace,
|
|
10
|
+
agentName: "support",
|
|
11
|
+
externalUserId,
|
|
12
|
+
sessionId: "session-a",
|
|
13
|
+
},
|
|
14
|
+
surface: "api",
|
|
15
|
+
now,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createApp(store = new InMemoryMemoryItemStore(), resolveContext = () => context(), overrides = {}) {
|
|
19
|
+
let nextId = 0;
|
|
20
|
+
return memoryItemRoutes(() => ({
|
|
21
|
+
memoryItemStore: store ?? undefined,
|
|
22
|
+
resolveMemoryContext: resolveContext,
|
|
23
|
+
createId: () => `memory-${++nextId}`,
|
|
24
|
+
createUsageId: () => `usage-${++nextId}`,
|
|
25
|
+
now: () => now,
|
|
26
|
+
...overrides,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
function json(method, body) {
|
|
30
|
+
return {
|
|
31
|
+
method,
|
|
32
|
+
headers: { "content-type": "application/json" },
|
|
33
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function itemInput(overrides = {}) {
|
|
37
|
+
return {
|
|
38
|
+
scope: { kind: "user", subjectId: "user-a", agentName: "support" },
|
|
39
|
+
kind: "preference",
|
|
40
|
+
content: "Prefers concise weekly summaries.",
|
|
41
|
+
provenance: {
|
|
42
|
+
source: "explicit",
|
|
43
|
+
actor: "user",
|
|
44
|
+
sourceId: "request-a",
|
|
45
|
+
},
|
|
46
|
+
...overrides,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function createItem(app, overrides = {}) {
|
|
50
|
+
const response = await app.request("/agents/support/memory/items", json("POST", itemInput(overrides)));
|
|
51
|
+
return {
|
|
52
|
+
response,
|
|
53
|
+
body: await response.json(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
describe("memoryItemRoutes", () => {
|
|
57
|
+
it("is host-neutral and unavailable until a store is explicitly wired", async () => {
|
|
58
|
+
const response = await createApp(null).request("/agents/support/memory/items");
|
|
59
|
+
expect(response.status).toBe(503);
|
|
60
|
+
expect(await response.json()).toEqual({
|
|
61
|
+
ok: false,
|
|
62
|
+
error: "Typed Memory is not available",
|
|
63
|
+
code: "MEMORY_UNAVAILABLE",
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it("creates, lists, searches, updates, and softly forgets typed items", async () => {
|
|
67
|
+
const app = createApp();
|
|
68
|
+
const created = await createItem(app);
|
|
69
|
+
expect(created.response.status).toBe(201);
|
|
70
|
+
expect(created.body.data.item).toMatchObject({
|
|
71
|
+
id: "memory-1",
|
|
72
|
+
kind: "preference",
|
|
73
|
+
content: "Prefers concise weekly summaries.",
|
|
74
|
+
status: "active",
|
|
75
|
+
});
|
|
76
|
+
const listed = await app.request("/agents/support/memory/items?kinds=preference&scopeKind=user"
|
|
77
|
+
+ "&scopeSubjectId=user-a&scopeAgentName=support&limit=10");
|
|
78
|
+
expect(listed.status).toBe(200);
|
|
79
|
+
expect((await listed.json()).data.items).toHaveLength(1);
|
|
80
|
+
const searched = await app.request("/agents/support/memory/search", json("POST", { query: "weekly summary", tokenBudget: 100, maxResults: 5 }));
|
|
81
|
+
const searchBody = await searched.json();
|
|
82
|
+
expect(searched.status).toBe(200);
|
|
83
|
+
expect(searchBody.data.results).toHaveLength(1);
|
|
84
|
+
expect(searchBody.data.results[0].item.id).toBe("memory-1");
|
|
85
|
+
const updated = await app.request("/agents/support/memory/items/memory-1", json("PATCH", { summary: "Weekly summaries", confidence: 0.9 }));
|
|
86
|
+
expect(updated.status).toBe(200);
|
|
87
|
+
expect((await updated.json()).data.item).toMatchObject({
|
|
88
|
+
summary: "Weekly summaries",
|
|
89
|
+
confidence: 0.9,
|
|
90
|
+
});
|
|
91
|
+
const forgotten = await app.request("/agents/support/memory/items/memory-1", { method: "DELETE" });
|
|
92
|
+
expect(forgotten.status).toBe(200);
|
|
93
|
+
expect(await forgotten.json()).toMatchObject({
|
|
94
|
+
ok: true,
|
|
95
|
+
data: { forgotten: true, itemId: "memory-1" },
|
|
96
|
+
});
|
|
97
|
+
const afterDelete = await app.request("/agents/support/memory/search", json("POST", { query: "weekly" }));
|
|
98
|
+
expect((await afterDelete.json()).data.results).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
it("records retrieval usage without exposing it in the search payload", async () => {
|
|
101
|
+
const store = new InMemoryMemoryItemStore();
|
|
102
|
+
const app = createApp(store);
|
|
103
|
+
await createItem(app);
|
|
104
|
+
await app.request("/agents/support/memory/search", json("POST", { query: "weekly" }));
|
|
105
|
+
expect(await store.listUsage("memory-1", context())).toEqual([
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
memoryId: "memory-1",
|
|
108
|
+
type: "written",
|
|
109
|
+
at: now,
|
|
110
|
+
}),
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
memoryId: "memory-1",
|
|
113
|
+
type: "retrieved",
|
|
114
|
+
at: now,
|
|
115
|
+
}),
|
|
116
|
+
]);
|
|
117
|
+
});
|
|
118
|
+
it("returns 409 for a duplicate semantic memory instead of persisting it twice", async () => {
|
|
119
|
+
const app = createApp();
|
|
120
|
+
expect((await createItem(app)).response.status).toBe(201);
|
|
121
|
+
const duplicate = await createItem(app, { id: "another-id" });
|
|
122
|
+
expect(duplicate.response.status).toBe(409);
|
|
123
|
+
expect(duplicate.body).toEqual({
|
|
124
|
+
ok: false,
|
|
125
|
+
error: "An equivalent Memory item already exists",
|
|
126
|
+
code: "MEMORY_CONFLICT",
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it.each([
|
|
130
|
+
["invalid JSON", "{", 400],
|
|
131
|
+
["empty body", undefined, 400],
|
|
132
|
+
["unknown kind", itemInput({ kind: "guess" }), 400],
|
|
133
|
+
["empty content", itemInput({ content: " " }), 400],
|
|
134
|
+
[
|
|
135
|
+
"invalid confidence",
|
|
136
|
+
itemInput({ confidence: 2 }),
|
|
137
|
+
400,
|
|
138
|
+
],
|
|
139
|
+
])("rejects %s without touching the store", async (_name, body, status) => {
|
|
140
|
+
const app = createApp();
|
|
141
|
+
const response = await app.request("/agents/support/memory/items", body === "{" ? {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: { "content-type": "application/json" },
|
|
144
|
+
body,
|
|
145
|
+
} : json("POST", body));
|
|
146
|
+
expect(response.status).toBe(status);
|
|
147
|
+
expect(await response.json()).toMatchObject({
|
|
148
|
+
ok: false,
|
|
149
|
+
code: "INVALID_MEMORY_REQUEST",
|
|
150
|
+
});
|
|
151
|
+
const listed = await app.request("/agents/support/memory/items");
|
|
152
|
+
expect((await listed.json()).data.items).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
it("fails closed on sensitive content and never echoes the secret", async () => {
|
|
155
|
+
const secret = "neutral-fixture-value-12345";
|
|
156
|
+
const app = createApp();
|
|
157
|
+
const response = await app.request("/agents/support/memory/items", json("POST", itemInput({ content: `api_key: ${secret}` })));
|
|
158
|
+
const raw = await response.text();
|
|
159
|
+
expect(response.status).toBe(422);
|
|
160
|
+
expect(raw).not.toContain(secret);
|
|
161
|
+
expect(JSON.parse(raw)).toEqual({
|
|
162
|
+
ok: false,
|
|
163
|
+
error: "Memory write denied by policy",
|
|
164
|
+
code: "MEMORY_POLICY_DENIED",
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
it("does not leak items across namespaces or external users", async () => {
|
|
168
|
+
const store = new InMemoryMemoryItemStore();
|
|
169
|
+
const projectA = createApp(store, () => context("project-a", "same-user"));
|
|
170
|
+
const projectB = createApp(store, () => context("project-b", "same-user"));
|
|
171
|
+
const anotherUser = createApp(store, () => context("project-a", "user-b"));
|
|
172
|
+
await createItem(projectA, {
|
|
173
|
+
scope: {
|
|
174
|
+
kind: "user",
|
|
175
|
+
subjectId: "same-user",
|
|
176
|
+
agentName: "support",
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
for (const app of [projectB, anotherUser]) {
|
|
180
|
+
const listed = await app.request("/agents/support/memory/items");
|
|
181
|
+
expect((await listed.json()).data.items).toEqual([]);
|
|
182
|
+
const update = await app.request("/agents/support/memory/items/memory-1", json("PATCH", { content: "stolen" }));
|
|
183
|
+
expect(update.status).toBe(404);
|
|
184
|
+
const remove = await app.request("/agents/support/memory/items/memory-1", { method: "DELETE" });
|
|
185
|
+
expect(remove.status).toBe(404);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
it("maps host authorization denial without invoking the store", async () => {
|
|
189
|
+
const app = createApp(new InMemoryMemoryItemStore(), () => {
|
|
190
|
+
throw new MemoryAuthorizationError("denied by host");
|
|
191
|
+
});
|
|
192
|
+
const response = await app.request("/agents/support/memory/items");
|
|
193
|
+
expect(response.status).toBe(403);
|
|
194
|
+
expect(await response.json()).toEqual({
|
|
195
|
+
ok: false,
|
|
196
|
+
error: "Memory access denied",
|
|
197
|
+
code: "MEMORY_FORBIDDEN",
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
it("rejects an agent name mismatch between route and resolved context", async () => {
|
|
201
|
+
const app = createApp(new InMemoryMemoryItemStore(), () => ({
|
|
202
|
+
...context(),
|
|
203
|
+
access: { ...context().access, agentName: "different-agent" },
|
|
204
|
+
}));
|
|
205
|
+
const response = await app.request("/agents/support/memory/items");
|
|
206
|
+
expect(response.status).toBe(403);
|
|
207
|
+
expect(await response.json()).toMatchObject({
|
|
208
|
+
ok: false,
|
|
209
|
+
code: "MEMORY_FORBIDDEN",
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
it("returns 404 for invisible or missing updates and deletes", async () => {
|
|
213
|
+
const app = createApp();
|
|
214
|
+
const update = await app.request("/agents/support/memory/items/missing", json("PATCH", { content: "new" }));
|
|
215
|
+
const remove = await app.request("/agents/support/memory/items/missing", { method: "DELETE" });
|
|
216
|
+
expect(update.status).toBe(404);
|
|
217
|
+
expect(remove.status).toBe(404);
|
|
218
|
+
});
|
|
219
|
+
it.each([
|
|
220
|
+
["non-string query", { query: 42 }],
|
|
221
|
+
["non-array kinds", { query: "weekly", kinds: "preference" }],
|
|
222
|
+
["negative token budget", { query: "weekly", tokenBudget: -1 }],
|
|
223
|
+
["client-controlled clock", { query: "weekly", now: "2020-01-01" }],
|
|
224
|
+
])("maps malformed search input (%s) to 400", async (_name, body) => {
|
|
225
|
+
const response = await createApp().request("/agents/support/memory/search", json("POST", body));
|
|
226
|
+
expect(response.status).toBe(400);
|
|
227
|
+
expect(await response.json()).toMatchObject({
|
|
228
|
+
ok: false,
|
|
229
|
+
code: "INVALID_MEMORY_REQUEST",
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
it("keeps a completed operation successful when usage telemetry fails", async () => {
|
|
233
|
+
class UsageFailureStore extends InMemoryMemoryItemStore {
|
|
234
|
+
async appendUsage() {
|
|
235
|
+
throw new Error("usage backend unavailable");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const errors = [];
|
|
239
|
+
const store = new UsageFailureStore();
|
|
240
|
+
const app = createApp(store, () => context(), {
|
|
241
|
+
onUsageError: (error) => {
|
|
242
|
+
errors.push(error);
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
const created = await createItem(app);
|
|
246
|
+
expect(created.response.status).toBe(201);
|
|
247
|
+
expect(errors).toHaveLength(1);
|
|
248
|
+
expect(await store.list({}, context())).toHaveLength(1);
|
|
249
|
+
});
|
|
250
|
+
it("redacts unexpected store errors instead of reflecting provider details", async () => {
|
|
251
|
+
const secret = "database-password-super-secret";
|
|
252
|
+
class FailingStore extends InMemoryMemoryItemStore {
|
|
253
|
+
async list() {
|
|
254
|
+
throw new Error(`Provider failed with ${secret}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const response = await createApp(new FailingStore()).request("/agents/support/memory/items");
|
|
258
|
+
const raw = await response.text();
|
|
259
|
+
expect(response.status).toBe(500);
|
|
260
|
+
expect(raw).not.toContain(secret);
|
|
261
|
+
expect(JSON.parse(raw)).toEqual({
|
|
262
|
+
ok: false,
|
|
263
|
+
error: "Memory operation failed",
|
|
264
|
+
code: "MEMORY_OPERATION_FAILED",
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it("decodes agent names and binds them to the resolved context", async () => {
|
|
268
|
+
let resolvedAgent = "";
|
|
269
|
+
const app = createApp(new InMemoryMemoryItemStore(), (agent) => {
|
|
270
|
+
resolvedAgent = agent;
|
|
271
|
+
return {
|
|
272
|
+
...context(),
|
|
273
|
+
access: { ...context().access, agentName: agent },
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
const response = await app.request("/agents/support%20eu/memory/items");
|
|
277
|
+
expect(response.status).toBe(200);
|
|
278
|
+
expect(resolvedAgent).toBe("support eu");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=memory-items.test.js.map
|