@sentry/junior-memory 0.198.0 → 0.200.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/events.ts DELETED
@@ -1,107 +0,0 @@
1
- import { defineConversationEvent } from "@sentry/junior-plugin-api";
2
- import { z } from "zod";
3
- import { MEMORY_KINDS, MEMORY_SCOPES } from "./types";
4
- import type { MemoryRecord } from "./store";
5
-
6
- const capturedMemoryFields = {
7
- content: z.string().min(1),
8
- id: z.string().min(1),
9
- kind: z.enum(MEMORY_KINDS),
10
- observedAtMs: z.number().finite(),
11
- };
12
-
13
- const legacyCapturedMemorySchema = z
14
- .object({
15
- ...capturedMemoryFields,
16
- scope: z.enum(["personal", "conversation"]),
17
- })
18
- .strict();
19
-
20
- const capturedMemorySchema = z
21
- .object({
22
- ...capturedMemoryFields,
23
- scope: z.enum(MEMORY_SCOPES),
24
- })
25
- .strict();
26
-
27
- const capturedMemoriesSchema = z
28
- .object({
29
- memories: z.array(capturedMemorySchema).max(100),
30
- costUsd: z.number().finite().nonnegative().optional(),
31
- })
32
- .strict();
33
-
34
- const recalledMemoriesSchema = z
35
- .object({
36
- // Matches the automatic-recall candidate window; admission packs by char budget.
37
- memories: z.array(z.string().min(1)).max(20),
38
- costUsd: z.number().finite().nonnegative().optional(),
39
- })
40
- .strict();
41
-
42
- function currentScope(
43
- scope: "personal" | "conversation" | "private" | "public",
44
- ) {
45
- if (scope === "personal") return "private";
46
- if (scope === "conversation") return "public";
47
- return scope;
48
- }
49
-
50
- function renderCapturedMemories(event: {
51
- memories: Array<
52
- | z.output<typeof legacyCapturedMemorySchema>
53
- | z.output<typeof capturedMemorySchema>
54
- >;
55
- }) {
56
- const count = event.memories.length;
57
- if (count === 0) return undefined;
58
- return {
59
- icon: "brain" as const,
60
- title: `${count} ${count === 1 ? "memory" : "memories"} captured`,
61
- details: event.memories.map((memory) => ({
62
- title: memory.content,
63
- metadata: [memory.kind, currentScope(memory.scope)],
64
- })),
65
- };
66
- }
67
-
68
- /** Previous stored memory-capture event shape retained for transcript rendering. */
69
- export const memoriesCapturedEventV1 = defineConversationEvent({
70
- name: "memories_captured",
71
- version: 1,
72
- schema: z
73
- .object({
74
- memories: z.array(legacyCapturedMemorySchema).min(1).max(100),
75
- })
76
- .strict(),
77
- renderEvent: renderCapturedMemories,
78
- });
79
-
80
- /** Durable outcome emitted after every completed passive memory extraction. */
81
- export const memoriesCapturedEvent = defineConversationEvent({
82
- name: "memories_captured",
83
- version: 2,
84
- schema: capturedMemoriesSchema,
85
- renderEvent: renderCapturedMemories,
86
- });
87
-
88
- /** Durable outcome emitted after one completed automatic recall attempt. */
89
- export const memoriesRecalledEvent = defineConversationEvent({
90
- name: "memories_recalled",
91
- version: 1,
92
- schema: recalledMemoriesSchema,
93
- renderEvent() {
94
- return undefined;
95
- },
96
- });
97
-
98
- /** Select the stable, safe memory fields retained in conversation history. */
99
- export function capturedMemory(memory: MemoryRecord) {
100
- return {
101
- content: memory.content,
102
- id: memory.id,
103
- kind: memory.kind,
104
- observedAtMs: memory.observedAtMs,
105
- scope: memory.scope,
106
- };
107
- }
package/src/index.ts DELETED
@@ -1,25 +0,0 @@
1
- export { memoryPlugin } from "./plugin";
2
- export {
3
- memoryApiSchema,
4
- memoryDashboardResponseSchema,
5
- memoryListResponseSchema,
6
- type MemoryApi,
7
- type MemoryDashboardResponse,
8
- type MemoryListResponse,
9
- } from "./api";
10
- export type { MemoryPluginOptions } from "./plugin";
11
- export { createMemoryStore } from "./store";
12
- export type {
13
- ArchiveMemoryInput,
14
- CreateMemoryInput,
15
- CreateMemoryResult,
16
- ListMemoriesInput,
17
- MemoryDb,
18
- MemoryEmbeddingProvider,
19
- MemoryRecord,
20
- MemoryStore,
21
- MemoryStoreOptions,
22
- SearchMemoriesInput,
23
- } from "./store";
24
- export { MEMORY_KINDS } from "./types";
25
- export type { MemoryKind, MemoryRuntimeContext } from "./types";
@@ -1,228 +0,0 @@
1
- import type {
2
- PluginConversationEventCostDay,
3
- PluginOperationalReportContent,
4
- } from "@sentry/junior-plugin-api";
5
- import { and, eq, gt, isNull, or, sql } from "drizzle-orm";
6
- import { z } from "zod";
7
- import { juniorMemoryEmbeddings, juniorMemoryMemories } from "./db/schema";
8
- import type { MemoryDb } from "./store";
9
-
10
- const DAY_MS = 24 * 60 * 60 * 1_000;
11
- // TODO: add hour categories + `1` when these system widgets can plot 24h hourly.
12
- const WINDOWS = [7, 30, 90] as const;
13
-
14
- const memoryDaySchema = z
15
- .object({
16
- date: z.string().date(),
17
- private: z.number().int().nonnegative(),
18
- public: z.number().int().nonnegative(),
19
- })
20
- .strict();
21
-
22
- function queryRows(result: unknown): unknown[] {
23
- if (
24
- typeof result !== "object" ||
25
- result === null ||
26
- !("rows" in result) ||
27
- !Array.isArray(result.rows)
28
- ) {
29
- throw new TypeError("Memory activity query did not return rows");
30
- }
31
- return result.rows;
32
- }
33
-
34
- function startOfUtcDay(value: number): Date {
35
- const date = new Date(value);
36
- date.setUTCHours(0, 0, 0, 0);
37
- return date;
38
- }
39
-
40
- async function aggregateMemoryDays(args: { db: MemoryDb; nowMs: number }) {
41
- const end = startOfUtcDay(args.nowMs);
42
- const start = startOfUtcDay(args.nowMs - (WINDOWS.at(-1)! - 1) * DAY_MS);
43
- const endExclusiveMs = end.getTime() + DAY_MS;
44
- const table = juniorMemoryMemories;
45
- const result = await args.db.execute(sql`
46
- WITH days AS (
47
- SELECT generate_series(
48
- date_trunc('day', ${start}::timestamptz AT TIME ZONE 'UTC'),
49
- date_trunc('day', ${end}::timestamptz AT TIME ZONE 'UTC'),
50
- interval '1 day'
51
- ) AS day
52
- ), daily AS (
53
- SELECT
54
- date_trunc(
55
- 'day',
56
- to_timestamp(${table.createdAtMs} / 1000.0) AT TIME ZONE 'UTC'
57
- ) AS day,
58
- count(*) FILTER (
59
- WHERE ${table.scope} = 'private'
60
- )::integer AS private,
61
- count(*) FILTER (
62
- WHERE ${table.scope} = 'public'
63
- )::integer AS public
64
- FROM ${table}
65
- WHERE ${table.createdAtMs} >= ${start.getTime()}
66
- AND ${table.createdAtMs} < ${endExclusiveMs}
67
- GROUP BY date_trunc(
68
- 'day',
69
- to_timestamp(${table.createdAtMs} / 1000.0) AT TIME ZONE 'UTC'
70
- )
71
- )
72
- SELECT
73
- to_char(days.day, 'YYYY-MM-DD') AS date,
74
- coalesce(daily.private, 0)::integer AS private,
75
- coalesce(daily.public, 0)::integer AS public
76
- FROM days
77
- LEFT JOIN daily ON daily.day = days.day
78
- ORDER BY days.day
79
- `);
80
- return z.array(memoryDaySchema).parse(queryRows(result));
81
- }
82
-
83
- function formatCount(value: number): string {
84
- return new Intl.NumberFormat("en-US").format(value);
85
- }
86
-
87
- function formatPercent(value: number): string {
88
- return new Intl.NumberFormat("en-US", {
89
- maximumFractionDigits: 0,
90
- style: "percent",
91
- }).format(value);
92
- }
93
-
94
- function formatUsd(value: number): string {
95
- const maximumFractionDigits = value > 0 && value < 0.01 ? 4 : 2;
96
- return new Intl.NumberFormat("en-US", {
97
- currency: "USD",
98
- maximumFractionDigits,
99
- minimumFractionDigits: 2,
100
- style: "currency",
101
- }).format(value);
102
- }
103
-
104
- /** Build aggregate memory storage and indexing diagnostics for the System page. */
105
- export async function buildMemoryOperationalReport(args: {
106
- db: MemoryDb;
107
- extractionDays: PluginConversationEventCostDay[];
108
- nowMs: number;
109
- }): Promise<PluginOperationalReportContent> {
110
- const active = and(
111
- isNull(juniorMemoryMemories.archivedAtMs),
112
- isNull(juniorMemoryMemories.supersededAtMs),
113
- isNull(juniorMemoryMemories.supersededById),
114
- or(
115
- isNull(juniorMemoryMemories.expiresAtMs),
116
- gt(juniorMemoryMemories.expiresAtMs, args.nowMs),
117
- ),
118
- );
119
- const [[counts], memoryDays] = await Promise.all([
120
- args.db
121
- .select({
122
- active: sql<number>`count(*) filter (where ${active})`.mapWith(Number),
123
- public:
124
- sql<number>`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'public')`.mapWith(
125
- Number,
126
- ),
127
- createdThirtyDays:
128
- sql<number>`count(*) filter (where ${juniorMemoryMemories.createdAtMs} >= ${args.nowMs - 30 * DAY_MS})`.mapWith(
129
- Number,
130
- ),
131
- embedded:
132
- sql<number>`count(${juniorMemoryEmbeddings.memoryId}) filter (where ${active})`.mapWith(
133
- Number,
134
- ),
135
- private:
136
- sql<number>`count(*) filter (where ${active} and ${juniorMemoryMemories.scope} = 'private')`.mapWith(
137
- Number,
138
- ),
139
- })
140
- .from(juniorMemoryMemories)
141
- .leftJoin(
142
- juniorMemoryEmbeddings,
143
- eq(juniorMemoryEmbeddings.memoryId, juniorMemoryMemories.id),
144
- ),
145
- aggregateMemoryDays(args),
146
- ]);
147
-
148
- const activeCount = counts?.active ?? 0;
149
- const embeddedCount = counts?.embedded ?? 0;
150
- const embeddingCoverage = activeCount === 0 ? 0 : embeddedCount / activeCount;
151
- const extractionThirtyDays = args.extractionDays.slice(-30);
152
- const extractionCostThirtyDays = extractionThirtyDays.reduce(
153
- (total, day) => total + day.costUsd,
154
- 0,
155
- );
156
-
157
- return {
158
- generatedAt: new Date(args.nowMs).toISOString(),
159
- title: "Memory",
160
- metrics: [
161
- {
162
- label: "active memories",
163
- tone: activeCount > 0 ? "good" : "neutral",
164
- value: formatCount(activeCount),
165
- },
166
- {
167
- label: "extraction cost · 30d",
168
- value: formatUsd(extractionCostThirtyDays),
169
- },
170
- {
171
- label: "created · 30d",
172
- value: formatCount(counts?.createdThirtyDays ?? 0),
173
- },
174
- {
175
- label: "private",
176
- value: formatCount(counts?.private ?? 0),
177
- },
178
- {
179
- label: "public",
180
- value: formatCount(counts?.public ?? 0),
181
- },
182
- {
183
- label: "embedding coverage",
184
- tone:
185
- activeCount === 0
186
- ? "neutral"
187
- : embeddedCount === activeCount
188
- ? "good"
189
- : "warning",
190
- value: formatPercent(embeddingCoverage),
191
- },
192
- ],
193
- widgets: [
194
- {
195
- categories: args.extractionDays.map((day) => ({
196
- id: day.date,
197
- label: day.date,
198
- values: { costUsd: day.costUsd },
199
- })),
200
- description: "Estimated model cost of passive memory extraction",
201
- id: "extraction-cost",
202
- series: [{ format: "usd", key: "costUsd", label: "Cost" }],
203
- timeRangeDays: [...WINDOWS],
204
- title: "Extraction cost",
205
- type: "bar_chart",
206
- },
207
- {
208
- categories: memoryDays.map((day) => ({
209
- id: day.date,
210
- label: day.date,
211
- values: {
212
- private: day.private,
213
- public: day.public,
214
- },
215
- })),
216
- description: "Memories stored by scope",
217
- id: "memories-created",
218
- series: [
219
- { key: "private", label: "Private" },
220
- { key: "public", label: "Public" },
221
- ],
222
- timeRangeDays: [...WINDOWS],
223
- title: "Memories created",
224
- type: "bar_chart",
225
- },
226
- ],
227
- };
228
- }
package/src/plugin.ts DELETED
@@ -1,186 +0,0 @@
1
- import { defineJuniorPlugin } from "@sentry/junior-plugin-api";
2
- import { createMemoryAgent } from "./agent";
3
- import { createMemoryApi } from "./api";
4
- import { createMemoryCliCommand } from "./cli";
5
- import {
6
- createMemoryCreateTool,
7
- createMemoryListTool,
8
- createMemoryRemoveTool,
9
- createMemorySearchTool,
10
- type MemoryCreateToolContext,
11
- type MemoryReviewer,
12
- type MemoryToolContext,
13
- } from "./tools";
14
- import { processMemorySession } from "./process-session";
15
- import { createMemoryPromptContributions } from "./recall";
16
- import { buildMemoryOperationalReport } from "./operational-report";
17
- import {
18
- memoriesCapturedEvent,
19
- memoriesCapturedEventV1,
20
- memoriesRecalledEvent,
21
- } from "./events";
22
- import type { MemoryDb } from "./store";
23
- import { createMemoryUserPage } from "./user-pages";
24
-
25
- const MEMORY_MODEL_ENV = "AI_MEMORY_MODEL";
26
-
27
- export interface MemoryPluginOptions {
28
- /** Disable automatic prompt recall while keeping explicit memory tools available. */
29
- disableRecall?: boolean;
30
- /** Disable passive memory extraction from completed sessions. */
31
- disableExtraction?: boolean;
32
- modelId?: string;
33
- }
34
-
35
- function memoryModelId(options: MemoryPluginOptions): string | undefined {
36
- const explicitModelId = options.modelId?.trim();
37
- if (explicitModelId) {
38
- return explicitModelId;
39
- }
40
- const envModelId = process.env[MEMORY_MODEL_ENV]?.trim();
41
- return envModelId || undefined;
42
- }
43
-
44
- function memoryToolContext(ctx: {
45
- agent: MemoryReviewer;
46
- conversationId?: string;
47
- db: MemoryToolContext["db"];
48
- embedder?: MemoryToolContext["embedder"];
49
- locationId?: string;
50
- actor?: MemoryToolContext["actor"];
51
- source: MemoryToolContext["source"];
52
- users: MemoryToolContext["users"];
53
- userText?: string;
54
- }): MemoryToolContext {
55
- return {
56
- agent: ctx.agent,
57
- ...(ctx.conversationId
58
- ? { conversationId: ctx.conversationId }
59
- : undefined),
60
- ...(ctx.actor ? { actor: ctx.actor } : undefined),
61
- db: ctx.db,
62
- ...(ctx.embedder ? { embedder: ctx.embedder } : undefined),
63
- ...(ctx.locationId ? { locationId: ctx.locationId } : undefined),
64
- source: ctx.source,
65
- users: ctx.users,
66
- ...(ctx.userText ? { userText: ctx.userText } : undefined),
67
- };
68
- }
69
-
70
- function memoryCreateToolContext(ctx: {
71
- agent: MemoryReviewer;
72
- conversationId?: string;
73
- db: MemoryCreateToolContext["db"];
74
- embedder?: MemoryCreateToolContext["embedder"];
75
- locationId?: string;
76
- actor?: MemoryCreateToolContext["actor"];
77
- source: MemoryCreateToolContext["source"];
78
- supersessionDecider: MemoryCreateToolContext["supersessionDecider"];
79
- users: MemoryCreateToolContext["users"];
80
- userText?: string;
81
- }): MemoryCreateToolContext {
82
- return {
83
- ...memoryToolContext(ctx),
84
- supersessionDecider: ctx.supersessionDecider,
85
- };
86
- }
87
-
88
- /** Register Junior's long-term memory plugin. */
89
- export function memoryPlugin(options: MemoryPluginOptions = {}) {
90
- const modelId = memoryModelId(options);
91
- return defineJuniorPlugin({
92
- manifest: {
93
- name: "memory",
94
- displayName: "Memory",
95
- description: "Long-term Junior memory storage and recall",
96
- },
97
- model: modelId
98
- ? { structuredModelId: modelId }
99
- : { structuredModel: "default" },
100
- packageName: "@sentry/junior-memory",
101
- conversationEvents: [
102
- memoriesCapturedEventV1,
103
- memoriesCapturedEvent,
104
- memoriesRecalledEvent,
105
- ],
106
- cli: {
107
- commands: [createMemoryCliCommand()],
108
- },
109
- tasks: options.disableExtraction
110
- ? {}
111
- : {
112
- processSession: {
113
- async run(ctx) {
114
- await processMemorySession(ctx);
115
- },
116
- },
117
- },
118
- userPages: [createMemoryUserPage()],
119
- hooks: {
120
- async operationalReport(ctx) {
121
- const extractionDays = await ctx.eventStats.costsByDay({
122
- days: 90,
123
- eventName: "memories_captured",
124
- });
125
- return await buildMemoryOperationalReport({
126
- db: ctx.db as MemoryDb,
127
- extractionDays,
128
- nowMs: ctx.nowMs,
129
- });
130
- },
131
- apiRoutes(ctx) {
132
- return createMemoryApi({
133
- db: ctx.db as MemoryDb,
134
- eventStats: ctx.eventStats,
135
- users: ctx.users,
136
- });
137
- },
138
- tools(ctx) {
139
- const agent = createMemoryAgent(ctx.model);
140
- const context = memoryToolContext({
141
- ...ctx,
142
- agent,
143
- db: ctx.db as MemoryDb,
144
- embedder: ctx.embedder,
145
- });
146
- return {
147
- createMemory: createMemoryCreateTool(
148
- memoryCreateToolContext({
149
- ...ctx,
150
- agent,
151
- db: ctx.db as MemoryDb,
152
- embedder: ctx.embedder,
153
- supersessionDecider: agent,
154
- }),
155
- ),
156
- removeMemory: createMemoryRemoveTool(context),
157
- listMemories: createMemoryListTool(context),
158
- searchMemories: createMemorySearchTool(context),
159
- };
160
- },
161
- ...(!options.disableRecall
162
- ? {
163
- async userPrompt(ctx) {
164
- return await createMemoryPromptContributions({
165
- agent: createMemoryAgent(ctx.model),
166
- ...(ctx.conversationId
167
- ? { conversationId: ctx.conversationId }
168
- : undefined),
169
- ...(ctx.actor ? { actor: ctx.actor } : undefined),
170
- db: ctx.db as MemoryDb,
171
- embedder: ctx.embedder,
172
- events: ctx.events,
173
- ...(ctx.locationId
174
- ? { locationId: ctx.locationId }
175
- : undefined),
176
- log: ctx.log,
177
- source: ctx.source,
178
- text: ctx.text,
179
- users: ctx.users,
180
- });
181
- },
182
- }
183
- : undefined),
184
- },
185
- });
186
- }