@sentry/junior-memory 0.124.2 → 0.126.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/src/recall.ts CHANGED
@@ -2,11 +2,13 @@ import {
2
2
  definePromptContext,
3
3
  type UserPromptContribution,
4
4
  type Actor,
5
+ type PluginConversationEvents,
5
6
  type PluginLogger,
6
7
  type Source,
7
8
  } from "@sentry/junior-plugin-api";
8
9
  import { z } from "zod";
9
- import type { MemoryAgent } from "./agent";
10
+ import type { MemoryAgent, MemoryRecallResult } from "./agent";
11
+ import { memoriesRecalledEvent } from "./events";
10
12
  import {
11
13
  createMemoryStore,
12
14
  type MemoryDb,
@@ -25,6 +27,7 @@ export interface MemoryRecallContext {
25
27
  conversationId?: string;
26
28
  db: MemoryDb;
27
29
  embedder?: MemoryEmbeddingProvider;
30
+ events?: PluginConversationEvents;
28
31
  log: PluginLogger;
29
32
  /** Maximum cosine distance for vector recall. Passed through to the memory store. */
30
33
  maxVectorDistance?: number;
@@ -101,6 +104,28 @@ function renderMemoryPrompt(memories: RecalledMemory[]): string {
101
104
  ].join("\n");
102
105
  }
103
106
 
107
+ function addUsd(
108
+ left: number | undefined,
109
+ right: number | undefined,
110
+ ): number | undefined {
111
+ if (left === undefined) return right;
112
+ if (right === undefined) return left;
113
+ return Math.round((left + right) * 1e12) / 1e12;
114
+ }
115
+
116
+ async function emitRecallOutcome(args: {
117
+ costUsd?: number;
118
+ events?: PluginConversationEvents;
119
+ memories: string[];
120
+ }): Promise<void> {
121
+ await args.events?.emit(
122
+ memoriesRecalledEvent({
123
+ memories: args.memories,
124
+ ...(args.costUsd !== undefined ? { costUsd: args.costUsd } : {}),
125
+ }),
126
+ );
127
+ }
128
+
104
129
  const memoryRecallContext = definePromptContext({
105
130
  kind: "recall",
106
131
  version: 1,
@@ -122,8 +147,19 @@ export async function createMemoryPromptContributions(
122
147
  ...(context.actor ? { actor: context.actor } : {}),
123
148
  source: context.source,
124
149
  });
150
+ let embeddingCostUsd: number | undefined;
151
+ const sourceEmbedder = context.embedder;
152
+ const embedder = sourceEmbedder
153
+ ? {
154
+ async embedTexts(input: { texts: string[] }) {
155
+ const result = await sourceEmbedder.embedTexts(input);
156
+ embeddingCostUsd = addUsd(embeddingCostUsd, result.costUsd);
157
+ return result;
158
+ },
159
+ }
160
+ : undefined;
125
161
  const candidates = await createMemoryStore(context.db, runtimeContext, {
126
- ...(context.embedder ? { embedder: context.embedder } : {}),
162
+ ...(embedder ? { embedder } : {}),
127
163
  ...(context.maxVectorDistance !== undefined
128
164
  ? { maxVectorDistance: context.maxVectorDistance }
129
165
  : {}),
@@ -132,11 +168,16 @@ export async function createMemoryPromptContributions(
132
168
  limit: RECALL_CANDIDATE_LIMIT,
133
169
  });
134
170
  if (candidates.length === 0) {
171
+ await emitRecallOutcome({
172
+ ...(embeddingCostUsd !== undefined ? { costUsd: embeddingCostUsd } : {}),
173
+ events: context.events,
174
+ memories: [],
175
+ });
135
176
  return undefined;
136
177
  }
137
- let relevantIds: string[];
178
+ let recall: MemoryRecallResult;
138
179
  try {
139
- relevantIds = await context.agent.selectRelevantMemories({
180
+ recall = await context.agent.selectRelevantMemories({
140
181
  candidates: candidates.map(({ content, id }) => ({ content, id })),
141
182
  userRequest: context.text,
142
183
  });
@@ -149,11 +190,17 @@ export async function createMemoryPromptContributions(
149
190
  const candidatesById = new Map(
150
191
  candidates.map((memory) => [memory.id, memory]),
151
192
  );
152
- const relevant = relevantIds
193
+ const relevant = recall.relevantIds
153
194
  .map((id) => candidatesById.get(id))
154
195
  .filter((memory): memory is MemoryRecord => memory !== undefined)
155
196
  .slice(0, DEFAULT_RECALL_LIMIT);
156
197
  const selected = selectPromptMemories(relevant);
198
+ const costUsd = addUsd(embeddingCostUsd, recall.costUsd);
199
+ await emitRecallOutcome({
200
+ ...(costUsd !== undefined ? { costUsd } : {}),
201
+ events: context.events,
202
+ memories: selected.map(({ id }) => id),
203
+ });
157
204
  if (selected.length === 0) {
158
205
  return undefined;
159
206
  }
package/src/store.ts CHANGED
@@ -180,6 +180,7 @@ const embeddingVectorSchema = z
180
180
  .length(MEMORY_EMBEDDING_DIMENSIONS);
181
181
  const embeddingResultSchema = z
182
182
  .object({
183
+ costUsd: z.number().finite().nonnegative().optional(),
183
184
  dimensions: z.literal(MEMORY_EMBEDDING_DIMENSIONS),
184
185
  model: nonEmptyStringSchema,
185
186
  provider: nonEmptyStringSchema,
@@ -272,6 +273,7 @@ export interface ArchiveExpiredMemoriesResult {
272
273
  export interface MemoryEmbeddingProvider {
273
274
  /** Embed normalized memory text for derived vector retrieval. */
274
275
  embedTexts(input: { texts: string[] }): Promise<{
276
+ costUsd?: number;
275
277
  dimensions: number;
276
278
  model: string;
277
279
  provider: string;
package/src/tools.ts CHANGED
@@ -420,6 +420,7 @@ function memoryToolResult<TData extends Record<string, unknown>>(
420
420
  /** Create a tool that submits an explicit memory candidate for storage. */
421
421
  export function createMemoryCreateTool(context: MemoryCreateToolContext) {
422
422
  return definePluginTool({
423
+ approvalMode: "approve",
423
424
  annotations: {
424
425
  destructiveHint: false,
425
426
  idempotentHint: true,