@sentry/junior-memory 0.124.2 → 0.125.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
@@ -15,7 +15,9 @@ exported types, tools, and tests are authoritative.
15
15
  inspection.
16
16
  - The dashboard exposes a searchable, paginated **Memories** user page for
17
17
  personal memories owned by actors linked to the signed-in viewer. Its
18
- **Forget** action archives the selected memory.
18
+ **Forget** action archives the selected memory. The overview charts global
19
+ passive-extraction cost from the durable `memory/memories_captured` events;
20
+ the System plugin report uses the same event-cost feed.
19
21
  - Authenticated REST clients can list and search personal memories through
20
22
  `GET /api/plugins/memory/memories`, read one through
21
23
  `GET /api/plugins/memory/memories/:id`, and archive one through
@@ -55,6 +57,10 @@ exported types, tools, and tests are authoritative.
55
57
  learning.
56
58
  - Passive extraction creates only durable, reusable facts—not transient tasks,
57
59
  conversation summaries, secrets, or speculative interpretation.
60
+ - Every completed passive extraction emits the namespaced
61
+ `memory/memories_captured` conversation event with its best-effort model cost.
62
+ Empty extraction outcomes remain durable for reporting but do not produce a
63
+ transcript row.
58
64
  - Candidate review resolves duplicates and supersession before activation.
59
65
  - Search combines independently ranked vector and PostgreSQL full-text matches
60
66
  with reciprocal rank fusion; provider-specific raw scores are never added
@@ -62,6 +68,10 @@ exported types, tools, and tests are authoritative.
62
68
  - Automatic recall retrieves a broad candidate window, then uses the
63
69
  memory-owned relevance model to admit at most five directly useful memories.
64
70
  An empty result contributes no filler prompt text.
71
+ - Every completed automatic recall attempt emits an invisible, namespaced
72
+ `memory/memories_recalled` conversation event with the admitted memory IDs
73
+ and best-effort embedding and relevance-model cost, including retrievals that
74
+ find no candidates and decisions that admit no memories.
65
75
  - Automatic recall degrades to no prompt contribution when relevance selection
66
76
  fails. Review, extraction, and write failures still fail their owning
67
77
  hook/task without corrupting existing memory state.
package/dist/agent.d.ts CHANGED
@@ -183,12 +183,22 @@ export type MemoryRecallInput = z.output<typeof memoryRecallInputSchema>;
183
183
  export type CreateMemoryRequest = z.output<typeof createMemoryRequestSchema>;
184
184
  export type ExtractSessionRequest = z.output<typeof extractSessionRequestSchema>;
185
185
  export type ExtractedMemory = z.output<typeof extractedMemoryResultSchema>;
186
+ /** Memories proposed by passive extraction and the model cost of that pass. */
187
+ export type MemoryExtractionResult = {
188
+ costUsd?: number;
189
+ memories: ExtractedMemory[];
190
+ };
191
+ /** Memories admitted by automatic recall and the model cost of that decision. */
192
+ export type MemoryRecallResult = {
193
+ costUsd?: number;
194
+ relevantIds: string[];
195
+ };
186
196
  export interface MemoryAgent {
187
197
  /** Select candidate memories that directly help with the current request. */
188
- selectRelevantMemories(request: MemoryRecallInput): Promise<string[]> | string[];
198
+ selectRelevantMemories(request: MemoryRecallInput): Promise<MemoryRecallResult> | MemoryRecallResult;
189
199
  /** Classify a new preference against related active preferences. */
190
200
  adjudicateSupersession(request: MemorySupersessionInput): Promise<MemorySupersessionDecision> | MemorySupersessionDecision;
191
- extractSessionMemories(request: ExtractSessionRequest): Promise<ExtractedMemory[]> | ExtractedMemory[];
201
+ extractSessionMemories(request: ExtractSessionRequest): Promise<MemoryExtractionResult> | MemoryExtractionResult;
192
202
  reviewCreateRequest(request: CreateMemoryRequest): Promise<MemoryReview> | MemoryReview;
193
203
  }
194
204
  /** Create the memory-owned agent that reviews, extracts, and recalls memories. */
package/dist/api.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * workspace scopes.
7
7
  */
8
8
  import { z } from "zod";
9
- import { type PluginRouteApp, type PluginUserPageActor } from "@sentry/junior-plugin-api";
9
+ import { type PluginConversationEventStats, type PluginRouteApp, type PluginUserPageActor } from "@sentry/junior-plugin-api";
10
10
  import type { MemoryDb } from "./store";
11
11
  export declare const memoryApiSchema: z.ZodObject<{
12
12
  content: z.ZodString;
@@ -49,6 +49,11 @@ export declare const memoryDashboardResponseSchema: z.ZodObject<{
49
49
  personal: z.ZodNumber;
50
50
  public: z.ZodNumber;
51
51
  }, z.core.$strict>>;
52
+ extractionDays: z.ZodArray<z.ZodObject<{
53
+ costUsd: z.ZodNumber;
54
+ date: z.ZodISODate;
55
+ events: z.ZodNumber;
56
+ }, z.core.$strict>>;
52
57
  generatedAt: z.ZodISODateTime;
53
58
  stats: z.ZodObject<{
54
59
  active: z.ZodNumber;
@@ -69,6 +74,7 @@ export type MemoryListResponse = z.output<typeof memoryListResponseSchema>;
69
74
  interface MemoryApiOptions {
70
75
  actors(email: string): Promise<PluginUserPageActor[]>;
71
76
  db: MemoryDb;
77
+ eventStats: PluginConversationEventStats;
72
78
  }
73
79
  /** Create the authenticated viewer-memory REST app. */
74
80
  export declare function createMemoryApi(options: MemoryApiOptions): PluginRouteApp;
package/dist/events.d.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  import type { MemoryRecord } from "./store";
2
- /** Durable transcript event emitted after background memory capture. */
2
+ /** Previous stored memory-capture event shape retained for transcript rendering. */
3
+ export declare const memoriesCapturedEventV1: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
4
+ memories: {
5
+ content: string;
6
+ id: string;
7
+ kind: "preference" | "procedure" | "knowledge";
8
+ observedAtMs: number;
9
+ scope: "personal" | "conversation";
10
+ }[];
11
+ }>;
12
+ /** Durable outcome emitted after every completed passive memory extraction. */
3
13
  export declare const memoriesCapturedEvent: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
4
14
  memories: {
5
15
  content: string;
@@ -8,6 +18,12 @@ export declare const memoriesCapturedEvent: import("@sentry/junior-plugin-api").
8
18
  observedAtMs: number;
9
19
  scope: "personal" | "conversation";
10
20
  }[];
21
+ costUsd?: number | undefined;
22
+ }>;
23
+ /** Durable outcome emitted after one completed automatic recall attempt. */
24
+ export declare const memoriesRecalledEvent: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
25
+ memories: string[];
26
+ costUsd?: number | undefined;
11
27
  }>;
12
28
  /** Select the stable, safe memory fields retained in conversation history. */
13
29
  export declare function capturedMemory(memory: MemoryRecord): {
package/dist/index.js CHANGED
@@ -397,6 +397,7 @@ var memoryRecordSchema = z2.object({
397
397
  }).strict();
398
398
  var embeddingVectorSchema = z2.array(numberSchema).length(MEMORY_EMBEDDING_DIMENSIONS);
399
399
  var embeddingResultSchema = z2.object({
400
+ costUsd: z2.number().finite().nonnegative().optional(),
400
401
  dimensions: z2.literal(MEMORY_EMBEDDING_DIMENSIONS),
401
402
  model: nonEmptyStringSchema2,
402
403
  provider: nonEmptyStringSchema2,
@@ -1680,9 +1681,12 @@ function createMemoryAgent(model) {
1680
1681
  });
1681
1682
  const decision = memoryRecallDecisionSchema.parse(result.object);
1682
1683
  const candidateIds = new Set(request.candidates.map(({ id }) => id));
1683
- return [...new Set(decision.relevantIds)].filter(
1684
- (id) => candidateIds.has(id)
1685
- );
1684
+ return {
1685
+ relevantIds: [...new Set(decision.relevantIds)].filter(
1686
+ (id) => candidateIds.has(id)
1687
+ ),
1688
+ ...result.costUsd !== void 0 ? { costUsd: result.costUsd } : {}
1689
+ };
1686
1690
  },
1687
1691
  async adjudicateSupersession(rawRequest) {
1688
1692
  const request = memorySupersessionInputSchema.parse(rawRequest);
@@ -1702,9 +1706,12 @@ function createMemoryAgent(model) {
1702
1706
  prompt: sessionExtractionPrompt(request),
1703
1707
  maxTokens: 1e3
1704
1708
  });
1705
- return extractedMemoriesFromResponse(
1706
- extractMemoriesResponseSchema.parse(result.object)
1707
- );
1709
+ return {
1710
+ memories: extractedMemoriesFromResponse(
1711
+ extractMemoriesResponseSchema.parse(result.object)
1712
+ ),
1713
+ ...result.costUsd !== void 0 ? { costUsd: result.costUsd } : {}
1714
+ };
1708
1715
  },
1709
1716
  async reviewCreateRequest(rawRequest) {
1710
1717
  const request = parseCreateMemoryRequest(rawRequest);
@@ -2170,8 +2177,14 @@ var memoryDashboardDaySchema = z6.object({
2170
2177
  personal: z6.number().int().min(0),
2171
2178
  public: z6.number().int().min(0)
2172
2179
  }).strict();
2180
+ var memoryExtractionDaySchema = z6.object({
2181
+ costUsd: z6.number().finite().nonnegative(),
2182
+ date: z6.iso.date(),
2183
+ events: z6.number().int().min(0)
2184
+ }).strict();
2173
2185
  var memoryDashboardResponseSchema = z6.object({
2174
2186
  days: z6.array(memoryDashboardDaySchema).length(90),
2187
+ extractionDays: z6.array(memoryExtractionDaySchema).length(90),
2175
2188
  generatedAt: z6.iso.datetime(),
2176
2189
  stats: z6.object({
2177
2190
  active: z6.number().int().min(0),
@@ -2233,12 +2246,17 @@ function createMemoryApi(options) {
2233
2246
  const memories = createViewerMemories(options.db, actors);
2234
2247
  try {
2235
2248
  if (isDashboard && (request.method === "GET" || request.method === "HEAD")) {
2236
- const [stats, days] = await Promise.all([
2249
+ const [stats, days, extractionDays] = await Promise.all([
2237
2250
  memories.stats(),
2238
- memories.timeline({ days: 90 })
2251
+ memories.timeline({ days: 90 }),
2252
+ options.eventStats.costsByDay({
2253
+ days: 90,
2254
+ eventName: "memories_captured"
2255
+ })
2239
2256
  ]);
2240
2257
  const body = memoryDashboardResponseSchema.parse({
2241
2258
  days,
2259
+ extractionDays,
2242
2260
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2243
2261
  stats
2244
2262
  });
@@ -2679,6 +2697,7 @@ function memoryToolResult(target, data) {
2679
2697
  }
2680
2698
  function createMemoryCreateTool(context) {
2681
2699
  return definePluginTool({
2700
+ approvalMode: "approve",
2682
2701
  annotations: {
2683
2702
  destructiveHint: false,
2684
2703
  idempotentHint: true,
@@ -2858,23 +2877,47 @@ function eventPreview(content) {
2858
2877
  if (trimmed.length <= MAX_EVENT_PREVIEW_CHARS) return trimmed;
2859
2878
  return `${trimmed.slice(0, MAX_EVENT_PREVIEW_CHARS - 3).trimEnd()}...`;
2860
2879
  }
2861
- var memoriesCapturedEvent = defineConversationEvent({
2880
+ var capturedMemoriesSchema = z8.object({
2881
+ memories: z8.array(capturedMemorySchema).max(100),
2882
+ costUsd: z8.number().finite().nonnegative().optional()
2883
+ }).strict();
2884
+ var recalledMemoriesSchema = z8.object({
2885
+ memories: z8.array(z8.string().min(1)).max(5),
2886
+ costUsd: z8.number().finite().nonnegative().optional()
2887
+ }).strict();
2888
+ function renderCapturedMemories(event) {
2889
+ const count = event.memories.length;
2890
+ if (count === 0) return void 0;
2891
+ return {
2892
+ icon: "brain",
2893
+ title: count === 1 ? "Memory captured" : "Memories captured",
2894
+ preview: count === 1 ? eventPreview(event.memories[0].content) : `${count} memories`,
2895
+ details: event.memories.map((memory) => ({
2896
+ title: memory.content,
2897
+ metadata: [memory.kind, memory.scope]
2898
+ }))
2899
+ };
2900
+ }
2901
+ var memoriesCapturedEventV1 = defineConversationEvent({
2862
2902
  name: "memories_captured",
2863
2903
  version: 1,
2864
2904
  schema: z8.object({
2865
2905
  memories: z8.array(capturedMemorySchema).min(1).max(100)
2866
2906
  }).strict(),
2867
- renderEvent(event) {
2868
- const count = event.memories.length;
2869
- return {
2870
- icon: "brain",
2871
- title: count === 1 ? "Memory captured" : "Memories captured",
2872
- preview: count === 1 ? eventPreview(event.memories[0].content) : `${count} memories`,
2873
- details: event.memories.map((memory) => ({
2874
- title: memory.content,
2875
- metadata: [memory.kind, memory.scope]
2876
- }))
2877
- };
2907
+ renderEvent: renderCapturedMemories
2908
+ });
2909
+ var memoriesCapturedEvent = defineConversationEvent({
2910
+ name: "memories_captured",
2911
+ version: 2,
2912
+ schema: capturedMemoriesSchema,
2913
+ renderEvent: renderCapturedMemories
2914
+ });
2915
+ var memoriesRecalledEvent = defineConversationEvent({
2916
+ name: "memories_recalled",
2917
+ version: 1,
2918
+ schema: recalledMemoriesSchema,
2919
+ renderEvent() {
2920
+ return void 0;
2878
2921
  }
2879
2922
  });
2880
2923
  function capturedMemory(memory) {
@@ -2895,14 +2938,19 @@ var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
2895
2938
  "searchMemories"
2896
2939
  ]);
2897
2940
  var MEMORY_TASK_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
2898
- var extractedMemoryCacheSchema = z9.array(
2941
+ var extractedMemorySchema2 = z9.object({
2942
+ content: z9.string().min(1),
2943
+ expiresAtMs: z9.number().finite().nullable(),
2944
+ kind: z9.enum(MEMORY_KINDS),
2945
+ evidenceMessageIndices: z9.array(z9.number().int().nonnegative()).min(1).max(10)
2946
+ }).strict().transform(parseExtractedMemory);
2947
+ var extractedMemoryCacheSchema = z9.union([
2899
2948
  z9.object({
2900
- content: z9.string().min(1),
2901
- expiresAtMs: z9.number().finite().nullable(),
2902
- kind: z9.enum(MEMORY_KINDS),
2903
- evidenceMessageIndices: z9.array(z9.number().int().nonnegative()).min(1).max(10)
2904
- }).strict().transform(parseExtractedMemory)
2905
- );
2949
+ costUsd: z9.number().finite().nonnegative().optional(),
2950
+ memories: z9.array(extractedMemorySchema2).max(5)
2951
+ }).strict(),
2952
+ z9.array(extractedMemorySchema2).max(5).transform((memories) => ({ memories }))
2953
+ ]);
2906
2954
  function recordCapturedMemory(captured, result) {
2907
2955
  const supersededIds = new Set(result.supersededIds ?? []);
2908
2956
  for (let index2 = captured.length - 1; index2 >= 0; index2 -= 1) {
@@ -2969,7 +3017,7 @@ function passiveInput(sessionId, memory, sourceKey2, target) {
2969
3017
  ...memory.expiresAtMs !== null ? { expiresAtMs: memory.expiresAtMs } : {}
2970
3018
  };
2971
3019
  }
2972
- async function getTaskMemories(context, extract) {
3020
+ async function getTaskExtraction(context, extract) {
2973
3021
  const cacheKey = `memory-extraction:${context.id}`;
2974
3022
  const cached = await context.state.get(cacheKey);
2975
3023
  if (cached !== void 0) {
@@ -2979,11 +3027,9 @@ async function getTaskMemories(context, extract) {
2979
3027
  }
2980
3028
  await context.state.delete(cacheKey);
2981
3029
  }
2982
- const memories = await extract();
2983
- if (memories.length > 0) {
2984
- await context.state.set(cacheKey, memories, MEMORY_TASK_STATE_TTL_MS);
2985
- }
2986
- return memories;
3030
+ const extraction = await extract();
3031
+ await context.state.set(cacheKey, extraction, MEMORY_TASK_STATE_TTL_MS);
3032
+ return extraction;
2987
3033
  }
2988
3034
  async function processMemorySession(context) {
2989
3035
  const run = await context.run.load();
@@ -3015,7 +3061,7 @@ async function processMemorySession(context) {
3015
3061
  supersessionDecider: agent
3016
3062
  });
3017
3063
  await store.archiveExpiredMemories();
3018
- const memories = await getTaskMemories(context, async () => {
3064
+ const extraction = await getTaskExtraction(context, async () => {
3019
3065
  const existingMemories = await store.searchMemories({
3020
3066
  limit: 10,
3021
3067
  query: evidenceText
@@ -3029,11 +3075,8 @@ async function processMemorySession(context) {
3029
3075
  runtimeContext: runtimeContext2
3030
3076
  });
3031
3077
  });
3032
- if (memories.length === 0) {
3033
- return;
3034
- }
3035
3078
  const captured = [];
3036
- for (const memory of memories) {
3079
+ for (const memory of extraction.memories) {
3037
3080
  const target = routeExtractedMemory(memory, transcript, run);
3038
3081
  if (target === "drop") {
3039
3082
  continue;
@@ -3047,9 +3090,12 @@ async function processMemorySession(context) {
3047
3090
  const result = await store.createMemory(input);
3048
3091
  recordCapturedMemory(captured, result);
3049
3092
  }
3050
- if (captured.length > 0) {
3051
- await context.events.emit(memoriesCapturedEvent({ memories: captured }));
3052
- }
3093
+ await context.events.emit(
3094
+ memoriesCapturedEvent({
3095
+ memories: captured,
3096
+ ...extraction.costUsd !== void 0 ? { costUsd: extraction.costUsd } : {}
3097
+ })
3098
+ );
3053
3099
  }
3054
3100
 
3055
3101
  // src/recall.ts
@@ -3113,6 +3159,19 @@ function renderMemoryPrompt(memories) {
3113
3159
  "Treat these as possibly stale context. Current user instructions and repository evidence take priority."
3114
3160
  ].join("\n");
3115
3161
  }
3162
+ function addUsd(left, right) {
3163
+ if (left === void 0) return right;
3164
+ if (right === void 0) return left;
3165
+ return Math.round((left + right) * 1e12) / 1e12;
3166
+ }
3167
+ async function emitRecallOutcome(args) {
3168
+ await args.events?.emit(
3169
+ memoriesRecalledEvent({
3170
+ memories: args.memories,
3171
+ ...args.costUsd !== void 0 ? { costUsd: args.costUsd } : {}
3172
+ })
3173
+ );
3174
+ }
3116
3175
  var memoryRecallContext = definePromptContext({
3117
3176
  kind: "recall",
3118
3177
  version: 1,
@@ -3128,19 +3187,33 @@ async function createMemoryPromptContributions(context) {
3128
3187
  ...context.actor ? { actor: context.actor } : {},
3129
3188
  source: context.source
3130
3189
  });
3190
+ let embeddingCostUsd;
3191
+ const sourceEmbedder = context.embedder;
3192
+ const embedder = sourceEmbedder ? {
3193
+ async embedTexts(input) {
3194
+ const result = await sourceEmbedder.embedTexts(input);
3195
+ embeddingCostUsd = addUsd(embeddingCostUsd, result.costUsd);
3196
+ return result;
3197
+ }
3198
+ } : void 0;
3131
3199
  const candidates = await createMemoryStore(context.db, runtimeContext2, {
3132
- ...context.embedder ? { embedder: context.embedder } : {},
3200
+ ...embedder ? { embedder } : {},
3133
3201
  ...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
3134
3202
  }).recallMemories({
3135
3203
  query: context.text,
3136
3204
  limit: RECALL_CANDIDATE_LIMIT
3137
3205
  });
3138
3206
  if (candidates.length === 0) {
3207
+ await emitRecallOutcome({
3208
+ ...embeddingCostUsd !== void 0 ? { costUsd: embeddingCostUsd } : {},
3209
+ events: context.events,
3210
+ memories: []
3211
+ });
3139
3212
  return void 0;
3140
3213
  }
3141
- let relevantIds;
3214
+ let recall;
3142
3215
  try {
3143
- relevantIds = await context.agent.selectRelevantMemories({
3216
+ recall = await context.agent.selectRelevantMemories({
3144
3217
  candidates: candidates.map(({ content, id }) => ({ content, id })),
3145
3218
  userRequest: context.text
3146
3219
  });
@@ -3151,8 +3224,14 @@ async function createMemoryPromptContributions(context) {
3151
3224
  const candidatesById = new Map(
3152
3225
  candidates.map((memory) => [memory.id, memory])
3153
3226
  );
3154
- const relevant = relevantIds.map((id) => candidatesById.get(id)).filter((memory) => memory !== void 0).slice(0, DEFAULT_RECALL_LIMIT);
3227
+ const relevant = recall.relevantIds.map((id) => candidatesById.get(id)).filter((memory) => memory !== void 0).slice(0, DEFAULT_RECALL_LIMIT);
3155
3228
  const selected = selectPromptMemories(relevant);
3229
+ const costUsd = addUsd(embeddingCostUsd, recall.costUsd);
3230
+ await emitRecallOutcome({
3231
+ ...costUsd !== void 0 ? { costUsd } : {},
3232
+ events: context.events,
3233
+ memories: selected.map(({ id }) => id)
3234
+ });
3156
3235
  if (selected.length === 0) {
3157
3236
  return void 0;
3158
3237
  }
@@ -3231,6 +3310,15 @@ function formatPercent(value) {
3231
3310
  style: "percent"
3232
3311
  }).format(value);
3233
3312
  }
3313
+ function formatUsd(value) {
3314
+ const maximumFractionDigits = value > 0 && value < 0.01 ? 4 : 2;
3315
+ return new Intl.NumberFormat("en-US", {
3316
+ currency: "USD",
3317
+ maximumFractionDigits,
3318
+ minimumFractionDigits: 2,
3319
+ style: "currency"
3320
+ }).format(value);
3321
+ }
3234
3322
  async function buildMemoryOperationalReport(args) {
3235
3323
  const active = and4(
3236
3324
  isNull3(juniorMemoryMemories.archivedAtMs),
@@ -3265,6 +3353,11 @@ async function buildMemoryOperationalReport(args) {
3265
3353
  const activeCount = counts?.active ?? 0;
3266
3354
  const embeddedCount = counts?.embedded ?? 0;
3267
3355
  const embeddingCoverage = activeCount === 0 ? 0 : embeddedCount / activeCount;
3356
+ const extractionThirtyDays = args.extractionDays.slice(-30);
3357
+ const extractionCostThirtyDays = extractionThirtyDays.reduce(
3358
+ (total, day) => total + day.costUsd,
3359
+ 0
3360
+ );
3268
3361
  return {
3269
3362
  generatedAt: new Date(args.nowMs).toISOString(),
3270
3363
  title: "Memory",
@@ -3274,6 +3367,10 @@ async function buildMemoryOperationalReport(args) {
3274
3367
  tone: activeCount > 0 ? "good" : "neutral",
3275
3368
  value: formatCount(activeCount)
3276
3369
  },
3370
+ {
3371
+ label: "extraction cost \xB7 30d",
3372
+ value: formatUsd(extractionCostThirtyDays)
3373
+ },
3277
3374
  {
3278
3375
  label: "created \xB7 30d",
3279
3376
  value: formatCount(counts?.createdThirtyDays ?? 0)
@@ -3293,6 +3390,19 @@ async function buildMemoryOperationalReport(args) {
3293
3390
  }
3294
3391
  ],
3295
3392
  widgets: [
3393
+ {
3394
+ categories: args.extractionDays.map((day) => ({
3395
+ id: day.date,
3396
+ label: day.date,
3397
+ values: { costUsd: day.costUsd }
3398
+ })),
3399
+ description: "Estimated model cost of passive memory extraction",
3400
+ id: "extraction-cost",
3401
+ series: [{ format: "usd", key: "costUsd", label: "Cost" }],
3402
+ timeRangeDays: [...WINDOWS],
3403
+ title: "Extraction cost",
3404
+ type: "bar_chart"
3405
+ },
3296
3406
  {
3297
3407
  categories: memoryDays.map((day) => ({
3298
3408
  id: day.date,
@@ -3453,7 +3563,11 @@ function memoryPlugin(options = {}) {
3453
3563
  },
3454
3564
  model: modelId ? { structuredModelId: modelId } : { structuredModel: "default" },
3455
3565
  packageName: "@sentry/junior-memory",
3456
- conversationEvents: [memoriesCapturedEvent],
3566
+ conversationEvents: [
3567
+ memoriesCapturedEventV1,
3568
+ memoriesCapturedEvent,
3569
+ memoriesRecalledEvent
3570
+ ],
3457
3571
  cli: {
3458
3572
  commands: [createMemoryCliCommand()]
3459
3573
  },
@@ -3467,15 +3581,21 @@ function memoryPlugin(options = {}) {
3467
3581
  userPages: [createMemoryUserPage()],
3468
3582
  hooks: {
3469
3583
  async operationalReport(ctx) {
3584
+ const extractionDays = await ctx.eventStats.costsByDay({
3585
+ days: 90,
3586
+ eventName: "memories_captured"
3587
+ });
3470
3588
  return await buildMemoryOperationalReport({
3471
3589
  db: ctx.db,
3590
+ extractionDays,
3472
3591
  nowMs: ctx.nowMs
3473
3592
  });
3474
3593
  },
3475
3594
  apiRoutes(ctx) {
3476
3595
  return createMemoryApi({
3477
3596
  actors: ctx.viewer.actors,
3478
- db: ctx.db
3597
+ db: ctx.db,
3598
+ eventStats: ctx.eventStats
3479
3599
  });
3480
3600
  },
3481
3601
  tools(ctx) {
@@ -3508,6 +3628,7 @@ function memoryPlugin(options = {}) {
3508
3628
  ...ctx.actor ? { actor: ctx.actor } : {},
3509
3629
  db: ctx.db,
3510
3630
  embedder: ctx.embedder,
3631
+ events: ctx.events,
3511
3632
  log: ctx.log,
3512
3633
  maxVectorDistance: recallMaxVectorDistance(options),
3513
3634
  source: ctx.source,