@sentry/junior-memory 0.197.0 → 0.199.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/dist/index.d.ts +887 -7
- package/package.json +5 -6
- package/dist/agent.d.ts +0 -280
- package/dist/api.d.ts +0 -121
- package/dist/cli/format.d.ts +0 -5
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/search.d.ts +0 -4
- package/dist/cli/show.d.ts +0 -4
- package/dist/db/schema.d.ts +0 -479
- package/dist/events.d.ts +0 -35
- package/dist/operational-report.d.ts +0 -8
- package/dist/plugin.d.ts +0 -9
- package/dist/process-session.d.ts +0 -9
- package/dist/ranking.d.ts +0 -18
- package/dist/recall.d.ts +0 -41
- package/dist/scope.d.ts +0 -19
- package/dist/store.d.ts +0 -229
- package/dist/tools.d.ts +0 -164
- package/dist/types.d.ts +0 -78
- package/dist/user-pages.d.ts +0 -4
- package/dist/viewer.d.ts +0 -91
- package/src/agent.ts +0 -663
- package/src/api.ts +0 -288
- package/src/cli/format.ts +0 -30
- package/src/cli/index.ts +0 -15
- package/src/cli/search.ts +0 -119
- package/src/cli/show.ts +0 -44
- package/src/db/schema.ts +0 -147
- package/src/events.ts +0 -107
- package/src/index.ts +0 -25
- package/src/operational-report.ts +0 -228
- package/src/plugin.ts +0 -186
- package/src/process-session.ts +0 -311
- package/src/ranking.ts +0 -107
- package/src/recall.ts +0 -212
- package/src/scope.ts +0 -75
- package/src/store.ts +0 -1578
- package/src/tools.ts +0 -586
- package/src/types.ts +0 -37
- package/src/user-pages.ts +0 -99
- package/src/viewer.ts +0 -441
package/src/api.ts
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authenticated REST access to memory.
|
|
3
|
-
*
|
|
4
|
-
* The signed-in User can read public memory and private memory that they own.
|
|
5
|
-
*/
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import {
|
|
8
|
-
pluginApiRouteRequestContextSchema,
|
|
9
|
-
type PluginConversationEventStats,
|
|
10
|
-
type PluginRouteApp,
|
|
11
|
-
type User,
|
|
12
|
-
} from "@sentry/junior-plugin-api";
|
|
13
|
-
import type { MemoryDb } from "./store";
|
|
14
|
-
import {
|
|
15
|
-
archiveMemory,
|
|
16
|
-
getMemory,
|
|
17
|
-
getMemoryStats,
|
|
18
|
-
getMemoryTimeline,
|
|
19
|
-
getMemoryTimelineHours,
|
|
20
|
-
InvalidMemoryCursorError,
|
|
21
|
-
listMemories,
|
|
22
|
-
MemoryNotFoundError,
|
|
23
|
-
type MemoryView,
|
|
24
|
-
} from "./viewer";
|
|
25
|
-
import { MEMORY_SOURCE_PLATFORMS } from "./types";
|
|
26
|
-
|
|
27
|
-
export const memoryApiSchema = z
|
|
28
|
-
.object({
|
|
29
|
-
content: z.string().min(1),
|
|
30
|
-
createdAt: z.iso.datetime(),
|
|
31
|
-
expiresAt: z.iso.datetime().optional(),
|
|
32
|
-
id: z.string().min(1),
|
|
33
|
-
kind: z.enum(["preference", "procedure", "knowledge"]),
|
|
34
|
-
observedAt: z.iso.datetime(),
|
|
35
|
-
origin: z.enum(["automatic", "explicit", "other"]),
|
|
36
|
-
sourcePlatform: z.enum(MEMORY_SOURCE_PLATFORMS),
|
|
37
|
-
visibility: z.enum(["private", "public"]),
|
|
38
|
-
})
|
|
39
|
-
.strict();
|
|
40
|
-
|
|
41
|
-
export const memoryListResponseSchema = z
|
|
42
|
-
.object({
|
|
43
|
-
memories: z.array(memoryApiSchema),
|
|
44
|
-
nextCursor: z.string().min(1).optional(),
|
|
45
|
-
})
|
|
46
|
-
.strict();
|
|
47
|
-
|
|
48
|
-
const memoryBucketSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2})?$/);
|
|
49
|
-
|
|
50
|
-
const memoryDashboardDaySchema = z
|
|
51
|
-
.object({
|
|
52
|
-
date: memoryBucketSchema,
|
|
53
|
-
personal: z.number().int().min(0),
|
|
54
|
-
public: z.number().int().min(0),
|
|
55
|
-
})
|
|
56
|
-
.strict();
|
|
57
|
-
|
|
58
|
-
const memoryCostDaySchema = z
|
|
59
|
-
.object({
|
|
60
|
-
costUsd: z.number().finite().nonnegative(),
|
|
61
|
-
date: memoryBucketSchema,
|
|
62
|
-
events: z.number().int().min(0),
|
|
63
|
-
})
|
|
64
|
-
.strict();
|
|
65
|
-
|
|
66
|
-
export const memoryDashboardResponseSchema = z
|
|
67
|
-
.object({
|
|
68
|
-
days: z.array(memoryDashboardDaySchema).length(90),
|
|
69
|
-
extractionDays: z.array(memoryCostDaySchema).length(90),
|
|
70
|
-
extractionHours: z.array(memoryCostDaySchema).min(24).optional(),
|
|
71
|
-
generatedAt: z.iso.datetime(),
|
|
72
|
-
hours: z.array(memoryDashboardDaySchema).min(24).optional(),
|
|
73
|
-
recallDays: z.array(memoryCostDaySchema).length(90),
|
|
74
|
-
recallHours: z.array(memoryCostDaySchema).min(24).optional(),
|
|
75
|
-
stats: z
|
|
76
|
-
.object({
|
|
77
|
-
active: z.number().int().min(0),
|
|
78
|
-
automatic: z.number().int().min(0),
|
|
79
|
-
createdThirtyDays: z.number().int().min(0),
|
|
80
|
-
embedded: z.number().int().min(0),
|
|
81
|
-
explicit: z.number().int().min(0),
|
|
82
|
-
knowledge: z.number().int().min(0),
|
|
83
|
-
personal: z.number().int().min(0),
|
|
84
|
-
preference: z.number().int().min(0),
|
|
85
|
-
procedure: z.number().int().min(0),
|
|
86
|
-
public: z.number().int().min(0),
|
|
87
|
-
})
|
|
88
|
-
.strict(),
|
|
89
|
-
})
|
|
90
|
-
.strict();
|
|
91
|
-
|
|
92
|
-
export type MemoryApi = z.output<typeof memoryApiSchema>;
|
|
93
|
-
export type MemoryDashboardResponse = z.output<
|
|
94
|
-
typeof memoryDashboardResponseSchema
|
|
95
|
-
>;
|
|
96
|
-
export type MemoryListResponse = z.output<typeof memoryListResponseSchema>;
|
|
97
|
-
|
|
98
|
-
const memoryListQuerySchema = z
|
|
99
|
-
.object({
|
|
100
|
-
cursor: z.string().min(1).max(1_000).optional(),
|
|
101
|
-
limit: z.coerce.number().int().min(1).max(50).default(25),
|
|
102
|
-
q: z.string().trim().max(200).optional(),
|
|
103
|
-
})
|
|
104
|
-
.strict();
|
|
105
|
-
|
|
106
|
-
interface MemoryApiOptions {
|
|
107
|
-
db: MemoryDb;
|
|
108
|
-
eventStats: PluginConversationEventStats;
|
|
109
|
-
users: {
|
|
110
|
-
resolve(email: string): Promise<User | undefined>;
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function json(body: unknown, status = 200): Response {
|
|
115
|
-
return Response.json(body, {
|
|
116
|
-
headers: { "cache-control": "no-store" },
|
|
117
|
-
status,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function apiMemory(memory: MemoryView): z.output<typeof memoryApiSchema> {
|
|
122
|
-
return {
|
|
123
|
-
content: memory.content,
|
|
124
|
-
createdAt: new Date(memory.createdAtMs).toISOString(),
|
|
125
|
-
...(memory.expiresAtMs !== undefined
|
|
126
|
-
? { expiresAt: new Date(memory.expiresAtMs).toISOString() }
|
|
127
|
-
: undefined),
|
|
128
|
-
id: memory.id,
|
|
129
|
-
kind: memory.kind,
|
|
130
|
-
observedAt: new Date(memory.observedAtMs).toISOString(),
|
|
131
|
-
origin: memory.origin,
|
|
132
|
-
sourcePlatform: memory.sourcePlatform,
|
|
133
|
-
visibility: memory.visibility,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function viewerEmail(context: unknown): string | undefined {
|
|
138
|
-
const parsed = pluginApiRouteRequestContextSchema.safeParse(context);
|
|
139
|
-
if (!parsed.success || parsed.data.auth.user.emailVerified !== true) {
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
return parsed.data.auth.user.email?.trim().toLowerCase() || undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Create the authenticated memory REST app. */
|
|
146
|
-
export function createMemoryApi(options: MemoryApiOptions): PluginRouteApp {
|
|
147
|
-
return {
|
|
148
|
-
async fetch(request, context) {
|
|
149
|
-
const email = viewerEmail(context);
|
|
150
|
-
if (!email) {
|
|
151
|
-
return json({ error: "Authentication required." }, 401);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const url = new URL(request.url);
|
|
155
|
-
const memoryPath = /^\/memories\/([^/]+)$/.exec(url.pathname);
|
|
156
|
-
const isCollection = url.pathname === "/memories";
|
|
157
|
-
const isDashboard = url.pathname === "/dashboard";
|
|
158
|
-
if (!isCollection && !isDashboard && !memoryPath) {
|
|
159
|
-
return json({ error: "Not found." }, 404);
|
|
160
|
-
}
|
|
161
|
-
const isRead = request.method === "GET" || request.method === "HEAD";
|
|
162
|
-
if (!isRead && !(memoryPath && request.method === "DELETE")) {
|
|
163
|
-
return json({ error: "Method not allowed." }, 405);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const viewer = await options.users.resolve(email);
|
|
167
|
-
if (!viewer) return json({ error: "Authentication required." }, 401);
|
|
168
|
-
const userId = viewer.id;
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
if (isDashboard && isRead) {
|
|
172
|
-
const [
|
|
173
|
-
stats,
|
|
174
|
-
days,
|
|
175
|
-
hours,
|
|
176
|
-
extractionDays,
|
|
177
|
-
extractionHours,
|
|
178
|
-
recallDays,
|
|
179
|
-
recallHours,
|
|
180
|
-
] = await Promise.all([
|
|
181
|
-
getMemoryStats(options.db, userId),
|
|
182
|
-
getMemoryTimeline(options.db, userId, 90),
|
|
183
|
-
getMemoryTimelineHours(options.db, userId, 7 * 24),
|
|
184
|
-
options.eventStats.costsByDay({
|
|
185
|
-
days: 90,
|
|
186
|
-
eventName: "memories_captured",
|
|
187
|
-
}),
|
|
188
|
-
options.eventStats.costsByHour({
|
|
189
|
-
eventName: "memories_captured",
|
|
190
|
-
hours: 7 * 24,
|
|
191
|
-
}),
|
|
192
|
-
options.eventStats.costsByDay({
|
|
193
|
-
days: 90,
|
|
194
|
-
eventName: "memories_recalled",
|
|
195
|
-
}),
|
|
196
|
-
options.eventStats.costsByHour({
|
|
197
|
-
eventName: "memories_recalled",
|
|
198
|
-
hours: 7 * 24,
|
|
199
|
-
}),
|
|
200
|
-
]);
|
|
201
|
-
const { private: personal, ...dashboardStats } = stats;
|
|
202
|
-
const body = memoryDashboardResponseSchema.parse({
|
|
203
|
-
days: days.map(({ private: personal, ...day }) => ({
|
|
204
|
-
...day,
|
|
205
|
-
personal,
|
|
206
|
-
})),
|
|
207
|
-
extractionDays,
|
|
208
|
-
extractionHours,
|
|
209
|
-
generatedAt: new Date().toISOString(),
|
|
210
|
-
hours: hours.map(({ private: personal, ...day }) => ({
|
|
211
|
-
...day,
|
|
212
|
-
personal,
|
|
213
|
-
})),
|
|
214
|
-
recallDays,
|
|
215
|
-
recallHours,
|
|
216
|
-
stats: { ...dashboardStats, personal },
|
|
217
|
-
});
|
|
218
|
-
return request.method === "HEAD"
|
|
219
|
-
? new Response(null, {
|
|
220
|
-
headers: { "cache-control": "no-store" },
|
|
221
|
-
status: 200,
|
|
222
|
-
})
|
|
223
|
-
: json(body);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (isCollection && isRead) {
|
|
227
|
-
const query = memoryListQuerySchema.parse({
|
|
228
|
-
cursor: url.searchParams.get("cursor") ?? undefined,
|
|
229
|
-
limit: url.searchParams.get("limit") ?? undefined,
|
|
230
|
-
q: url.searchParams.get("q") ?? undefined,
|
|
231
|
-
});
|
|
232
|
-
const page = await listMemories(options.db, userId, {
|
|
233
|
-
cursor: query.cursor,
|
|
234
|
-
limit: query.limit,
|
|
235
|
-
...(query.q ? { query: query.q } : undefined),
|
|
236
|
-
});
|
|
237
|
-
const body = memoryListResponseSchema.parse({
|
|
238
|
-
memories: page.memories.map(apiMemory),
|
|
239
|
-
...(page.nextCursor ? { nextCursor: page.nextCursor } : undefined),
|
|
240
|
-
});
|
|
241
|
-
return request.method === "HEAD"
|
|
242
|
-
? new Response(null, {
|
|
243
|
-
headers: { "cache-control": "no-store" },
|
|
244
|
-
status: 200,
|
|
245
|
-
})
|
|
246
|
-
: json(body);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (memoryPath && isRead) {
|
|
250
|
-
const memory = await getMemory(
|
|
251
|
-
options.db,
|
|
252
|
-
userId,
|
|
253
|
-
decodeURIComponent(memoryPath[1]!),
|
|
254
|
-
);
|
|
255
|
-
const body = memoryApiSchema.parse(apiMemory(memory));
|
|
256
|
-
return request.method === "HEAD"
|
|
257
|
-
? new Response(null, {
|
|
258
|
-
headers: { "cache-control": "no-store" },
|
|
259
|
-
status: 200,
|
|
260
|
-
})
|
|
261
|
-
: json(body);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (memoryPath && request.method === "DELETE") {
|
|
265
|
-
const id = decodeURIComponent(memoryPath[1]!);
|
|
266
|
-
await archiveMemory(options.db, userId, id);
|
|
267
|
-
return new Response(null, {
|
|
268
|
-
headers: { "cache-control": "no-store" },
|
|
269
|
-
status: 204,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
} catch (error) {
|
|
273
|
-
if (
|
|
274
|
-
error instanceof z.ZodError ||
|
|
275
|
-
error instanceof InvalidMemoryCursorError
|
|
276
|
-
) {
|
|
277
|
-
return json({ error: "Invalid memory request." }, 400);
|
|
278
|
-
}
|
|
279
|
-
if (error instanceof MemoryNotFoundError) {
|
|
280
|
-
return json({ error: error.message }, 404);
|
|
281
|
-
}
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return json({ error: "Method not allowed." }, 405);
|
|
286
|
-
},
|
|
287
|
-
};
|
|
288
|
-
}
|
package/src/cli/format.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { juniorMemoryMemories } from "../db/schema";
|
|
2
|
-
|
|
3
|
-
function formatDate(ms: number | null): string {
|
|
4
|
-
return ms === null ? "-" : new Date(ms).toISOString();
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/** Format a memory row as an operator-safe CLI projection. */
|
|
8
|
-
export function formatMemory(
|
|
9
|
-
row: typeof juniorMemoryMemories.$inferSelect,
|
|
10
|
-
args: {
|
|
11
|
-
showContent: boolean;
|
|
12
|
-
},
|
|
13
|
-
): string {
|
|
14
|
-
const lines = [
|
|
15
|
-
`id=${row.id}`,
|
|
16
|
-
`scope=${row.scope}`,
|
|
17
|
-
`scope_key=${row.scopeKey}`,
|
|
18
|
-
`subject_type=${row.subjectType}`,
|
|
19
|
-
...(row.subjectKey ? [`subject_key=${row.subjectKey}`] : []),
|
|
20
|
-
`kind=${row.kind}`,
|
|
21
|
-
`created_at=${formatDate(row.createdAtMs)}`,
|
|
22
|
-
`observed_at=${formatDate(row.observedAtMs)}`,
|
|
23
|
-
`expires_at=${formatDate(row.expiresAtMs)}`,
|
|
24
|
-
`archived_at=${formatDate(row.archivedAtMs)}`,
|
|
25
|
-
];
|
|
26
|
-
if (args.showContent) {
|
|
27
|
-
lines.push(`content=${row.content}`);
|
|
28
|
-
}
|
|
29
|
-
return lines.join("\n");
|
|
30
|
-
}
|
package/src/cli/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { PluginCliCommandDefinition } from "@sentry/junior-plugin-api";
|
|
2
|
-
import { configureMemorySearchCommand } from "./search";
|
|
3
|
-
import { configureMemoryShowCommand } from "./show";
|
|
4
|
-
|
|
5
|
-
/** Create the plugin-owned memory admin CLI command. */
|
|
6
|
-
export function createMemoryCliCommand(): PluginCliCommandDefinition {
|
|
7
|
-
return {
|
|
8
|
-
name: "memory",
|
|
9
|
-
summary: "Inspect Junior memory state",
|
|
10
|
-
configure(command, junior) {
|
|
11
|
-
configureMemorySearchCommand(command, junior);
|
|
12
|
-
configureMemoryShowCommand(command, junior);
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
}
|
package/src/cli/search.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { InvalidArgumentError, Option, type Command } from "commander";
|
|
2
|
-
import { and, desc, eq, gt, ilike, isNull, or, type SQL } from "drizzle-orm";
|
|
3
|
-
import type {
|
|
4
|
-
PluginCliActionContext,
|
|
5
|
-
PluginCliHost,
|
|
6
|
-
} from "@sentry/junior-plugin-api";
|
|
7
|
-
import { juniorMemoryMemories } from "../db/schema";
|
|
8
|
-
import type { MemoryDb } from "../store";
|
|
9
|
-
import { MEMORY_SCOPES, type MemoryScope } from "../types";
|
|
10
|
-
import { formatMemory } from "./format";
|
|
11
|
-
|
|
12
|
-
interface SearchOptions {
|
|
13
|
-
limit: number;
|
|
14
|
-
scope: MemoryScope;
|
|
15
|
-
scopeKey: string;
|
|
16
|
-
showContent?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function parseLimit(value: string): number {
|
|
20
|
-
const parsed = Number(value);
|
|
21
|
-
if (!Number.isFinite(parsed)) {
|
|
22
|
-
throw new InvalidArgumentError("--limit must be a number");
|
|
23
|
-
}
|
|
24
|
-
return Math.min(100, Math.max(1, Math.floor(parsed)));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function runSearch(
|
|
28
|
-
ctx: PluginCliActionContext,
|
|
29
|
-
queryParts: string[] | undefined,
|
|
30
|
-
options: SearchOptions,
|
|
31
|
-
): Promise<number> {
|
|
32
|
-
const query = (queryParts ?? []).join(" ").trim();
|
|
33
|
-
const nowMs = Date.now();
|
|
34
|
-
const terms = [
|
|
35
|
-
...new Set(
|
|
36
|
-
query
|
|
37
|
-
.toLowerCase()
|
|
38
|
-
.split(/[^a-z0-9_'-]+/)
|
|
39
|
-
.map((term) => term.trim())
|
|
40
|
-
.filter((term) => term.length >= 2),
|
|
41
|
-
),
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
const db = ctx.db as MemoryDb;
|
|
45
|
-
const activeExpirationPredicate = or(
|
|
46
|
-
isNull(juniorMemoryMemories.expiresAtMs),
|
|
47
|
-
gt(juniorMemoryMemories.expiresAtMs, nowMs),
|
|
48
|
-
);
|
|
49
|
-
const predicates: SQL[] = [
|
|
50
|
-
eq(juniorMemoryMemories.scope, options.scope),
|
|
51
|
-
eq(juniorMemoryMemories.scopeKey, options.scopeKey),
|
|
52
|
-
isNull(juniorMemoryMemories.archivedAtMs),
|
|
53
|
-
isNull(juniorMemoryMemories.supersededAtMs),
|
|
54
|
-
isNull(juniorMemoryMemories.supersededById),
|
|
55
|
-
];
|
|
56
|
-
if (activeExpirationPredicate) {
|
|
57
|
-
predicates.push(activeExpirationPredicate);
|
|
58
|
-
}
|
|
59
|
-
if (terms.length > 0) {
|
|
60
|
-
const termPredicate = or(
|
|
61
|
-
...terms.map((term) => ilike(juniorMemoryMemories.content, `%${term}%`)),
|
|
62
|
-
);
|
|
63
|
-
if (termPredicate) {
|
|
64
|
-
predicates.push(termPredicate);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const rows = await db
|
|
68
|
-
.select()
|
|
69
|
-
.from(juniorMemoryMemories)
|
|
70
|
-
.where(and(...predicates))
|
|
71
|
-
.orderBy(desc(juniorMemoryMemories.createdAtMs))
|
|
72
|
-
.limit(options.limit);
|
|
73
|
-
|
|
74
|
-
if (rows.length === 0) {
|
|
75
|
-
await ctx.io.writeOutput("No memories matched.\n");
|
|
76
|
-
return 0;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await ctx.io.writeOutput(
|
|
80
|
-
`${rows
|
|
81
|
-
.map((row) =>
|
|
82
|
-
formatMemory(row, { showContent: Boolean(options.showContent) }),
|
|
83
|
-
)
|
|
84
|
-
.join("\n\n")}\n`,
|
|
85
|
-
);
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Wire the memory search admin subcommand under the plugin namespace. */
|
|
90
|
-
export function configureMemorySearchCommand(
|
|
91
|
-
parent: Command,
|
|
92
|
-
junior: PluginCliHost,
|
|
93
|
-
): void {
|
|
94
|
-
parent
|
|
95
|
-
.command("search")
|
|
96
|
-
.description("Search visible memories")
|
|
97
|
-
.argument("[query...]", "Search query")
|
|
98
|
-
.addOption(
|
|
99
|
-
new Option("--scope <scope>", "Memory scope")
|
|
100
|
-
.choices([...MEMORY_SCOPES])
|
|
101
|
-
.makeOptionMandatory(),
|
|
102
|
-
)
|
|
103
|
-
.requiredOption("--scope-key <key>", "Scope key")
|
|
104
|
-
.addOption(
|
|
105
|
-
new Option("--limit <n>", "Maximum rows")
|
|
106
|
-
.argParser(parseLimit)
|
|
107
|
-
.default(20),
|
|
108
|
-
)
|
|
109
|
-
.option("--show-content", "Print raw memory content")
|
|
110
|
-
.action(
|
|
111
|
-
junior.action(async (ctx, queryParts, options) => {
|
|
112
|
-
return await runSearch(
|
|
113
|
-
ctx,
|
|
114
|
-
queryParts as string[] | undefined,
|
|
115
|
-
options as SearchOptions,
|
|
116
|
-
);
|
|
117
|
-
}),
|
|
118
|
-
);
|
|
119
|
-
}
|
package/src/cli/show.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
2
|
-
import type {
|
|
3
|
-
PluginCliActionContext,
|
|
4
|
-
PluginCliHost,
|
|
5
|
-
} from "@sentry/junior-plugin-api";
|
|
6
|
-
import { eq } from "drizzle-orm";
|
|
7
|
-
import { juniorMemoryMemories } from "../db/schema";
|
|
8
|
-
import type { MemoryDb } from "../store";
|
|
9
|
-
import { formatMemory } from "./format";
|
|
10
|
-
|
|
11
|
-
async function runShow(
|
|
12
|
-
ctx: PluginCliActionContext,
|
|
13
|
-
id: string,
|
|
14
|
-
): Promise<number> {
|
|
15
|
-
const db = ctx.db as MemoryDb;
|
|
16
|
-
const rows = await db
|
|
17
|
-
.select()
|
|
18
|
-
.from(juniorMemoryMemories)
|
|
19
|
-
.where(eq(juniorMemoryMemories.id, id))
|
|
20
|
-
.limit(1);
|
|
21
|
-
if (!rows[0]) {
|
|
22
|
-
await ctx.io.writeError(`Memory not found: ${id}\n`);
|
|
23
|
-
return 1;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
await ctx.io.writeOutput(`${formatMemory(rows[0], { showContent: true })}\n`);
|
|
27
|
-
return 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Wire the explicit raw-content memory inspection subcommand. */
|
|
31
|
-
export function configureMemoryShowCommand(
|
|
32
|
-
parent: Command,
|
|
33
|
-
junior: PluginCliHost,
|
|
34
|
-
): void {
|
|
35
|
-
parent
|
|
36
|
-
.command("show")
|
|
37
|
-
.description("Show one memory")
|
|
38
|
-
.argument("<id>", "Memory id")
|
|
39
|
-
.action(
|
|
40
|
-
junior.action(async (ctx, id) => {
|
|
41
|
-
return await runShow(ctx, id as string);
|
|
42
|
-
}),
|
|
43
|
-
);
|
|
44
|
-
}
|
package/src/db/schema.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drizzle source of truth for memory plugin SQL migrations.
|
|
3
|
-
*
|
|
4
|
-
* Update this schema first, then regenerate packaged migrations with
|
|
5
|
-
* `pnpm --filter @sentry/junior-memory db:generate`.
|
|
6
|
-
*/
|
|
7
|
-
import { sql } from "drizzle-orm";
|
|
8
|
-
import {
|
|
9
|
-
bigint,
|
|
10
|
-
check,
|
|
11
|
-
customType,
|
|
12
|
-
index,
|
|
13
|
-
integer,
|
|
14
|
-
pgTable,
|
|
15
|
-
text,
|
|
16
|
-
uniqueIndex,
|
|
17
|
-
vector,
|
|
18
|
-
} from "drizzle-orm/pg-core";
|
|
19
|
-
import {
|
|
20
|
-
MEMORY_EMBEDDING_DIMENSIONS,
|
|
21
|
-
MEMORY_EMBEDDING_METRICS,
|
|
22
|
-
MEMORY_SCOPES,
|
|
23
|
-
MEMORY_SOURCE_PLATFORMS,
|
|
24
|
-
MEMORY_SUBJECT_TYPES,
|
|
25
|
-
MEMORY_KINDS,
|
|
26
|
-
} from "../types";
|
|
27
|
-
|
|
28
|
-
const tsvector = customType<{ data: string }>({
|
|
29
|
-
dataType() {
|
|
30
|
-
return "tsvector";
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
export const juniorMemoryMemories = pgTable(
|
|
35
|
-
"junior_memory_memories",
|
|
36
|
-
{
|
|
37
|
-
id: text("id").primaryKey(),
|
|
38
|
-
scope: text("scope", { enum: MEMORY_SCOPES }).notNull(),
|
|
39
|
-
scopeKey: text("scope_key").notNull(),
|
|
40
|
-
kind: text("type", { enum: MEMORY_KINDS }).notNull(),
|
|
41
|
-
subjectType: text("subject_type", { enum: MEMORY_SUBJECT_TYPES }).notNull(),
|
|
42
|
-
subjectKey: text("subject_key"),
|
|
43
|
-
content: text("content").notNull(),
|
|
44
|
-
searchVector: tsvector("search_vector").generatedAlwaysAs(
|
|
45
|
-
sql`to_tsvector('english', "content")`,
|
|
46
|
-
),
|
|
47
|
-
sourcePlatform: text("source_platform", {
|
|
48
|
-
enum: MEMORY_SOURCE_PLATFORMS,
|
|
49
|
-
}).notNull(),
|
|
50
|
-
sourceKey: text("source_key").notNull(),
|
|
51
|
-
/** Location where Junior learned the memory, when known. */
|
|
52
|
-
locationId: text("location_id"),
|
|
53
|
-
idempotencyKey: text("idempotency_key"),
|
|
54
|
-
observedAtMs: bigint("observed_at_ms", { mode: "number" }).notNull(),
|
|
55
|
-
createdAtMs: bigint("created_at_ms", { mode: "number" }).notNull(),
|
|
56
|
-
expiresAtMs: bigint("expires_at_ms", { mode: "number" }),
|
|
57
|
-
supersededAtMs: bigint("superseded_at_ms", { mode: "number" }),
|
|
58
|
-
supersededById: text("superseded_by_id"),
|
|
59
|
-
archivedAtMs: bigint("archived_at_ms", { mode: "number" }),
|
|
60
|
-
archiveReason: text("archive_reason"),
|
|
61
|
-
},
|
|
62
|
-
(table) => [
|
|
63
|
-
index("junior_memory_memories_visible_idx")
|
|
64
|
-
.on(table.scope, table.scopeKey, table.createdAtMs.desc(), table.id)
|
|
65
|
-
.where(
|
|
66
|
-
sql`${table.archivedAtMs} IS NULL AND ${table.supersededAtMs} IS NULL AND ${table.supersededById} IS NULL`,
|
|
67
|
-
),
|
|
68
|
-
index("junior_memory_memories_expiration_idx")
|
|
69
|
-
.on(table.expiresAtMs)
|
|
70
|
-
.where(
|
|
71
|
-
sql`${table.archivedAtMs} IS NULL AND ${table.expiresAtMs} IS NOT NULL`,
|
|
72
|
-
),
|
|
73
|
-
index("junior_memory_memories_search_idx")
|
|
74
|
-
.using("gin", table.scope, table.scopeKey, table.searchVector)
|
|
75
|
-
.where(
|
|
76
|
-
sql`${table.archivedAtMs} IS NULL AND ${table.supersededAtMs} IS NULL AND ${table.supersededById} IS NULL`,
|
|
77
|
-
),
|
|
78
|
-
uniqueIndex("junior_memory_memories_idempotency_idx")
|
|
79
|
-
.on(table.scope, table.scopeKey, table.idempotencyKey)
|
|
80
|
-
.where(
|
|
81
|
-
sql`${table.idempotencyKey} IS NOT NULL AND ${table.archivedAtMs} IS NULL AND ${table.supersededAtMs} IS NULL AND ${table.supersededById} IS NULL`,
|
|
82
|
-
),
|
|
83
|
-
check(
|
|
84
|
-
"junior_memory_memories_scope_check",
|
|
85
|
-
sql`${table.scope} IN ('private', 'public')`,
|
|
86
|
-
),
|
|
87
|
-
check(
|
|
88
|
-
"junior_memory_memories_kind_check",
|
|
89
|
-
sql`${table.kind} IN (
|
|
90
|
-
'preference',
|
|
91
|
-
'procedure',
|
|
92
|
-
'knowledge'
|
|
93
|
-
)`,
|
|
94
|
-
),
|
|
95
|
-
check(
|
|
96
|
-
"junior_memory_memories_subject_type_check",
|
|
97
|
-
sql`${table.subjectType} IN ('user', 'conversation', 'general')`,
|
|
98
|
-
),
|
|
99
|
-
check(
|
|
100
|
-
"junior_memory_memories_subject_key_check",
|
|
101
|
-
sql`(${table.subjectType} = 'general' AND ${table.subjectKey} IS NULL) OR (${table.subjectType} IN ('user', 'conversation') AND ${table.subjectKey} IS NOT NULL AND length(${table.subjectKey}) > 0)`,
|
|
102
|
-
),
|
|
103
|
-
check(
|
|
104
|
-
"junior_memory_memories_source_platform_check",
|
|
105
|
-
sql`${table.sourcePlatform} IN ('slack', 'local', 'web')`,
|
|
106
|
-
),
|
|
107
|
-
],
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
export const juniorMemoryEmbeddings = pgTable(
|
|
111
|
-
"junior_memory_embeddings",
|
|
112
|
-
{
|
|
113
|
-
memoryId: text("memory_id")
|
|
114
|
-
.primaryKey()
|
|
115
|
-
.references(() => juniorMemoryMemories.id, { onDelete: "cascade" }),
|
|
116
|
-
provider: text("provider").notNull(),
|
|
117
|
-
model: text("model").notNull(),
|
|
118
|
-
dimensions: integer("dimensions").notNull(),
|
|
119
|
-
metric: text("metric", { enum: MEMORY_EMBEDDING_METRICS }).notNull(),
|
|
120
|
-
contentHash: text("content_hash").notNull(),
|
|
121
|
-
embedding: vector("embedding", {
|
|
122
|
-
dimensions: MEMORY_EMBEDDING_DIMENSIONS,
|
|
123
|
-
}).notNull(),
|
|
124
|
-
createdAtMs: bigint("created_at_ms", { mode: "number" }).notNull(),
|
|
125
|
-
},
|
|
126
|
-
(table) => [
|
|
127
|
-
index("junior_memory_embeddings_model_idx").on(
|
|
128
|
-
table.provider,
|
|
129
|
-
table.model,
|
|
130
|
-
table.dimensions,
|
|
131
|
-
table.metric,
|
|
132
|
-
),
|
|
133
|
-
// Cosine ANN for vector recall/search. Ops must match cosineDistance (<=>).
|
|
134
|
-
// Keep this unfiltered so planners can use HNSW before scope/status joins.
|
|
135
|
-
index("junior_memory_embeddings_embedding_hnsw_idx")
|
|
136
|
-
.using("hnsw", table.embedding.op("vector_cosine_ops"))
|
|
137
|
-
.with({ m: 16, ef_construction: 64 }),
|
|
138
|
-
check(
|
|
139
|
-
"junior_memory_embeddings_metric_check",
|
|
140
|
-
sql`${table.metric} IN ('cosine')`,
|
|
141
|
-
),
|
|
142
|
-
check(
|
|
143
|
-
"junior_memory_embeddings_dimensions_check",
|
|
144
|
-
sql`${table.dimensions} = ${sql.raw(String(MEMORY_EMBEDDING_DIMENSIONS))}`,
|
|
145
|
-
),
|
|
146
|
-
],
|
|
147
|
-
);
|