@sentry/junior-memory 0.127.0 → 0.128.1

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/README.md CHANGED
@@ -28,9 +28,9 @@ exported types, tools, and tests are authoritative.
28
28
 
29
29
  - Memory scope is derived from the active actor and source, never from
30
30
  model-supplied ownership fields.
31
- - Dashboard and REST requests authorize one verified viewer, then resolve every
32
- linked platform actor internally so one arbitrary actor is never treated as
33
- the viewer's canonical identity.
31
+ - Dashboard and REST requests authorize one verified viewer, then derive access
32
+ from every linked provider identity so no arbitrary identity is treated as
33
+ the canonical user.
34
34
  - Private conversations and local sources remain private by default.
35
35
  - Recall filters candidates by actor, source, visibility, status, and relevance
36
36
  before content reaches the model.
package/dist/api.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * Authenticated REST resources for viewer-visible memories.
3
3
  *
4
- * HTTP identity is one verified viewer. Linked platform actors are resolved
5
- * behind the route boundary and used only to authorize personal and public
6
- * workspace scopes.
4
+ * HTTP identity is one verified user whose linked identities authorize
5
+ * personal and public workspace scopes.
7
6
  */
8
7
  import { z } from "zod";
9
- import { type PluginConversationEventStats, type PluginRouteApp, type PluginUserPageActor } from "@sentry/junior-plugin-api";
8
+ import { type PluginConversationEventStats, type PluginRouteApp, type User } from "@sentry/junior-plugin-api";
10
9
  import type { MemoryDb } from "./store";
11
10
  export declare const memoryApiSchema: z.ZodObject<{
12
11
  content: z.ZodString;
@@ -77,9 +76,11 @@ export type MemoryApi = z.output<typeof memoryApiSchema>;
77
76
  export type MemoryDashboardResponse = z.output<typeof memoryDashboardResponseSchema>;
78
77
  export type MemoryListResponse = z.output<typeof memoryListResponseSchema>;
79
78
  interface MemoryApiOptions {
80
- actors(email: string): Promise<PluginUserPageActor[]>;
81
79
  db: MemoryDb;
82
80
  eventStats: PluginConversationEventStats;
81
+ users: {
82
+ resolve(email: string): Promise<User | undefined>;
83
+ };
83
84
  }
84
85
  /** Create the authenticated viewer-memory REST app. */
85
86
  export declare function createMemoryApi(options: MemoryApiOptions): PluginRouteApp;
package/dist/index.js CHANGED
@@ -228,6 +228,46 @@ function rankMemoryMatches(matches, options) {
228
228
 
229
229
  // src/scope.ts
230
230
  import { isPrivateSource } from "@sentry/junior-plugin-api";
231
+ function uniqueScopes(scopes) {
232
+ return [
233
+ ...new Map(
234
+ scopes.map((scope) => [`${scope.scope}:${scope.scopeKey}`, scope])
235
+ ).values()
236
+ ];
237
+ }
238
+ function deriveViewerMemoryScopes(identities) {
239
+ const privateScopes = identities.flatMap((identity) => {
240
+ if (identity.provider === "local") {
241
+ return [
242
+ {
243
+ scope: "personal",
244
+ scopeKey: `local:${identity.providerSubjectId}`
245
+ }
246
+ ];
247
+ }
248
+ if (identity.provider === "slack" && identity.providerTenantId) {
249
+ return [
250
+ {
251
+ scope: "personal",
252
+ scopeKey: `slack:${identity.providerTenantId}:${identity.providerSubjectId}`
253
+ }
254
+ ];
255
+ }
256
+ return [];
257
+ });
258
+ const publicScopes = identities.flatMap(
259
+ (identity) => identity.provider === "slack" && identity.providerTenantId ? [
260
+ {
261
+ scope: "conversation",
262
+ scopeKey: `slack:${identity.providerTenantId}`
263
+ }
264
+ ] : []
265
+ );
266
+ return {
267
+ privateScopes: uniqueScopes(privateScopes),
268
+ publicScopes: uniqueScopes(publicScopes)
269
+ };
270
+ }
231
271
  function sourceConversationKey(ctx) {
232
272
  if (ctx.source.platform === "local") {
233
273
  return ctx.source.conversationId;
@@ -964,7 +1004,7 @@ async function searchVisibleVectorMemories(args) {
964
1004
  });
965
1005
  }
966
1006
  function createMemoryStore(db, context, options = {}) {
967
- const runtimeContext2 = memoryRuntimeContextSchema.parse(context);
1007
+ const runtimeContext = memoryRuntimeContextSchema.parse(context);
968
1008
  const parsedOptions = memoryStoreOptionsSchema.parse({ now: options.now });
969
1009
  const embedder = options.embedder;
970
1010
  const maxVectorDistance = options.maxVectorDistance;
@@ -976,14 +1016,14 @@ function createMemoryStore(db, context, options = {}) {
976
1016
  db,
977
1017
  limit: input.limit,
978
1018
  nowMs,
979
- scopes: deriveVisibleMemoryScopes(runtimeContext2)
1019
+ scopes: deriveVisibleMemoryScopes(runtimeContext)
980
1020
  });
981
1021
  }
982
1022
  async function reuseDuplicateMemory(args) {
983
1023
  await rememberDuplicateIdempotency({
984
1024
  ...args,
985
1025
  db,
986
- runtimeContext: runtimeContext2
1026
+ runtimeContext
987
1027
  });
988
1028
  await storeEmbedding({
989
1029
  content: args.duplicate.content,
@@ -998,8 +1038,8 @@ function createMemoryStore(db, context, options = {}) {
998
1038
  const input = createMemoryInputSchema.parse(rawInput);
999
1039
  const nowMs = getNowMs();
1000
1040
  const content = normalizeContent(input.content);
1001
- const scope = deriveMemoryScope(runtimeContext2, scopeKind);
1002
- const subject = deriveMemorySubject(runtimeContext2, scope);
1041
+ const scope = deriveMemoryScope(runtimeContext, scopeKind);
1042
+ const subject = deriveMemorySubject(runtimeContext, scope);
1003
1043
  if (content.length > MAX_MEMORY_CONTENT_CHARS) {
1004
1044
  throw new Error("Memory content exceeds the maximum length.");
1005
1045
  }
@@ -1072,7 +1112,7 @@ function createMemoryStore(db, context, options = {}) {
1072
1112
  candidates: preferenceCandidates,
1073
1113
  content,
1074
1114
  decider: supersessionDecider,
1075
- runtimeContext: runtimeContext2
1115
+ runtimeContext
1076
1116
  });
1077
1117
  if (adjudication.decision === "duplicate") {
1078
1118
  return await reuseDuplicateMemory({
@@ -1099,8 +1139,8 @@ function createMemoryStore(db, context, options = {}) {
1099
1139
  observedAtMs: nowMs,
1100
1140
  scope: scope.scope,
1101
1141
  scopeKey: scope.scopeKey,
1102
- sourceKey: sourceKey(runtimeContext2),
1103
- sourcePlatform: runtimeContext2.source.platform,
1142
+ sourceKey: sourceKey(runtimeContext),
1143
+ sourcePlatform: runtimeContext.source.platform,
1104
1144
  subjectKey: subject.subjectKey,
1105
1145
  subjectType: subject.subjectType,
1106
1146
  kind: input.kind
@@ -1173,7 +1213,7 @@ function createMemoryStore(db, context, options = {}) {
1173
1213
  async function retrieveVisibleMemories(rawInput, vectorMaxDistance) {
1174
1214
  const input = searchMemoriesInputSchema.parse(rawInput);
1175
1215
  const nowMs = getNowMs();
1176
- const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1216
+ const scopes = deriveVisibleMemoryScopes(runtimeContext);
1177
1217
  await archiveExpiredMemoryBatch({
1178
1218
  db,
1179
1219
  nowMs,
@@ -1199,7 +1239,7 @@ function createMemoryStore(db, context, options = {}) {
1199
1239
  scopes
1200
1240
  })
1201
1241
  ]);
1202
- const channelPrefix = sourceChannelPrefix(runtimeContext2);
1242
+ const channelPrefix = sourceChannelPrefix(runtimeContext);
1203
1243
  return rankMemoryMatches([...vectorMatches, ...lexicalMatches], {
1204
1244
  nowMs,
1205
1245
  ...channelPrefix ? { channelPrefix } : {}
@@ -1218,7 +1258,7 @@ function createMemoryStore(db, context, options = {}) {
1218
1258
  async listMemories(input) {
1219
1259
  input = listMemoriesInputSchema.parse(input);
1220
1260
  const nowMs = getNowMs();
1221
- const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1261
+ const scopes = deriveVisibleMemoryScopes(runtimeContext);
1222
1262
  await archiveExpiredMemoryBatch({
1223
1263
  db,
1224
1264
  nowMs,
@@ -1234,7 +1274,7 @@ function createMemoryStore(db, context, options = {}) {
1234
1274
  async listPersonalMemories(input) {
1235
1275
  input = listMemoriesInputSchema.parse(input);
1236
1276
  const nowMs = getNowMs();
1237
- const scopes = [deriveMemoryScope(runtimeContext2, "personal")];
1277
+ const scopes = [deriveMemoryScope(runtimeContext, "personal")];
1238
1278
  await archiveExpiredMemoryBatch({
1239
1279
  db,
1240
1280
  nowMs,
@@ -1256,7 +1296,7 @@ function createMemoryStore(db, context, options = {}) {
1256
1296
  async archiveMemory(input) {
1257
1297
  input = archiveMemoryInputSchema.parse(input);
1258
1298
  const nowMs = getNowMs();
1259
- const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1299
+ const scopes = deriveVisibleMemoryScopes(runtimeContext);
1260
1300
  const predicate = activeVisiblePredicate({ nowMs, scopes });
1261
1301
  const idPrefix = input.id.trim();
1262
1302
  if (!idPrefix) {
@@ -1795,35 +1835,6 @@ var PersonalMemoryNotFoundError = class extends Error {
1795
1835
  this.name = "PersonalMemoryNotFoundError";
1796
1836
  }
1797
1837
  };
1798
- function personalScopes(runtimeContexts) {
1799
- return [
1800
- ...new Map(
1801
- runtimeContexts.map((context) => {
1802
- const scope = deriveMemoryScope(context, "personal");
1803
- return [`${scope.scope}:${scope.scopeKey}`, scope];
1804
- })
1805
- ).values()
1806
- ];
1807
- }
1808
- function publicWorkspaceScopes(runtimeContexts) {
1809
- return [
1810
- ...new Map(
1811
- runtimeContexts.flatMap((context) => {
1812
- if (context.source.platform !== "slack" || !context.actor) {
1813
- return [];
1814
- }
1815
- if (context.actor.platform !== "slack") {
1816
- return [];
1817
- }
1818
- const scope = {
1819
- scope: "conversation",
1820
- scopeKey: `slack:${context.actor.teamId}`
1821
- };
1822
- return [[`${scope.scope}:${scope.scopeKey}`, scope]];
1823
- })
1824
- ).values()
1825
- ];
1826
- }
1827
1838
  function scopePredicate(scopes) {
1828
1839
  if (scopes.length === 0) return void 0;
1829
1840
  return or2(
@@ -1876,9 +1887,8 @@ function emptyStats() {
1876
1887
  public: 0
1877
1888
  };
1878
1889
  }
1879
- function createPersonalMemoryCollection(db, runtimeContexts, options = {}) {
1880
- const privateScopes = personalScopes(runtimeContexts);
1881
- const publicScopes = publicWorkspaceScopes(runtimeContexts);
1890
+ function createPersonalMemoryCollection(db, scopes, options = {}) {
1891
+ const { privateScopes, publicScopes } = scopes;
1882
1892
  const allScopes = [...privateScopes, ...publicScopes];
1883
1893
  const getNowMs = () => options.now?.() ?? Date.now();
1884
1894
  function scopesForVisibility(visibility) {
@@ -1923,9 +1933,9 @@ function createPersonalMemoryCollection(db, runtimeContexts, options = {}) {
1923
1933
  async list(input) {
1924
1934
  input = personalMemoryPageInputSchema.parse(input);
1925
1935
  const nowMs = getNowMs();
1926
- const scopes = scopesForVisibility(input.visibility);
1927
- await archiveExpiredMemoryBatch({ db, nowMs, scopes });
1928
- const active = activeVisiblePredicate({ nowMs, scopes });
1936
+ const scopes2 = scopesForVisibility(input.visibility);
1937
+ await archiveExpiredMemoryBatch({ db, nowMs, scopes: scopes2 });
1938
+ const active = activeVisiblePredicate({ nowMs, scopes: scopes2 });
1929
1939
  if (!active) {
1930
1940
  return { memories: [] };
1931
1941
  }
@@ -2071,27 +2081,6 @@ var InvalidMemoryCursorError = class extends Error {
2071
2081
  this.name = "InvalidMemoryCursorError";
2072
2082
  }
2073
2083
  };
2074
- function runtimeContext(actor) {
2075
- if (actor.platform === "slack") {
2076
- return {
2077
- actor,
2078
- source: {
2079
- platform: "slack",
2080
- visibility: "private",
2081
- teamId: actor.teamId,
2082
- channelId: "DDASHBOARD"
2083
- }
2084
- };
2085
- }
2086
- return {
2087
- actor,
2088
- source: {
2089
- platform: "local",
2090
- visibility: "private",
2091
- conversationId: "local:dashboard:memories"
2092
- }
2093
- };
2094
- }
2095
2084
  function decodeCursor(value, input) {
2096
2085
  if (!value) return void 0;
2097
2086
  try {
@@ -2119,10 +2108,10 @@ function encodeCursor(cursor, input) {
2119
2108
  "utf8"
2120
2109
  ).toString("base64url");
2121
2110
  }
2122
- function createViewerMemories(db, actors) {
2111
+ function createViewerMemories(db, user) {
2123
2112
  const collection = createPersonalMemoryCollection(
2124
2113
  db,
2125
- actors.map(runtimeContext)
2114
+ deriveViewerMemoryScopes(user.identities)
2126
2115
  );
2127
2116
  return {
2128
2117
  async archive(id) {
@@ -2243,10 +2232,15 @@ function createMemoryApi(options) {
2243
2232
  if (!isCollection && !isDashboard && !memoryPath) {
2244
2233
  return json({ error: "Not found." }, 404);
2245
2234
  }
2246
- const actors = await options.actors(email);
2247
- const memories = createViewerMemories(options.db, actors);
2235
+ const isRead = request.method === "GET" || request.method === "HEAD";
2236
+ if (!isRead && !(memoryPath && request.method === "DELETE")) {
2237
+ return json({ error: "Method not allowed." }, 405);
2238
+ }
2239
+ const user = await options.users.resolve(email);
2240
+ if (!user) return json({ error: "Authentication required." }, 401);
2241
+ const memories = createViewerMemories(options.db, user);
2248
2242
  try {
2249
- if (isDashboard && (request.method === "GET" || request.method === "HEAD")) {
2243
+ if (isDashboard && isRead) {
2250
2244
  const [stats, days, extractionDays, recallDays] = await Promise.all([
2251
2245
  memories.stats(),
2252
2246
  memories.timeline({ days: 90 }),
@@ -2271,7 +2265,7 @@ function createMemoryApi(options) {
2271
2265
  status: 200
2272
2266
  }) : json(body);
2273
2267
  }
2274
- if (isCollection && (request.method === "GET" || request.method === "HEAD")) {
2268
+ if (isCollection && isRead) {
2275
2269
  const query = memoryListQuerySchema.parse({
2276
2270
  cursor: url.searchParams.get("cursor") ?? void 0,
2277
2271
  limit: url.searchParams.get("limit") ?? void 0,
@@ -2291,7 +2285,7 @@ function createMemoryApi(options) {
2291
2285
  status: 200
2292
2286
  }) : json(body);
2293
2287
  }
2294
- if (memoryPath && (request.method === "GET" || request.method === "HEAD")) {
2288
+ if (memoryPath && isRead) {
2295
2289
  const memory = memoryApiSchema.parse(
2296
2290
  apiMemory(await memories.get(decodeURIComponent(memoryPath[1])))
2297
2291
  );
@@ -2457,7 +2451,7 @@ import {
2457
2451
  definePluginTool,
2458
2452
  getSourceKey,
2459
2453
  PluginToolInputError,
2460
- pluginToolResultSchema
2454
+ pluginToolOutputSchema
2461
2455
  } from "@sentry/junior-plugin-api";
2462
2456
  import { z as z7 } from "zod";
2463
2457
  var MAX_TOOL_CONTENT_CHARS = 4e3;
@@ -2621,36 +2615,17 @@ var memoryProjectionOutputSchema = z7.object({
2621
2615
  observedAtMs: z7.number(),
2622
2616
  expiresAtMs: z7.number().optional()
2623
2617
  });
2624
- var memoryCreateDataOutputSchema = z7.object({
2625
- created: z7.boolean(),
2626
- memory: memoryProjectionOutputSchema
2627
- });
2628
- var memorySingleDataOutputSchema = z7.object({
2629
- memory: memoryProjectionOutputSchema
2630
- });
2631
- var memoryManyDataOutputSchema = z7.object({
2632
- memories: z7.array(memoryProjectionOutputSchema)
2633
- });
2634
- var memoryCreateOutputSchema = pluginToolResultSchema.extend({
2635
- ok: z7.literal(true),
2636
- status: z7.literal("success"),
2618
+ var memoryCreateOutputSchema = pluginToolOutputSchema.extend({
2637
2619
  target: z7.string(),
2638
- data: memoryCreateDataOutputSchema,
2639
2620
  created: z7.boolean(),
2640
2621
  memory: memoryProjectionOutputSchema
2641
2622
  });
2642
- var memorySingleOutputSchema = pluginToolResultSchema.extend({
2643
- ok: z7.literal(true),
2644
- status: z7.literal("success"),
2623
+ var memorySingleOutputSchema = pluginToolOutputSchema.extend({
2645
2624
  target: z7.string(),
2646
- data: memorySingleDataOutputSchema,
2647
2625
  memory: memoryProjectionOutputSchema
2648
2626
  });
2649
- var memoryManyOutputSchema = pluginToolResultSchema.extend({
2650
- ok: z7.literal(true),
2651
- status: z7.literal("success"),
2627
+ var memoryManyOutputSchema = pluginToolOutputSchema.extend({
2652
2628
  target: z7.string(),
2653
- data: memoryManyDataOutputSchema,
2654
2629
  memories: z7.array(memoryProjectionOutputSchema)
2655
2630
  });
2656
2631
  function parseMemoryToolInput(schema, input) {
@@ -2694,10 +2669,7 @@ function compactMemory(memory) {
2694
2669
  }
2695
2670
  function memoryToolResult(target, data) {
2696
2671
  return {
2697
- ok: true,
2698
- status: "success",
2699
2672
  target,
2700
- data,
2701
2673
  ...data
2702
2674
  };
2703
2675
  }
@@ -2718,7 +2690,7 @@ function createMemoryCreateTool(context) {
2718
2690
  const parsedInput = parseMemoryToolInput(createMemoryInputSchema2, input);
2719
2691
  const toolCallId = requireToolCallId(options.toolCallId);
2720
2692
  const requestedExpiresAtMs = parseExpiresAt(parsedInput.expires_at);
2721
- const runtimeContext2 = memoryRuntimeContext(context);
2693
+ const runtimeContext = memoryRuntimeContext(context);
2722
2694
  const store = memoryStore(context, {
2723
2695
  supersessionDecider: context.supersessionDecider
2724
2696
  });
@@ -2729,7 +2701,7 @@ function createMemoryCreateTool(context) {
2729
2701
  parseCreateMemoryRequest({
2730
2702
  content: requireMemoryContent(parsedInput.content),
2731
2703
  ...requestedExpiresAtMs !== void 0 ? { expiresAtMs: requestedExpiresAtMs } : {},
2732
- runtimeContext: runtimeContext2,
2704
+ runtimeContext,
2733
2705
  ...context.userText?.trim() ? {
2734
2706
  sourceContext: {
2735
2707
  currentUserText: context.userText.trim()
@@ -2870,7 +2842,6 @@ import { z as z9 } from "zod";
2870
2842
  // src/events.ts
2871
2843
  import { defineConversationEvent } from "@sentry/junior-plugin-api";
2872
2844
  import { z as z8 } from "zod";
2873
- var MAX_EVENT_PREVIEW_CHARS = 500;
2874
2845
  var capturedMemorySchema = z8.object({
2875
2846
  content: z8.string().min(1),
2876
2847
  id: z8.string().min(1),
@@ -2878,11 +2849,6 @@ var capturedMemorySchema = z8.object({
2878
2849
  observedAtMs: z8.number().finite(),
2879
2850
  scope: z8.enum(MEMORY_SCOPES)
2880
2851
  }).strict();
2881
- function eventPreview(content) {
2882
- const trimmed = content.trim();
2883
- if (trimmed.length <= MAX_EVENT_PREVIEW_CHARS) return trimmed;
2884
- return `${trimmed.slice(0, MAX_EVENT_PREVIEW_CHARS - 3).trimEnd()}...`;
2885
- }
2886
2852
  var capturedMemoriesSchema = z8.object({
2887
2853
  memories: z8.array(capturedMemorySchema).max(100),
2888
2854
  costUsd: z8.number().finite().nonnegative().optional()
@@ -2896,8 +2862,7 @@ function renderCapturedMemories(event) {
2896
2862
  if (count === 0) return void 0;
2897
2863
  return {
2898
2864
  icon: "brain",
2899
- title: count === 1 ? "Memory captured" : "Memories captured",
2900
- preview: count === 1 ? eventPreview(event.memories[0].content) : `${count} memories`,
2865
+ title: `${count} ${count === 1 ? "memory" : "memories"} captured`,
2901
2866
  details: event.memories.map((memory) => ({
2902
2867
  title: memory.content,
2903
2868
  metadata: [memory.kind, memory.scope]
@@ -3056,13 +3021,13 @@ async function processMemorySession(context) {
3056
3021
  if (!evidenceText) {
3057
3022
  return;
3058
3023
  }
3059
- const runtimeContext2 = memoryRuntimeContextSchema.parse({
3024
+ const runtimeContext = memoryRuntimeContextSchema.parse({
3060
3025
  conversationId: run.conversationId,
3061
3026
  ...run.actor ? { actor: run.actor } : {},
3062
3027
  source: run.source
3063
3028
  });
3064
3029
  const agent = createMemoryAgent(context.model);
3065
- const store = createMemoryStore(context.db, runtimeContext2, {
3030
+ const store = createMemoryStore(context.db, runtimeContext, {
3066
3031
  embedder: context.embedder,
3067
3032
  supersessionDecider: agent
3068
3033
  });
@@ -3078,7 +3043,7 @@ async function processMemorySession(context) {
3078
3043
  })),
3079
3044
  actors: run.actors,
3080
3045
  transcript,
3081
- runtimeContext: runtimeContext2
3046
+ runtimeContext
3082
3047
  });
3083
3048
  });
3084
3049
  const captured = [];
@@ -3188,7 +3153,7 @@ async function createMemoryPromptContributions(context) {
3188
3153
  if (!context.text.trim()) {
3189
3154
  return void 0;
3190
3155
  }
3191
- const runtimeContext2 = memoryRuntimeContextSchema.parse({
3156
+ const runtimeContext = memoryRuntimeContextSchema.parse({
3192
3157
  ...context.conversationId ? { conversationId: context.conversationId } : {},
3193
3158
  ...context.actor ? { actor: context.actor } : {},
3194
3159
  source: context.source
@@ -3202,7 +3167,7 @@ async function createMemoryPromptContributions(context) {
3202
3167
  return result;
3203
3168
  }
3204
3169
  } : void 0;
3205
- const candidates = await createMemoryStore(context.db, runtimeContext2, {
3170
+ const candidates = await createMemoryStore(context.db, runtimeContext, {
3206
3171
  ...embedder ? { embedder } : {},
3207
3172
  ...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
3208
3173
  }).recallMemories({
@@ -3469,10 +3434,7 @@ function createMemoryUserPage() {
3469
3434
  navigation: "primary",
3470
3435
  description: "Personal and public memories Junior can use across conversations.",
3471
3436
  async read(ctx, input) {
3472
- const memories = createViewerMemories(
3473
- ctx.db,
3474
- ctx.viewer.actors
3475
- );
3437
+ const memories = createViewerMemories(ctx.db, ctx.viewer);
3476
3438
  const page = await memories.list({
3477
3439
  cursor: input.cursor,
3478
3440
  ...pageFilter(input.filter),
@@ -3599,9 +3561,9 @@ function memoryPlugin(options = {}) {
3599
3561
  },
3600
3562
  apiRoutes(ctx) {
3601
3563
  return createMemoryApi({
3602
- actors: ctx.viewer.actors,
3603
3564
  db: ctx.db,
3604
- eventStats: ctx.eventStats
3565
+ eventStats: ctx.eventStats,
3566
+ users: ctx.users
3605
3567
  });
3606
3568
  },
3607
3569
  tools(ctx) {