agent-relay-server 0.32.1 → 0.32.3

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.
Files changed (97) hide show
  1. package/docs/openapi.json +57 -127
  2. package/package.json +1 -1
  3. package/public/assets/{activity-C6nbfryG.js → activity-DT1JGHnp.js} +2 -2
  4. package/public/assets/{activity-C6nbfryG.js.map → activity-DT1JGHnp.js.map} +1 -1
  5. package/public/assets/{agent-profiles-FEITAgHs.js → agent-profiles-CrMemMkZ.js} +2 -2
  6. package/public/assets/{agent-profiles-FEITAgHs.js.map → agent-profiles-CrMemMkZ.js.map} +1 -1
  7. package/public/assets/{agents-D4S0yIbe.js → agents-Bl-rrgOy.js} +2 -2
  8. package/public/assets/{agents-D4S0yIbe.js.map → agents-Bl-rrgOy.js.map} +1 -1
  9. package/public/assets/{analytics-DM2g62T_.js → analytics-a663ak56.js} +2 -2
  10. package/public/assets/{analytics-DM2g62T_.js.map → analytics-a663ak56.js.map} +1 -1
  11. package/public/assets/{automation-3D2pQa1C.js → automation-CiaLThdO.js} +2 -2
  12. package/public/assets/{automation-3D2pQa1C.js.map → automation-CiaLThdO.js.map} +1 -1
  13. package/public/assets/{branch-state-badge-Bi4IbkOZ.js → branch-state-badge-D4ur3m3_.js} +2 -2
  14. package/public/assets/{branch-state-badge-Bi4IbkOZ.js.map → branch-state-badge-D4ur3m3_.js.map} +1 -1
  15. package/public/assets/{channels-QNp7zmA_.js → channels-o9KLTHoK.js} +2 -2
  16. package/public/assets/{channels-QNp7zmA_.js.map → channels-o9KLTHoK.js.map} +1 -1
  17. package/public/assets/{chat-jeXt_SFs.js → chat-5hvHZcAe.js} +2 -2
  18. package/public/assets/{chat-jeXt_SFs.js.map → chat-5hvHZcAe.js.map} +1 -1
  19. package/public/assets/{connectors-BGJARDui.js → connectors-CdC806mA.js} +2 -2
  20. package/public/assets/{connectors-BGJARDui.js.map → connectors-CdC806mA.js.map} +1 -1
  21. package/public/assets/{formatted-body-impl-B7FgqkYL.js → formatted-body-impl-Ca74OAEH.js} +2 -2
  22. package/public/assets/{formatted-body-impl-B7FgqkYL.js.map → formatted-body-impl-Ca74OAEH.js.map} +1 -1
  23. package/public/assets/{index-2m9mT8kV.js → index-C_33ymaw.js} +6 -6
  24. package/public/assets/{index-2m9mT8kV.js.map → index-C_33ymaw.js.map} +1 -1
  25. package/public/assets/{integrations-CJm8-FcG.js → integrations-1nxMizDY.js} +2 -2
  26. package/public/assets/{integrations-CJm8-FcG.js.map → integrations-1nxMizDY.js.map} +1 -1
  27. package/public/assets/{maintenance-CBvZrVAG.js → maintenance-DiFNzNPN.js} +2 -2
  28. package/public/assets/{maintenance-CBvZrVAG.js.map → maintenance-DiFNzNPN.js.map} +1 -1
  29. package/public/assets/{managed-agents-Dcmm8YKt.js → managed-agents-Do3dKvfj.js} +2 -2
  30. package/public/assets/{managed-agents-Dcmm8YKt.js.map → managed-agents-Do3dKvfj.js.map} +1 -1
  31. package/public/assets/{markdown-preview-impl-7xjqdiEu.js → markdown-preview-impl-CLA0J255.js} +2 -2
  32. package/public/assets/{markdown-preview-impl-7xjqdiEu.js.map → markdown-preview-impl-CLA0J255.js.map} +1 -1
  33. package/public/assets/{memory-BmGNW61h.js → memory-IjwqFzBd.js} +2 -2
  34. package/public/assets/{memory-BmGNW61h.js.map → memory-IjwqFzBd.js.map} +1 -1
  35. package/public/assets/{messages-BvMMhoy-.js → messages-DjvWqHyn.js} +2 -2
  36. package/public/assets/{messages-BvMMhoy-.js.map → messages-DjvWqHyn.js.map} +1 -1
  37. package/public/assets/{orchestrators-DsstaupT.js → orchestrators-D2IqDxDT.js} +2 -2
  38. package/public/assets/{orchestrators-DsstaupT.js.map → orchestrators-D2IqDxDT.js.map} +1 -1
  39. package/public/assets/{overview-kK6PTce3.js → overview-DKC3TbAh.js} +2 -2
  40. package/public/assets/{overview-kK6PTce3.js.map → overview-DKC3TbAh.js.map} +1 -1
  41. package/public/assets/{pairs-BEFvTW6X.js → pairs-WpKCPE1n.js} +2 -2
  42. package/public/assets/{pairs-BEFvTW6X.js.map → pairs-WpKCPE1n.js.map} +1 -1
  43. package/public/assets/{security-Dc5wZwv0.js → security-BF7ZtPQe.js} +2 -2
  44. package/public/assets/{security-Dc5wZwv0.js.map → security-BF7ZtPQe.js.map} +1 -1
  45. package/public/assets/{settings-CEtJrORa.js → settings-CQnjrTa-.js} +2 -2
  46. package/public/assets/{settings-CEtJrORa.js.map → settings-CQnjrTa-.js.map} +1 -1
  47. package/public/assets/{store-DkmReBlH.js → store-C9VcSo05.js} +2 -2
  48. package/public/assets/{store-DkmReBlH.js.map → store-C9VcSo05.js.map} +1 -1
  49. package/public/assets/{tasks-pQKtxqeV.js → tasks-CbN_GSSb.js} +2 -2
  50. package/public/assets/{tasks-pQKtxqeV.js.map → tasks-CbN_GSSb.js.map} +1 -1
  51. package/public/assets/{terminal-viewer-impl-Cc769mYy.js → terminal-viewer-impl-BJRohThT.js} +2 -2
  52. package/public/assets/{terminal-viewer-impl-Cc769mYy.js.map → terminal-viewer-impl-BJRohThT.js.map} +1 -1
  53. package/public/assets/{work-queue-DjAanr02.js → work-queue-C5xLBLmm.js} +2 -2
  54. package/public/assets/{work-queue-DjAanr02.js.map → work-queue-C5xLBLmm.js.map} +1 -1
  55. package/public/assets/{workspaces-DLBNyR4k.js → workspaces-D91H3wDX.js} +2 -2
  56. package/public/assets/{workspaces-DLBNyR4k.js.map → workspaces-D91H3wDX.js.map} +1 -1
  57. package/public/index.html +2 -2
  58. package/scripts/orchestrator-spawn-smoke.ts +2 -1
  59. package/src/automations.ts +2 -4
  60. package/src/managed-policy.ts +2 -4
  61. package/src/mcp.ts +3 -3
  62. package/src/ratchet-files.ts +37 -0
  63. package/src/routes/_shared.ts +376 -0
  64. package/src/routes/activity.ts +61 -0
  65. package/src/routes/agent-profiles.ts +47 -0
  66. package/src/routes/agent-sessions.ts +488 -0
  67. package/src/routes/agents-spawn.ts +274 -0
  68. package/src/routes/agents.ts +251 -0
  69. package/src/routes/artifacts.ts +226 -0
  70. package/src/routes/automations.ts +83 -0
  71. package/src/routes/commands.ts +317 -0
  72. package/src/routes/config.ts +66 -0
  73. package/src/routes/connectors.ts +108 -0
  74. package/src/routes/inbox.ts +142 -0
  75. package/src/routes/index.ts +293 -0
  76. package/src/routes/insights.ts +81 -0
  77. package/src/routes/integrations.ts +592 -0
  78. package/src/routes/memory.ts +337 -0
  79. package/src/routes/messages.ts +529 -0
  80. package/src/routes/orchestrator-bootstrap.ts +100 -0
  81. package/src/routes/orchestrator-proxy.ts +160 -0
  82. package/src/routes/orchestrator.ts +490 -0
  83. package/src/routes/pairs.ts +197 -0
  84. package/src/routes/provider-config.ts +112 -0
  85. package/src/routes/recipes.ts +113 -0
  86. package/src/routes/spawn-policy.ts +231 -0
  87. package/src/routes/spec.ts +54 -0
  88. package/src/routes/sse.ts +9 -0
  89. package/src/routes/stats.ts +32 -0
  90. package/src/routes/steward.ts +45 -0
  91. package/src/routes/tasks.ts +174 -0
  92. package/src/routes/tokens.ts +311 -0
  93. package/src/routes/workspaces.ts +355 -0
  94. package/src/routes.ts +3 -6892
  95. package/src/runtime-tokens.ts +17 -8
  96. package/src/security.ts +0 -2
  97. package/src/validation.ts +134 -0
@@ -0,0 +1,337 @@
1
+ // Auto-split from routes.ts (#299). Domain: memory.
2
+ import { MAX_BODY_BYTES } from "../config";
3
+ import { ValidationError, getAgent } from "../db";
4
+ import { assertMemoryCreateAllowed, assertMemoryUpdateAllowed } from "../memory-security";
5
+ import { authorizeRoute, emitCommand, error, json, memoryContext, memoryErrorResponse, parseBody, type Handler } from "./_shared";
6
+ import { cleanParams, cleanString, cleanStringArray, optionalEnum } from "../validation";
7
+ import { getComponentAuth } from "../security";
8
+ import { injectMemoryContext, memoryBroker, memoryBrokerConfig } from "../memory-service";
9
+ import { isRecord } from "agent-relay-sdk";
10
+ import { type ContextBudget, type CreateMemoryInput, type MemoryBrokerContext, type MemoryConfidence, type MemoryQuery, type MemoryRedactionState, type MemorySensitivity, type MemoryType, type MemoryVisibility, type TaskRoutingHints, type UpdateMemoryInput } from "../types";
11
+
12
+ const VALID_MEMORY_TYPES = ["organization", "role", "project", "task", "interaction", "agent"] as const;
13
+
14
+ const VALID_MEMORY_VISIBILITIES = ["private", "project", "org", "public"] as const;
15
+
16
+ const VALID_MEMORY_SENSITIVITIES = ["public", "normal", "sensitive", "secret"] as const;
17
+
18
+ const VALID_MEMORY_CONFIDENCES = ["reported", "inferred", "verified"] as const;
19
+
20
+ const VALID_MEMORY_REDACTION_STATES = ["raw", "redacted", "rejected"] as const;
21
+
22
+ function cleanMemoryQueryFromParams(params: URLSearchParams): MemoryQuery {
23
+ const query: MemoryQuery = {};
24
+ const type = params.get("type");
25
+ const scope = params.get("scope");
26
+ const visibility = params.get("visibility");
27
+ const minRelevance = params.get("minRelevance");
28
+ const limit = params.get("limit");
29
+ if (type) query.type = optionalEnum(type, "type", VALID_MEMORY_TYPES) as MemoryType;
30
+ if (scope) query.scope = cleanString(scope, "scope", { max: 240 });
31
+ const tags = [...params.getAll("tag"), ...(params.get("tags")?.split(",") ?? [])].map((tag) => tag.trim()).filter(Boolean);
32
+ if (tags.length) query.tags = cleanStringArray(tags, "tags", { itemMax: 80, maxItems: 50 });
33
+ if (minRelevance !== null) {
34
+ const parsed = Number(minRelevance);
35
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) throw new ValidationError("minRelevance must be between 0 and 1");
36
+ query.minRelevance = parsed;
37
+ }
38
+ if (limit !== null) {
39
+ const parsed = Number(limit);
40
+ if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > 100) throw new ValidationError("limit must be an integer between 1 and 100");
41
+ query.limit = parsed;
42
+ }
43
+ if (visibility) query.visibility = optionalEnum(visibility, "visibility", VALID_MEMORY_VISIBILITIES) as MemoryVisibility;
44
+ query.includeExpired = params.get("includeExpired") === "true" ? true : undefined;
45
+ query.includeSensitive = params.get("includeSensitive") === "true" ? true : undefined;
46
+ return query;
47
+ }
48
+
49
+ function cleanMemoryQueryFromBody(body: unknown): MemoryQuery {
50
+ if (!isRecord(body)) throw new ValidationError("memory query body must be an object");
51
+ const query: MemoryQuery = {};
52
+ query.type = optionalEnum(body.type, "type", VALID_MEMORY_TYPES) as MemoryType | undefined;
53
+ query.scope = cleanString(body.scope, "scope", { max: 240 });
54
+ query.tags = cleanStringArray(body.tags, "tags", { itemMax: 80, maxItems: 50 });
55
+ if (body.minRelevance !== undefined) {
56
+ if (typeof body.minRelevance !== "number" || body.minRelevance < 0 || body.minRelevance > 1) throw new ValidationError("minRelevance must be between 0 and 1");
57
+ query.minRelevance = body.minRelevance;
58
+ }
59
+ if (body.limit !== undefined) {
60
+ if (typeof body.limit !== "number" || !Number.isSafeInteger(body.limit) || body.limit <= 0 || body.limit > 100) throw new ValidationError("limit must be an integer between 1 and 100");
61
+ query.limit = body.limit;
62
+ }
63
+ query.includeExpired = typeof body.includeExpired === "boolean" ? body.includeExpired : undefined;
64
+ query.visibility = optionalEnum(body.visibility, "visibility", VALID_MEMORY_VISIBILITIES) as MemoryVisibility | undefined;
65
+ query.includeSensitive = typeof body.includeSensitive === "boolean" ? body.includeSensitive : undefined;
66
+ return query;
67
+ }
68
+
69
+ function authorizeMemoryScope(req: Request, scope: string | undefined, requiredScope: "memory:read" | "memory:write" | "memory:admin"): Response | null {
70
+ const component = getComponentAuth(req);
71
+ if (component?.constraints?.memoryScopes?.length && !scope) {
72
+ return error("memory scope required for constrained token", 403);
73
+ }
74
+ return authorizeRoute(req, {
75
+ scope: requiredScope,
76
+ resource: scope ? { memoryScope: scope } : undefined,
77
+ });
78
+ }
79
+
80
+ function cleanCreateMemoryInput(body: unknown, ctx: MemoryBrokerContext): CreateMemoryInput {
81
+ if (!isRecord(body)) throw new ValidationError("memory body must be an object");
82
+ const ttlMs = body.ttlMs === undefined
83
+ ? undefined
84
+ : cleanMemoryPositiveInteger(body.ttlMs, "ttlMs");
85
+ return {
86
+ type: optionalEnum(body.type, "type", VALID_MEMORY_TYPES) as MemoryType,
87
+ scope: cleanString(body.scope, "scope", { required: true, max: 240 })!,
88
+ title: cleanString(body.title, "title", { required: true, max: 240 })!,
89
+ content: cleanString(body.content, "content", { required: true, max: MAX_BODY_BYTES })!,
90
+ tags: cleanStringArray(body.tags, "tags", { itemMax: 80, maxItems: 50 }),
91
+ visibility: optionalEnum(body.visibility, "visibility", VALID_MEMORY_VISIBILITIES) as MemoryVisibility | undefined,
92
+ sensitivity: optionalEnum(body.sensitivity, "sensitivity", VALID_MEMORY_SENSITIVITIES) as MemorySensitivity | undefined,
93
+ confidence: optionalEnum(body.confidence, "confidence", VALID_MEMORY_CONFIDENCES) as MemoryConfidence | undefined,
94
+ redactionState: optionalEnum(body.redactionState, "redactionState", VALID_MEMORY_REDACTION_STATES) as MemoryRedactionState | undefined,
95
+ relevanceScore: cleanMemoryScore(body.relevanceScore, "relevanceScore"),
96
+ sourceAgent: cleanString(body.sourceAgent, "sourceAgent", { max: 200 }),
97
+ sourceTask: body.sourceTask === undefined ? undefined : cleanMemoryPositiveInteger(body.sourceTask, "sourceTask"),
98
+ createdBy: cleanString(body.createdBy, "createdBy", { max: 200 }) ?? ctx.actor,
99
+ metadata: cleanParams(body.metadata, "metadata"),
100
+ ttlMs,
101
+ };
102
+ }
103
+
104
+ function cleanUpdateMemoryInput(body: unknown): UpdateMemoryInput {
105
+ if (!isRecord(body)) throw new ValidationError("memory patch body must be an object");
106
+ const patch: UpdateMemoryInput = {};
107
+ const title = cleanString(body.title, "title", { max: 240 });
108
+ const content = cleanString(body.content, "content", { max: MAX_BODY_BYTES });
109
+ const tags = cleanStringArray(body.tags, "tags", { itemMax: 80, maxItems: 50 });
110
+ const visibility = optionalEnum(body.visibility, "visibility", VALID_MEMORY_VISIBILITIES) as MemoryVisibility | undefined;
111
+ const sensitivity = optionalEnum(body.sensitivity, "sensitivity", VALID_MEMORY_SENSITIVITIES) as MemorySensitivity | undefined;
112
+ const confidence = optionalEnum(body.confidence, "confidence", VALID_MEMORY_CONFIDENCES) as MemoryConfidence | undefined;
113
+ const redactionState = optionalEnum(body.redactionState, "redactionState", VALID_MEMORY_REDACTION_STATES) as MemoryRedactionState | undefined;
114
+ const relevanceScore = cleanMemoryScore(body.relevanceScore, "relevanceScore");
115
+ const metadata = cleanParams(body.metadata, "metadata");
116
+ if (title !== undefined) patch.title = title;
117
+ if (content !== undefined) patch.content = content;
118
+ if (tags !== undefined) patch.tags = tags;
119
+ if (visibility !== undefined) patch.visibility = visibility;
120
+ if (sensitivity !== undefined) patch.sensitivity = sensitivity;
121
+ if (confidence !== undefined) patch.confidence = confidence;
122
+ if (redactionState !== undefined) patch.redactionState = redactionState;
123
+ if (relevanceScore !== undefined) patch.relevanceScore = relevanceScore;
124
+ if (metadata !== undefined) patch.metadata = metadata;
125
+ if (Object.prototype.hasOwnProperty.call(body, "expiresAt")) {
126
+ patch.expiresAt = body.expiresAt === null ? null : cleanMemoryPositiveInteger(body.expiresAt, "expiresAt");
127
+ }
128
+ if (Object.keys(patch).length === 0) throw new ValidationError("memory patch must include at least one field");
129
+ return patch;
130
+ }
131
+
132
+ function cleanMemoryInjectInput(body: unknown): {
133
+ agentId: string;
134
+ memoryIds: string[];
135
+ query?: MemoryQuery;
136
+ task?: TaskRoutingHints;
137
+ budget?: ContextBudget;
138
+ reason: string;
139
+ } {
140
+ if (!isRecord(body)) throw new ValidationError("memory injection body must be an object");
141
+ const agentId = cleanString(body.agentId ?? body.target, "agentId", { required: true, max: 200 })!;
142
+ const memoryIds = cleanStringArray(body.memoryIds, "memoryIds", { itemMax: 80, maxItems: 50 }) ?? [];
143
+ const query = body.query === undefined ? undefined : cleanMemoryQueryFromBody(body.query);
144
+ if (memoryIds.length === 0 && !query) throw new ValidationError("memoryIds or query required");
145
+ return {
146
+ agentId,
147
+ memoryIds,
148
+ query,
149
+ task: cleanTaskRoutingHints(body.task),
150
+ budget: cleanContextBudget(body.budget),
151
+ reason: cleanString(body.reason, "reason", { max: 200 }) ?? "manual",
152
+ };
153
+ }
154
+
155
+ function cleanTaskRoutingHints(value: unknown): TaskRoutingHints | undefined {
156
+ if (value === undefined || value === null) return undefined;
157
+ if (!isRecord(value)) throw new ValidationError("task must be an object");
158
+ const id = value.id === undefined ? undefined : cleanMemoryPositiveInteger(value.id, "task.id");
159
+ return {
160
+ id,
161
+ title: cleanString(value.title, "task.title", { max: 240 }),
162
+ text: cleanString(value.text, "task.text", { max: 4000 }),
163
+ scope: cleanString(value.scope, "task.scope", { max: 240 }),
164
+ tags: cleanStringArray(value.tags, "task.tags", { itemMax: 80, maxItems: 50 }),
165
+ capabilities: cleanStringArray(value.capabilities, "task.capabilities", { itemMax: 80, maxItems: 50 }),
166
+ target: cleanString(value.target, "task.target", { max: 200 }),
167
+ };
168
+ }
169
+
170
+ function cleanContextBudget(value: unknown): ContextBudget | undefined {
171
+ if (value === undefined || value === null) return undefined;
172
+ if (!isRecord(value)) throw new ValidationError("budget must be an object");
173
+ const maxTokens = cleanMemoryPositiveInteger(value.maxTokens, "budget.maxTokens");
174
+ const maxMemories = cleanMemoryPositiveInteger(value.maxMemories, "budget.maxMemories");
175
+ const priorityCutoff = value.priorityCutoff;
176
+ if (priorityCutoff !== 1 && priorityCutoff !== 2 && priorityCutoff !== 3) throw new ValidationError("budget.priorityCutoff must be 1, 2, or 3");
177
+ return { maxTokens, maxMemories, priorityCutoff };
178
+ }
179
+
180
+ function cleanMemoryPositiveInteger(value: unknown, field: string): number {
181
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new ValidationError(`${field} must be a positive integer`);
182
+ return value;
183
+ }
184
+
185
+ function cleanMemoryScore(value: unknown, field: string): number | undefined {
186
+ if (value === undefined || value === null) return undefined;
187
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) throw new ValidationError(`${field} must be between 0 and 1`);
188
+ return value;
189
+ }
190
+
191
+ export const getMemories: Handler = async (req) => {
192
+ try {
193
+ const query = cleanMemoryQueryFromParams(new URL(req.url).searchParams);
194
+ const denied = authorizeMemoryScope(req, query.scope, "memory:read");
195
+ if (denied) return denied;
196
+ return json(await memoryBroker.search(query, memoryContext(req)));
197
+ } catch (e) {
198
+ return memoryErrorResponse(e);
199
+ }
200
+ };
201
+
202
+ export const postMemorySearch: Handler = async (req) => {
203
+ const parsed = await parseBody<unknown>(req);
204
+ if (!parsed.ok) return error(parsed.error, parsed.status);
205
+ try {
206
+ const query = cleanMemoryQueryFromBody(parsed.body ?? {});
207
+ const denied = authorizeMemoryScope(req, query.scope, "memory:read");
208
+ if (denied) return denied;
209
+ return json(await memoryBroker.search(query, memoryContext(req)));
210
+ } catch (e) {
211
+ return memoryErrorResponse(e);
212
+ }
213
+ };
214
+
215
+ export const postMemory: Handler = async (req) => {
216
+ const parsed = await parseBody<unknown>(req);
217
+ if (!parsed.ok) return error(parsed.error, parsed.status);
218
+ try {
219
+ const ctx = memoryContext(req);
220
+ const input = cleanCreateMemoryInput(parsed.body, ctx);
221
+ const denied = authorizeMemoryScope(req, input.scope, "memory:write");
222
+ if (denied) return denied;
223
+ assertMemoryCreateAllowed(input, ctx);
224
+ return json(await memoryBroker.create(input, ctx), 201);
225
+ } catch (e) {
226
+ return memoryErrorResponse(e);
227
+ }
228
+ };
229
+
230
+ export const getMemoryStats: Handler = async (req) => {
231
+ try {
232
+ return json(await memoryBroker.stats(memoryContext(req)));
233
+ } catch (e) {
234
+ return memoryErrorResponse(e);
235
+ }
236
+ };
237
+
238
+ export const getMemoryBrokerInfo: Handler = async () => {
239
+ try {
240
+ const capabilities = await memoryBroker.capabilities();
241
+ return json({
242
+ type: memoryBrokerConfig.type,
243
+ external: capabilities.external,
244
+ capabilities,
245
+ config: publicMemoryBrokerConfig(),
246
+ });
247
+ } catch (e) {
248
+ return memoryErrorResponse(e);
249
+ }
250
+ };
251
+
252
+ export const postMemoryInject: Handler = async (req) => {
253
+ const parsed = await parseBody<unknown>(req);
254
+ if (!parsed.ok) return error(parsed.error, parsed.status);
255
+ try {
256
+ const input = cleanMemoryInjectInput(parsed.body);
257
+ const agent = getAgent(input.agentId);
258
+ if (!agent) return error("agent not found", 404);
259
+ const ctx = memoryContext(req);
260
+ const result = await injectMemoryContext({
261
+ agent,
262
+ memoryIds: input.memoryIds,
263
+ query: input.query,
264
+ task: input.task,
265
+ budget: input.budget,
266
+ reason: input.reason,
267
+ source: "system",
268
+ ctx,
269
+ });
270
+ if (!result) return error("no injectable memories matched", 400);
271
+ emitCommand(result.command);
272
+ return json(result, 202);
273
+ } catch (e) {
274
+ return memoryErrorResponse(e);
275
+ }
276
+ };
277
+
278
+ export const getMemoryById: Handler = async (req, params) => {
279
+ const ctx = memoryContext(req);
280
+ const memory = await memoryBroker.get(params.id!, ctx);
281
+ if (!memory) return error("memory not found", 404);
282
+ const denied = authorizeMemoryScope(req, memory.scope, "memory:read");
283
+ if (denied) return denied;
284
+ if (memory.redactionState === "rejected") return error("memory not found", 404);
285
+ if (memory.sensitivity === "secret" && !memoryContextHasAdmin(ctx)) return error("memory not found", 404);
286
+ return json(memory);
287
+ };
288
+
289
+ export const patchMemory: Handler = async (req, params) => {
290
+ const parsed = await parseBody<unknown>(req);
291
+ if (!parsed.ok) return error(parsed.error, parsed.status);
292
+ try {
293
+ const ctx = memoryContext(req);
294
+ const patch = cleanUpdateMemoryInput(parsed.body);
295
+ const current = await memoryBroker.get(params.id!, ctx);
296
+ if (!current) return error("memory not found", 404);
297
+ const denied = authorizeMemoryScope(req, current.scope, "memory:write");
298
+ if (denied) return denied;
299
+ assertMemoryUpdateAllowed(patch, ctx);
300
+ return json(await memoryBroker.update(params.id!, patch, ctx));
301
+ } catch (e) {
302
+ if (e instanceof ValidationError && e.message === "memory not found") return error(e.message, 404);
303
+ return memoryErrorResponse(e);
304
+ }
305
+ };
306
+
307
+ export const deleteMemoryRoute: Handler = async (req, params) => {
308
+ const ctx = memoryContext(req);
309
+ const current = await memoryBroker.get(params.id!, ctx);
310
+ if (!current) return error("memory not found", 404);
311
+ const denied = authorizeMemoryScope(req, current.scope, "memory:admin");
312
+ if (denied) return denied;
313
+ await memoryBroker.delete(params.id!, ctx);
314
+ return json({ ok: true });
315
+ };
316
+
317
+ function memoryContextHasAdmin(ctx: MemoryBrokerContext): boolean {
318
+ return ctx.scopes.includes("*") || ctx.scopes.includes("memory:admin");
319
+ }
320
+
321
+ function publicMemoryBrokerConfig(): Record<string, unknown> {
322
+ if (memoryBrokerConfig.type === "sqlite") return { type: "sqlite" };
323
+ if (memoryBrokerConfig.type === "http") {
324
+ return {
325
+ type: "http",
326
+ url: memoryBrokerConfig.url,
327
+ tokenEnv: memoryBrokerConfig.tokenEnv,
328
+ timeoutMs: memoryBrokerConfig.timeoutMs,
329
+ };
330
+ }
331
+ return {
332
+ type: "command",
333
+ command: memoryBrokerConfig.command,
334
+ args: memoryBrokerConfig.args,
335
+ timeoutMs: memoryBrokerConfig.timeoutMs,
336
+ };
337
+ }