@sentry/junior-memory 0.180.0 → 0.181.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/dist/index.js CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  } from "drizzle-orm";
26
26
  import { cosineDistance } from "drizzle-orm/sql/functions";
27
27
  import { z as z2 } from "zod";
28
+ import { getSourceKey } from "@sentry/junior-plugin-api";
28
29
 
29
30
  // src/db/schema.ts
30
31
  import { sql } from "drizzle-orm";
@@ -44,7 +45,7 @@ import {
44
45
  import { actorSchema, sourceSchema } from "@sentry/junior-plugin-api";
45
46
  import { z } from "zod";
46
47
  var MEMORY_KINDS = ["preference", "procedure", "knowledge"];
47
- var MEMORY_SCOPES = ["personal", "conversation"];
48
+ var MEMORY_SCOPES = ["private", "public"];
48
49
  var MEMORY_SUBJECT_TYPES = [
49
50
  "user",
50
51
  "conversation",
@@ -56,8 +57,11 @@ var MEMORY_EMBEDDING_DIMENSIONS = 1536;
56
57
  var nonEmptyStringSchema = z.string().min(1);
57
58
  var memoryRuntimeContextSchema = z.object({
58
59
  conversationId: nonEmptyStringSchema.optional(),
60
+ locationId: nonEmptyStringSchema.optional(),
59
61
  actor: actorSchema.optional(),
60
- source: sourceSchema
62
+ source: sourceSchema,
63
+ /** User linked to the active Actor. */
64
+ userId: nonEmptyStringSchema.optional()
61
65
  }).strict();
62
66
 
63
67
  // src/db/schema.ts
@@ -83,6 +87,8 @@ var juniorMemoryMemories = pgTable(
83
87
  enum: MEMORY_SOURCE_PLATFORMS
84
88
  }).notNull(),
85
89
  sourceKey: text("source_key").notNull(),
90
+ /** Location where Junior learned the memory, when known. */
91
+ locationId: text("location_id"),
86
92
  idempotencyKey: text("idempotency_key"),
87
93
  observedAtMs: bigint("observed_at_ms", { mode: "number" }).notNull(),
88
94
  createdAtMs: bigint("created_at_ms", { mode: "number" }).notNull(),
@@ -107,7 +113,7 @@ var juniorMemoryMemories = pgTable(
107
113
  ),
108
114
  check(
109
115
  "junior_memory_memories_scope_check",
110
- sql`${table.scope} IN ('personal', 'conversation')`
116
+ sql`${table.scope} IN ('private', 'public')`
111
117
  ),
112
118
  check(
113
119
  "junior_memory_memories_kind_check",
@@ -176,9 +182,6 @@ function reciprocalRank(rank, weight) {
176
182
  function matchScore(match, weights) {
177
183
  return (match.vector ? reciprocalRank(match.vector.rank, weights.vectorWeight) : 0) + (match.lexical ? reciprocalRank(match.lexical.rank, weights.lexicalWeight) : 0);
178
184
  }
179
- function currentChannel(match, channelPrefix) {
180
- return channelPrefix ? match.sourceKey.startsWith(channelPrefix) : false;
181
- }
182
185
  function observedAgeRank(memory, nowMs) {
183
186
  const ageMs = Math.max(0, nowMs - memory.observedAtMs);
184
187
  if (ageMs <= 7 * ONE_DAY_MS) {
@@ -218,140 +221,55 @@ function rankMemoryMatches(matches, options) {
218
221
  if (scoreDelta !== 0) {
219
222
  return scoreDelta;
220
223
  }
221
- const personalDelta = Number(right.memory.scope === "personal") - Number(left.memory.scope === "personal");
222
- if (personalDelta !== 0) {
223
- return personalDelta;
224
- }
225
- const channelDelta = Number(currentChannel(right, options.channelPrefix)) - Number(currentChannel(left, options.channelPrefix));
226
- if (channelDelta !== 0) {
227
- return channelDelta;
224
+ const privateDelta = Number(right.memory.scope === "private") - Number(left.memory.scope === "private");
225
+ if (privateDelta !== 0) {
226
+ return privateDelta;
228
227
  }
229
228
  return observedAgeRank(right.memory, options.nowMs) - observedAgeRank(left.memory, options.nowMs) || right.memory.observedAtMs - left.memory.observedAtMs || left.memory.id.localeCompare(right.memory.id);
230
229
  });
231
230
  }
232
231
 
233
232
  // src/scope.ts
234
- function uniqueScopes(scopes) {
235
- return [
236
- ...new Map(
237
- scopes.map((scope) => [`${scope.scope}:${scope.scopeKey}`, scope])
238
- ).values()
239
- ];
240
- }
241
- function personalScopeFromIdentity(identity) {
242
- if (identity.provider === "local") {
243
- return {
244
- scope: "personal",
245
- scopeKey: `local:${identity.providerSubjectId}`
246
- };
247
- }
248
- if (identity.provider === "junior") {
249
- return {
250
- scope: "personal",
251
- scopeKey: `junior:${identity.providerSubjectId}`
252
- };
253
- }
254
- if (identity.provider === "slack" && identity.providerTenantId) {
255
- return {
256
- scope: "personal",
257
- scopeKey: `slack:${identity.providerTenantId}:${identity.providerSubjectId}`
258
- };
259
- }
260
- return void 0;
261
- }
262
- function deriveViewerMemoryScopes(identities) {
263
- const privateScopes = identities.flatMap((identity) => {
264
- const scope = personalScopeFromIdentity(identity);
265
- return scope ? [scope] : [];
266
- });
267
- const publicScopes = identities.flatMap(
268
- (identity) => identity.provider === "slack" && identity.providerTenantId ? [
269
- {
270
- scope: "conversation",
271
- scopeKey: `slack:${identity.providerTenantId}`
272
- }
273
- ] : []
274
- );
275
- return {
276
- privateScopes: uniqueScopes(privateScopes),
277
- publicScopes: uniqueScopes(publicScopes)
278
- };
279
- }
280
- function sourceConversationKey(source) {
281
- switch (source.platform) {
282
- case "web":
283
- case "local":
284
- return source.conversationId;
285
- case "slack": {
286
- if (source.visibility === "public") {
287
- return `slack:${source.teamId}`;
288
- }
289
- const threadKey = source.threadTs ?? source.messageTs;
290
- if (!threadKey) {
291
- return void 0;
292
- }
293
- return `slack:${source.teamId}:${source.channelId}:${threadKey}`;
294
- }
295
- }
296
- }
297
- function actorScopeKey(actor) {
298
- if (!actor) {
299
- return void 0;
300
- }
301
- switch (actor.platform) {
302
- case "system":
303
- return void 0;
304
- case "slack":
305
- return `slack:${actor.teamId}:${actor.userId}`;
306
- case "local":
307
- return `local:${actor.userId}`;
308
- case "web": {
309
- const email = actor.email?.trim().toLowerCase();
310
- return email ? `junior:${email}` : void 0;
311
- }
312
- }
233
+ var PUBLIC_SCOPE_KEY = "public";
234
+ var publicMemoryScope = {
235
+ scope: "public",
236
+ scopeKey: PUBLIC_SCOPE_KEY
237
+ };
238
+ function privateMemoryScope(userId) {
239
+ return { scope: "private", scopeKey: userId };
313
240
  }
314
- function deriveMemoryScope(ctx, scope) {
315
- if (scope === "personal") {
316
- const scopeKey2 = actorScopeKey(ctx.actor);
317
- if (!scopeKey2) {
318
- throw new Error("Personal memory requires actor context.");
319
- }
320
- return { scope, scopeKey: scopeKey2 };
241
+ function deriveMemoryScope(ctx) {
242
+ if (ctx.source.visibility === "public") {
243
+ return publicMemoryScope;
321
244
  }
322
- const scopeKey = sourceConversationKey(ctx.source);
323
- if (!scopeKey) {
324
- throw new Error("Conversation memory requires conversation context.");
245
+ if (!ctx.userId) {
246
+ throw new Error("Private memory requires a User.");
325
247
  }
326
- return { scope, scopeKey };
248
+ return privateMemoryScope(ctx.userId);
327
249
  }
328
- function deriveMemorySubject(ctx, scope) {
329
- if (scope.scope === "personal") {
330
- const subjectKey2 = actorScopeKey(ctx.actor);
331
- if (!subjectKey2) {
332
- throw new Error("User-subject memory requires actor context.");
250
+ function deriveMemorySubject(ctx, subjectType) {
251
+ if (subjectType === "user") {
252
+ if (!ctx.userId) {
253
+ throw new Error("User memory requires a User.");
333
254
  }
334
- return { subjectType: "user", subjectKey: subjectKey2 };
255
+ return { subjectType, subjectKey: ctx.userId };
335
256
  }
336
- const subjectKey = sourceConversationKey(ctx.source);
257
+ const subjectKey = ctx.conversationId;
337
258
  if (!subjectKey) {
338
259
  throw new Error(
339
260
  "Conversation-subject memory requires conversation context."
340
261
  );
341
262
  }
342
- return { subjectType: "conversation", subjectKey };
263
+ return {
264
+ subjectType,
265
+ subjectKey
266
+ };
343
267
  }
344
268
  function deriveVisibleMemoryScopes(ctx) {
345
- const scopes = [];
346
- try {
347
- scopes.push(deriveMemoryScope(ctx, "personal"));
348
- } catch {
349
- }
350
- try {
351
- scopes.push(deriveMemoryScope(ctx, "conversation"));
352
- } catch {
269
+ if (!ctx.userId) {
270
+ return [publicMemoryScope];
353
271
  }
354
- return scopes;
272
+ return [publicMemoryScope, privateMemoryScope(ctx.userId)];
355
273
  }
356
274
 
357
275
  // src/store.ts
@@ -418,6 +336,7 @@ var memoryRowSchema = z2.object({
418
336
  expiresAtMs: optionalNumberSchema,
419
337
  id: z2.string().min(1),
420
338
  idempotencyKey: optionalStringSchema,
339
+ locationId: optionalNonEmptyStringSchema,
421
340
  observedAtMs: z2.coerce.number(),
422
341
  searchVector: z2.string().optional(),
423
342
  scope: z2.enum(MEMORY_SCOPES),
@@ -515,40 +434,12 @@ function boundedLimit(value, fallback) {
515
434
  }
516
435
  return Math.min(200, Math.max(1, Math.floor(value)));
517
436
  }
518
- function memorySourcePlatform(source) {
519
- switch (source.platform) {
520
- case "slack":
521
- return "slack";
522
- case "local":
523
- return "local";
524
- case "web":
525
- return "web";
526
- }
527
- }
528
437
  function sourceKey(ctx) {
529
- switch (ctx.source.platform) {
530
- case "web":
531
- case "local":
532
- return ctx.source.conversationId;
533
- case "slack": {
534
- const threadKey = ctx.source.threadTs ?? ctx.source.messageTs;
535
- if (!threadKey) {
536
- throw new Error(
537
- "Memory source requires a Slack message or thread timestamp."
538
- );
539
- }
540
- return `slack:${ctx.source.teamId}:${ctx.source.channelId}:${threadKey}`;
541
- }
542
- }
543
- }
544
- function sourceChannelPrefix(ctx) {
545
- switch (ctx.source.platform) {
546
- case "slack":
547
- return `slack:${ctx.source.teamId}:${ctx.source.channelId}:`;
548
- case "web":
549
- case "local":
550
- return void 0;
438
+ const key = getSourceKey(ctx.source);
439
+ if (!key) {
440
+ throw new Error("Memory Source has no stable key.");
551
441
  }
442
+ return key;
552
443
  }
553
444
  function parseMemoryRow(row) {
554
445
  const parsed = memoryRowSchema.parse(row);
@@ -581,12 +472,12 @@ function visibleScopePredicate(scopes) {
581
472
  );
582
473
  }
583
474
  function activeVisiblePredicate(args) {
584
- const scopePredicate2 = visibleScopePredicate(args.scopes);
585
- if (!scopePredicate2) {
475
+ const scopePredicate = visibleScopePredicate(args.scopes);
476
+ if (!scopePredicate) {
586
477
  return void 0;
587
478
  }
588
479
  return and(
589
- scopePredicate2,
480
+ scopePredicate,
590
481
  isNull(juniorMemoryMemories.archivedAtMs),
591
482
  isNull(juniorMemoryMemories.supersededAtMs),
592
483
  isNull(juniorMemoryMemories.supersededById),
@@ -656,12 +547,12 @@ async function findByIdempotencyKey(args) {
656
547
  return void 0;
657
548
  }
658
549
  async function archiveExpiredMemoryBatch(args) {
659
- const scopePredicate2 = visibleScopePredicate(args.scopes);
660
- if (!scopePredicate2) {
550
+ const scopePredicate = visibleScopePredicate(args.scopes);
551
+ if (!scopePredicate) {
661
552
  return { archivedCount: 0 };
662
553
  }
663
554
  const predicates = [
664
- scopePredicate2,
555
+ scopePredicate,
665
556
  isNull(juniorMemoryMemories.archivedAtMs),
666
557
  isNull(juniorMemoryMemories.supersededAtMs),
667
558
  isNull(juniorMemoryMemories.supersededById),
@@ -769,7 +660,7 @@ function activeScopedSubjectPredicate(args) {
769
660
  eq(juniorMemoryMemories.scopeKey, args.scope.scopeKey),
770
661
  eq(juniorMemoryMemories.kind, args.kind),
771
662
  eq(juniorMemoryMemories.subjectType, args.subject.subjectType),
772
- args.subject.subjectKey === void 0 ? isNull(juniorMemoryMemories.subjectKey) : eq(juniorMemoryMemories.subjectKey, args.subject.subjectKey),
663
+ eq(juniorMemoryMemories.subjectKey, args.subject.subjectKey),
773
664
  isNull(juniorMemoryMemories.archivedAtMs),
774
665
  isNull(juniorMemoryMemories.supersededAtMs),
775
666
  isNull(juniorMemoryMemories.supersededById),
@@ -809,11 +700,12 @@ async function rememberDuplicateIdempotency(args) {
809
700
  targetId: args.duplicate.id
810
701
  }),
811
702
  idempotencyKey: args.idempotencyKey,
703
+ locationId: args.runtimeContext.locationId,
812
704
  observedAtMs: args.nowMs,
813
705
  scope: args.scope.scope,
814
706
  scopeKey: args.scope.scopeKey,
815
707
  sourceKey: sourceKey(args.runtimeContext),
816
- sourcePlatform: memorySourcePlatform(args.runtimeContext.source),
708
+ sourcePlatform: args.runtimeContext.source.platform,
817
709
  subjectKey: args.subject.subjectKey,
818
710
  subjectType: args.subject.subjectType,
819
711
  supersededAtMs: args.nowMs,
@@ -998,8 +890,7 @@ async function searchVisibleLexicalMemories(args) {
998
890
  const ranks = denseRanks(rows, (row) => Number(row.textRank));
999
891
  return rows.map((row, index2) => ({
1000
892
  lexical: { rank: ranks[index2] },
1001
- memory: parseMemoryRow(row.memory),
1002
- sourceKey: row.memory.sourceKey
893
+ memory: parseMemoryRow(row.memory)
1003
894
  }));
1004
895
  }
1005
896
  async function searchVisibleVectorMemories(args) {
@@ -1043,7 +934,6 @@ async function searchVisibleVectorMemories(args) {
1043
934
  return [
1044
935
  {
1045
936
  memory: parseMemoryRow(row.memory),
1046
- sourceKey: row.memory.sourceKey,
1047
937
  vector: {
1048
938
  rank: ranks[index2]
1049
939
  }
@@ -1081,12 +971,12 @@ function createMemoryStore(db, context, options = {}) {
1081
971
  });
1082
972
  return { created: false, memory: args.duplicate };
1083
973
  }
1084
- async function createScopedMemory(rawInput, scopeKind) {
974
+ async function createScopedMemory(rawInput, subjectType) {
1085
975
  const input = createMemoryInputSchema.parse(rawInput);
1086
976
  const nowMs = getNowMs();
1087
977
  const content = normalizeContent(input.content);
1088
- const scope = deriveMemoryScope(runtimeContext, scopeKind);
1089
- const subject = deriveMemorySubject(runtimeContext, scope);
978
+ const scope = deriveMemoryScope(runtimeContext);
979
+ const subject = deriveMemorySubject(runtimeContext, subjectType);
1090
980
  if (content.length > MAX_MEMORY_CONTENT_CHARS) {
1091
981
  throw new Error("Memory content exceeds the maximum length.");
1092
982
  }
@@ -1147,7 +1037,7 @@ function createMemoryStore(db, context, options = {}) {
1147
1037
  }
1148
1038
  }
1149
1039
  let supersededIds = [];
1150
- if (scopeKind === "personal" && input.kind === "preference" && supersessionDecider && (input.expiresAtMs === void 0 || input.expiresAtMs > nowMs)) {
1040
+ if (subjectType === "user" && input.kind === "preference" && supersessionDecider && (input.expiresAtMs === void 0 || input.expiresAtMs > nowMs)) {
1151
1041
  const preferenceCandidates = await listPreferenceAdjudicationCandidates({
1152
1042
  db,
1153
1043
  ...candidateEmbedding ? { embedding: candidateEmbedding } : void 0,
@@ -1183,11 +1073,12 @@ function createMemoryStore(db, context, options = {}) {
1183
1073
  expiresAtMs: input.expiresAtMs,
1184
1074
  id,
1185
1075
  idempotencyKey: input.idempotencyKey,
1076
+ locationId: runtimeContext.locationId,
1186
1077
  observedAtMs: nowMs,
1187
1078
  scope: scope.scope,
1188
1079
  scopeKey: scope.scopeKey,
1189
1080
  sourceKey: sourceKey(runtimeContext),
1190
- sourcePlatform: memorySourcePlatform(runtimeContext.source),
1081
+ sourcePlatform: runtimeContext.source.platform,
1191
1082
  subjectKey: subject.subjectKey,
1192
1083
  subjectType: subject.subjectType,
1193
1084
  kind: input.kind
@@ -1269,8 +1160,8 @@ function createMemoryStore(db, context, options = {}) {
1269
1160
  const limit = boundedLimit(input.limit, DEFAULT_SEARCH_LIMIT);
1270
1161
  const overfetch = vectorMaxDistance === void 0 ? SEARCH_RETRIEVAL_OVERFETCH : RECALL_RETRIEVAL_OVERFETCH;
1271
1162
  const candidateLimit = retrievalLegLimit(limit, overfetch);
1272
- const personalScopes = scopes.filter((scope) => scope.scope === "personal");
1273
- const probePersonal = vectorMaxDistance !== void 0 && personalScopes.length > 0;
1163
+ const privateScopes = scopes.filter((scope) => scope.scope === "private");
1164
+ const probePrivate = vectorMaxDistance !== void 0 && privateScopes.length > 0;
1274
1165
  const query = normalizeRetrievalQuery(input.query);
1275
1166
  let queryEmbedding;
1276
1167
  if (embedder && query) {
@@ -1300,25 +1191,23 @@ function createMemoryStore(db, context, options = {}) {
1300
1191
  ...lexicalArgs,
1301
1192
  scopes
1302
1193
  }),
1303
- queryEmbedding && probePersonal ? searchVisibleVectorMemories({
1194
+ queryEmbedding && probePrivate ? searchVisibleVectorMemories({
1304
1195
  db,
1305
1196
  embedding: queryEmbedding,
1306
1197
  limit: candidateLimit,
1307
1198
  maxDistance: vectorMaxDistance,
1308
1199
  nowMs,
1309
- scopes: personalScopes
1200
+ scopes: privateScopes
1310
1201
  }) : emptyMatches,
1311
- probePersonal ? searchVisibleLexicalMemories({
1202
+ probePrivate ? searchVisibleLexicalMemories({
1312
1203
  ...lexicalArgs,
1313
- scopes: personalScopes
1204
+ scopes: privateScopes
1314
1205
  }) : emptyMatches
1315
1206
  ]);
1316
- const channelPrefix = sourceChannelPrefix(runtimeContext);
1317
1207
  return rankMemoryMatches(matches.flat(), {
1318
1208
  nowMs,
1319
1209
  // Slight lexical preference protects exact ids/names/timezones on ties.
1320
- ...vectorMaxDistance === void 0 ? void 0 : { lexicalWeight: 1, vectorWeight: 0.85 },
1321
- ...channelPrefix ? { channelPrefix } : void 0
1210
+ ...vectorMaxDistance === void 0 ? void 0 : { lexicalWeight: 1, vectorWeight: 0.85 }
1322
1211
  }).slice(0, limit).map(({ memory }) => memory);
1323
1212
  }
1324
1213
  return {
@@ -1326,7 +1215,7 @@ function createMemoryStore(db, context, options = {}) {
1326
1215
  return await archiveExpiredVisibleMemories(input, getNowMs());
1327
1216
  },
1328
1217
  async createMemory(input) {
1329
- return await createScopedMemory(input, "personal");
1218
+ return await createScopedMemory(input, "user");
1330
1219
  },
1331
1220
  async createConversationMemory(input) {
1332
1221
  return await createScopedMemory(input, "conversation");
@@ -1347,22 +1236,6 @@ function createMemoryStore(db, context, options = {}) {
1347
1236
  scopes
1348
1237
  });
1349
1238
  },
1350
- async listPersonalMemories(input) {
1351
- input = listMemoriesInputSchema.parse(input);
1352
- const nowMs = getNowMs();
1353
- const scopes = [deriveMemoryScope(runtimeContext, "personal")];
1354
- await archiveExpiredMemoryBatch({
1355
- db,
1356
- nowMs,
1357
- scopes
1358
- });
1359
- return await listVisibleMemories({
1360
- db,
1361
- limit: input.limit,
1362
- nowMs,
1363
- scopes
1364
- });
1365
- },
1366
1239
  async recallMemories(input) {
1367
1240
  return await retrieveVisibleMemories(input, RECALL_MAX_VECTOR_DISTANCE);
1368
1241
  },
@@ -1372,7 +1245,9 @@ function createMemoryStore(db, context, options = {}) {
1372
1245
  async archiveMemory(input) {
1373
1246
  input = archiveMemoryInputSchema.parse(input);
1374
1247
  const nowMs = getNowMs();
1375
- const scopes = deriveVisibleMemoryScopes(runtimeContext);
1248
+ const scopes = deriveVisibleMemoryScopes(runtimeContext).filter(
1249
+ (scope) => scope.scope === "private"
1250
+ );
1376
1251
  const predicate = activeVisiblePredicate({ nowMs, scopes });
1377
1252
  const idPrefix = input.id.trim();
1378
1253
  if (!idPrefix) {
@@ -1517,13 +1392,13 @@ var extractedMemoryResultSchema = z3.object({
1517
1392
  }).strict();
1518
1393
  var extractMemoriesResponseSchema = z3.object({
1519
1394
  memories: z3.array(extractedMemorySchema).max(5).describe(
1520
- "Accepted public/shareable durable memories from the completed run. Return one object per distinct source assertion and classify it with kind."
1395
+ "Accepted durable memories from the completed run. Return one object per distinct source assertion and classify it with kind."
1521
1396
  )
1522
1397
  }).strict();
1523
1398
  var MEMORY_REVIEW_SYSTEM = [
1524
1399
  "You are Junior's memory review agent.",
1525
1400
  "Review one memory candidate and return one structured review decision.",
1526
- "Store only public/shareable, self-contained facts that are useful beyond this turn.",
1401
+ "Store only self-contained facts that are useful beyond this turn and safe for the current Source.",
1527
1402
  "Reject secrets, credentials, private or sensitive personal details, gossip, speculative claims about other people, assistant/system implementation details, vague references, and low-durability chatter.",
1528
1403
  "Use the runtime context only for authority and scope; do not accept model-provided actor ids, scope ids, aliases, or arbitrary subjects."
1529
1404
  ].join("\n");
@@ -1532,7 +1407,7 @@ var MEMORY_EXTRACTION_SYSTEM = [
1532
1407
  "Use the completed run transcript as source evidence, including user-authored messages and tool results.",
1533
1408
  "Assistant text is context for interpreting the run, not independent evidence for new facts.",
1534
1409
  "Reject secrets, credentials, private or sensitive personal details, gossip, speculative claims about other people, assistant/system implementation details, vague references, and low-durability chatter.",
1535
- "If no public, durable, self-contained memory remains after rewriting, return an empty memories array."
1410
+ "If no durable, self-contained memory remains after rewriting, return an empty memories array."
1536
1411
  ].join("\n");
1537
1412
  var MEMORY_RECALL_SYSTEM = [
1538
1413
  "You are Junior's memory recall relevance agent.",
@@ -1650,7 +1525,7 @@ function reviewPrompt(request) {
1650
1525
  "</candidate>",
1651
1526
  "",
1652
1527
  "<rules>",
1653
- "- Return store only when the candidate is public/shareable, durable, and self-contained.",
1528
+ "- Return store only when the candidate is durable, self-contained, and safe for the current Source.",
1654
1529
  "- First classify the memory kind: preference, procedure, or knowledge.",
1655
1530
  "- Use kind=preference only for first-person facts authored by the current actor about their own preference, opinion, habit, identity, or workflow.",
1656
1531
  "- Reject named third-person personal facts such as another person's preference, opinion, habit, identity, relationship, or workflow. Do not assume a named person is the current actor.",
@@ -1889,49 +1764,85 @@ function parseCreateMemoryRequest(request) {
1889
1764
  }
1890
1765
 
1891
1766
  // src/api.ts
1892
- import { z as z6 } from "zod";
1767
+ import { z as z5 } from "zod";
1893
1768
  import {
1894
1769
  pluginApiRouteRequestContextSchema
1895
1770
  } from "@sentry/junior-plugin-api";
1896
1771
 
1897
- // src/personal.ts
1898
- import { z as z5 } from "zod";
1899
-
1900
- // src/personal-store.ts
1901
- import { and as and2, asc as asc2, desc as desc2, eq as eq2, gt as gt2, ilike, like as like2, lt, or as or2, sql as sql3 } from "drizzle-orm";
1772
+ // src/viewer.ts
1773
+ import {
1774
+ and as and2,
1775
+ asc as asc2,
1776
+ desc as desc2,
1777
+ eq as eq2,
1778
+ gt as gt2,
1779
+ ilike,
1780
+ isNull as isNull2,
1781
+ like as like2,
1782
+ lt,
1783
+ or as or2,
1784
+ sql as sql3
1785
+ } from "drizzle-orm";
1902
1786
  import { z as z4 } from "zod";
1787
+ var DAY_MS = 24 * 60 * 60 * 1e3;
1903
1788
  var nonEmptyStringSchema3 = z4.string().min(1);
1904
1789
  var memoryVisibilitySchema = z4.enum(["private", "public"]);
1905
- var personalMemoryCursorSchema = z4.object({
1790
+ var cursorSchema = z4.object({
1906
1791
  createdAtMs: z4.number().finite(),
1907
- id: nonEmptyStringSchema3
1792
+ id: nonEmptyStringSchema3,
1793
+ kind: z4.enum(MEMORY_KINDS).optional(),
1794
+ origin: z4.enum(["automatic", "explicit"]).optional(),
1795
+ query: z4.string().max(200).optional(),
1796
+ version: z4.literal(1),
1797
+ visibility: memoryVisibilitySchema.optional()
1908
1798
  }).strict();
1909
- var personalMemoryPageInputSchema = z4.object({
1910
- cursor: personalMemoryCursorSchema.optional(),
1799
+ var pageInputSchema = z4.object({
1800
+ cursor: z4.string().min(1).max(1e3).optional(),
1911
1801
  kind: z4.enum(MEMORY_KINDS).optional(),
1912
1802
  limit: z4.number().int().min(1).max(50),
1913
1803
  origin: z4.enum(["automatic", "explicit"]).optional(),
1914
1804
  query: z4.string().max(200).optional(),
1915
1805
  visibility: memoryVisibilitySchema.optional()
1916
1806
  }).strict();
1917
- var personalMemoryTimelineInputSchema = z4.object({
1918
- days: z4.number().int().min(1).max(365)
1919
- }).strict();
1920
- var DAY_MS = 24 * 60 * 60 * 1e3;
1921
- var PersonalMemoryNotFoundError = class extends Error {
1807
+ var timelineDaysSchema = z4.number().int().min(1).max(365);
1808
+ var InvalidMemoryCursorError = class extends Error {
1809
+ constructor() {
1810
+ super("Memory cursor is invalid.");
1811
+ this.name = "InvalidMemoryCursorError";
1812
+ }
1813
+ };
1814
+ var MemoryNotFoundError = class extends Error {
1922
1815
  constructor() {
1923
- super("Memory was not found for the authenticated viewer.");
1924
- this.name = "PersonalMemoryNotFoundError";
1816
+ super("Memory was not found for this user.");
1817
+ this.name = "MemoryNotFoundError";
1925
1818
  }
1926
1819
  };
1927
- function scopePredicate(scopes) {
1928
- if (scopes.length === 0) return void 0;
1929
- return or2(
1930
- ...scopes.map(
1931
- (scope) => and2(
1932
- eq2(juniorMemoryMemories.scope, scope.scope),
1933
- eq2(juniorMemoryMemories.scopeKey, scope.scopeKey)
1934
- )
1820
+ function publicScopePredicate() {
1821
+ return and2(
1822
+ eq2(juniorMemoryMemories.scope, publicMemoryScope.scope),
1823
+ eq2(juniorMemoryMemories.scopeKey, publicMemoryScope.scopeKey)
1824
+ );
1825
+ }
1826
+ function privateScopePredicate(userId) {
1827
+ return and2(
1828
+ eq2(juniorMemoryMemories.scope, "private"),
1829
+ eq2(juniorMemoryMemories.scopeKey, userId)
1830
+ );
1831
+ }
1832
+ function visibleScopePredicate2(userId, visibility) {
1833
+ if (visibility === "public") return publicScopePredicate();
1834
+ if (visibility === "private") return privateScopePredicate(userId);
1835
+ return or2(publicScopePredicate(), privateScopePredicate(userId));
1836
+ }
1837
+ function activeMemoryPredicate(userId, nowMs, visibility) {
1838
+ return and2(
1839
+ visibleScopePredicate2(userId, visibility),
1840
+ isNull2(juniorMemoryMemories.archivedAtMs),
1841
+ isNull2(juniorMemoryMemories.supersededAtMs),
1842
+ isNull2(juniorMemoryMemories.supersededById),
1843
+ or2(
1844
+ isNull2(juniorMemoryMemories.expiresAtMs),
1845
+ gt2(juniorMemoryMemories.expiresAtMs, nowMs)
1935
1846
  )
1936
1847
  );
1937
1848
  }
@@ -1950,233 +1861,30 @@ function memoryOrigin(idempotencyKey) {
1950
1861
  if (idempotencyKey?.startsWith("tool:")) return "explicit";
1951
1862
  return "other";
1952
1863
  }
1953
- function memoryVisibility(scope) {
1954
- return scope === "personal" ? "private" : "public";
1955
- }
1956
- function personalMemoryRecord(row) {
1864
+ function toMemoryView(row) {
1957
1865
  const memory = parseMemoryRow(row);
1958
1866
  return {
1959
1867
  ...memory,
1960
1868
  origin: memoryOrigin(row.idempotencyKey),
1961
1869
  sourcePlatform: row.sourcePlatform,
1962
- visibility: memoryVisibility(memory.scope)
1870
+ visibility: memory.scope
1963
1871
  };
1964
1872
  }
1965
- function emptyStats() {
1873
+ function cursorFilters(input) {
1966
1874
  return {
1967
- active: 0,
1968
- automatic: 0,
1969
- createdThirtyDays: 0,
1970
- embedded: 0,
1971
- explicit: 0,
1972
- knowledge: 0,
1973
- personal: 0,
1974
- preference: 0,
1975
- procedure: 0,
1976
- public: 0
1977
- };
1978
- }
1979
- function createPersonalMemoryCollection(db, scopes, options = {}) {
1980
- const { privateScopes, publicScopes } = scopes;
1981
- const allScopes = [...privateScopes, ...publicScopes];
1982
- const getNowMs = () => options.now?.() ?? Date.now();
1983
- function scopesForVisibility(visibility) {
1984
- if (visibility === "private") return privateScopes;
1985
- if (visibility === "public") return publicScopes;
1986
- return allScopes;
1987
- }
1988
- return {
1989
- async archive(id) {
1990
- const memoryId = nonEmptyStringSchema3.parse(id);
1991
- const nowMs = getNowMs();
1992
- const predicate = activeVisiblePredicate({
1993
- nowMs,
1994
- scopes: privateScopes
1995
- });
1996
- if (!predicate) {
1997
- throw new PersonalMemoryNotFoundError();
1998
- }
1999
- const updated = await db.update(juniorMemoryMemories).set({
2000
- archivedAtMs: nowMs,
2001
- archiveReason: "user_removed"
2002
- }).where(and2(predicate, eq2(juniorMemoryMemories.id, memoryId))).returning();
2003
- if (!updated[0]) {
2004
- throw new PersonalMemoryNotFoundError();
2005
- }
2006
- await db.delete(juniorMemoryEmbeddings).where(eq2(juniorMemoryEmbeddings.memoryId, memoryId));
2007
- return parseMemoryRow(updated[0]);
2008
- },
2009
- async get(id) {
2010
- const memoryId = nonEmptyStringSchema3.parse(id);
2011
- const nowMs = getNowMs();
2012
- const predicate = activeVisiblePredicate({ nowMs, scopes: allScopes });
2013
- if (!predicate) {
2014
- throw new PersonalMemoryNotFoundError();
2015
- }
2016
- const rows = await db.select().from(juniorMemoryMemories).where(and2(predicate, eq2(juniorMemoryMemories.id, memoryId))).limit(1);
2017
- if (!rows[0]) {
2018
- throw new PersonalMemoryNotFoundError();
2019
- }
2020
- return personalMemoryRecord(rows[0]);
2021
- },
2022
- async list(input) {
2023
- input = personalMemoryPageInputSchema.parse(input);
2024
- const nowMs = getNowMs();
2025
- const scopes2 = scopesForVisibility(input.visibility);
2026
- await archiveExpiredMemoryBatch({ db, nowMs, scopes: scopes2 });
2027
- const active = activeVisiblePredicate({ nowMs, scopes: scopes2 });
2028
- if (!active) {
2029
- return { memories: [] };
2030
- }
2031
- const cursor = input.cursor ? or2(
2032
- lt(juniorMemoryMemories.createdAtMs, input.cursor.createdAtMs),
2033
- and2(
2034
- eq2(juniorMemoryMemories.createdAtMs, input.cursor.createdAtMs),
2035
- gt2(juniorMemoryMemories.id, input.cursor.id)
2036
- )
2037
- ) : void 0;
2038
- const terms = input.query ? searchTerms(input.query) : [];
2039
- const search = input.query === void 0 ? void 0 : terms.length === 0 ? sql3`false` : or2(
2040
- ...terms.map(
2041
- (term) => ilike(juniorMemoryMemories.content, `%${term}%`)
2042
- )
2043
- );
2044
- const kind = input.kind ? eq2(juniorMemoryMemories.kind, input.kind) : void 0;
2045
- const origin = input.origin === "automatic" ? like2(juniorMemoryMemories.idempotencyKey, "session:%") : input.origin === "explicit" ? like2(juniorMemoryMemories.idempotencyKey, "tool:%") : void 0;
2046
- const rows = await db.select().from(juniorMemoryMemories).where(and2(active, cursor, search, kind, origin)).orderBy(
2047
- desc2(juniorMemoryMemories.createdAtMs),
2048
- asc2(juniorMemoryMemories.id)
2049
- ).limit(input.limit + 1);
2050
- const hasNextPage = rows.length > input.limit;
2051
- const memories = rows.slice(0, input.limit).map(personalMemoryRecord);
2052
- const last = memories.at(-1);
2053
- return {
2054
- memories,
2055
- ...hasNextPage && last ? {
2056
- nextCursor: {
2057
- createdAtMs: last.createdAtMs,
2058
- id: last.id
2059
- }
2060
- } : void 0
2061
- };
2062
- },
2063
- async stats() {
2064
- const nowMs = getNowMs();
2065
- await archiveExpiredMemoryBatch({ db, nowMs, scopes: allScopes });
2066
- const active = activeVisiblePredicate({ nowMs, scopes: allScopes });
2067
- if (!active) {
2068
- return emptyStats();
2069
- }
2070
- const [counts] = await db.select({
2071
- active: sql3`count(*)`.mapWith(Number),
2072
- automatic: sql3`count(*) filter (where ${juniorMemoryMemories.idempotencyKey} like 'session:%')`.mapWith(
2073
- Number
2074
- ),
2075
- createdThirtyDays: sql3`count(*) filter (where ${juniorMemoryMemories.createdAtMs} >= ${nowMs - 30 * 24 * 60 * 60 * 1e3})`.mapWith(
2076
- Number
2077
- ),
2078
- embedded: sql3`count(${juniorMemoryEmbeddings.memoryId})`.mapWith(
2079
- Number
2080
- ),
2081
- explicit: sql3`count(*) filter (where ${juniorMemoryMemories.idempotencyKey} like 'tool:%')`.mapWith(
2082
- Number
2083
- ),
2084
- knowledge: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'knowledge')`.mapWith(
2085
- Number
2086
- ),
2087
- personal: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'personal')`.mapWith(
2088
- Number
2089
- ),
2090
- preference: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'preference')`.mapWith(
2091
- Number
2092
- ),
2093
- procedure: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'procedure')`.mapWith(
2094
- Number
2095
- ),
2096
- public: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'conversation')`.mapWith(
2097
- Number
2098
- )
2099
- }).from(juniorMemoryMemories).leftJoin(
2100
- juniorMemoryEmbeddings,
2101
- eq2(juniorMemoryEmbeddings.memoryId, juniorMemoryMemories.id)
2102
- ).where(active);
2103
- return {
2104
- active: counts?.active ?? 0,
2105
- automatic: counts?.automatic ?? 0,
2106
- createdThirtyDays: counts?.createdThirtyDays ?? 0,
2107
- embedded: counts?.embedded ?? 0,
2108
- explicit: counts?.explicit ?? 0,
2109
- knowledge: counts?.knowledge ?? 0,
2110
- personal: counts?.personal ?? 0,
2111
- preference: counts?.preference ?? 0,
2112
- procedure: counts?.procedure ?? 0,
2113
- public: counts?.public ?? 0
2114
- };
2115
- },
2116
- async timeline(input) {
2117
- input = personalMemoryTimelineInputSchema.parse(input);
2118
- const todayMs = Date.parse(`${utcDate(getNowMs())}T00:00:00.000Z`);
2119
- const startMs = todayMs - (input.days - 1) * DAY_MS;
2120
- const ownership = scopePredicate(allScopes);
2121
- if (!ownership) {
2122
- return Array.from({ length: input.days }, (_, index2) => ({
2123
- date: utcDate(startMs + index2 * DAY_MS),
2124
- personal: 0,
2125
- public: 0
2126
- }));
2127
- }
2128
- const rows = await db.select({
2129
- date: sql3`to_char(to_timestamp(${juniorMemoryMemories.createdAtMs} / 1000.0) AT TIME ZONE 'UTC', 'YYYY-MM-DD')`.as(
2130
- "date"
2131
- ),
2132
- personal: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'personal')`.mapWith(
2133
- Number
2134
- ),
2135
- public: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'conversation')`.mapWith(
2136
- Number
2137
- )
2138
- }).from(juniorMemoryMemories).where(
2139
- and2(ownership, gt2(juniorMemoryMemories.createdAtMs, startMs - 1))
2140
- ).groupBy(
2141
- sql3`to_char(to_timestamp(${juniorMemoryMemories.createdAtMs} / 1000.0) AT TIME ZONE 'UTC', 'YYYY-MM-DD')`
2142
- );
2143
- const byDate = new Map(rows.map((row) => [row.date, row]));
2144
- return Array.from({ length: input.days }, (_, index2) => {
2145
- const date = utcDate(startMs + index2 * DAY_MS);
2146
- const row = byDate.get(date);
2147
- return {
2148
- date,
2149
- personal: row?.personal ?? 0,
2150
- public: row?.public ?? 0
2151
- };
2152
- });
2153
- }
1875
+ kind: input.kind,
1876
+ origin: input.origin,
1877
+ query: input.query?.trim() || void 0,
1878
+ visibility: input.visibility
2154
1879
  };
2155
1880
  }
2156
-
2157
- // src/personal.ts
2158
- var cursorSchema = z5.object({
2159
- createdAtMs: z5.number().finite(),
2160
- id: z5.string().min(1),
2161
- kind: z5.enum(["preference", "procedure", "knowledge"]).optional(),
2162
- origin: z5.enum(["automatic", "explicit"]).optional(),
2163
- query: z5.string().max(200).optional(),
2164
- version: z5.literal(1),
2165
- visibility: z5.enum(["private", "public"]).optional()
2166
- }).strict();
2167
- var InvalidMemoryCursorError = class extends Error {
2168
- constructor() {
2169
- super("Memory cursor is invalid.");
2170
- this.name = "InvalidMemoryCursorError";
2171
- }
2172
- };
2173
- function decodeCursor(value, input) {
1881
+ function decodeCursor(value, filters) {
2174
1882
  if (!value) return void 0;
2175
1883
  try {
2176
1884
  const parsed = cursorSchema.parse(
2177
1885
  JSON.parse(Buffer.from(value, "base64url").toString("utf8"))
2178
1886
  );
2179
- if (parsed.query !== input.query || parsed.kind !== input.kind || parsed.origin !== input.origin || parsed.visibility !== input.visibility) {
1887
+ if (parsed.query !== filters.query || parsed.kind !== filters.kind || parsed.origin !== filters.origin || parsed.visibility !== filters.visibility) {
2180
1888
  throw new InvalidMemoryCursorError();
2181
1889
  }
2182
1890
  return { createdAtMs: parsed.createdAtMs, id: parsed.id };
@@ -2184,106 +1892,190 @@ function decodeCursor(value, input) {
2184
1892
  throw new InvalidMemoryCursorError();
2185
1893
  }
2186
1894
  }
2187
- function encodeCursor(cursor, input) {
1895
+ function encodeCursor(createdBefore, filters) {
2188
1896
  return Buffer.from(
2189
- JSON.stringify({
2190
- ...cursor,
2191
- ...input.query ? { query: input.query } : void 0,
2192
- ...input.kind ? { kind: input.kind } : void 0,
2193
- ...input.origin ? { origin: input.origin } : void 0,
2194
- ...input.visibility ? { visibility: input.visibility } : void 0,
2195
- version: 1
2196
- }),
1897
+ JSON.stringify({ ...createdBefore, ...filters, version: 1 }),
2197
1898
  "utf8"
2198
1899
  ).toString("base64url");
2199
1900
  }
2200
- function createViewerMemories(db, user) {
2201
- const collection = createPersonalMemoryCollection(
2202
- db,
2203
- deriveViewerMemoryScopes(user.identities)
1901
+ async function archiveMemory(db, userId, id) {
1902
+ const memoryId = nonEmptyStringSchema3.parse(id);
1903
+ const nowMs = Date.now();
1904
+ const updated = await db.update(juniorMemoryMemories).set({
1905
+ archivedAtMs: nowMs,
1906
+ archiveReason: "user_removed"
1907
+ }).where(
1908
+ and2(
1909
+ activeMemoryPredicate(userId, nowMs, "private"),
1910
+ eq2(juniorMemoryMemories.id, memoryId)
1911
+ )
1912
+ ).returning();
1913
+ if (!updated[0]) throw new MemoryNotFoundError();
1914
+ await db.delete(juniorMemoryEmbeddings).where(eq2(juniorMemoryEmbeddings.memoryId, memoryId));
1915
+ return parseMemoryRow(updated[0]);
1916
+ }
1917
+ async function getMemory(db, userId, id) {
1918
+ const memoryId = nonEmptyStringSchema3.parse(id);
1919
+ const nowMs = Date.now();
1920
+ const rows = await db.select().from(juniorMemoryMemories).where(
1921
+ and2(
1922
+ activeMemoryPredicate(userId, nowMs),
1923
+ eq2(juniorMemoryMemories.id, memoryId)
1924
+ )
1925
+ ).limit(1);
1926
+ if (!rows[0]) throw new MemoryNotFoundError();
1927
+ return toMemoryView(rows[0]);
1928
+ }
1929
+ async function listMemories(db, userId, input) {
1930
+ input = pageInputSchema.parse(input);
1931
+ const filters = cursorFilters(input);
1932
+ const cursor = decodeCursor(input.cursor, filters);
1933
+ const active = activeMemoryPredicate(userId, Date.now(), input.visibility);
1934
+ const createdBefore = cursor ? or2(
1935
+ lt(juniorMemoryMemories.createdAtMs, cursor.createdAtMs),
1936
+ and2(
1937
+ eq2(juniorMemoryMemories.createdAtMs, cursor.createdAtMs),
1938
+ gt2(juniorMemoryMemories.id, cursor.id)
1939
+ )
1940
+ ) : void 0;
1941
+ const terms = input.query ? searchTerms(input.query) : [];
1942
+ const search = input.query === void 0 ? void 0 : terms.length === 0 ? sql3`false` : or2(
1943
+ ...terms.map(
1944
+ (term) => ilike(juniorMemoryMemories.content, `%${term}%`)
1945
+ )
2204
1946
  );
2205
- return {
2206
- async archive(id) {
2207
- return await collection.archive(id);
2208
- },
2209
- async get(id) {
2210
- return await collection.get(id);
2211
- },
2212
- async list(input) {
2213
- const query = input.query?.trim() || void 0;
2214
- const filters = {
2215
- ...input.kind ? { kind: input.kind } : void 0,
2216
- ...input.origin ? { origin: input.origin } : void 0,
2217
- ...query ? { query } : void 0,
2218
- ...input.visibility ? { visibility: input.visibility } : void 0
2219
- };
2220
- const page = await collection.list({
2221
- cursor: decodeCursor(input.cursor, filters),
2222
- ...filters,
2223
- limit: input.limit
2224
- });
2225
- return {
2226
- memories: page.memories,
2227
- ...page.nextCursor ? { nextCursor: encodeCursor(page.nextCursor, filters) } : void 0
2228
- };
2229
- },
2230
- async stats() {
2231
- return await collection.stats();
2232
- },
2233
- async timeline(input) {
2234
- return await collection.timeline(input);
2235
- }
2236
- };
1947
+ const kind = input.kind ? eq2(juniorMemoryMemories.kind, input.kind) : void 0;
1948
+ const origin = input.origin === "automatic" ? like2(juniorMemoryMemories.idempotencyKey, "session:%") : input.origin === "explicit" ? like2(juniorMemoryMemories.idempotencyKey, "tool:%") : void 0;
1949
+ const rows = await db.select().from(juniorMemoryMemories).where(and2(active, createdBefore, search, kind, origin)).orderBy(
1950
+ desc2(juniorMemoryMemories.createdAtMs),
1951
+ asc2(juniorMemoryMemories.id)
1952
+ ).limit(input.limit + 1);
1953
+ const memories = rows.slice(0, input.limit).map(toMemoryView);
1954
+ const last = memories.at(-1);
1955
+ if (rows.length <= input.limit || !last) return { memories };
1956
+ const next = { createdAtMs: last.createdAtMs, id: last.id };
1957
+ return { memories, nextCursor: encodeCursor(next, filters) };
1958
+ }
1959
+ async function getMemoryStats(db, userId) {
1960
+ const nowMs = Date.now();
1961
+ const [counts] = await db.select({
1962
+ active: sql3`count(*)`.mapWith(Number),
1963
+ automatic: sql3`count(*) filter (where ${juniorMemoryMemories.idempotencyKey} like 'session:%')`.mapWith(
1964
+ Number
1965
+ ),
1966
+ createdThirtyDays: sql3`count(*) filter (where ${juniorMemoryMemories.createdAtMs} >= ${nowMs - 30 * DAY_MS})`.mapWith(
1967
+ Number
1968
+ ),
1969
+ embedded: sql3`count(${juniorMemoryEmbeddings.memoryId})`.mapWith(
1970
+ Number
1971
+ ),
1972
+ explicit: sql3`count(*) filter (where ${juniorMemoryMemories.idempotencyKey} like 'tool:%')`.mapWith(
1973
+ Number
1974
+ ),
1975
+ knowledge: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'knowledge')`.mapWith(
1976
+ Number
1977
+ ),
1978
+ private: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'private')`.mapWith(
1979
+ Number
1980
+ ),
1981
+ preference: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'preference')`.mapWith(
1982
+ Number
1983
+ ),
1984
+ procedure: sql3`count(*) filter (where ${juniorMemoryMemories.kind} = 'procedure')`.mapWith(
1985
+ Number
1986
+ ),
1987
+ public: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'public')`.mapWith(
1988
+ Number
1989
+ )
1990
+ }).from(juniorMemoryMemories).leftJoin(
1991
+ juniorMemoryEmbeddings,
1992
+ eq2(juniorMemoryEmbeddings.memoryId, juniorMemoryMemories.id)
1993
+ ).where(activeMemoryPredicate(userId, nowMs));
1994
+ if (!counts) throw new Error("Memory stats query returned no row.");
1995
+ return counts;
1996
+ }
1997
+ async function getMemoryTimeline(db, userId, days) {
1998
+ days = timelineDaysSchema.parse(days);
1999
+ const todayMs = Date.parse(`${utcDate(Date.now())}T00:00:00.000Z`);
2000
+ const startMs = todayMs - (days - 1) * DAY_MS;
2001
+ const rows = await db.select({
2002
+ date: sql3`to_char(to_timestamp(${juniorMemoryMemories.createdAtMs} / 1000.0) AT TIME ZONE 'UTC', 'YYYY-MM-DD')`.as(
2003
+ "date"
2004
+ ),
2005
+ private: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'private')`.mapWith(
2006
+ Number
2007
+ ),
2008
+ public: sql3`count(*) filter (where ${juniorMemoryMemories.scope} = 'public')`.mapWith(
2009
+ Number
2010
+ )
2011
+ }).from(juniorMemoryMemories).where(
2012
+ and2(
2013
+ visibleScopePredicate2(userId),
2014
+ gt2(juniorMemoryMemories.createdAtMs, startMs - 1)
2015
+ )
2016
+ ).groupBy(
2017
+ sql3`to_char(to_timestamp(${juniorMemoryMemories.createdAtMs} / 1000.0) AT TIME ZONE 'UTC', 'YYYY-MM-DD')`
2018
+ );
2019
+ const byDate = new Map(rows.map((row) => [row.date, row]));
2020
+ return Array.from({ length: days }, (_, index2) => {
2021
+ const date = utcDate(startMs + index2 * DAY_MS);
2022
+ const row = byDate.get(date);
2023
+ return {
2024
+ date,
2025
+ private: row?.private ?? 0,
2026
+ public: row?.public ?? 0
2027
+ };
2028
+ });
2237
2029
  }
2238
2030
 
2239
2031
  // src/api.ts
2240
- var memoryApiSchema = z6.object({
2241
- content: z6.string().min(1),
2242
- createdAt: z6.iso.datetime(),
2243
- expiresAt: z6.iso.datetime().optional(),
2244
- id: z6.string().min(1),
2245
- kind: z6.enum(["preference", "procedure", "knowledge"]),
2246
- observedAt: z6.iso.datetime(),
2247
- origin: z6.enum(["automatic", "explicit", "other"]),
2248
- sourcePlatform: z6.enum(MEMORY_SOURCE_PLATFORMS),
2249
- visibility: z6.enum(["private", "public"])
2032
+ var memoryApiSchema = z5.object({
2033
+ content: z5.string().min(1),
2034
+ createdAt: z5.iso.datetime(),
2035
+ expiresAt: z5.iso.datetime().optional(),
2036
+ id: z5.string().min(1),
2037
+ kind: z5.enum(["preference", "procedure", "knowledge"]),
2038
+ observedAt: z5.iso.datetime(),
2039
+ origin: z5.enum(["automatic", "explicit", "other"]),
2040
+ sourcePlatform: z5.enum(MEMORY_SOURCE_PLATFORMS),
2041
+ visibility: z5.enum(["private", "public"])
2250
2042
  }).strict();
2251
- var memoryListResponseSchema = z6.object({
2252
- memories: z6.array(memoryApiSchema),
2253
- nextCursor: z6.string().min(1).optional()
2043
+ var memoryListResponseSchema = z5.object({
2044
+ memories: z5.array(memoryApiSchema),
2045
+ nextCursor: z5.string().min(1).optional()
2254
2046
  }).strict();
2255
- var memoryDashboardDaySchema = z6.object({
2256
- date: z6.iso.date(),
2257
- personal: z6.number().int().min(0),
2258
- public: z6.number().int().min(0)
2047
+ var memoryDashboardDaySchema = z5.object({
2048
+ date: z5.iso.date(),
2049
+ personal: z5.number().int().min(0),
2050
+ public: z5.number().int().min(0)
2259
2051
  }).strict();
2260
- var memoryCostDaySchema = z6.object({
2261
- costUsd: z6.number().finite().nonnegative(),
2262
- date: z6.iso.date(),
2263
- events: z6.number().int().min(0)
2052
+ var memoryCostDaySchema = z5.object({
2053
+ costUsd: z5.number().finite().nonnegative(),
2054
+ date: z5.iso.date(),
2055
+ events: z5.number().int().min(0)
2264
2056
  }).strict();
2265
- var memoryDashboardResponseSchema = z6.object({
2266
- days: z6.array(memoryDashboardDaySchema).length(90),
2267
- extractionDays: z6.array(memoryCostDaySchema).length(90),
2268
- generatedAt: z6.iso.datetime(),
2269
- recallDays: z6.array(memoryCostDaySchema).length(90),
2270
- stats: z6.object({
2271
- active: z6.number().int().min(0),
2272
- automatic: z6.number().int().min(0),
2273
- createdThirtyDays: z6.number().int().min(0),
2274
- embedded: z6.number().int().min(0),
2275
- explicit: z6.number().int().min(0),
2276
- knowledge: z6.number().int().min(0),
2277
- personal: z6.number().int().min(0),
2278
- preference: z6.number().int().min(0),
2279
- procedure: z6.number().int().min(0),
2280
- public: z6.number().int().min(0)
2057
+ var memoryDashboardResponseSchema = z5.object({
2058
+ days: z5.array(memoryDashboardDaySchema).length(90),
2059
+ extractionDays: z5.array(memoryCostDaySchema).length(90),
2060
+ generatedAt: z5.iso.datetime(),
2061
+ recallDays: z5.array(memoryCostDaySchema).length(90),
2062
+ stats: z5.object({
2063
+ active: z5.number().int().min(0),
2064
+ automatic: z5.number().int().min(0),
2065
+ createdThirtyDays: z5.number().int().min(0),
2066
+ embedded: z5.number().int().min(0),
2067
+ explicit: z5.number().int().min(0),
2068
+ knowledge: z5.number().int().min(0),
2069
+ personal: z5.number().int().min(0),
2070
+ preference: z5.number().int().min(0),
2071
+ procedure: z5.number().int().min(0),
2072
+ public: z5.number().int().min(0)
2281
2073
  }).strict()
2282
2074
  }).strict();
2283
- var memoryListQuerySchema = z6.object({
2284
- cursor: z6.string().min(1).max(1e3).optional(),
2285
- limit: z6.coerce.number().int().min(1).max(50).default(25),
2286
- q: z6.string().trim().max(200).optional()
2075
+ var memoryListQuerySchema = z5.object({
2076
+ cursor: z5.string().min(1).max(1e3).optional(),
2077
+ limit: z5.coerce.number().int().min(1).max(50).default(25),
2078
+ q: z5.string().trim().max(200).optional()
2287
2079
  }).strict();
2288
2080
  function json(body, status = 200) {
2289
2081
  return Response.json(body, {
@@ -2329,14 +2121,14 @@ function createMemoryApi(options) {
2329
2121
  if (!isRead && !(memoryPath && request.method === "DELETE")) {
2330
2122
  return json({ error: "Method not allowed." }, 405);
2331
2123
  }
2332
- const user = await options.users.resolve(email);
2333
- if (!user) return json({ error: "Authentication required." }, 401);
2334
- const memories = createViewerMemories(options.db, user);
2124
+ const viewer = await options.users.resolve(email);
2125
+ if (!viewer) return json({ error: "Authentication required." }, 401);
2126
+ const userId = viewer.id;
2335
2127
  try {
2336
2128
  if (isDashboard && isRead) {
2337
2129
  const [stats, days, extractionDays, recallDays] = await Promise.all([
2338
- memories.stats(),
2339
- memories.timeline({ days: 90 }),
2130
+ getMemoryStats(options.db, userId),
2131
+ getMemoryTimeline(options.db, userId, 90),
2340
2132
  options.eventStats.costsByDay({
2341
2133
  days: 90,
2342
2134
  eventName: "memories_captured"
@@ -2346,12 +2138,16 @@ function createMemoryApi(options) {
2346
2138
  eventName: "memories_recalled"
2347
2139
  })
2348
2140
  ]);
2141
+ const { private: personal, ...dashboardStats } = stats;
2349
2142
  const body = memoryDashboardResponseSchema.parse({
2350
- days,
2143
+ days: days.map(({ private: personal2, ...day }) => ({
2144
+ ...day,
2145
+ personal: personal2
2146
+ })),
2351
2147
  extractionDays,
2352
2148
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2353
2149
  recallDays,
2354
- stats
2150
+ stats: { ...dashboardStats, personal }
2355
2151
  });
2356
2152
  return request.method === "HEAD" ? new Response(null, {
2357
2153
  headers: { "cache-control": "no-store" },
@@ -2364,7 +2160,7 @@ function createMemoryApi(options) {
2364
2160
  limit: url.searchParams.get("limit") ?? void 0,
2365
2161
  q: url.searchParams.get("q") ?? void 0
2366
2162
  });
2367
- const page = await memories.list({
2163
+ const page = await listMemories(options.db, userId, {
2368
2164
  cursor: query.cursor,
2369
2165
  limit: query.limit,
2370
2166
  ...query.q ? { query: query.q } : void 0
@@ -2379,26 +2175,30 @@ function createMemoryApi(options) {
2379
2175
  }) : json(body);
2380
2176
  }
2381
2177
  if (memoryPath && isRead) {
2382
- const memory = memoryApiSchema.parse(
2383
- apiMemory(await memories.get(decodeURIComponent(memoryPath[1])))
2178
+ const memory = await getMemory(
2179
+ options.db,
2180
+ userId,
2181
+ decodeURIComponent(memoryPath[1])
2384
2182
  );
2183
+ const body = memoryApiSchema.parse(apiMemory(memory));
2385
2184
  return request.method === "HEAD" ? new Response(null, {
2386
2185
  headers: { "cache-control": "no-store" },
2387
2186
  status: 200
2388
- }) : json(memory);
2187
+ }) : json(body);
2389
2188
  }
2390
2189
  if (memoryPath && request.method === "DELETE") {
2391
- await memories.archive(decodeURIComponent(memoryPath[1]));
2190
+ const id = decodeURIComponent(memoryPath[1]);
2191
+ await archiveMemory(options.db, userId, id);
2392
2192
  return new Response(null, {
2393
2193
  headers: { "cache-control": "no-store" },
2394
2194
  status: 204
2395
2195
  });
2396
2196
  }
2397
2197
  } catch (error) {
2398
- if (error instanceof z6.ZodError || error instanceof InvalidMemoryCursorError) {
2198
+ if (error instanceof z5.ZodError || error instanceof InvalidMemoryCursorError) {
2399
2199
  return json({ error: "Invalid memory request." }, 400);
2400
2200
  }
2401
- if (error instanceof PersonalMemoryNotFoundError) {
2201
+ if (error instanceof MemoryNotFoundError) {
2402
2202
  return json({ error: error.message }, 404);
2403
2203
  }
2404
2204
  throw error;
@@ -2410,7 +2210,7 @@ function createMemoryApi(options) {
2410
2210
 
2411
2211
  // src/cli/search.ts
2412
2212
  import { InvalidArgumentError, Option } from "commander";
2413
- import { and as and3, desc as desc3, eq as eq3, gt as gt3, ilike as ilike2, isNull as isNull2, or as or3 } from "drizzle-orm";
2213
+ import { and as and3, desc as desc3, eq as eq3, gt as gt3, ilike as ilike2, isNull as isNull3, or as or3 } from "drizzle-orm";
2414
2214
 
2415
2215
  // src/cli/format.ts
2416
2216
  function formatDate(ms) {
@@ -2453,15 +2253,15 @@ async function runSearch(ctx, queryParts, options) {
2453
2253
  ];
2454
2254
  const db = ctx.db;
2455
2255
  const activeExpirationPredicate = or3(
2456
- isNull2(juniorMemoryMemories.expiresAtMs),
2256
+ isNull3(juniorMemoryMemories.expiresAtMs),
2457
2257
  gt3(juniorMemoryMemories.expiresAtMs, nowMs)
2458
2258
  );
2459
2259
  const predicates = [
2460
2260
  eq3(juniorMemoryMemories.scope, options.scope),
2461
2261
  eq3(juniorMemoryMemories.scopeKey, options.scopeKey),
2462
- isNull2(juniorMemoryMemories.archivedAtMs),
2463
- isNull2(juniorMemoryMemories.supersededAtMs),
2464
- isNull2(juniorMemoryMemories.supersededById)
2262
+ isNull3(juniorMemoryMemories.archivedAtMs),
2263
+ isNull3(juniorMemoryMemories.supersededAtMs),
2264
+ isNull3(juniorMemoryMemories.supersededById)
2465
2265
  ];
2466
2266
  if (activeExpirationPredicate) {
2467
2267
  predicates.push(activeExpirationPredicate);
@@ -2542,11 +2342,11 @@ import { Type } from "@sinclair/typebox";
2542
2342
  import { Value } from "@sinclair/typebox/value";
2543
2343
  import {
2544
2344
  definePluginTool,
2545
- getSourceKey,
2345
+ getSourceKey as getSourceKey2,
2546
2346
  PluginToolInputError,
2547
2347
  pluginToolOutputSchema
2548
2348
  } from "@sentry/junior-plugin-api";
2549
- import { z as z7 } from "zod";
2349
+ import { z as z6 } from "zod";
2550
2350
  var MAX_TOOL_CONTENT_CHARS = 4e3;
2551
2351
  var DEFAULT_RESULT_LIMIT = 20;
2552
2352
  var DEFAULT_SEARCH_LIMIT2 = 10;
@@ -2558,8 +2358,8 @@ var KNOWN_TOOL_INPUT_ERROR_MESSAGES = /* @__PURE__ */ new Set([
2558
2358
  "Memory id is required.",
2559
2359
  "Memory was not found in the current context.",
2560
2360
  "Memory id prefix is ambiguous.",
2561
- "Personal memory requires actor context.",
2562
- "User-subject memory requires actor context."
2361
+ "Private memory requires a User.",
2362
+ "User memory requires a User."
2563
2363
  ]);
2564
2364
  function throwToolInputError(message) {
2565
2365
  throw new PluginToolInputError(message);
@@ -2573,15 +2373,18 @@ function asToolInputError(error) {
2573
2373
  }
2574
2374
  throw error;
2575
2375
  }
2576
- function memoryRuntimeContext(context) {
2376
+ async function memoryRuntimeContext(context) {
2377
+ const actorUser = (await context.users.resolveActor())?.user;
2577
2378
  return memoryRuntimeContextSchema.parse({
2578
2379
  ...context.conversationId ? { conversationId: context.conversationId } : void 0,
2579
2380
  ...context.actor ? { actor: context.actor } : void 0,
2580
- source: context.source
2381
+ ...context.locationId ? { locationId: context.locationId } : void 0,
2382
+ source: context.source,
2383
+ ...actorUser ? { userId: actorUser.id } : void 0
2581
2384
  });
2582
2385
  }
2583
- function memoryStore(context, options = {}) {
2584
- return createMemoryStore(context.db, memoryRuntimeContext(context), {
2386
+ function memoryStore(context, runtimeContext, options = {}) {
2387
+ return createMemoryStore(context.db, runtimeContext, {
2585
2388
  embedder: context.embedder,
2586
2389
  ...options.supersessionDecider ? { supersessionDecider: options.supersessionDecider } : void 0
2587
2390
  });
@@ -2673,23 +2476,23 @@ function requireMemoryContent(value) {
2673
2476
  }
2674
2477
  return value;
2675
2478
  }
2676
- var createMemoryInputSchema2 = z7.object({
2677
- content: z7.string().min(1).max(MAX_TOOL_CONTENT_CHARS).describe(
2678
- "Self-contained public/shareable memory candidate. Include the subject in natural language when it matters; do not rely on surrounding chat context."
2479
+ var createMemoryInputSchema2 = z6.object({
2480
+ content: z6.string().min(1).max(MAX_TOOL_CONTENT_CHARS).describe(
2481
+ "Self-contained memory candidate. Include the subject in natural language when it matters; do not rely on surrounding chat context."
2679
2482
  ),
2680
- expires_at: z7.string().min(1).describe(
2483
+ expires_at: z6.string().min(1).describe(
2681
2484
  'Expiration selector. Omit or use "never" when the memory should not expire, or use an exact ISO timestamp such as "2027-06-21T00:00:00Z".'
2682
2485
  ).optional()
2683
2486
  }).strict();
2684
- var removeMemoryInputSchema = z7.object({
2685
- id: z7.string().min(1).describe("Memory id or unambiguous short id prefix to remove.")
2487
+ var removeMemoryInputSchema = z6.object({
2488
+ id: z6.string().min(1).describe("Memory id or unambiguous short id prefix to remove.")
2686
2489
  }).strict();
2687
- var listMemoriesInputSchema2 = z7.object({
2688
- limit: z7.number().min(1).max(50).describe("Maximum number of visible memories to return.").optional()
2490
+ var listMemoriesInputSchema2 = z6.object({
2491
+ limit: z6.number().min(1).max(50).describe("Maximum number of visible memories to return.").optional()
2689
2492
  }).strict();
2690
- var searchMemoriesInputSchema2 = z7.object({
2691
- query: z7.string().min(1).describe("Search query for visible memory content."),
2692
- limit: z7.number().min(1).max(50).describe("Maximum number of matching memories to return.").optional()
2493
+ var searchMemoriesInputSchema2 = z6.object({
2494
+ query: z6.string().min(1).describe("Search query for visible memory content."),
2495
+ limit: z6.number().min(1).max(50).describe("Maximum number of matching memories to return.").optional()
2693
2496
  }).strict();
2694
2497
  var memoryToolProjectionSchema = Type.Object(
2695
2498
  {
@@ -2701,25 +2504,25 @@ var memoryToolProjectionSchema = Type.Object(
2701
2504
  },
2702
2505
  { additionalProperties: false }
2703
2506
  );
2704
- var memoryProjectionOutputSchema = z7.object({
2705
- id: z7.string(),
2706
- content: z7.string(),
2707
- createdAtMs: z7.number(),
2708
- observedAtMs: z7.number(),
2709
- expiresAtMs: z7.number().optional()
2507
+ var memoryProjectionOutputSchema = z6.object({
2508
+ id: z6.string(),
2509
+ content: z6.string(),
2510
+ createdAtMs: z6.number(),
2511
+ observedAtMs: z6.number(),
2512
+ expiresAtMs: z6.number().optional()
2710
2513
  });
2711
2514
  var memoryCreateOutputSchema = pluginToolOutputSchema.extend({
2712
- target: z7.string(),
2713
- created: z7.boolean(),
2515
+ target: z6.string(),
2516
+ created: z6.boolean(),
2714
2517
  memory: memoryProjectionOutputSchema
2715
2518
  });
2716
2519
  var memorySingleOutputSchema = pluginToolOutputSchema.extend({
2717
- target: z7.string(),
2520
+ target: z6.string(),
2718
2521
  memory: memoryProjectionOutputSchema
2719
2522
  });
2720
2523
  var memoryManyOutputSchema = pluginToolOutputSchema.extend({
2721
- target: z7.string(),
2722
- memories: z7.array(memoryProjectionOutputSchema)
2524
+ target: z6.string(),
2525
+ memories: z6.array(memoryProjectionOutputSchema)
2723
2526
  });
2724
2527
  function parseMemoryToolInput(schema, input) {
2725
2528
  const result = schema.safeParse(input);
@@ -2731,7 +2534,7 @@ function parseMemoryToolInput(schema, input) {
2731
2534
  return result.data;
2732
2535
  }
2733
2536
  function sourceIdempotencyKey(context) {
2734
- const sourceKey2 = getSourceKey(context.source);
2537
+ const sourceKey2 = getSourceKey2(context.source);
2735
2538
  if (!sourceKey2) {
2736
2539
  throwToolInputError("Memory creation requires source message context.");
2737
2540
  }
@@ -2775,7 +2578,7 @@ function createMemoryCreateTool(context) {
2775
2578
  openWorldHint: false,
2776
2579
  readOnlyHint: false
2777
2580
  },
2778
- description: "Explicit memory-write tool. Use only when the latest user message directly asks Junior to remember, store, save, or forget-and-replace a public/shareable fact. Do not use for ordinary statements like 'I prefer X', 'I use Y', or 'X goes before Y' unless the user also asks you to remember/store/save it; passive memory learning handles those after the visible reply. Pass one self-contained natural-language candidate preserving the user's explicit memory intent. Do not ask the user to rephrase ordinary first-person facts, and do not rewrite them into display-name or third-person wording. Do not include secrets, private personal details, medical/legal/financial/sensitive facts, or another person's personal preference, opinion, habit, identity, relationship, workflow, or private life. Runtime context derives actor, scope, source, and subject ids; the memory agent decides canonical stored content and memory kind, then the plugin derives storage target from kind.",
2581
+ description: "Explicit memory-write tool. Use only when the latest user message directly asks Junior to remember, store, save, or forget-and-replace a fact. Do not use for ordinary statements like 'I prefer X', 'I use Y', or 'X goes before Y' unless the user also asks you to remember/store/save it; passive memory learning handles those after the visible reply. Pass one self-contained natural-language candidate preserving the user's explicit memory intent. Do not ask the user to rephrase ordinary first-person facts, and do not rewrite them into display-name or third-person wording. Do not include secrets, private personal details, medical/legal/financial/sensitive facts, or another person's personal preference, opinion, habit, identity, relationship, workflow, or private life. Junior sets access, Location, Source, and subject. The memory agent rewrites the content and sets the memory kind.",
2779
2582
  executionMode: "sequential",
2780
2583
  inputSchema: createMemoryInputSchema2,
2781
2584
  outputSchema: memoryCreateOutputSchema,
@@ -2783,8 +2586,8 @@ function createMemoryCreateTool(context) {
2783
2586
  const parsedInput = parseMemoryToolInput(createMemoryInputSchema2, input);
2784
2587
  const toolCallId = requireToolCallId(options.toolCallId);
2785
2588
  const requestedExpiresAtMs = parseExpiresAt(parsedInput.expires_at);
2786
- const runtimeContext = memoryRuntimeContext(context);
2787
- const store = memoryStore(context, {
2589
+ const runtimeContext = await memoryRuntimeContext(context);
2590
+ const store = memoryStore(context, runtimeContext, {
2788
2591
  supersessionDecider: context.supersessionDecider
2789
2592
  });
2790
2593
  const review = await (async () => {
@@ -2853,15 +2656,16 @@ function createMemoryRemoveTool(context) {
2853
2656
  openWorldHint: false,
2854
2657
  readOnlyHint: false
2855
2658
  },
2856
- description: "Forget one memory visible in the active context. Use only ids or short id prefixes returned by listMemories or searchMemories. Never remove memories by hidden actor, Slack, scope, or subject identifiers.",
2659
+ description: "Forget one private memory owned by the current User. Public memories are read-only. Use only ids or short id prefixes returned by listMemories or searchMemories. Never remove memories by hidden Actor, provider, scope, or subject ids.",
2857
2660
  executionMode: "sequential",
2858
2661
  inputSchema: removeMemoryInputSchema,
2859
2662
  outputSchema: memorySingleOutputSchema,
2860
2663
  execute: async (input) => {
2861
2664
  const parsedInput = parseMemoryToolInput(removeMemoryInputSchema, input);
2665
+ const runtimeContext = await memoryRuntimeContext(context);
2862
2666
  const memory = await (async () => {
2863
2667
  try {
2864
- return await memoryStore(context).archiveMemory({
2668
+ return await memoryStore(context, runtimeContext).archiveMemory({
2865
2669
  id: parsedInput.id,
2866
2670
  reason: "tool_removed"
2867
2671
  });
@@ -2888,7 +2692,8 @@ function createMemoryListTool(context) {
2888
2692
  outputSchema: memoryManyOutputSchema,
2889
2693
  execute: async (input) => {
2890
2694
  const parsedInput = parseMemoryToolInput(listMemoriesInputSchema2, input);
2891
- const memories = await memoryStore(context).listMemories({
2695
+ const runtimeContext = await memoryRuntimeContext(context);
2696
+ const memories = await memoryStore(context, runtimeContext).listMemories({
2892
2697
  limit: boundedLimit2(parsedInput.limit, DEFAULT_RESULT_LIMIT)
2893
2698
  });
2894
2699
  return memoryToolResult("listMemories", {
@@ -2899,7 +2704,7 @@ function createMemoryListTool(context) {
2899
2704
  }
2900
2705
  function createMemorySearchTool(context) {
2901
2706
  return definePluginTool({
2902
- description: "Search active memories visible in the current context. Use when the model needs targeted memory recall. The tool searches only the current actor and active conversation scopes.",
2707
+ description: "Search active memories visible in the current context. Use when the model needs targeted memory recall. Public memories are visible everywhere. Private memories belong to the current User.",
2903
2708
  annotations: {
2904
2709
  destructiveHint: false,
2905
2710
  idempotentHint: true,
@@ -2913,7 +2718,11 @@ function createMemorySearchTool(context) {
2913
2718
  searchMemoriesInputSchema2,
2914
2719
  input
2915
2720
  );
2916
- const memories = await memoryStore(context).searchMemories({
2721
+ const runtimeContext = await memoryRuntimeContext(context);
2722
+ const memories = await memoryStore(
2723
+ context,
2724
+ runtimeContext
2725
+ ).searchMemories({
2917
2726
  query: parsedInput.query,
2918
2727
  limit: boundedLimit2(parsedInput.limit, DEFAULT_SEARCH_LIMIT2)
2919
2728
  });
@@ -2927,29 +2736,41 @@ function createMemorySearchTool(context) {
2927
2736
  // src/process-session.ts
2928
2737
  import { createHash as createHash2 } from "crypto";
2929
2738
  import {
2930
- getSourceKey as getSourceKey2
2739
+ getSourceKey as getSourceKey3
2931
2740
  } from "@sentry/junior-plugin-api";
2932
- import { z as z9 } from "zod";
2741
+ import { z as z8 } from "zod";
2933
2742
 
2934
2743
  // src/events.ts
2935
2744
  import { defineConversationEvent } from "@sentry/junior-plugin-api";
2936
- import { z as z8 } from "zod";
2937
- var capturedMemorySchema = z8.object({
2938
- content: z8.string().min(1),
2939
- id: z8.string().min(1),
2940
- kind: z8.enum(MEMORY_KINDS),
2941
- observedAtMs: z8.number().finite(),
2942
- scope: z8.enum(MEMORY_SCOPES)
2745
+ import { z as z7 } from "zod";
2746
+ var capturedMemoryFields = {
2747
+ content: z7.string().min(1),
2748
+ id: z7.string().min(1),
2749
+ kind: z7.enum(MEMORY_KINDS),
2750
+ observedAtMs: z7.number().finite()
2751
+ };
2752
+ var legacyCapturedMemorySchema = z7.object({
2753
+ ...capturedMemoryFields,
2754
+ scope: z7.enum(["personal", "conversation"])
2755
+ }).strict();
2756
+ var capturedMemorySchema = z7.object({
2757
+ ...capturedMemoryFields,
2758
+ scope: z7.enum(MEMORY_SCOPES)
2943
2759
  }).strict();
2944
- var capturedMemoriesSchema = z8.object({
2945
- memories: z8.array(capturedMemorySchema).max(100),
2946
- costUsd: z8.number().finite().nonnegative().optional()
2760
+ var capturedMemoriesSchema = z7.object({
2761
+ memories: z7.array(capturedMemorySchema).max(100),
2762
+ costUsd: z7.number().finite().nonnegative().optional()
2947
2763
  }).strict();
2948
- var recalledMemoriesSchema = z8.object({
2764
+ var recalledMemoriesSchema = z7.object({
2949
2765
  // Matches the automatic-recall candidate window; admission packs by char budget.
2950
- memories: z8.array(z8.string().min(1)).max(20),
2951
- costUsd: z8.number().finite().nonnegative().optional()
2766
+ memories: z7.array(z7.string().min(1)).max(20),
2767
+ costUsd: z7.number().finite().nonnegative().optional()
2952
2768
  }).strict();
2769
+ function currentScope(scope) {
2770
+ if (scope === "personal") return "private";
2771
+ if (scope === "conversation") return "public";
2772
+ return scope;
2773
+ }
2953
2774
  function renderCapturedMemories(event) {
2954
2775
  const count = event.memories.length;
2955
2776
  if (count === 0) return void 0;
@@ -2958,15 +2779,15 @@ function renderCapturedMemories(event) {
2958
2779
  title: `${count} ${count === 1 ? "memory" : "memories"} captured`,
2959
2780
  details: event.memories.map((memory) => ({
2960
2781
  title: memory.content,
2961
- metadata: [memory.kind, memory.scope]
2782
+ metadata: [memory.kind, currentScope(memory.scope)]
2962
2783
  }))
2963
2784
  };
2964
2785
  }
2965
2786
  var memoriesCapturedEventV1 = defineConversationEvent({
2966
2787
  name: "memories_captured",
2967
2788
  version: 1,
2968
- schema: z8.object({
2969
- memories: z8.array(capturedMemorySchema).min(1).max(100)
2789
+ schema: z7.object({
2790
+ memories: z7.array(legacyCapturedMemorySchema).min(1).max(100)
2970
2791
  }).strict(),
2971
2792
  renderEvent: renderCapturedMemories
2972
2793
  });
@@ -3002,28 +2823,19 @@ var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
3002
2823
  "searchMemories"
3003
2824
  ]);
3004
2825
  var MEMORY_TASK_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
3005
- var extractedMemorySchema2 = z9.object({
3006
- content: z9.string().min(1),
3007
- expiresAtMs: z9.number().finite().nullable(),
3008
- kind: z9.enum(MEMORY_KINDS),
3009
- evidenceMessageIndices: z9.array(z9.number().int().nonnegative()).min(1).max(10)
2826
+ var extractedMemorySchema2 = z8.object({
2827
+ content: z8.string().min(1),
2828
+ expiresAtMs: z8.number().finite().nullable(),
2829
+ kind: z8.enum(MEMORY_KINDS),
2830
+ evidenceMessageIndices: z8.array(z8.number().int().nonnegative()).min(1).max(10)
3010
2831
  }).strict().transform(parseExtractedMemory);
3011
- var extractedMemoryCacheSchema = z9.union([
3012
- z9.object({
3013
- costUsd: z9.number().finite().nonnegative().optional(),
3014
- memories: z9.array(extractedMemorySchema2).max(5)
2832
+ var extractedMemoryCacheSchema = z8.union([
2833
+ z8.object({
2834
+ costUsd: z8.number().finite().nonnegative().optional(),
2835
+ memories: z8.array(extractedMemorySchema2).max(5)
3015
2836
  }).strict(),
3016
- z9.array(extractedMemorySchema2).max(5).transform((memories) => ({ memories }))
2837
+ z8.array(extractedMemorySchema2).max(5).transform((memories) => ({ memories }))
3017
2838
  ]);
3018
- function allowsPassiveMemoryExtraction(source) {
3019
- switch (source.platform) {
3020
- case "local":
3021
- return true;
3022
- case "web":
3023
- case "slack":
3024
- return source.visibility === "public";
3025
- }
3026
- }
3027
2839
  function recordCapturedMemory(captured, result) {
3028
2840
  const supersededIds = new Set(result.supersededIds ?? []);
3029
2841
  for (let index2 = captured.length - 1; index2 >= 0; index2 -= 1) {
@@ -3069,11 +2881,11 @@ function routeExtractedMemory(memory, transcript, run) {
3069
2881
  return "drop";
3070
2882
  }
3071
2883
  if (memory.kind === "preference") {
3072
- const exactlyOneHumanRunActor = run.actor !== void 0 && run.actor.platform !== "system" && run.actors.length === 1 && run.actors[0]?.platform !== "system";
2884
+ const exactlyOneHumanRunActor = run.actorUserId !== void 0 && run.actor !== void 0 && run.actor.platform !== "system" && run.actors.length === 1 && run.actors[0]?.platform !== "system";
3073
2885
  if (!exactlyOneHumanRunActor) {
3074
2886
  return "drop";
3075
2887
  }
3076
- return cited.entries.every(isRunActorInstruction) ? "personal" : "drop";
2888
+ return cited.entries.every(isRunActorInstruction) ? "user" : "drop";
3077
2889
  }
3078
2890
  return cited.entries.every(
3079
2891
  (entry) => isRunActorInstruction(entry) || isConversationEvidence(entry)
@@ -3106,16 +2918,16 @@ async function getTaskExtraction(context, extract) {
3106
2918
  }
3107
2919
  async function processMemorySession(context) {
3108
2920
  const run = await context.run.load();
2921
+ if (run.source.platform === "local") {
2922
+ return;
2923
+ }
3109
2924
  if (run.transcript.some(
3110
2925
  (entry) => entry.type === "toolResult" && MEMORY_TOOL_NAMES.has(entry.toolName)
3111
2926
  )) {
3112
2927
  return;
3113
2928
  }
3114
- if (!allowsPassiveMemoryExtraction(run.source)) {
3115
- return;
3116
- }
3117
- const sourceKey2 = getSourceKey2(run.source);
3118
- if (!sourceKey2) {
2929
+ const sourceKey2 = getSourceKey3(run.source);
2930
+ if (!sourceKey2 || run.source.visibility === "private" && !run.actorUserId) {
3119
2931
  return;
3120
2932
  }
3121
2933
  const transcript = run.transcript.filter((entry) => entry.text?.trim()).map((entry) => ({ ...entry, text: entry.text.trim() }));
@@ -3125,8 +2937,10 @@ async function processMemorySession(context) {
3125
2937
  }
3126
2938
  const runtimeContext = memoryRuntimeContextSchema.parse({
3127
2939
  conversationId: run.conversationId,
2940
+ ...run.locationId ? { locationId: run.locationId } : void 0,
3128
2941
  ...run.actor ? { actor: run.actor } : void 0,
3129
- source: run.source
2942
+ source: run.source,
2943
+ ...run.actorUserId ? { userId: run.actorUserId } : void 0
3130
2944
  });
3131
2945
  const agent = createMemoryAgent(context.model);
3132
2946
  const store = createMemoryStore(context.db, runtimeContext, {
@@ -3175,7 +2989,7 @@ async function processMemorySession(context) {
3175
2989
  import {
3176
2990
  definePromptContext
3177
2991
  } from "@sentry/junior-plugin-api";
3178
- import { z as z10 } from "zod";
2992
+ import { z as z9 } from "zod";
3179
2993
  var RECALL_CANDIDATE_LIMIT = 20;
3180
2994
  var MAX_PROMPT_CHARS = 4e3;
3181
2995
  var MAX_MEMORY_LINE_CHARS = 600;
@@ -3189,16 +3003,17 @@ function trimContent(content, maxLength) {
3189
3003
  function formatObservedDate(observedAtMs) {
3190
3004
  return new Date(observedAtMs).toISOString().slice(0, 10);
3191
3005
  }
3192
- var recalledMemorySchema = z10.object({
3193
- id: z10.string().min(1),
3194
- content: z10.string().min(1).max(MAX_MEMORY_LINE_CHARS),
3195
- observedAtMs: z10.number().finite(),
3196
- scope: z10.enum(["personal", "conversation"]),
3197
- kind: z10.enum(["preference", "procedure", "knowledge"])
3006
+ var recalledMemorySchema = z9.object({
3007
+ id: z9.string().min(1),
3008
+ content: z9.string().min(1).max(MAX_MEMORY_LINE_CHARS),
3009
+ observedAtMs: z9.number().finite(),
3010
+ // Stored version 1 uses the old scope labels. Prompt rendering ignores them.
3011
+ scope: z9.enum(["personal", "conversation"]),
3012
+ kind: z9.enum(["preference", "procedure", "knowledge"])
3198
3013
  }).strict();
3199
- var memoryRecallContextSchema = z10.object({
3014
+ var memoryRecallContextSchema = z9.object({
3200
3015
  // Count is a safety rail only. Admission packs by MAX_PROMPT_CHARS.
3201
- memories: z10.array(recalledMemorySchema).min(1).max(RECALL_CANDIDATE_LIMIT)
3016
+ memories: z9.array(recalledMemorySchema).min(1).max(RECALL_CANDIDATE_LIMIT)
3202
3017
  }).strict();
3203
3018
  function selectPromptMemories(memories) {
3204
3019
  const header = "Relevant memories for this request:";
@@ -3215,7 +3030,7 @@ function selectPromptMemories(memories) {
3215
3030
  id: memory.id,
3216
3031
  content,
3217
3032
  observedAtMs: memory.observedAtMs,
3218
- scope: memory.scope,
3033
+ scope: memory.scope === "private" ? "personal" : "conversation",
3219
3034
  kind: memory.kind
3220
3035
  });
3221
3036
  totalChars += line.length + 1;
@@ -3255,10 +3070,13 @@ async function createMemoryPromptContributions(context) {
3255
3070
  if (!context.text.trim()) {
3256
3071
  return void 0;
3257
3072
  }
3073
+ const actorUser = (await context.users.resolveActor())?.user;
3258
3074
  const runtimeContext = memoryRuntimeContextSchema.parse({
3259
3075
  ...context.conversationId ? { conversationId: context.conversationId } : void 0,
3260
3076
  ...context.actor ? { actor: context.actor } : void 0,
3261
- source: context.source
3077
+ ...context.locationId ? { locationId: context.locationId } : void 0,
3078
+ source: context.source,
3079
+ ...actorUser ? { userId: actorUser.id } : void 0
3262
3080
  });
3263
3081
  let embeddingCostUsd;
3264
3082
  const sourceEmbedder = context.embedder;
@@ -3311,14 +3129,14 @@ async function createMemoryPromptContributions(context) {
3311
3129
  }
3312
3130
 
3313
3131
  // src/operational-report.ts
3314
- import { and as and4, eq as eq5, gt as gt4, isNull as isNull3, or as or4, sql as sql4 } from "drizzle-orm";
3315
- import { z as z11 } from "zod";
3132
+ import { and as and4, eq as eq5, gt as gt4, isNull as isNull4, or as or4, sql as sql4 } from "drizzle-orm";
3133
+ import { z as z10 } from "zod";
3316
3134
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
3317
3135
  var WINDOWS = [7, 30, 90];
3318
- var memoryDaySchema = z11.object({
3319
- conversation: z11.number().int().nonnegative(),
3320
- date: z11.string().date(),
3321
- personal: z11.number().int().nonnegative()
3136
+ var memoryDaySchema = z10.object({
3137
+ date: z10.string().date(),
3138
+ private: z10.number().int().nonnegative(),
3139
+ public: z10.number().int().nonnegative()
3322
3140
  }).strict();
3323
3141
  function queryRows(result) {
3324
3142
  if (typeof result !== "object" || result === null || !("rows" in result) || !Array.isArray(result.rows)) {
@@ -3350,11 +3168,11 @@ async function aggregateMemoryDays(args) {
3350
3168
  to_timestamp(${table.createdAtMs} / 1000.0) AT TIME ZONE 'UTC'
3351
3169
  ) AS day,
3352
3170
  count(*) FILTER (
3353
- WHERE ${table.scope} = 'personal'
3354
- )::integer AS personal,
3171
+ WHERE ${table.scope} = 'private'
3172
+ )::integer AS private,
3355
3173
  count(*) FILTER (
3356
- WHERE ${table.scope} = 'conversation'
3357
- )::integer AS conversation
3174
+ WHERE ${table.scope} = 'public'
3175
+ )::integer AS public
3358
3176
  FROM ${table}
3359
3177
  WHERE ${table.createdAtMs} >= ${start.getTime()}
3360
3178
  AND ${table.createdAtMs} < ${endExclusiveMs}
@@ -3365,13 +3183,13 @@ async function aggregateMemoryDays(args) {
3365
3183
  )
3366
3184
  SELECT
3367
3185
  to_char(days.day, 'YYYY-MM-DD') AS date,
3368
- coalesce(daily.personal, 0)::integer AS personal,
3369
- coalesce(daily.conversation, 0)::integer AS conversation
3186
+ coalesce(daily.private, 0)::integer AS private,
3187
+ coalesce(daily.public, 0)::integer AS public
3370
3188
  FROM days
3371
3189
  LEFT JOIN daily ON daily.day = days.day
3372
3190
  ORDER BY days.day
3373
3191
  `);
3374
- return z11.array(memoryDaySchema).parse(queryRows(result));
3192
+ return z10.array(memoryDaySchema).parse(queryRows(result));
3375
3193
  }
3376
3194
  function formatCount(value) {
3377
3195
  return new Intl.NumberFormat("en-US").format(value);
@@ -3393,18 +3211,18 @@ function formatUsd(value) {
3393
3211
  }
3394
3212
  async function buildMemoryOperationalReport(args) {
3395
3213
  const active = and4(
3396
- isNull3(juniorMemoryMemories.archivedAtMs),
3397
- isNull3(juniorMemoryMemories.supersededAtMs),
3398
- isNull3(juniorMemoryMemories.supersededById),
3214
+ isNull4(juniorMemoryMemories.archivedAtMs),
3215
+ isNull4(juniorMemoryMemories.supersededAtMs),
3216
+ isNull4(juniorMemoryMemories.supersededById),
3399
3217
  or4(
3400
- isNull3(juniorMemoryMemories.expiresAtMs),
3218
+ isNull4(juniorMemoryMemories.expiresAtMs),
3401
3219
  gt4(juniorMemoryMemories.expiresAtMs, args.nowMs)
3402
3220
  )
3403
3221
  );
3404
3222
  const [[counts], memoryDays] = await Promise.all([
3405
3223
  args.db.select({
3406
3224
  active: sql4`count(*) filter (where ${active})`.mapWith(Number),
3407
- conversation: sql4`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'conversation')`.mapWith(
3225
+ public: sql4`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'public')`.mapWith(
3408
3226
  Number
3409
3227
  ),
3410
3228
  createdThirtyDays: sql4`count(*) filter (where ${juniorMemoryMemories.createdAtMs} >= ${args.nowMs - 30 * DAY_MS2})`.mapWith(
@@ -3413,7 +3231,7 @@ async function buildMemoryOperationalReport(args) {
3413
3231
  embedded: sql4`count(${juniorMemoryEmbeddings.memoryId}) filter (where ${active})`.mapWith(
3414
3232
  Number
3415
3233
  ),
3416
- personal: sql4`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'personal')`.mapWith(
3234
+ private: sql4`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'private')`.mapWith(
3417
3235
  Number
3418
3236
  )
3419
3237
  }).from(juniorMemoryMemories).leftJoin(
@@ -3448,12 +3266,12 @@ async function buildMemoryOperationalReport(args) {
3448
3266
  value: formatCount(counts?.createdThirtyDays ?? 0)
3449
3267
  },
3450
3268
  {
3451
- label: "personal",
3452
- value: formatCount(counts?.personal ?? 0)
3269
+ label: "private",
3270
+ value: formatCount(counts?.private ?? 0)
3453
3271
  },
3454
3272
  {
3455
- label: "conversation",
3456
- value: formatCount(counts?.conversation ?? 0)
3273
+ label: "public",
3274
+ value: formatCount(counts?.public ?? 0)
3457
3275
  },
3458
3276
  {
3459
3277
  label: "embedding coverage",
@@ -3480,15 +3298,15 @@ async function buildMemoryOperationalReport(args) {
3480
3298
  id: day.date,
3481
3299
  label: day.date,
3482
3300
  values: {
3483
- conversation: day.conversation,
3484
- personal: day.personal
3301
+ private: day.private,
3302
+ public: day.public
3485
3303
  }
3486
3304
  })),
3487
3305
  description: "Memories stored per day by scope",
3488
3306
  id: "memories-created",
3489
3307
  series: [
3490
- { key: "personal", label: "Personal" },
3491
- { key: "conversation", label: "Conversation" }
3308
+ { key: "private", label: "Private" },
3309
+ { key: "public", label: "Public" }
3492
3310
  ],
3493
3311
  timeRangeDays: [...WINDOWS],
3494
3312
  title: "Memories created",
@@ -3535,8 +3353,7 @@ function createMemoryUserPage() {
3535
3353
  navigation: "primary",
3536
3354
  description: "Personal and public memories Junior can use across conversations.",
3537
3355
  async read(ctx, input) {
3538
- const memories = createViewerMemories(ctx.db, ctx.viewer);
3539
- const page = await memories.list({
3356
+ const page = await listMemories(ctx.db, ctx.viewer.id, {
3540
3357
  cursor: input.cursor,
3541
3358
  ...pageFilter(input.filter),
3542
3359
  limit: input.limit,
@@ -3597,7 +3414,9 @@ function memoryToolContext(ctx) {
3597
3414
  ...ctx.actor ? { actor: ctx.actor } : void 0,
3598
3415
  db: ctx.db,
3599
3416
  ...ctx.embedder ? { embedder: ctx.embedder } : void 0,
3417
+ ...ctx.locationId ? { locationId: ctx.locationId } : void 0,
3600
3418
  source: ctx.source,
3419
+ users: ctx.users,
3601
3420
  ...ctx.userText ? { userText: ctx.userText } : void 0
3602
3421
  };
3603
3422
  }
@@ -3684,9 +3503,11 @@ function memoryPlugin(options = {}) {
3684
3503
  db: ctx.db,
3685
3504
  embedder: ctx.embedder,
3686
3505
  events: ctx.events,
3506
+ ...ctx.locationId ? { locationId: ctx.locationId } : void 0,
3687
3507
  log: ctx.log,
3688
3508
  source: ctx.source,
3689
- text: ctx.text
3509
+ text: ctx.text,
3510
+ users: ctx.users
3690
3511
  });
3691
3512
  }
3692
3513
  } : void 0