@sentry/junior-memory 0.124.1 → 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 +11 -1
- package/dist/agent.d.ts +12 -2
- package/dist/api.d.ts +23 -7
- package/dist/events.d.ts +17 -1
- package/dist/index.js +282 -106
- package/dist/index.js.map +1 -1
- package/dist/operational-report.d.ts +2 -1
- package/dist/personal-store.d.ts +22 -11
- package/dist/personal.d.ts +5 -3
- package/dist/recall.d.ts +2 -1
- package/dist/store.d.ts +1 -0
- package/dist/user-pages.d.ts +2 -2
- package/package.json +2 -2
- package/src/agent.ts +26 -8
- package/src/api.ts +32 -9
- package/src/events.ts +53 -16
- package/src/operational-report.ts +37 -1
- package/src/personal-store.ts +108 -46
- package/src/personal.ts +14 -6
- package/src/plugin.ts +17 -2
- package/src/process-session.ts +39 -28
- package/src/recall.ts +52 -5
- package/src/store.ts +2 -0
- package/src/tools.ts +1 -0
- package/src/user-pages.ts +34 -23
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
|
-
...(
|
|
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
|
|
178
|
+
let recall: MemoryRecallResult;
|
|
138
179
|
try {
|
|
139
|
-
|
|
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,
|
package/src/user-pages.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
/** Project viewer-
|
|
1
|
+
/** Project viewer-visible memories into Junior's core-rendered user page. */
|
|
2
2
|
import type { PluginUserPageDefinition } from "@sentry/junior-plugin-api";
|
|
3
3
|
import { createViewerMemories } from "./personal";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
MemoryVisibility,
|
|
6
|
+
PersonalMemoryRecord,
|
|
7
|
+
} from "./personal-store";
|
|
5
8
|
import type { MemoryDb } from "./store";
|
|
6
9
|
|
|
7
10
|
function titleCase(value: string): string {
|
|
@@ -22,31 +25,33 @@ function originLabel(origin: PersonalMemoryRecord["origin"]): string {
|
|
|
22
25
|
return "Other";
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
function visibilityLabel(visibility: MemoryVisibility): string {
|
|
29
|
+
return visibility === "public" ? "Public" : "Private";
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function pageFilter(filter: string | undefined): {
|
|
26
|
-
|
|
27
|
-
origin?: "automatic" | "explicit";
|
|
33
|
+
visibility?: MemoryVisibility;
|
|
28
34
|
} {
|
|
29
|
-
if (filter === "
|
|
30
|
-
if (filter === "
|
|
31
|
-
if (filter === "explicit") return { origin: "explicit" };
|
|
35
|
+
if (filter === "private") return { visibility: "private" };
|
|
36
|
+
if (filter === "public") return { visibility: "public" };
|
|
32
37
|
return {};
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
function pageEmptyText(input: { filter?: string; query?: string }): string {
|
|
36
41
|
if (input.query) return "No memories matched your search.";
|
|
37
|
-
if (input.filter === "
|
|
38
|
-
if (input.filter === "
|
|
39
|
-
|
|
40
|
-
return "No personal memories yet.";
|
|
42
|
+
if (input.filter === "private") return "No private memories yet.";
|
|
43
|
+
if (input.filter === "public") return "No public memories yet.";
|
|
44
|
+
return "No memories yet.";
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
/** Create the interactive
|
|
47
|
+
/** Create the interactive Memories dashboard page. */
|
|
44
48
|
export function createMemoryUserPage(): PluginUserPageDefinition {
|
|
45
49
|
return {
|
|
46
50
|
id: "memories",
|
|
47
51
|
label: "Memories",
|
|
48
52
|
navigation: "primary",
|
|
49
|
-
description:
|
|
53
|
+
description:
|
|
54
|
+
"Personal and public memories Junior can use across conversations.",
|
|
50
55
|
async read(ctx, input) {
|
|
51
56
|
const memories = createViewerMemories(
|
|
52
57
|
ctx.db as MemoryDb,
|
|
@@ -64,22 +69,28 @@ export function createMemoryUserPage(): PluginUserPageDefinition {
|
|
|
64
69
|
...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),
|
|
65
70
|
searchPlaceholder: "Search memories",
|
|
66
71
|
records: page.memories.map((memory) => ({
|
|
67
|
-
actions:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
actions:
|
|
73
|
+
memory.visibility === "private"
|
|
74
|
+
? [
|
|
75
|
+
{
|
|
76
|
+
confirmation: "Forget this memory?",
|
|
77
|
+
href: `/api/plugins/memory/memories/${encodeURIComponent(memory.id)}`,
|
|
78
|
+
label: "Forget",
|
|
79
|
+
method: "DELETE" as const,
|
|
80
|
+
tone: "danger" as const,
|
|
81
|
+
},
|
|
82
|
+
]
|
|
83
|
+
: [],
|
|
76
84
|
id: memory.id,
|
|
77
85
|
title: memory.content,
|
|
78
86
|
metadata: [
|
|
79
87
|
{ label: "Type", value: titleCase(memory.kind) },
|
|
80
88
|
{ label: "Learned", value: originLabel(memory.origin) },
|
|
81
89
|
{ label: "Source", value: titleCase(memory.sourcePlatform) },
|
|
82
|
-
{
|
|
90
|
+
{
|
|
91
|
+
label: "Visibility",
|
|
92
|
+
value: visibilityLabel(memory.visibility),
|
|
93
|
+
},
|
|
83
94
|
{ label: "Remembered", value: rememberedDate(memory.createdAtMs) },
|
|
84
95
|
{ label: "Observed", value: rememberedDate(memory.observedAtMs) },
|
|
85
96
|
{
|