@sentry/junior-memory 0.116.1 → 0.118.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,8 @@ exported types, tools, and tests are authoritative.
13
13
  learning.
14
14
  - The `memory` CLI namespace provides explicit administrative search and
15
15
  inspection.
16
+ - The dashboard exposes a read-only **Memories** user page for personal
17
+ memories owned by actors linked to the signed-in user.
16
18
 
17
19
  ## Scope And Visibility
18
20
 
package/dist/index.js CHANGED
@@ -949,7 +949,7 @@ function mergeSearchCandidates(candidates, nowMs, currentChannelKey) {
949
949
  }).map((candidate) => candidate.memory);
950
950
  }
951
951
  function createMemoryStore(db, context, options = {}) {
952
- const runtimeContext = memoryRuntimeContextSchema.parse(context);
952
+ const runtimeContext2 = memoryRuntimeContextSchema.parse(context);
953
953
  const parsedOptions = memoryStoreOptionsSchema.parse({ now: options.now });
954
954
  const embedder = options.embedder;
955
955
  const maxVectorDistance = options.maxVectorDistance;
@@ -961,14 +961,14 @@ function createMemoryStore(db, context, options = {}) {
961
961
  db,
962
962
  limit: input.limit,
963
963
  nowMs,
964
- scopes: deriveVisibleMemoryScopes(runtimeContext)
964
+ scopes: deriveVisibleMemoryScopes(runtimeContext2)
965
965
  });
966
966
  }
967
967
  async function reuseDuplicateMemory(args) {
968
968
  await rememberDuplicateIdempotency({
969
969
  ...args,
970
970
  db,
971
- runtimeContext
971
+ runtimeContext: runtimeContext2
972
972
  });
973
973
  await storeEmbedding({
974
974
  content: args.duplicate.content,
@@ -983,8 +983,8 @@ function createMemoryStore(db, context, options = {}) {
983
983
  const input = createMemoryInputSchema.parse(rawInput);
984
984
  const nowMs = getNowMs();
985
985
  const content = normalizeContent(input.content);
986
- const scope = deriveMemoryScope(runtimeContext, scopeKind);
987
- const subject = deriveMemorySubject(runtimeContext, scope);
986
+ const scope = deriveMemoryScope(runtimeContext2, scopeKind);
987
+ const subject = deriveMemorySubject(runtimeContext2, scope);
988
988
  if (content.length > MAX_MEMORY_CONTENT_CHARS) {
989
989
  throw new Error("Memory content exceeds the maximum length.");
990
990
  }
@@ -1075,7 +1075,7 @@ function createMemoryStore(db, context, options = {}) {
1075
1075
  candidates: preferenceCandidates,
1076
1076
  content,
1077
1077
  decider: supersessionDecider,
1078
- runtimeContext
1078
+ runtimeContext: runtimeContext2
1079
1079
  });
1080
1080
  if (adjudication.decision === "duplicate") {
1081
1081
  return await reuseDuplicateMemory({
@@ -1102,8 +1102,8 @@ function createMemoryStore(db, context, options = {}) {
1102
1102
  observedAtMs: nowMs,
1103
1103
  scope: scope.scope,
1104
1104
  scopeKey: scope.scopeKey,
1105
- sourceKey: sourceKey(runtimeContext),
1106
- sourcePlatform: runtimeContext.source.platform,
1105
+ sourceKey: sourceKey(runtimeContext2),
1106
+ sourcePlatform: runtimeContext2.source.platform,
1107
1107
  subjectKey: subject.subjectKey,
1108
1108
  subjectType: subject.subjectType,
1109
1109
  kind: input.kind
@@ -1182,7 +1182,23 @@ function createMemoryStore(db, context, options = {}) {
1182
1182
  async listMemories(input) {
1183
1183
  input = listMemoriesInputSchema.parse(input);
1184
1184
  const nowMs = getNowMs();
1185
- const scopes = deriveVisibleMemoryScopes(runtimeContext);
1185
+ const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1186
+ await archiveExpiredMemoryBatch({
1187
+ db,
1188
+ nowMs,
1189
+ scopes
1190
+ });
1191
+ return await listVisibleMemories({
1192
+ db,
1193
+ limit: input.limit,
1194
+ nowMs,
1195
+ scopes
1196
+ });
1197
+ },
1198
+ async listPersonalMemories(input) {
1199
+ input = listMemoriesInputSchema.parse(input);
1200
+ const nowMs = getNowMs();
1201
+ const scopes = [deriveMemoryScope(runtimeContext2, "personal")];
1186
1202
  await archiveExpiredMemoryBatch({
1187
1203
  db,
1188
1204
  nowMs,
@@ -1198,7 +1214,7 @@ function createMemoryStore(db, context, options = {}) {
1198
1214
  async searchMemories(input) {
1199
1215
  input = searchMemoriesInputSchema.parse(input);
1200
1216
  const nowMs = getNowMs();
1201
- const scopes = deriveVisibleMemoryScopes(runtimeContext);
1217
+ const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1202
1218
  await archiveExpiredMemoryBatch({
1203
1219
  db,
1204
1220
  nowMs,
@@ -1228,13 +1244,13 @@ function createMemoryStore(db, context, options = {}) {
1228
1244
  return mergeSearchCandidates(
1229
1245
  [...vectorCandidates, ...lexicalCandidates],
1230
1246
  nowMs,
1231
- sourceChannelPrefix(runtimeContext)
1247
+ sourceChannelPrefix(runtimeContext2)
1232
1248
  ).slice(0, limit);
1233
1249
  },
1234
1250
  async archiveMemory(input) {
1235
1251
  input = archiveMemoryInputSchema.parse(input);
1236
1252
  const nowMs = getNowMs();
1237
- const scopes = deriveVisibleMemoryScopes(runtimeContext);
1253
+ const scopes = deriveVisibleMemoryScopes(runtimeContext2);
1238
1254
  const predicate = activeVisiblePredicate({ nowMs, scopes });
1239
1255
  const idPrefix = input.id.trim();
1240
1256
  if (!idPrefix) {
@@ -2072,7 +2088,7 @@ function createMemoryCreateTool(context) {
2072
2088
  const parsedInput = parseMemoryToolInput(createMemoryInputSchema2, input);
2073
2089
  const toolCallId = requireToolCallId(options.toolCallId);
2074
2090
  const requestedExpiresAtMs = parseExpiresAt(parsedInput.expires_at);
2075
- const runtimeContext = memoryRuntimeContext(context);
2091
+ const runtimeContext2 = memoryRuntimeContext(context);
2076
2092
  const store = memoryStore(context, {
2077
2093
  supersessionDecider: context.supersessionDecider
2078
2094
  });
@@ -2083,7 +2099,7 @@ function createMemoryCreateTool(context) {
2083
2099
  parseCreateMemoryRequest({
2084
2100
  content: requireMemoryContent(parsedInput.content),
2085
2101
  ...requestedExpiresAtMs !== void 0 ? { expiresAtMs: requestedExpiresAtMs } : {},
2086
- runtimeContext,
2102
+ runtimeContext: runtimeContext2,
2087
2103
  ...context.userText?.trim() ? {
2088
2104
  sourceContext: {
2089
2105
  currentUserText: context.userText.trim()
@@ -2309,13 +2325,13 @@ async function processMemorySession(context) {
2309
2325
  if (!evidenceText) {
2310
2326
  return;
2311
2327
  }
2312
- const runtimeContext = memoryRuntimeContextSchema.parse({
2328
+ const runtimeContext2 = memoryRuntimeContextSchema.parse({
2313
2329
  conversationId: run.conversationId,
2314
2330
  ...run.actor ? { actor: run.actor } : {},
2315
2331
  source: run.source
2316
2332
  });
2317
2333
  const agent = createMemoryAgent(context.model);
2318
- const store = createMemoryStore(context.db, runtimeContext, {
2334
+ const store = createMemoryStore(context.db, runtimeContext2, {
2319
2335
  embedder: context.embedder,
2320
2336
  supersessionDecider: agent
2321
2337
  });
@@ -2331,7 +2347,7 @@ async function processMemorySession(context) {
2331
2347
  })),
2332
2348
  actors: run.actors,
2333
2349
  transcript,
2334
- runtimeContext
2350
+ runtimeContext: runtimeContext2
2335
2351
  });
2336
2352
  });
2337
2353
  if (memories.length === 0) {
@@ -2352,6 +2368,10 @@ async function processMemorySession(context) {
2352
2368
  }
2353
2369
 
2354
2370
  // src/recall.ts
2371
+ import {
2372
+ definePromptContext
2373
+ } from "@sentry/junior-plugin-api";
2374
+ import { z as z6 } from "zod";
2355
2375
  var DEFAULT_RECALL_LIMIT = 5;
2356
2376
  var MAX_PROMPT_CHARS = 4e3;
2357
2377
  var MAX_MEMORY_LINE_CHARS = 600;
@@ -2365,48 +2385,151 @@ function trimContent(content, maxLength) {
2365
2385
  function formatObservedDate(observedAtMs) {
2366
2386
  return new Date(observedAtMs).toISOString().slice(0, 10);
2367
2387
  }
2368
- function renderMemoryPrompt(memories) {
2388
+ var recalledMemorySchema = z6.object({
2389
+ id: z6.string().min(1),
2390
+ content: z6.string().min(1).max(MAX_MEMORY_LINE_CHARS),
2391
+ observedAtMs: z6.number().finite(),
2392
+ scope: z6.enum(["personal", "conversation"]),
2393
+ kind: z6.enum(["preference", "procedure", "knowledge"])
2394
+ }).strict();
2395
+ var memoryRecallContextSchema = z6.object({
2396
+ memories: z6.array(recalledMemorySchema).min(1).max(DEFAULT_RECALL_LIMIT)
2397
+ }).strict();
2398
+ function selectPromptMemories(memories) {
2369
2399
  const header = "Relevant memories for this request:";
2370
2400
  const footer = "Treat these as possibly stale context. Current user instructions and repository evidence take priority.";
2371
- const lines = [];
2401
+ const selected = [];
2372
2402
  let totalChars = header.length + footer.length + 2;
2373
2403
  for (const memory of memories) {
2374
- const line = `- Observed ${formatObservedDate(memory.observedAtMs)}: ${trimContent(
2375
- memory.content,
2376
- MAX_MEMORY_LINE_CHARS
2377
- )}`;
2404
+ const content = trimContent(memory.content, MAX_MEMORY_LINE_CHARS);
2405
+ const line = `- Observed ${formatObservedDate(memory.observedAtMs)}: ${content}`;
2378
2406
  if (totalChars + line.length + 1 > MAX_PROMPT_CHARS) {
2379
2407
  break;
2380
2408
  }
2381
- lines.push(line);
2409
+ selected.push({
2410
+ id: memory.id,
2411
+ content,
2412
+ observedAtMs: memory.observedAtMs,
2413
+ scope: memory.scope,
2414
+ kind: memory.kind
2415
+ });
2382
2416
  totalChars += line.length + 1;
2383
2417
  }
2384
- if (lines.length === 0) {
2385
- return void 0;
2386
- }
2387
- return `${header}
2388
- ${lines.join("\n")}
2389
-
2390
- ${footer}`;
2418
+ return selected;
2391
2419
  }
2392
- async function createMemoryPromptMessages(context) {
2420
+ function renderMemoryPrompt(memories) {
2421
+ return [
2422
+ "Relevant memories for this request:",
2423
+ ...memories.map(
2424
+ (memory) => `- Observed ${formatObservedDate(memory.observedAtMs)}: ${memory.content}`
2425
+ ),
2426
+ "",
2427
+ "Treat these as possibly stale context. Current user instructions and repository evidence take priority."
2428
+ ].join("\n");
2429
+ }
2430
+ var memoryRecallContext = definePromptContext({
2431
+ kind: "recall",
2432
+ version: 1,
2433
+ schema: memoryRecallContextSchema,
2434
+ renderPrompt: (content) => renderMemoryPrompt(content.memories)
2435
+ });
2436
+ async function createMemoryPromptContributions(context) {
2393
2437
  if (!context.text.trim()) {
2394
2438
  return void 0;
2395
2439
  }
2396
- const runtimeContext = memoryRuntimeContextSchema.parse({
2440
+ const runtimeContext2 = memoryRuntimeContextSchema.parse({
2397
2441
  ...context.conversationId ? { conversationId: context.conversationId } : {},
2398
2442
  ...context.actor ? { actor: context.actor } : {},
2399
2443
  source: context.source
2400
2444
  });
2401
- const memories = await createMemoryStore(context.db, runtimeContext, {
2445
+ const memories = await createMemoryStore(context.db, runtimeContext2, {
2402
2446
  ...context.embedder ? { embedder: context.embedder } : {},
2403
2447
  ...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
2404
2448
  }).searchMemories({
2405
2449
  query: context.text,
2406
2450
  limit: DEFAULT_RECALL_LIMIT
2407
2451
  });
2408
- const text2 = renderMemoryPrompt(memories);
2409
- return text2 ? [{ text: text2 }] : void 0;
2452
+ const selected = selectPromptMemories(memories);
2453
+ if (selected.length === 0) {
2454
+ return void 0;
2455
+ }
2456
+ if (selected.some((memory) => memory.scope === "personal")) {
2457
+ return [{ text: renderMemoryPrompt(selected) }];
2458
+ }
2459
+ return [memoryRecallContext({ memories: selected })];
2460
+ }
2461
+
2462
+ // src/user-pages.ts
2463
+ var MEMORY_PAGE_LIMIT = 100;
2464
+ function runtimeContext(actor) {
2465
+ if (actor.platform === "slack") {
2466
+ return {
2467
+ actor,
2468
+ source: {
2469
+ platform: "slack",
2470
+ type: "priv",
2471
+ teamId: actor.teamId,
2472
+ channelId: "DDASHBOARD"
2473
+ }
2474
+ };
2475
+ }
2476
+ return {
2477
+ actor,
2478
+ source: {
2479
+ platform: "local",
2480
+ type: "priv",
2481
+ conversationId: "local:dashboard:memories"
2482
+ }
2483
+ };
2484
+ }
2485
+ function memoryKindLabel(kind) {
2486
+ return kind.charAt(0).toUpperCase() + kind.slice(1);
2487
+ }
2488
+ function rememberedDate(createdAtMs) {
2489
+ return new Intl.DateTimeFormat("en-US", {
2490
+ dateStyle: "medium",
2491
+ timeZone: "UTC"
2492
+ }).format(new Date(createdAtMs));
2493
+ }
2494
+ async function readPersonalMemories(db, actors) {
2495
+ const memories = await Promise.all(
2496
+ actors.flatMap((actor) => {
2497
+ const context = runtimeContext(actor);
2498
+ return context ? [
2499
+ createMemoryStore(db, context).listPersonalMemories({
2500
+ limit: MEMORY_PAGE_LIMIT
2501
+ })
2502
+ ] : [];
2503
+ })
2504
+ );
2505
+ return [
2506
+ ...new Map(memories.flat().map((memory) => [memory.id, memory])).values()
2507
+ ].sort((left, right) => right.createdAtMs - left.createdAtMs).slice(0, MEMORY_PAGE_LIMIT);
2508
+ }
2509
+ function createMemoryUserPage() {
2510
+ return {
2511
+ id: "memories",
2512
+ label: "Memories",
2513
+ description: "Personal facts and preferences Junior remembers about you.",
2514
+ async read(ctx) {
2515
+ const memories = await readPersonalMemories(
2516
+ ctx.db,
2517
+ ctx.viewer.actors
2518
+ );
2519
+ return {
2520
+ type: "list",
2521
+ emptyText: "No personal memories yet.",
2522
+ records: memories.map((memory) => ({
2523
+ id: memory.id,
2524
+ title: memory.content,
2525
+ metadata: [
2526
+ { label: "Type", value: memoryKindLabel(memory.kind) },
2527
+ { label: "Remembered", value: rememberedDate(memory.createdAtMs) }
2528
+ ]
2529
+ }))
2530
+ };
2531
+ }
2532
+ };
2410
2533
  }
2411
2534
 
2412
2535
  // src/plugin.ts
@@ -2471,6 +2594,7 @@ function createMemoryPlugin(options = {}) {
2471
2594
  }
2472
2595
  }
2473
2596
  },
2597
+ userPages: [createMemoryUserPage()],
2474
2598
  hooks: {
2475
2599
  tools(ctx) {
2476
2600
  const agent = createMemoryAgent(ctx.model);
@@ -2496,7 +2620,7 @@ function createMemoryPlugin(options = {}) {
2496
2620
  };
2497
2621
  },
2498
2622
  async userPrompt(ctx) {
2499
- return await createMemoryPromptMessages({
2623
+ return await createMemoryPromptContributions({
2500
2624
  ...ctx.conversationId ? { conversationId: ctx.conversationId } : {},
2501
2625
  ...ctx.actor ? { actor: ctx.actor } : {},
2502
2626
  db: ctx.db,